Retrieve the current status of a search job created via Create Search. Safe to call repeatedly. Once status is "completed", the response includes a pre-signed download_url for the results file — see the CSV Output 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 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 HTTP200, regardless of whether the job itself isqueued,processing,completed, orfailed. The HTTP code reflects "the status check succeeded" — the job's actual state lives in the response body'sstatusfield. Read it, not the HTTP code, to detect job failure.
Headers
| Header | Required | Value |
|---|---|---|
Authorization | Yes | Bearer kh_live_<your_key> — see Authentication |
Path Parameters
| Parameter | Type | Description |
|---|---|---|
job_id | string (UUIDv4) | The job identifier returned from Create Search. |
Status values
status | Meaning |
|---|---|
queued | Accepted and waiting for a worker. Usually flips to processing within seconds. |
processing | Worker is running the search against BigQuery. |
completed | Terminal. download_url and expires_at are set. |
failed | Terminal. 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
{
"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
{
"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_url": "https://storage.googleapis.com/...csv.gz?...",
"expires_at": "2026-04-11T10:40:39.343468+00:00",
"status_url": "https://api.keeperhealth.com/v1/searches/6f4245de-..."
}| Field | Type | Description |
|---|---|---|
download_url | string (URL) | Pre-signed URL to download the gzipped CSV results file. No authentication header is required. Only present when status is "completed". |
expires_at | string (ISO 8601) | Expiration time for the download URL (~1 hour after it was minted). Re-poll the status endpoint to obtain a fresh URL after expiry. |
completed_at | string (ISO 8601) | Timestamp when job processing finished. Only present when status is "completed". |
Download URLs expire. The pre-signed URL is valid for approximately 1 hour. If the link has expired, simply poll the status endpoint again to receive a fresh one. Do not cache or persist signed URLs — always re-fetch them via this endpoint.
Response — failed
200 OK
{
"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"
}| Field | Type | Description |
|---|---|---|
error | string | Human-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.type | HTTP | Meaning |
|---|---|---|
unauthorized | 401 | Missing, malformed, or invalid API key. See Authentication. |
forbidden | 403 | API key revoked or organization inactive. See Authentication. |
not_found | 404 | The 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_error | 500 | Contact support. |
See also
- Create Search — submit the job you're polling here
- CSV Output Schema — columns you'll find in the downloaded file
- Code Examples — complete polling loops with error handling
- Rate Limits — polling cadence and
Retry-Afterhandling - Errors — full error envelope taxonomy