Search inputs
Use the people search API with natural-language criteria plus structured filters such as role, company, location, keywords, and pagination controls.
Send one JSON object. query, when present, must be a natural-language JSON string, never an object or array. Put exact fields under custom_filters. For example, send {"query":"People currently working at acme.com","limit":1}, or a filter-only request such as {"custom_filters":{"companies":["acme.com"]},"limit":25}. Do not send {"query":{"company_domain":"acme.com"}}. Submit each page once. A new search returns 202 plus a request_id; a page already cached for that request can return 200 immediately. The POST prepares the page but does not return its result rows. Read the page with GET /person/search using that request_id and the page's offset/limit; if POST returned 200, issue that GET once without entering a polling loop. A processing poll is 202 with data.completed: false; a successful terminal poll is 200 with data.completed: true. Stop polling whenever completed is true, regardless of whether status is completed, exhausted, or stale. A bare GET with only request_id always reads offset 0; it does not remember the most recently submitted cursor. Polling is free. callback_url can notify you after queued session work, but it is not a per-page pagination mechanism: a successful callback is not re-emitted for every later page, and an immediate cached 200 does not enqueue one. Always retain the exact GET retrieval path. Use /company/employees for a broad company roster and /person/search when role, title, location, or other person criteria matter.
Manual credentials take precedence over your account key.
The People Search API finds matching people from a natural-language query and structured filters, then returns profile candidates you can qualify, enrich, or route into sales and data workflows.
Use the people search API with natural-language criteria plus structured filters such as role, company, location, keywords, and pagination controls.
Each result is a candidate person record with enough profile context to qualify the match before you enrich, score, or route it.
Send matched identifiers into Person Enrichment when you need a complete profile, contact fields, or social context.
query and/or structured custom_filters - at least one is required unless paginating with cursor or request_id. With no query, the search runs purely on custom_filters.insights.why_matched contains concise evidence chips showing the criterion, evidence type, confidence, and a safe short matched phrase when available. Most useful when a query is supplied.score (integer 1-5) ranking how well each profile matches the query. Most useful when a query is supplied.locations, titles, companies, universities, degrees, specialization_categories, plus the array filters industries (industry names, lowercase; common taxonomy synonyms accepted, e.g. ["fintech","financial services"]), keywords (free-text terms that must each appear in the profile text/skills/interests; multiple keywords are ANDed), and languages (spoken languages, 2-3 common forms each, e.g. ["spanish","espanol"] or ["chinese","mandarin","中文"]). companies matches current employers and accepts company names, domains such as acme.com, or company profile URLs. Employer-company filters include min_company_employee_count/max_company_employee_count, company_employee_count_scope, min_company_total_funding_raised/max_company_total_funding_raised, company_funding_stages, company_latest_funding_stages, company_investor_names, company_last_funding_after_date/company_last_funding_before_date, company_funding_round_filters, and company_funding_scope. Each company_funding_round_filters item binds its supplied stages, investor_names, min_amount/max_amount, funded_after_date/funded_before_date, and funded_within_days to the same funding event. Supports array filters (any-of), numeric ranges, booleans, and exact-match values.next_cursor. Submit it in a new POST /person/search to fetch that page. Preserve the offset and limit returned by this POST and include them on the GET that polls/retrieves the page.offset + limit may not exceed 10000.POST with a new offset/limit to fetch another page. Use GET with that same request_id, offset, and limit to poll/retrieve the requested page. A bare GET does not launch a page fetch; it reads offset 0. Polling never consumes credits.This endpoint is asynchronous. A successful submit returns a request_id while the job runs in the background. Poll the same path with a GET request - same authentication headers - passing the request_id and any
endpoint-specific page values shown below as query parameters:
For People Search, data.completed is authoritative for successful responses: keep polling only while it is false, and stop whenever it is true regardless of the successful status value. Any terminal error envelope or non-retriable non-2xx job response also stops that request; transient transport/status-infrastructure responses such as 503 may be retried with backoff. Polling an
unknown or expired request_id returns 404 request_not_found.
| Status | Meaning |
|---|---|
| processing | HTTP 202 with completed: false. The requested page is still being prepared; keep polling. |
| pending / enriching | HTTP 202 with completed: false. These non-terminal values can appear only when polling an older request created by the legacy search implementation; keep polling. |
| completed | HTTP 200 with completed: true. The requested page is ready. Use has_more to decide whether to request another page. |
| exhausted | HTTP 200 with completed: true. The search source cannot add more matches. This can still have has_more: true when later rows are already stored in the session; has_more, not this status word, decides pagination. |
| stale | HTTP 200 with completed: true. Cached results are older than 30 days; the response includes a warning and remains a terminal success. |
Poll every few seconds at first, backing off for long-running jobs. Polling is free - status checks never burn credits.
callback_url parameter and the completed payload is POSTed to your endpoint
when the job finishes - no polling required. Delivery is retried up to 5 times with exponential backoff (1s,
5s, 15s, 1m, 5m) and a 30-second timeout per attempt; respond with a 2xx status to acknowledge receipt.Each page has two phases: submit exactly one POST to fetch/prepare that page, then use GET to poll and read that exact slice. POST returns page metadata, not result rows. If POST immediately returns 200 with completed: true, skip repeated polling but still issue one GET for the rows. For pages after the first, include the offset and limit from the page-submit response on every GET. A GET with only request_id defaults to offset 0 and therefore re-reads page 1; it neither remembers a cursor POST nor launches a new page fetch. Each successfully fulfilled page POST is billed for results on that page, and replaying an already completed/cached page can charge that page again; GET polling/retrieval is free. offset + limit may not exceed 10000.
Read the pagination fields present on a completed response to decide whether to fetch another page:
| Field | What it tells you |
|---|---|
| completed | The definitive polling signal. false means keep polling; true means stop polling and consume this page, regardless of whether status is completed, exhausted, or stale. |
| status | processing is non-terminal. completed, exhausted, and stale are successful terminal states. exhausted describes upstream fetchability, not whether the current slice has a cached next page. |
| has_more | true means more results are available beyond this page - fetch the next page. false means you have reached the end; stop. |
| next_cursor | An opaque token for the next page. Present only when has_more is true (omitted on the last page). Pass it back as the cursor parameter - it already encodes the next offset, limit and request_id. |
| total_estimate | Approximate total number of matches for the query. Use it to size a progress bar or decide how many pages to pull; treat it as an estimate, not an exact count. |
| credits_charged | Credits recorded on this search session's fetch activity. It is not a charge for the current GET, and it is not the authoritative account billing ledger; polling and stored-page reads are free. Use GET /usage for account-level consumption. |
| offset / limit | The slice this response represents: results offset through offset + limit - 1. |
| Method | How |
|---|---|
| Cursor (recommended) | Take next_cursor from the response and submit POST /person/search exactly once with {"cursor":"..."} (no other body fields needed). Record the POST response's request_id, offset, and limit. Poll/read that page with GET /person/search?request_id=...&offset=...&limit=.... If POST already returned completed: true, perform the GET once to read the rows. Repeat the page cycle only while the GET response has has_more: true. |
| request_id + offset | Submit the original request_id in a new POST with an increasing offset and the chosen limit. Then poll/read with a GET carrying the same request_id, offset, and limit. |
| Repeat the query + offset | Re-submit the same query and identical search options with a higher offset. The service matches it to the existing search session. Poll/read with the returned request_id and that same offset/limit. Prefer cursor or request_id when available because they are unambiguous. |
Do not use a bare GET ?request_id=... to retrieve a page submitted at a nonzero offset: the GET defaults to offset 0 and will correctly return page 1 with the same first-page cursor. Do not resubmit POST as a status check: an identical POST can become another billable page request. Carry the page's offset/limit through the complete POST-to-GET lifecycle. Results retain stable positions within the session, and re-reading stored data with GET is free.
Credits are charged based on the matched configuration. The listed cost is the per-result unit price.
| Feature | Credits | Notes |
|---|---|---|
| Person Search | 1 | base credits per result returned |
| Smart Ranking | +1 | per result when profile_scoring: true |
| Candidate Insights | +1 | per result when insights: true |
| Structured Filters | +1 | per result when custom_filters is non-empty |
| Email Data | +6 | per result when show_emails or require_emails is true |
| Phone Data | +6 | per result when show_phone_numbers or require_phone_numbers is true |
| Any Contact Data | +6 | per result when require_phones_or_emails is true and no email/phone add-on is already enabled |
A newly queued submit returns 202 Accepted. It contains the polling identifier, not the result:
{
"success": true,
"data": {
"request_id": "abc12345_1737123456_1234",
"status": "processing",
"completed": false,
"message": "Search request is being processed. Poll GET /person/search with request_id to check status.",
"offset": 0,
"limit": 10
},
"timestamp": "2026-01-01T00:00:00"
}A successful terminal poll returns 200 with the completed payload:
{
"success": true,
"data": {
"request_id": "abc12345_1737123456_1234",
"status": "completed",
"completed": true,
"results": [
{
"profile_id": "johnsmith",
"displayname": "John Smith",
"firstname": "John",
"middlename": "A",
"lastname": "Smith",
"gender": "male",
"headline": "Senior Sales Manager at Tech Corp",
"bio": "Enterprise sales leader with 15 years closing Fortune 500 accounts.",
"location": "San Francisco, CA",
"altemails": [
"john.smith@techcorp.com",
"john.smith@gmail.com"
],
"fullphone": [
{
"fullphone": "+1-555-123-4567",
"phone_type": "mobile"
}
],
"social_profiles": {
"linkedin": {
"url": "https://linkedin.com/in/johnsmith",
"username": "johnsmith",
"followers": 1200,
"connections": 500
}
},
"organizations": [
{
"name": "Tech Corp",
"company_domain": "techcorp.com",
"company_employee_count": 1200,
"company_employee_count_range_start": 1001,
"company_employee_count_range_end": 5000,
"company_total_funding_raised": 25000000,
"company_latest_funding_stage": "series b",
"company_funding_stages": [
"seed",
"series a",
"series b"
],
"company_funding_rounds": [
{
"stage": "series b",
"date": "2025-10-03",
"amount": 15000000,
"investor_names": [
"sequoia capital"
]
}
],
"company_last_funding_date": "2025-10-03",
"company_investor_names": [
"sequoia capital"
],
"title": "Senior Sales Manager",
"startDate": "2020-03",
"endDate": null
}
],
"schools_info": [
{
"name": "University of California, Berkeley",
"degree": "BS",
"specialization_category": "Business",
"startDate": "2001",
"endDate": "2005"
}
],
"skills": [
"enterprise sales",
"negotiation"
],
"languages": [
"english"
],
"estimated_age": 42,
"score": 5,
"insights": {
"overall_summary": "Strong match: senior sales leadership at a large enterprise in the target metro.",
"why_matched": [
{
"criterion": "Sales managers at Fortune 500 companies",
"evidence_type": "work_experience",
"confidence": "strong",
"display_text": "Current role shows senior sales management at Tech Corp.",
"matched_phrase": "Senior Sales Manager at Tech Corp"
},
{
"criterion": "San Francisco",
"evidence_type": "location",
"confidence": "strong",
"display_text": "Profile location is in the requested metro."
}
],
"query_insights": [
{
"subquery_idx": 0,
"subquery": "Sales managers at Fortune 500 companies",
"priority": "Essential",
"match_level": "Meets Expectations",
"short_rationale": "Senior Sales Manager at a Fortune 500 company.",
"rationale": "Currently Senior Sales Manager at Tech Corp, a Fortune 500 enterprise.",
"short_quotes": [
"Senior Sales Manager at Tech Corp"
]
}
]
}
}
],
"offset": 0,
"limit": 10,
"total_estimate": 500,
"has_more": true,
"next_cursor": "eyJvIjoxMCwiciI6ImFiYzEyMzQ1…",
"credits_charged": 160
},
"timestamp": "2026-01-01T00:00:00"
}