Skip to main content
Batch search runs one query asynchronously and delivers the entire result set as a single file. Where Person Search returns one cursor page per call, a batch job walks every page for you.
POST https://api.crustdata.com/batch/person/search
For fresher results retrieved from the web in real time, see Batch Person Live Search.
  • One query, whole result set. You submit a single query; the job paginates server-side until max_results is reached or the matches run out.
  • max_results is the only volume control. It clamps to 10,000 and defaults to that cap when omitted. Values above the cap are silently clamped; zero or negative values return 400. The non-batch paging knobs — limit, page, preview — are silently ignored.
  • Flat records. Each line in the results file has exactly the non-batch search record shape. No envelope.
  • Exact field projection. When you pass fields, each record contains exactly those fields — nothing more. Omit fields to get every field your account can read.

POST /batch/person/search takes the same filter fields and operators as Person Search, with one extra rule: the top level of filters must be an {op, conditions} group — a bare {field, type, value} condition is rejected with 400.
curl --request POST \
  --url https://api.crustdata.com/batch/person/search \
  --header 'authorization: Bearer YOUR_API_KEY' \
  --header 'content-type: application/json' \
  --header 'x-api-version: 2025-11-01' \
  --data '{
    "filters": {
      "op": "and",
      "conditions": [
        {"field": "experience.employment_details.current.title", "type": "(.)", "value": "engineer"}
      ]
    },
    "max_results": 2,
    "fields": ["basic_profile.name", "basic_profile.current_title"]
  }'
Search jobs always report identifier_count: 1 — the one query. When the job completes, the downloaded file contains flat records with exactly the requested fields:
The results file (all records)
{"basic_profile": {"current_title": "Principal Software Engineer", "name": "Man Sum Simon Yuen"}}
{"basic_profile": {"current_title": "Knowledge Systems Architect · Context Engineer", "name": "Emily Anderson"}}

Nest groups for or-logic

Groups nest inside conditions. This finds engineers in either the United States or Canada:
curl --request POST \
  --url https://api.crustdata.com/batch/person/search \
  --header 'authorization: Bearer YOUR_API_KEY' \
  --header 'content-type: application/json' \
  --header 'x-api-version: 2025-11-01' \
  --data '{
    "filters": {
      "op": "and",
      "conditions": [
        {"field": "experience.employment_details.current.title", "type": "(.)", "value": "engineer"},
        {
          "op": "or",
          "conditions": [
            {"field": "basic_profile.location.country", "type": "=", "value": "United States"},
            {"field": "basic_profile.location.country", "type": "=", "value": "Canada"}
          ]
        }
      ]
    },
    "max_results": 2,
    "fields": ["basic_profile.name", "basic_profile.location.country"]
  }'
Exact-match values must match the indexed value — both "United States" and "United States of America" exist as basic_profile.location.country values. Use an or group or Person Autocomplete to discover indexed values first.

Two warnings before you submit large jobs

Unknown filter field names are not rejected at submit time the way non-batch search rejects them — a typo in field simply produces a job that completes with 0 results. Double-check field names against the search reference first.
Do not pass a non-empty sorts array — the job will complete with 0 results. Sort the downloaded file instead, for example jq -s 'sort_by(.basic_profile.name) | .[]' results.jsonl.


Errors

400 — no filters
{
    "error": {
        "type": "invalid_request",
        "message": "`filters` must be provided for search",
        "metadata": []
    }
}
400 — bare condition instead of a group
{
    "error": {
        "type": "invalid_request",
        "message": "Invalid filter shape: [{'type': 'missing', 'loc': ('op',), 'msg': 'Field required', ...}]",
        "metadata": []
    }
}
400 — search URL on a database search
{
    "error": {
        "type": "invalid_request",
        "message": "`professional_network_search_url` is only valid for live search",
        "metadata": []
    }
}

What to do next