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:
perPagestays, with a new default of25(still 1 to 100).pageis gone. Send thenextCursorvalue from the previous response ascursorto get the next page.nextCursorisnullon 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:
perPageis 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.totalfrom 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
- Replace the search path with
POST /v2/search/peopleand the available-fields path withGET /v2/search/people/available-fields. - Replace
pagewith cursor handling: loop whiledata.metadata.nextCursoris notnull. - Review your
perPage: the default is now25, and each delivered profile costs 1 credit. - Replace
data.metadata.totalreads with a call toPOST /v2/search/people/count. - Drop the Profile calls you used to chain after a search: results are already full profiles.
- Map your title filters:
currentPositionTitlestill matches the current position, and the newpositionTitlematches current and past positions.