Skip to content

Migrations & changes

Migrating people search from persons to people

New

Move people search calls from /v2/search/persons to /v2/search/people.

People search has a new home: POST /v2/search/people. It returns full profiles instead of search hits, paginates with cursors instead of page numbers, accepts a larger filter set, and ships with a free count endpoint.

The previous endpoint, POST /v2/search/persons, remains functional until October 13, 2026, when it is retired. See the endpoint transition overview for the full timeline. It is no longer documented in the endpoint catalog, and this guide covers everything that changes when you move to /v2/search/people.

What changes

AreaPOST /v2/search/personsPOST /v2/search/people
Path/v2/search/persons/v2/search/people
ResultsSearch hits (name, title, company, location)Full profiles, the same shape as the Profile endpoint
Paginationpage and perPage (default 100)cursor and perPage (default 25)
MetadatacurrentPage, pageNumber, perPage, totalperPage, count, nextCursor
Total matchesdata.metadata.total on every responseFree count endpoint
Pricing1 credit per page1 credit per profile delivered
Available fieldsGET /v2/search/persons/available-fieldsGET /v2/search/people/available-fields

Paths

BeforeAfter
POST /v2/search/personsPOST /v2/search/people
GET /v2/search/persons/available-fieldsGET /v2/search/people/available-fields
(no equivalent)POST /v2/search/people/count

The method and the Authorization: Bearer YOUR_API_KEY header stay the same. Only the path changes.

Results are full profiles

The previous endpoint returned a light search hit, and the documented flow was to chain into the Profile endpoint for the full data of a hit. People search returns complete profiles directly, so the second call is no longer needed.

Before, a hit looked like this:

JSON
{
  "id": "prs_01h2x3y4z5a6b7c8d9e0f1g2h3",
  "publicId": "janedoe",
  "linkedinUrl": "https://www.social.com/in/janedoe",
  "firstName": "Jane",
  "lastName": "Doe",
  "headline": "VP of Marketing @ Acme Corp",
  "currentPositionTitle": "VP of Marketing",
  "currentCompanyName": "Acme Corp",
  "updateDate": "2026-03-18T09:12:44.000Z"
}

After, each item of data.data is a full profile, with currentPosition, experience, education, skills and the other sections documented on the Profile endpoint. If you chained Profile calls after a search, drop them: the data is already in the search response.

Pagination moves from pages to cursors

The previous endpoint used page (1 to 50) and perPage (1 to 100, default 100), and reported currentPage, pageNumber and total in data.metadata. People search uses cursor pagination:

  • perPage stays, with a new default of 25 (still 1 to 100).
  • page is gone. Send the nextCursor value from the previous response as cursor to get the next page.
  • nextCursor is null on the last page.
  • The cursor is opaque: send it back unchanged, without parsing or altering it.

Before:

JavaScript
const response = await fetch("https://api.reversecontact.com/v2/search/persons", {
  method: "POST",
  headers: { "Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json" },
  body: JSON.stringify({ currentPositionTitle: "VP of Marketing", page: 2, perPage: 100 })
});

const payload = await response.json();
const hits = payload.data.data;
console.log(`Page ${payload.data.metadata.currentPage} of ${payload.data.metadata.pageNumber}, ${payload.data.metadata.total} total`);

After:

JavaScript
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({ currentSeniority: "VP", perPage: 25, cursor })
  });

  return response.json();
};

const page = await searchPage();
console.log(`${page.data.metadata.count} profiles in this page`);
if (page.data.metadata.nextCursor) {
  const nextPage = await searchPage(page.data.metadata.nextCursor);
}

Totals move to the count endpoint

The previous data.metadata.total field is gone: data.metadata.count is the number of profiles in the current page, not the size of the whole matching set. To size a query, call the free People search count endpoint with the same filters:

Shell
curl -X POST https://api.reversecontact.com/v2/search/people/count \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "currentSeniority": "VP" }'

The response is data.count, the total number of matching profiles across all pages, and it costs 0 credits.

Pricing changes

The previous endpoint charged 1 credit per page, whatever the page contained. People search charges 1 credit per profile delivered: a page that returns 25 profiles costs 25 credits, and a page that returns no profile costs 0 credits. Rejected filters still cost 0 credits.

Two consequences for your migration:

  • perPage is now a credit control. Lower it to cap what a single request can spend.
  • Size queries first with the free count endpoint instead of reading metadata.total from a billed page.

Filter changes

Existing filters keep their names, including currentPositionTitle, excludeCurrentPositionTitle, currentCompanyIndustry, currentCompanyEmployeeCountRange, location, followersCount and maxDataAgeDate. People search also accepts filters the previous endpoint did not have:

FilterWhat it does
currentFunction, excludeCurrentFunctionMatch the current function from a 32-value position taxonomy
currentSubFunction, excludeCurrentSubFunctionMatch the current sub-function from a 386-value taxonomy
currentSeniority, excludeCurrentSeniorityMatch the current seniority from a 10-value taxonomy
experienceRequire past experience in a function, sub-function or seniority, with an optional minimum tenure in months
positionTitle, companyName, companyId, companyLinkedinId, companyPublicId, companyIndustry and their exclude... counterpartsMatch current and past positions, not only the current one
excludeCurrentCompanyName, excludeCurrentCompanyId, excludeCurrentCompanyLinkedinId, excludeCurrentCompanyPublicIdExclude a specific current employer
emailFilter by email address
isCurrentlyEmployedRestrict to profiles that have a current employer
skillsMatch the skills listed on the profile
languages, excludeLanguagesMatch or exclude the languages a person speaks
keywordsRequire every word somewhere in the profile, in any field
changedJobsWithinDaysKeep people who started their current position within the last 30, 60, 90 or 120 days

The taxonomy lists are documented on the People search page and returned dynamically by available fields. Validation is unchanged in spirit: unknown fields, blank values and values outside a closed list return 422 with suggestions and cost 0 credits.

Available fields moves too

If you discover filters dynamically, switch GET /v2/search/persons/available-fields to GET /v2/search/people/available-fields. The response shape is unchanged: data.entity is "person" and data.fields lists every accepted filter, with allowedValues for closed vocabularies such as the position taxonomy.

Migration checklist

  1. Replace the search path with POST /v2/search/people and the available-fields path with GET /v2/search/people/available-fields.
  2. Replace page with cursor handling: loop while data.metadata.nextCursor is not null.
  3. Review your perPage: the default is now 25, and each delivered profile costs 1 credit.
  4. Replace data.metadata.total reads with a call to POST /v2/search/people/count.
  5. Drop the Profile calls you used to chain after a search: results are already full profiles.
  6. Map your title filters: currentPositionTitle still matches the current position, and the new positionTitle matches current and past positions.