Documentation

Search for people

Last updated:

Search for people with a natural-language query — or companies, by sending target: company.

POST/v1/search

Authenticated with your x-api-key. See Authentication.

This endpoint searches people by default. To search companies instead, send target: company — see Search for companies below.

Request

POST /v1/search
Host: search-api.xverum.com
x-api-key: XVERUM-API-KEY
Content-Type: application/json

Body

FieldTypeRequiredConstraintsDescription
querystringyes1–500 chars, trimmed, must not be blankNatural-language search query
pageintegerno≥ 1, default 11-based page number
page_sizeintegerno1–100, default 10Results per page
targetstringnopeople or company, default peopleWhich entity to return. company returns companies (see Search for companies); no automatic people-vs-company detection is done

Example body

{
  "query": "senior Python developers with Kubernetes in Seattle",
  "page": 1,
  "page_size": 10
}

Pagination

Offset-based:

  • page — integer, ≥ 1 (default 1).
  • page_size — integer, `1` to `100` (default 10).
  • page * page_size must not exceed 1000 — the max result window.

A page_size outside 1–100 returns 422 validation_error; a page * page_size over 1000 returns 400 invalid_pagination. Deep pagination over the entire corpus is intentionally unsupported — refine the query instead.

total_count is an estimate of total matches, not an exact count. Use it as a rough size signal, not for precise "page N of M" math.

Example

curl -X POST https://search-api.xverum.com/v1/search \
  -H "x-api-key: XVERUM-API-KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "senior Python developers with Kubernetes in Seattle"
  }'

Response — 200 OK

FieldTypeDescription
result_typestringpeople, or company when you send target: company — discriminates the shape of results
resultsProfile[]Matched profiles (see schema below)
total_countintegerEstimated total number of matches
pageinteger1-based page number echoed back
page_sizeintegerResults per page echoed back
credits_usedintegerCredits deducted on this call (1 per returned result)
credits_remainingintegerCredits left after this call — a best-effort fresh read from billing; on a transient billing outage it falls back to the balance seen at auth time
request_idstringCorrelation id — also returned in the X-Request-Id header

Every response also carries X-Request-Id and the X-RateLimit-* headers (see Rate limits).

To get the complete profile — employment history, education and profile summary — pass a result's id to GET /v1/profiles/{id}.

Profile schema

FieldTypeDescription
idstringOpaque profile id, stable across sessions and queries under the current scheme. Pass to GET /v1/profiles/{id} for the full profile, or to GET /v1/profiles/{id}/job-change for the Next Move Signal score. Store it — any id you already hold stays fetchable.
social_urlstring | nullPerson social URL; null when no public profile URL is on record for the person
full_namestringFull name
headlinestring | nullPerson social headline or summary; null when the person has not written one
locationstring | nullLocation string; null when no location is on record for the person
company_namestring | nullMost recent employer; null when no current employer is held for the person
positionstring | nullMost recent job title; null when no current role is held for the person
industrystring | nullIndustry vertical, taken from the person's current employer; null when that employer, or its industry, is not known
evidence_summarystringHow recently this profile was verified, from its refresh date — Verified last 30 days through Verified over 120 days ago

Example response

{
  "result_type": "people",
  "results": [
    {
      "id": "a1b2c3d4",
      "social_url": "https://www.linkedin.com/in/jane-doe-sea",
      "full_name": "Jane Doe",
      "headline": "Senior Python Engineer at Acme",
      "location": "Seattle, WA",
      "company_name": "Acme",
      "position": "Senior Python Engineer",
      "industry": "software",
      "evidence_summary": "Verified last 30 days"
    },
    …
  ],
  "total_count": 128,
  "page": 1,
  "page_size": 10,
  "credits_used": 10,
  "credits_remaining": 4990,
  "request_id": "8b1d4f0c12a44b7a"
}

Billing

Each profile returned in results consumes 1 credit from your account, echoed back as credits_used; credits_remaining shows the balance left. A request that returns 10 profiles spends 10 credits.

credits_remaining is read fresh from billing on each successful call and is best-effort: if billing is briefly unavailable it falls back to the balance seen at authentication. Treat it as an indicative post-call balance, not a settled ledger figure.

Search for companies

Send target: "company" on this same POST /v1/search endpoint to search companies instead of people. query, page and page_size behave exactly as above, and pagination and the 1000 max result window are the same.

Example body

{
  "query": "fintech companies in London with 50–200 employees",
  "page": 1,
  "page_size": 10,
  "target": "company"
}

The response envelope is identical to the people response, except result_type is company and each item in results is a company card.

Company schema

FieldTypeDescription
idstringOpaque company id. Pass to GET /v1/companies/{company_id} for the full company record. Store it — any id you already hold stays fetchable.
social_urlstring | nullCompany social URL; null when none is on record for the company
company_namestring | nullCompany name; null when no name is on record for the company
sloganstring | nullShort tagline; null when none is on record
headquartersstring | nullHeadquarters as City, Region; null when no location is on record
country_codestring | nullLowercase country code of the headquarters; null when not known
industrystring | nullIndustry vertical; null when not known
employees_numinteger | nullEmployee count; null when not known
websitestring | nullCompany website; null when none is on record
foundedinteger | nullYear the company was founded; null when not known
evidence_summarystringHow recently this company record was verified, from its refresh date — Verified last 30 days through Verified over 120 days ago

Company results are billed exactly like people results — 1 credit per company returned, echoed back as credits_used.

To get a company's full record — description, specialties, office locations, organisation type, follower count and funding — pass a result's id to GET /v1/companies/{company_id}.

Errors

All /v1 errors use the structured envelope (see Authentication):

Statuserror.codeCondition
400off_topic_queryQuery is not a Xverum data query
400invalid_paginationpage * page_size exceeds the 1000 max result window
401invalid_api_keyMissing or invalid API key
402out_of_creditsOut of credits — the envelope carries an upgrade_url to top up
403account_not_authorizedAccount not authorized
422validation_errorBody failed validation (e.g. query blank or longer than 500 chars)
429rate_limitedRate limit exceeded — see Rate limits
502upstream_unavailableAn upstream dependency failed — retry shortly
503search_unavailableSearch is temporarily saturated or unreachable — retry shortly
503auth_unavailableAuthentication is briefly unreachable — retry shortly
503classification_degradedQuery understanding is degraded — retry shortly
{
  "error": {
    "code": "off_topic_query",
    "message": "Query is not a Xverum data query.",
    "request_id": "8b1d4f0c12a44b7a"
  }
}

The message is advisory and target-specific — match on error.code, not its text. A target: "company" search that is off topic returns a different message:

{
  "error": {
    "code": "off_topic_query",
    "message": "Query is not a company search. If you were looking for people, retry with target=people.",
    "request_id": "8b1d4f0c12a44b7a"
  }
}

A company search cannot fall back to people, so this points a mis-targeted request at the right target rather than guessing which half to answer.