Nyne.ai API Get API key
Person APIs

People Search API

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.

POST https://api.nyne.ai/person/search API key

Manual credentials take precedence over your account key.

Overview

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.

Best for

  • Building filtered people lists
  • Finding prospects by role, company, location, or keywords
  • Starting a workflow before you have a specific identifier

Returns

  • Matched person profiles
  • Profile identifiers for enrichment
  • Pagination signals for larger result sets

Implementation guide

Search inputs

Use the people search API with natural-language criteria plus structured filters such as role, company, location, keywords, and pagination controls.

Result schema

Each result is a candidate person record with enough profile context to qualify the match before you enrich, score, or route it.

Next step

Send matched identifiers into Person Enrichment when you need a complete profile, contact fields, or social context.

Parameters

query string optional
Natural-language JSON string describing who you're looking for (max 1000 chars). It must not be an object, array, or search DSL. Provide a 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.
limit integer optional
Results per page. Default 10; valid values are 1-100. Larger result sets are retrieved by paginating (see Pagination). Values outside the range return HTTP 400.
show_emails boolean optional
Include verified email addresses in each result.
e.g. true
show_phone_numbers boolean optional
Include phone numbers in each result.
e.g. true
require_emails boolean optional
Only return people for whom a verified email could be found.
e.g. true
require_phone_numbers boolean optional
Only return people for whom a phone number could be found.
e.g. true
require_phones_or_emails boolean optional
Only return people with at least one contact method (email or phone).
e.g. true
insights boolean optional
Attach AI-generated relevance insights per result. When present, 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.
e.g. true
profile_scoring boolean optional
Return a query-fit score (integer 1-5) ranking how well each profile matches the query. Most useful when a query is supplied.
e.g. true
custom_filters object optional
Structured filters - 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.
cursor string optional
Opaque pagination token from a previous response's 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 integer optional
Starting position for offset pagination (0-indexed). Default 0; offset + limit may not exceed 10000.
request_id string optional
ID of an existing search (returned by the first call). Re-send it in a 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.
callback_url string optional
HTTPS URL for a session callback after queued search work. This is not a per-page pagination mechanism: after a successful callback it is not re-emitted for every later page, and an immediately cached page does not enqueue one. Process callbacks idempotently by request_id, and retain the exact request_id/offset/limit GET for recovery and authoritative page retrieval.

Polling for the result

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:

Loading your API credentials...

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.

Prefer push delivery?
Supply the optional 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.

Pagination

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.

Knowing when to paginate

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.

Fetching the next page

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.

Credit usage

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
Heads up
Empty results do not burn credits.

Responses

202 Search queued or still processing - poll with the returned request_id
200 Requested page is ready; successful terminal statuses are completed, exhausted, and stale, all with completed: true
400 Invalid parameters, including an object-valued query or missing search scope
401 Missing, invalid, or expired API credentials
402 Insufficient credits
403 Valid credentials, but the account, plan, product, subscription, or request IP is not allowed
404 Unknown request_id or a request_id not owned by this API key
429 Rate or monthly request limit exceeded
500 The queued search reached a terminal processing error; do not keep polling that request_id
503 Search or status infrastructure is temporarily unavailable; retry with backoff

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"
}