Migrating people search from persons to people

4 min read

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 while customers migrate. 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

Area POST /v2/search/persons POST /v2/search/people
Path /v2/search/persons /v2/search/people
Results Search hits (name, title, company, location) Full profiles, the same shape as the Profile endpoint
Pagination page and perPage (default 100) cursor and perPage (default 25)
Metadata currentPage, pageNumber, perPage, total perPage, count, nextCursor
Total matches data.metadata.total on every response Free count endpoint
Pricing 1 credit per page 1 credit per profile delivered
Available fields GET /v2/search/persons/available-fields GET /v2/search/people/available-fields

Paths

Before After
POST /v2/search/persons POST /v2/search/people
GET /v2/search/persons/available-fields GET /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:

{
  "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:

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:

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:

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:

Filter What it does
currentFunction, excludeCurrentFunction Match the current function from a 32-value position taxonomy
currentSubFunction, excludeCurrentSubFunction Match the current sub-function from a 386-value taxonomy
currentSeniority, excludeCurrentSeniority Match the current seniority from a 10-value taxonomy
experience Require 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... counterparts Match current and past positions, not only the current one
excludeCurrentCompanyName, excludeCurrentCompanyId, excludeCurrentCompanyLinkedinId, excludeCurrentCompanyPublicId Exclude a specific current employer
email Filter by email address
isCurrentlyEmployed Restrict to profiles that have a current employer

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.

Previous

Migrating from V1 to V2

Next

Enrich profile