Automate your OKCupid Activity. This is an API Wrapper for OkCupid App, allowing you to automate processes and collect data for further analysis
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.
custom_modules Merge branch 'master' into fix-messages-and-edit-profile Aug 14, 2018
examples/search fix search example Sep 16, 2018
lib Merge branch 'master' into fix-messages-and-edit-profile Aug 14, 2018
.gitignore fix search example Sep 16, 2018
LICENSE.TXT Tested and working Aug 29, 2014 Add document describing search pagination with example Jul 16, 2017
index.js Tested and working Aug 29, 2014
package-lock.json add search example Sep 2, 2018
package.json updates version for get matches endpoint Aug 13, 2018


Automate your OKCupid Activity. This is an API Wrapper for OkCupid App, allowing you to automate processes and collect data for further analysis.

We're on Slack!

Talk about the project on our slack:


npm install --save okcupidjs


All methods are asynchronous, and will return a standard (err, res, body) params to your callback. The body will already be json parsed into a json object for easy handling. The following demonstrates how to require and instantiate OKCupidjs.

var OKCupid = require('okcupidjs')

var okc = new OKCupid()

okc.login("username", "password", function(err, res, body) {

The library may be converted to a promise-style API by using a promise library like bluebird.js. This is an example using bluebird:

var OKCupid = require('okcupidjs');
var Promise = require('bluebird');

var okc = Promise.promisifyAll(new OKCupid());

okc.loginAsync("username", "password").then(function(done) {


Working endpoints as of 9/21/18

Title Method Works? Comment
Login .login(username, password, callback)
Get Quickmatch .getQuickmatch(callback)
Visit User .visitUser(username, callback) needs the okc_api=1 and returns nothing
Like .like(user_id, callback)
Unlike .unlike(user_id, callback)
Get User's Profile .getUserProfile(username, callback) Always returns html, newline if okc_api
Get User's Questions .getUserQuestions(username, low, callback) always returns html
Get Visitors .getVisitors(callback) get JSON OKC developer/recruiting message
Send Message .sendMessage(user_id, message_body, callback)
Get Recent Messages .getRecentMessages(callback) route changed
Search (Browse Matches) .search(options, callback)
Get Message Thread .getMessageThread(thread_id, callback) adjusted to use oauth routes
Edit Profile .editProfile(edit_category, options, callback) adjusted to use oauth routes
Get Likes .getLikes(options, callback)


.login(username, password, callback)

Call this first to authenticate before calling other methods.

Takes a username and password, the same ones you use to login on the okc website.

Upon success, response will automatically store a cookie for subsequent requests.

Get Quickmatch


This is the method you want to call first. It gets you a new user in the area. Json body will contain target_userid and username for you to call .rate, .visitUser, .getUserProfile.

Username is obtained via

Target_userid is obtained via body.tuid

Visit User

.visitUser(username, callback)

Takes a username. This visit method will emulate you actually visiting the user via browser.

Will register on the user's visitors list, so they will know you visited them.


.like(user_id, callback)

Takes a username. Will cause you to like the user associated with that user ID.


.unlike(user_id, callback)

Takes a user ID. Will cause you to unlike the user associated with that user ID.

Get User's Profile

.getUserProfile(username, callback)

Returns a json of the user profile. Contains all the information as you would see if you visited the user's profile via browser. This will not register your name under the user's "visitor" list.

Get User's Questions

.getUserQuestions(username, low, callback)

Returns a json of user question data, beginning with the "low" question.

The OkCupid API enforces pagination and won't return more than 10 questions per request, so to fetch all question data for a user, you need to make multiple calls and increment the "low" value. The index of a user's first answered question is 1. (Passing a value of 0 returns nothing.) For example:

okc.getUserQuestions(username, 1, cb) // First 10 questions
okc.getUserQuestions(username, 11, cb) // Next 10 questions
// Etc...

Total number of questions for a user is obtained via body.pagination.raw.total_num_results

Get Visitors


Returns a list of your "stalkers" who have visited you.

Send Message

.sendMessage(user_id, message_body, callback)

Send a message to a user.

Get Recent Messages


Body parameter contains metadata about the inbox and a collection of the first 30 message objects in the inbox.

The data structure with important members shown is:

	messages: [ ... ], // collection of inbox message objects
	num_threads: ..., // number of threads in the inbox
	numunread: ..., // number of undread threads
	pct_full: ..., // percentage full 

An inbox message object will contain metadata about the message and a snippet of the message body. The full body can be accessed by using getMessageThread. The following structure shows some of the important members in a message object:

	is_new: ..., // 0 or 1
	person: ..., // username of the other person
	personid: ..., // user id of the other person
	snippet: ..., // short preview of the message body
	thread_id: ..., // thread ID
	timestamp: ..., // time stamp
	status: ..., // string status of message, will be "repliedMessage" if message has been replied to

Get Message Thread

getMessageThread(thread_id, callback)

Get the message thread (all sent and received messages) with a thread ID. Body parameter contains metadata about the thread and a collection of message objects.

The data structure with important members shown is:

	buddyname: ..., // username of the other person 
	num_msg: ..., // number of messages in the thread
	messages: [ ... ], // collection of thread message objects

A thread message object contains metadata about the message and the complete message body.

The data structure with important members shown is:

	timestamp: ..., 
	receiver_name: ..., 
	sender_name: ..., 
	msgid: ..., // a message ID
	rawbody: ... // complete body of the message

Usage Note: The thread ID can be collected from an inbox message object. Get the most recent messages with getRecentMessages.

Search (Browse Matches)

.search(options, callback)

Perform a search as if visiting "Browse Matches" tab on the website. Search takes a options for determining filters, order, etc.

	// Primary filter
	"i_want": "women", // "men" {
	"they_want": "men", // "women" | "everyone"
	"minimum_age": 18,
	"maximum_age": 40,
	"radius": 25, // miles from location
	"last_login": 86400, // seconds since last login

	// Looks filter
	// Height: each inch is 254
	// e.g. 5'4  is 5 * 12 * 254  + 4 * 254 = 16256
	"maximum_height": null,
	"minimum_height": null, 

	// A-list features
	"minimum_attractiveness" : null, // 4000 | 6000 | 8000 | 10000
	"maximum_attractiveness" : null, // 4000 | 6000 | 8000 | 10000
	"bodytype": [], "thin" | "fit" | "average" | "jacked" | "overweight" | "a_little_extra" | "full_figured" | "curvy"
	// Background filter
	"languages": 0,
	"speaks_my_language": true,
	"ethnicity": [], "asian" | "black" | "native_american" | ...
	"religion": ["agnosticism", "atheism"], "buddhism" | "catholicism" | "christianity" | "sikh" | ...

	// Availabilty filter
	"availability": "single", // "not_single"
	"monogamy": "yes", // "no"
	"looking_for": ["short_term_dating"], // "new_friends" | "long_term_dating" | "casual_sex"

	// Personality filter
	"personality_filters": {
		"self_confidence" : ..., // "more" | "less"
		"compassion" : ..., // "more" | "less"
		"independence" : ..., // "more" | "less"
		"introversion" : ..., // "more" | "less"
		"adventuresome" : ..., // "more" | "less"
		"artsiness" : ..., // "more" | "less"
		"romantic" : ..., // "more" | "less"
		"sex_experience" : ..., // "more" | "less"
		"old_fashionedness" : ..., // "more" | "less"
		"trust_in_others" : ..., // "more" | "less"
		"purity" : ... // "more" | "less"

	// Vices filter
	"smoking": ["no", "sometimes"] // "when_drinking" | "trying_to_quit" | "yes"
	"drinking": ["socially", "rarely", "very_often"] // "not_at_all" | "often" | desperately"
	"drugs": ["sometimes"], // "never" | "often"
	// Questions filter	
	"questions": [403], // question id collection, 403 is "Do you enjoy discussing politics?" 
	"answers": [2], // 2 for "yes" to question 403
	// maps by index to answer in "answers" field

	// More filter
	"interest_ids": [], // interests id
	"education": ["post_grad"], // "two_year_college" | "college_university" | "high_school"
	"children": ["wants_kids", "doesnt_have"] // "might_want" | "doesnt_want" | "has_one_or_more" 
	"cats": ["has"], 
	"dogs": [], "has"

	// Additional metadata
	"limit": 18, // max number of results
	"fields": "userinfo,thumbs,percentages,likes,last_contacts,online" // additional data to be returned

The body returned (with no fields set) will have the following structure:

	"total_matches": ..., // some number 
	"data": [...], // results collection
	"paging" : {
		"cursors" : {
			"before" : ..., // before page hash
			"current" : ..., // current page hash
			"after" : ... // after page hash

The data collection contains all the search results. The objects take the structure:

	"inactive": false,
	"username": ..., // username 
	"userid": ...,  // user id
	"staff": false,
	"isAdmin": false

The results come back with a page hash. To get the next page results, this method can be called again with an additional after field set in the options object. The value should be set to the value stored in body.paging.after. This process can iterated to produce all search results.

The same process can also be applied in reverse with before.

Edit Profile

.editProfile(edit_category, options, callback)

Edit the desired category.

The categories available are the following:

  • essays

There are 10 essays that you can edit:

  • 0: My self-summary
  • 1: What I’m doing with my life
  • 2: I’m really good at
  • 3: The first things people usually notice about me
  • 4: Favorite books, movies, shows, music, and food
  • 5: Six things I could never do without
  • 6: I spend a lot of time thinking about
  • 7: On a typical Friday night I am
  • 8: The most private thing I’m willing to admit
  • 9: You should message me if

For example, if you want to edit the 4th essay, your query needs to be the following:

var query = {
	'essays': {
		'4': 'Matrix is my favorite movie'

Get Likes

.getLikes(options, callback)

Perform a request for Likes, as seen on the Likes page.

The options variable is used to construct the query string for the request, for example:

var options = {
	fields: "likes,thumbs.limit(1){225x225},location,userinfo,online,percentages,last_contacts"

It is possible to distinguish a "match" from a "like" by looking at the returned section property for each user.

The results of this request are paginated in the same way as the search method. To learn how to access data on additional pages, read

Code Samples

Here is a sample request to login, go to the quickmatch/double-take page, and subsequently LIKE the user.

var OKCupid = require('okcupidjs')
var okc = new OKCupid()

okc.login('okc_username', 'okc_password', function(err, res, body) {
    okc.getQuickmatch(function(err, res, body) {;

If you then go onto the okc web page, and go to Likes -> Who you like, you will see that you have liked a new person. I hope this helps.


Another use case is searching for users. The search API is rich and one may query on many different attributes. Perhaps you are interseted in a single, straight woman who is between the ages 31 and 37. This women has cats and never does drugs. They are also more adventurous and less artistic. This criteria can be made into a query object that can get passed to the search method on an okc object.

var OKCupid = require('okcupidjs')
var okc = new OKCupid()

okc.login('okc_username', 'okc_password', function(err, res, body) {
  var query = {
    "i_want": "women",
    "minimum_age": 31,
    "maximum_age": 37,
    "cats": ["has"], 
    "personality_filters": {
      "adventuresome" : "more",
      "artsiness" : "less"
    "drugs": ["never"]
  }, function(err, res, body) {
    if (err) {
      console.log("Failed to get search results.");
    else {
      // use the body object to extract the search results
      var results =;

      results.forEach(function(user) {
        console.log(user.username + '\n');

Search results are limited to about 30 results per request. To scrape more data from search, one has to sequentially create requests to retrieve search result pages. See the search pagination guide for details and an example.


Feel free to send suggestions, ask questinos, or report issues via the issues board. Pull requests and feature enhancements are very welcome.

Credits And Special Thanks

Special Thanks To

  • Mackenzie Clark (@xmclark) for major contributions in user messaging flow, user search, and bug fixes.
  • Joshua Beeler (@joshuabeeler) for contributions in getting user questions.
  • Pierrick Turelier (@PierrickGT) for contributions in editing user profile.
  • Tamara Jordan (@tamj0rd2) for get matches endpoint.

Copyright © 2014 Hung Tran For reference, I wrote a small piece on OKCupid, online dating, and data privacy here:

Released under the MIT License, which can be found in the repository in LICENSE.txt.