Endpoints // Search
People search
NewSearch the database for people with filters and get full profiles back with cursor pagination.
Introduction
The People Search endpoint returns full person profiles matching a combination of filters. All filters are optional and combined with AND logic. Results are drawn from our cache-based search index. This endpoint never triggers a live fetch.
Every result is a complete profile, the same shape the Profile endpoint returns, so you do not need a second call to get the full data of a hit. Use the free People search count endpoint to check how many profiles match before you spend credits.
Coming from
POST /v2/search/persons? See Migrating people search from persons to people.
Authorization
| Name | Type | Required | Description |
|---|---|---|---|
apikey | string | Yes | Your API key from the developer dashboard. Pass it via Authorization: Bearer YOUR_API_KEY header. |
Request body
All filters are optional and combined with AND logic. Scalar filters live in the table below; the nested-object filters (followersCount, currentCompanyEmployeeCountRange, totalExperienceMonths, location, experience) are documented as expandable field items right after, and pagination (cursor, perPage) closes the section.
| Name | Type | Required | Description |
|---|---|---|---|
firstName | string | No | Filter by first name |
lastName | string | No | Filter by last name |
headline | string | No | Match keywords found in the profile headline |
email | string | No | Filter by email address |
isOpenToWork | boolean | No | Restrict to profiles flagged open-to-work |
isCurrentlyEmployed | boolean | No | Restrict to profiles that have a current employer |
isPremium | boolean | No | Restrict to Social Premium members |
isVerified | boolean | No | Restrict to profiles with a verification badge |
currentPositionTitle | string | string | No | Current position title, or array of titles: a person matches if their current title matches any of them |
excludeCurrentPositionTitle | string | string | No | Current position title, or array of titles, to exclude from results |
positionTitle | string | string | No | Current or past position title, or array of titles (OR-matched) |
excludePositionTitle | string | string | No | Current or past position title, or array of titles, to exclude |
currentCompanyName | string | No | Match the current employer name |
excludeCurrentCompanyName | string | No | Exclude the current employer name |
companyName | string | string | No | Current or past company name, or array of names (OR-matched) |
excludeCompanyName | string | string | No | Current or past company name, or array of names, to exclude |
currentCompanyId | string | string | No | ReverseContact internal id (com_...) of the current employer |
excludeCurrentCompanyId | string | string | No | Current employer com_... id, or array of ids, to exclude |
companyId | string | string | No | Current or past company com_... id, or array of ids |
excludeCompanyId | string | string | No | Current or past company com_... id, or array of ids, to exclude |
currentCompanyLinkedinId | string | string | No | Current employer Social identifier, or array of identifiers |
excludeCurrentCompanyLinkedinId | string | string | No | Current employer Social identifier, or array, to exclude |
companyLinkedinId | string | string | No | Current or past company Social identifier, or array of identifiers |
excludeCompanyLinkedinId | string | string | No | Current or past company Social identifier, or array, to exclude |
currentCompanyPublicId | string | string | No | Current employer public ID or slug, or array |
excludeCurrentCompanyPublicId | string | string | No | Current employer public ID or slug, or array, to exclude |
companyPublicId | string | string | No | Current or past company public ID or slug, or array |
excludeCompanyPublicId | string | string | No | Current or past company public ID or slug, or array, to exclude |
currentCompanyIndustry | string | string | No | Current employer industry. See Industry values for the full list |
companyIndustry | string | string | No | Current or past company industry, from the same industry list |
excludeCompanyIndustry | string | string | No | Current or past company industry, or array, to exclude |
currentFunction | string | string | No | Current function from the position taxonomy |
excludeCurrentFunction | string | string | No | Current function, or array of functions, to exclude |
currentSubFunction | string | string | No | Current sub-function from the position taxonomy |
excludeCurrentSubFunction | string | string | No | Current sub-function, or array of sub-functions, to exclude |
currentSeniority | string | string | No | Current seniority from the position taxonomy |
excludeCurrentSeniority | string | string | No | Current seniority, or array of seniorities, to exclude |
skills | string | string | No | Skill name, or array of skill names: a person matches if they list any of them. See Skills |
languages | string | string | No | Language name in English, or array: a person matches if they speak any of them. See Languages |
excludeLanguages | string | string | No | Language name in English, or array, to exclude. Needs a positive filter next to it |
keywords | string | No | Words searched across the whole profile: every word must appear, in any field. See Keywords |
changedJobsWithinDays | number | No | 30, 60, 90 or 120: the current position started within that many days. See Recent job change |
maxDataAgeDate | string | No | ISO 8601 datetime. Only returns profiles refreshed after this date |
cursor | string | No | Opaque cursor returned as metadata.nextCursor; request the next page |
perPage | number | No | Profiles per page (1 to 100, default 25) |
Filter validation: filters are validated before the query runs. Unknown field names, blank values, values outside a closed list (
currentCompanyIndustry,currentFunction,currentSubFunction,currentSeniority,changedJobsWithinDays,location.countryCode),keywordswithout a single word and inconsistent ranges return HTTP422with the offending field, the reason and suggested values, and cost 0 credits. Unknown fields are rejected rather than ignored, because an ignored filter would return unfiltered results. Each list filter accepts at most 20 values. See Search filter validation.
Note: Title matching is token-based, not exact:
"CTO"also matches"Deputy CTO". Multiple titles are OR-matched, and exclusions always win over matches. ThepositionTitle,companyName,companyId,companyLinkedinId,companyPublicIdandcompanyIndustryfilters match current and past positions, while theircurrent...counterparts match the current position only.
followersCountobjectFilter by follower count range. Both bounds are optional and inclusive.
Show child attributesHide child attributes
| Field | Type | Description |
|---|---|---|
min | number | Minimum value, inclusive |
max | number | Maximum value, inclusive |
currentCompanyEmployeeCountRangeobjectFilter by the current company employee count range. Both bounds are optional and inclusive. Headcount is stored as fixed size brackets (1, 2-10, 11-50, 51-200, 201-500, 501-1000, 1001-5000, 5001-10000, 10001+), not as an exact number, and a range only matches a bracket it fully contains. Send bracket boundaries, otherwise the range contains no bracket, matches nothing and returns an empty data.data array.
Show child attributesHide child attributes
| Field | Type | Description |
|---|---|---|
min | number | Lower bound, inclusive. Must be a bracket start: 1, 2, 11, 51, 201, 501, 1001, 5001, 10001 |
max | number | Upper bound, inclusive. Must be a bracket end: 1, 10, 50, 200, 500, 1000, 5000, 10000. Omit it to include 10001+ |
Examples:
{ "min": 11, "max": 50 }matches the11-50bracket.{ "min": 11, "max": 200 }spans11-50and51-200.{ "min": 10001 }matches10001+.{ "min": 11, "max": 20 }returns an empty array, because no whole bracket fits between 11 and 20.
totalExperienceMonthsobjectFilter by total career length in months. Both bounds are optional and inclusive, from 0 to 1200.
Show child attributesHide child attributes
| Field | Type | Description |
|---|---|---|
min | number | Minimum value, inclusive |
max | number | Maximum value, inclusive |
locationobjectMulti-field location filter. Each sub-field is optional and accepts a single string or an array of strings (OR matching), with a maximum of 20 values per sub-field (for example 20 region values). Beyond that, the request returns HTTP 422 with FILTER_VALUES_LIMIT_EXCEEDED and costs 0 credits: split it into several searches.
Show child attributesHide child attributes
| Field | Type | Description |
|---|---|---|
country | string | string | Country name (e.g. "France") |
countryCode | string | string | ISO 3166 country code (e.g. "FR") |
region | string | string | State or region |
city | string | string | City |
area | string | string | Broader geographic area |
experienceobject[]Required past experience. Each entry demands a position taxonomy value, with an optional minimum tenure. Entries are AND-combined, up to 10 per request.
Show child attributesHide child attributes
| Field | Type | Description |
|---|---|---|
dimension | string | Taxonomy dimension: function, subFunction or seniority |
value | string | Value from the selected dimension of the position taxonomy |
minMonths | number | Optional minimum tenure in that dimension, in months (0 to 720) |
Position taxonomy
currentFunction, currentSubFunction, currentSeniority and the experience filter match a closed taxonomy of professional functions. A value must come from the taxonomy: case and surrounding spaces are ignored, and any other value is rejected with HTTP 422 and the closest valid values, never silently ignored.
There are 32 functions, 386 sub-functions and 10 seniorities. The functions and seniorities are listed below; the full sub-function list is long, so read it from the available fields endpoint, which returns every accepted value for each filter, plus the function each sub-function belongs to.
Functions (32)
- Administrative
- Agriculture and Environment
- Construction and Trades
- Consulting and Advisory
- Customer Service
- Design
- Education
- Energy and Utilities
- Entertainment and Gaming
- Executive and Leadership
- Finance
- Hospitality and Tourism
- Human Resources
- Legal
- Marketing
- Media and Communications
- Medical and Health
- Nonprofit and Government
- Not Employed
- Operations
- Other
- Personal and Home Services
- Product
- Project and Program Management
- Public Safety and Security
- Real Estate
- Research and Science
- Retail and Consumer
- Sales
- Software
- Traditional Engineering
- Transportation and Logistics
Seniorities (10)
- CXO
- VP
- Director
- Head
- Manager
- Partner
- Owner / Founder
- Individual Contributor
- Intern
- Other
Skills, languages, keywords and job changes
These four filters read the whole profile rather than one position. They combine with every other filter, and each of them except excludeLanguages is a valid search on its own.
Skills
skills matches the skills listed on the profile, whatever position or school they come from. Matching ignores case and accents and compares whole skill names, never part of one: "Java" does not match a profile that only lists "JavaScript", and "Machine Learning" is matched as one value. A list is OR-matched: a person matches when they list at least one of the requested skills.
Languages
languages keeps people who speak at least one of the requested languages, and excludeLanguages removes people who speak any of the listed ones. Profiles that list no language are kept by excludeLanguages, which only refines a search: sent alone, it is rejected.
Send language names in English. Profiles store their languages in their own spelling (Français, Inglese, Deutsch), and the following names also match those spellings: Arabic, Bengali, Catalan, Chinese, Czech, Danish, Dutch, English, Finnish, French, German, Greek, Hebrew, Hindi, Hungarian, Indonesian, Italian, Japanese, Korean, Mandarin, Norwegian, Polish, Portuguese, Punjabi, Romanian, Russian, Spanish, Swedish, Tagalog, Thai, Turkish, Ukrainian, Urdu, Vietnamese. Any other value is accepted and matches its exact spelling only. Matching compares whole names: "French" does not match "French Creole". There is no proficiency level: any level matches.
Note: about one profile in six lists its languages, so a
languagesfilter leaves out every profile that lists none.
Keywords
keywords is one string searched across the whole profile: names, headline, summary, current and past job titles, companies, industries, skills, schools, degrees, certifications, languages and location. The value is split into words, case and accents are ignored, and every word must appear somewhere in the profile, in any field and in any order: "react paris" means react and paris. Words are matched whole: "react" does not match "reactive". Because the words may come from different fields, there is no exact-phrase match. A value with no word at all, such as "!!!", returns HTTP 422.
Recent job change
changedJobsWithinDays keeps people whose current position started within the last 30, 60, 90 or 120 days, boundary included, counted in UTC days. A profile with no current position, or whose current position has no start date, does not match, and a start date in the future never matches. Any other value returns HTTP 422 with the accepted ones.
Example
curl -X POST https://api.reversecontact.com/v2/search/people \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"currentFunction": "Software",
"skills": ["Python", "Machine Learning"],
"languages": "German",
"keywords": "fintech",
"changedJobsWithinDays": 90,
"location": { "countryCode": "DE" },
"perPage": 25
}'Tip: on the largest countries, a lone
skillsorlanguagesfilter has to scan a very large set of profiles and can be slow. Pair it with a title, a function or a company to keep the search fast.
Pagination and credits
People search uses cursor pagination. Each response returns up to perPage profiles (1 to 100, default 25) and a metadata.nextCursor value. Pass that value back as cursor in the next request to get the following page; nextCursor is null on the last page. The cursor is opaque: send it back unchanged, without parsing or altering it. There is no page parameter.
A nextCursor has no hard expiry. The result list behind it is cached for 10 minutes. If you use a cursor after that, the search is re-run with the same filters and you get the next page from the refreshed list. If the data changed in the meantime, that page can contain a profile you already received or skip one. For a consistent result set, fetch each page within 10 minutes of the previous one.
An invalid or expired cursor returns HTTP 400 with the message Invalid or expired cursor. This happens when a filter is changed between two pages (keep the request body identical and only add the cursor), or when the cursor is malformed (truncated or altered).
perPage above 100 returns HTTP 422 with error.code PAGINATION_LIMIT_EXCEEDED, and the error.details object identifies the field and maximum, for example { "field": "perPage", "max": 100 }.
People search costs 1 credit per profile delivered. Compliance-blocked profiles are removed before the response and are not billed. A page that returns 25 profiles costs 25 credits, and a page that returns no profile costs 0 credits. Check the size of a query first with the free People search count endpoint, and lower perPage to control how many credits a single request can spend.
Response structure
Note: This endpoint uses a nested envelope. The top-level V2
datafield wraps an inner search payload that contains the actual results array (data.data) and the pagination info (data.metadata). See the example response for the exact shape.
| Field | Type | Description |
|---|---|---|
success | boolean | true if the request was processed successfully |
error | null | Always null on success |
data.dataobject[]Full profiles returned for this page, wrapped under data.data in the envelope. Empty array if no match. Each item is the same full profile shape as the Profile endpoint; the table below lists its main fields.
Show child attributesHide child attributes
| Field | Type | Description |
|---|---|---|
id | string | ReverseContact internal person identifier (prs_...) |
publicId | string | Social public identifier (the /in/xxx slug) |
memberId | string | null | Upstream member identifier when available |
linkedinUrl | string | Full Social profile URL |
firstName | string | null | First name |
lastName | string | null | Last name |
headline | string | null | Profile headline |
summary | string | null | Profile summary |
isOpenToWork | boolean | Whether the profile is flagged open to work |
hasPremium | boolean | Whether the profile has Social Premium |
hasVerificationBadge | boolean | Whether the profile has a verification badge |
photoUrl | string | null | Profile photo URL |
creationDate | string | null | When the profile was created (ISO 8601) |
followersCount | number | null | Follower count |
connectionsCount | number | null | Connection count |
currentPosition | object | null | Current job position (how it is chosen) |
experience | array | Professional experiences, same shape as currentPosition |
education | array | Education history |
skills | string | Skills listed on the profile |
languages | array | Spoken languages |
certifications | array | Professional certifications |
recommendations | array | Received recommendations |
locationobjectGeographic location (null if unknown)
Show child attributesHide child attributes
| Field | Type | Description |
|---|---|---|
city | string | null | City |
state | string | null | State or region |
country | string | null | Country name |
countryCode | string | null | ISO country code (e.g. "US") |
rawLocation | string | null | Raw location string as displayed on the profile |
currentPositionobjectCurrent job position (null if no current position)
Show child attributesHide child attributes
| Field | Type | Description |
|---|---|---|
title | string | null | Job title |
description | string | null | Position description |
contractType | string | null | Contract type |
companyName | string | null | Company name |
companyLinkedinId | string | null | Company Social identifier |
companyUrl | string | null | Company Social URL |
companyLocation | string | null | Company location |
companyLogoUrl | string | null | Company logo URL |
startEndDate | object | null | Period boundaries (DateRange) |
data.metadataobjectPagination metadata wrapped under data.metadata in the envelope.
Show child attributesHide child attributes
| Field | Type | Description |
|---|---|---|
perPage | number | Profiles requested per page |
count | number | Profiles returned in this page. Use People search count for the total matching set |
nextCursor | string | null | Opaque cursor for the next page, null on the last page |
quotasobjectCredits and rate limit usage after this request
Show child attributesHide child attributes
| Field | Type | Description |
|---|---|---|
creditsConsumed | number | Credits consumed by this request |
workspaceobjectWorkspace-level limits
Show child attributesHide child attributes
| Field | Type | Description |
|---|---|---|
id | string | Your workspace identifier |
hasUnlimitedCredits | boolean | Whether workspace credits are unlimited |
allowDailyOvercost | boolean | Whether the workspace may exceed its daily cap |
creditsobjectCredit balance
Show child attributesHide child attributes
| Field | Type | Description |
|---|---|---|
total | number | Total credits in your plan |
used | number | Credits consumed so far |
left | number | Remaining credits |
dailyLimitobjectDaily request limit (null if not configured)
Show child attributesHide child attributes
| Field | Type | Description |
|---|---|---|
limit | number | Maximum requests per day |
used | number | Requests made today |
left | number | Remaining requests today |
nextReset | string | When the daily window resets (ISO 8601) |
minuteRateLimitobjectPer-minute rate limit
Show child attributesHide child attributes
| Field | Type | Description |
|---|---|---|
limit | number | Maximum requests per minute |
used | number | Requests made in the current minute |
left | number | Remaining requests in the current minute |
nextReset | string | When the minute window resets (ISO 8601) |
keyobjectAPI key-level limits (null when request is from dashboard)
Show child attributesHide child attributes
| Field | Type | Description |
|---|---|---|
id | string | API key identifier |
dailyLimitobjectDaily limit for this key (null if not configured)
Show child attributesHide child attributes
| Field | Type | Description |
|---|---|---|
limit | number | Maximum requests per day |
used | number | Requests made today |
left | number | Remaining requests today |
nextReset | string | When the daily window resets (ISO 8601) |
minuteRateLimitobjectMinute rate limit for this key
Show child attributesHide child attributes
| Field | Type | Description |
|---|---|---|
limit | number | Maximum requests per minute |
used | number | Requests made in the current minute |
left | number | Remaining requests in the current minute |
nextReset | string | When the minute window resets (ISO 8601) |
metadataobjectRequest tracking information (not to be confused with data.metadata which is pagination)
Show child attributesHide child attributes
| Field | Type | Description |
|---|---|---|
requestId | string | Unique identifier for this request |
executionTimeMs | number | Total execution time in milliseconds |
Code examples
Code Examples
curl -X POST https://api.reversecontact.com/v2/search/people \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"currentFunction": "Marketing",
"currentSeniority": ["Director", "VP"],
"excludeCurrentSubFunction": "Marketing Operations",
"currentCompanyIndustry": ["Software Development", "Technology, Information and Internet"],
"location": {
"country": "United States",
"city": ["San Francisco", "New York"]
},
"followersCount": { "min": 1000 },
"experience": [
{ "dimension": "function", "value": "Sales", "minMonths": 24 }
],
"perPage": 25
}'const searchPage = async (cursor) => {
const response = await fetch("https://api.reversecontact.com/v2/search/people", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({
currentFunction: "Marketing",
currentSeniority: ["Director", "VP"],
currentCompanyIndustry: ["Software Development", "Technology, Information and Internet"],
location: { country: "United States", city: ["San Francisco", "New York"] },
followersCount: { min: 1000 },
perPage: 25,
cursor
})
});
return response.json();
};
const firstPage = await searchPage();
const profiles = firstPage.data.data;
const { count, nextCursor } = firstPage.data.metadata;
console.log(`Got ${count} profiles, credits used: ${firstPage.quotas.creditsConsumed}`);
if (nextCursor) {
const secondPage = await searchPage(nextCursor);
profiles.push(...secondPage.data.data);
}import requests
response = requests.post(
"https://api.reversecontact.com/v2/search/people",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json"
},
json={
"currentFunction": "Marketing",
"currentSeniority": ["Director", "VP"],
"currentCompanyIndustry": ["Software Development", "Technology, Information and Internet"],
"location": {
"country": "United States",
"city": ["San Francisco", "New York"]
},
"followersCount": {"min": 1000},
"experience": [
{"dimension": "function", "value": "Sales", "minMonths": 24}
],
"perPage": 25
}
)
payload = response.json()
profiles = payload["data"]["data"]
metadata = payload["data"]["metadata"]
print(f"Got {metadata['count']} profiles, next cursor: {metadata['nextCursor']}")Example response
Example Response
{
"success": true,
"data": {
"data": [
{
"id": "prs_01h2x3y4z5a6b7c8d9e0f1g2h3",
"publicId": "janedoe",
"memberId": "ACoAAB12345",
"linkedinUrl": "https://www.social.com/in/janedoe",
"firstName": "Jane",
"lastName": "Doe",
"headline": "VP of Marketing at Acme Corp | Demand Gen & Brand",
"summary": "Experienced marketing leader with 15+ years driving growth for B2B SaaS companies.",
"pronoun": "she/her",
"isOpenToWork": false,
"hasPremium": true,
"hasVerificationBadge": true,
"isInRemembrance": false,
"photoUrl": "https://media.licdn.com/dms/image/v2/D5603AQ.../profile-photo.jpg",
"backgroundUrl": null,
"creationDate": "2009-03-15T00:00:00.000Z",
"followersCount": 12480,
"connectionsCount": 500,
"location": {
"city": "San Francisco",
"state": "California",
"country": "United States of America",
"countryCode": "US",
"rawLocation": "San Francisco Bay Area"
},
"currentPosition": {
"title": "VP of Marketing",
"description": "Leading a team of 25 marketers across demand gen, product marketing, and brand.",
"contractType": "Full-time",
"companyName": "Acme Corp",
"companyLinkedinId": "12345678",
"companyUrl": "https://www.social.com/company/acme-corp",
"companyLocation": "San Francisco, CA",
"companyLogoUrl": "https://media.licdn.com/dms/image/v2/C4D0BAQ.../logo.jpg",
"startEndDate": {
"start": "2021-06-01T00:00:00.000Z",
"end": null
}
},
"experience": [
{
"title": "VP of Marketing",
"description": "Leading a team of 25 marketers across demand gen, product marketing, and brand.",
"contractType": "Full-time",
"companyName": "Acme Corp",
"companyLinkedinId": "12345678",
"companyUrl": "https://www.social.com/company/acme-corp",
"companyLocation": "San Francisco, CA",
"companyLogoUrl": "https://media.licdn.com/dms/image/v2/C4D0BAQ.../logo.jpg",
"startEndDate": {
"start": "2021-06-01T00:00:00.000Z",
"end": null
}
},
{
"title": "Director of Marketing",
"description": "Built the marketing function from scratch. Scaled from 0 to $5M ARR.",
"contractType": "Full-time",
"companyName": "Startup Inc",
"companyLinkedinId": "98765432",
"companyUrl": "https://www.social.com/company/startup-inc",
"companyLocation": "Remote",
"companyLogoUrl": null,
"startEndDate": {
"start": "2018-01-01T00:00:00.000Z",
"end": "2021-05-31T00:00:00.000Z"
}
}
],
"education": [
{
"degreeName": "MBA",
"fieldOfStudy": "Marketing",
"description": null,
"grade": null,
"schoolName": "Stanford Graduate School of Business",
"schoolUrl": "https://www.social.com/school/stanford-gsb",
"schoolLogoUrl": "https://media.licdn.com/dms/image/v2/C4D0BAQ.../logo.jpg",
"startEndDate": {
"start": "2012-09-01T00:00:00.000Z",
"end": "2014-06-15T00:00:00.000Z"
}
}
],
"skills": ["Marketing Strategy", "B2B SaaS", "Demand Generation", "Product Marketing"],
"languages": [{ "language": "English", "proficiency": "Native or bilingual" }],
"certifications": [],
"recommendations": [],
"testScores": []
}
],
"metadata": {
"perPage": 25,
"count": 1,
"nextCursor": "cur_01j9x7q4m2z8b6d4f0a2c8e6"
}
},
"error": null,
"metadata": {
"requestId": "req_s1e2a3r4c5h6",
"executionTimeMs": 410
},
"quotas": {
"creditsConsumed": 1,
"workspace": {
"id": "ws_abc123",
"credits": {
"total": 10000,
"used": 4522,
"left": 5478
},
"dailyLimit": null,
"minuteRateLimit": {
"limit": 60,
"used": 5,
"left": 55,
"nextReset": "2026-09-14T14:16:00.000Z"
},
"hasUnlimitedCredits": false,
"allowDailyOvercost": false
},
"key": {
"id": "key_abc123",
"dailyLimit": null,
"minuteRateLimit": {
"limit": 60,
"used": 5,
"left": 55,
"nextReset": "2026-09-14T14:16:00.000Z"
}
}
}
}