API Docs/Create Search

Create Search

POST/v1/searches

Submit an asynchronous search job to query negotiated rates across payer machine-readable files. Returns immediately with a job_id you can pass to Get Search Status to poll for completion.

Create search endpoint: POST /v1/searches request and response structure

You can make a request for a single Payer and Plan combination (canonically called a fee schedule), or multiple fee schedules at once.

Example request for multiple fee schedules:POST/v1/searches

JSON
{
  "npis": [1144218512, 1234567890],
  "billing_codes": ["99213", "99214"],
  "fee_schedules": [
    { "payer": "Cigna", "plan": "PPO" },
    { "payer": "UnitedHealthcare", "plan": "Other" }
  ],
  "testing": false
}

Headers

HeaderRequiredValue
AuthorizationYesBearer kh_live_<your_key> — see Authentication
Content-TypeYesapplication/json
Idempotency-KeyStrongly recommendedA client-chosen string, up to 255 printable ASCII characters. Repeat POSTs with the same key within 24 hours return the original job_id instead of creating a duplicate billable job. See Idempotency.

Request Body

FieldTypeRequiredDescription
npisinteger[]YesOne or more 10-digit National Provider Identifier numbers. Up to 1000 per request.
billing_codesstring[]YesCPT or HCPCS billing codes to retrieve rates for. Up to 100 per request.
fee_schedulesobject[]YesFilter results by payer and plan. Up to 20 per request. See fields below.
testingbooleanNoIf true, uses development datasets instead of production data. Default false.

fee_schedules object

FieldTypeDescription
payerstringPayer name (e.g. "Cigna", "Aetna", "UnitedHealthcare").
planstringPlan type (e.g. "PPO", "HMO", "Choice Plus").

Requests that exceed the hard per-request limits (1000 NPIs, 100 billing codes, 20 fee schedules) are rejected with 400 validation_error before any work is enqueued. See Rate Limits for the full limit table and Errors for the validation-error envelope shape.

Response

202 Accepted — the job has been queued for processing. The response includes a Location header with the status polling URL.

JSON
{
  "job_id": "6f4245de-4b44-4bb6-aaae-f11c0d4f45c0",
  "status": "queued",
  "created_at": "2026-04-11T09:39:35.373732+00:00",
  "status_url": "https://api.keeperhealth.com/v1/searches/6f4245de-4b44-4bb6-aaae-f11c0d4f45c0"
}
FieldTypeDescription
job_idstring (UUID)Unique identifier for this search job. Pass it to Get Search Status to poll and retrieve results.
statusstringCurrent job state. One of: queued, processing, completed, failed. New jobs always start in queued. See the full state machine.
created_atstring (ISO 8601)Timestamp when the job was created.
status_urlstring (URL)Convenience URL for polling the job status.

Idempotency

Strongly recommended. We strongly recommend sending an Idempotency-Key on every POST /v1/searches request. Searches are billable, long-running jobs — an accidental retry without a key creates a duplicate job, duplicate work, and duplicate cost. Treat the key as mandatory in production code paths.

POST /v1/searches accepts an Idempotency-Key header. When present, repeat requests from the same organization with the same key within a 24-hour window return the original job_id (still 202, with an Idempotent-Replay: true response header) instead of creating a second job.

Use this on any retry path that might fire twice — cron jobs over flaky networks, job runners with at-least-once semantics, UI flows where a user could double-click a submit button.

cURL
curl -X POST https://api.keeperhealth.com/v1/searches \
  -H "Authorization: Bearer $KEEPER_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: acme-nightly-2026-04-13" \
  -d '{
    "npis": [1144218512],
    "billing_codes": ["99213"],
    "fee_schedules": [{"payer": "Cigna", "plan": "PPO"}]
  }'

Requirements:

  • Up to 255 printable ASCII characters
  • Unique per logical request — reuse the same key for safe retries of the same intended operation, generate a new one for a new intended operation
  • Scoped per organization — two different orgs using the same key are independent

Without an Idempotency-Key, every POST creates a new job, including retries. If your code might retry network failures, store the job_id after the first 202 and re-poll instead of re-POSTing.

Error responses

All error responses use the standard error envelope.

error.typeHTTPMeaning
validation_error400A field failed validation (e.g. empty npis, over a per-request limit). details lists each failing field.
invalid_request400The request body is missing, not valid JSON, or the Idempotency-Key header is malformed.
unauthorized401Missing, malformed, or invalid API key. See Authentication.
forbidden403API key revoked or organization inactive. See Authentication.
enqueue_failed503Transient — safe to retry. Retry-After header is set. See Rate Limits.
internal_error500Contact support.

See also

Keeper Health API v1 · Questions? company@keeperhealth.com