API Docs/Get Search Status

Get Search Status

GET/v1/searches/{job_id}

Retrieve the current status of a search job created via Create Search. Safe to call repeatedly. Once status is "completed", the response includes a download_urls array of pre-signed URLs — one per Parquet shard. See the Reimbursement Data Schema for what's inside.

Example request:GET/v1/searches/{job_id}

GET requests have no body — pass the job_id in the URL path and the API key in the Authorization header.

cURL
curl https://api.keeperhealth.com/v1/searches/6f4245de-4b44-4bb6-aaae-f11c0d4f45c0 \
  -H "Authorization: Bearer $KEEPER_API_KEY"

The three possible successful response shapes — queued/processing, completed, and failed — are shown below.

HTTP status is always 200. A successful request to this endpoint always returns HTTP 200, regardless of whether the job itself is queued, processing, completed, or failed. The HTTP code reflects "the status check succeeded" — the job's actual state lives in the response body's status field. Read it, not the HTTP code, to detect job failure.

Headers

HeaderRequiredValue
AuthorizationYesBearer kh_live_<your_key> — see Authentication

Path Parameters

ParameterTypeDescription
job_idstring (UUIDv4)The job identifier returned from Create Search.

Status values

statusMeaning
queuedAccepted and waiting for a worker. Usually flips to processing within seconds.
processingWorker is running the search against BigQuery.
completedTerminal. download_urls and expires_at are set.
failedTerminal. The error field contains a human-readable failure reason.

completed and failed are terminal — once a job reaches either, it will never change state again. Stop polling as soon as you see one. Poll every 15–30 seconds; see Rate Limits for the recommended cadence, and Code Examples for complete polling loops in four languages.

Response — queued / processing

200 OK

JSON
{
  "job_id": "6f4245de-4b44-4bb6-aaae-f11c0d4f45c0",
  "status": "processing",
  "created_at": "2026-04-11T09:39:35.736000+00:00",
  "status_url": "https://api.keeperhealth.com/v1/searches/6f4245de-..."
}

Response — completed

200 OK

JSON
{
  "job_id": "6f4245de-4b44-4bb6-aaae-f11c0d4f45c0",
  "status": "completed",
  "created_at": "2026-04-11T09:39:35.736000+00:00",
  "completed_at": "2026-04-11T09:40:18.847000+00:00",
  "download_urls": [
    "https://storage.googleapis.com/...&X-Goog-Signature=...",
    "https://storage.googleapis.com/...&X-Goog-Signature=..."
  ],
  "expires_at": "2026-04-11T10:40:39.343468+00:00",
  "status_url": "https://api.keeperhealth.com/v1/searches/6f4245de-..."
}
FieldTypeDescription
download_urlsstring[] (URLs)List of pre-signed URLs, one per Parquet shard. Always an array — length is 1 or more, even for single-file results. No authentication header is required on the downloads themselves. Only present when status is "completed".
expires_atstring (ISO 8601)Shared expiration time for every URL in download_urls (~1 hour after they were minted). Re-poll the status endpoint to obtain a fresh set after expiry.
completed_atstring (ISO 8601)Timestamp when job processing finished. Only present when status is "completed".

Why multiple URLs? BigQuery shards large result sets into multiple Parquet files on export (each shard is capped at 1 GB). Every file has the same schema; together they are the complete result. Always iterate download_urls — do not assume a length. Readers like DuckDB, pandas (pd.read_parquet([...])), and pyarrow accept a list of Parquet files as a single dataset.

Download URLs expire. The pre-signed URLs are valid for approximately 1 hour and all share the same expires_at. If the links have expired, simply poll the status endpoint again to receive a fresh set. Do not cache or persist signed URLs — always re-fetch them via this endpoint.

Response — failed

200 OK

JSON
{
  "job_id": "6f4245de-4b44-4bb6-aaae-f11c0d4f45c0",
  "status": "failed",
  "created_at": "2026-04-11T09:39:35.736000+00:00",
  "status_url": "https://api.keeperhealth.com/v1/searches/6f4245de-...",
  "error": "Payer 'FooBar' not supported"
}
FieldTypeDescription
errorstringHuman-readable reason the job failed. Only present when status is "failed".

Failed jobs are terminal. Read the error field, correct the underlying issue, and submit a corrected request as a new job via Create Search.

Error responses

All error responses use the standard error envelope.

error.typeHTTPMeaning
unauthorized401Missing, malformed, or invalid API key. See Authentication.
forbidden403API key revoked or organization inactive. See Authentication.
not_found404The job_id does not exist, or belongs to a different organization. These cases are intentionally indistinguishable to avoid confirming the existence of other customers' jobs.
internal_error500Contact support.

See also

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