Batches
A batch is up to 100 notes analyzed in the background. You create it in one request, then check on it until every note has finished. There are no webhooks; your code polls.
How batches work
- You send the notes. The API checks your balance, holds one credit per note, queues every note, and answers
202at once with the batch ID. - The notes run in the background. Each note moves from
queuedtorunning, then tosucceeded,failed, orout_of_scope. - You poll the batch. When every note has finished, the batch's
finished_atis set. - You read each finished note with
GET /v1/analyses/{id}, the same as a single analysis.
Only notes that succeed are charged. The held credit for a note that fails or is out of scope returns to your balance.
Limits:
- A batch has 1 to 100 notes. Each note follows the same rules as a single analysis, including 20 to 100,000 characters.
- The request body can be at most 12 MB.
- An organization can have at most 5 unfinished batches at a time, including batches started in the web app.
- Creating a batch counts as one call toward the per-minute rate limit, however many notes it has. For more information, see Rate limits.
- Notes that end out of scope or fail count toward your organization's limit of 25 uncharged notes a day, like single analyses. For more information, see Uncharged notes.
Create a batch
POST/v1/batches
| Field | Type | Description |
|---|---|---|
items | array of objects | Required. 1 to 100 notes. Each item takes the same fields as the analysis request body, except test_data: document_text (required), cpts, icds, jurisdiction, visit_type, provider_type, patient_demographics, active_problems, and client_reference. |
label | string | Your name for the batch, up to 120 characters. |
test_data | boolean | Required as true while your organization is in test mode. Default false. |
You can send an Idempotency-Key header, as with a single analysis. For more information, see
Idempotency.
curl -s https://api.medchartscan.io/v1/batches \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 0d6f3a52-1c7e-4b8a-a4f1-93e2c5b7d018" \
-d '{
"label": "Clinic day 2026-10-25",
"items": [
{"document_text": "Established patient seen for follow-up of type 2 diabetes and hypertension. Reports good adherence to metformin; no hypoglycemia. BP 138/86. A1c 7.4% reviewed. Assessment: diabetes, improving; hypertension, not at goal. Plan: increase lisinopril to 20 mg daily, recheck BMP in 2 weeks, follow up in 3 months.", "jurisdiction": "TX", "client_reference": "enc-0001"},
{"document_text": "New patient with acute low back pain after lifting, no red flags. Exam: paraspinal tenderness, normal strength and reflexes. Plan: NSAIDs, activity as tolerated, return if worse.", "client_reference": "enc-0002"}
]
}'{
"id": "c29da7b5-0b37-4ea6-8db5-24f7d96f4c8e",
"label": "Clinic day 2026-10-25",
"source": "api",
"is_test": false,
"created_at": "2026-10-25T05:09:30.425542Z",
"finished_at": null,
"created_by": "Epic integration (prod)",
"counts": {"queued": 2, "running": 0, "succeeded": 0, "failed": 0, "out_of_scope": 0},
"items": [
{"id": "088d3c79-485d-4131-bbd6-f54a9188bad8", "client_reference": "enc-0001", "created_at": "2026-10-25T05:09:30.425542Z", "status": "queued", "final_cpt_code": null, "retryable": false, "submitted_code": null, "coding_change": null, "missed_revenue_count": null, "denial_risk_count": null},
{"id": "997aecf7-4ac2-4c79-a1bd-0e8f30047b88", "client_reference": "enc-0002", "created_at": "2026-10-25T05:09:30.425542Z", "status": "queued", "final_cpt_code": null, "retryable": false, "submitted_code": null, "coding_change": null, "missed_revenue_count": null, "denial_risk_count": null}
]
}Items are in the order you sent them. Each item's id is an analysis ID, and its created_at is when that attempt
was submitted: a retried note shows the time of its retry.
Errors
| Status | Code | Cause |
|---|---|---|
400 | invalid_request | No items, more than 100 items, or an invalid field (the message names it). |
402 | insufficient_credits | The balance doesn't cover every note. Nothing is stored or held. |
403 | baa_resign_required | The organization is paused until an owner signs the updated BAA. |
409 | idempotency_conflict, idempotency_in_progress | See Idempotency. |
413 | note_too_long | A note is longer than 100,000 characters. |
413 | request_too_large | The request body is larger than 12 MB. |
422 | test_data_required | The organization is in test mode and the request didn't send "test_data": true. |
429 | rate_limited | The per-minute limit is reached. Wait for Retry-After seconds. |
429 | too_many_batches | The organization already has 5 unfinished batches. Wait for one to finish. |
429 | too_many_uncharged_runs | 25 of your organization's notes ended out of scope or failed in the last 24 hours, or the batch would put more notes in progress than the uncharged limit allows. Wait for Retry-After seconds, or send a smaller batch. Retrying failed notes gets the same answer. |
Check on a batch
GET/v1/batches/{id}
Returns the batch with its status counts and every note, in the order you sent them. Poll it every 5 to 10 seconds
until finished_at isn't null. You can open each note as soon as its status is succeeded, without waiting for the
whole batch.
curl -s https://api.medchartscan.io/v1/batches/$BATCH_ID \
-H "Authorization: Bearer $ACCESS_TOKEN"A finished batch where one note failed:
{
"id": "c29da7b5-0b37-4ea6-8db5-24f7d96f4c8e",
"label": "Clinic day 2026-10-25",
"source": "api",
"is_test": false,
"created_at": "2026-10-25T05:09:30.425542Z",
"finished_at": "2026-10-25T05:09:30.483722Z",
"created_by": "Epic integration (prod)",
"counts": {"queued": 0, "running": 0, "succeeded": 1, "failed": 1, "out_of_scope": 0},
"items": [
{"id": "088d3c79-485d-4131-bbd6-f54a9188bad8", "client_reference": "enc-0001", "created_at": "2026-10-25T05:09:30.425542Z", "status": "failed", "final_cpt_code": null, "retryable": true, "submitted_code": null, "coding_change": null, "missed_revenue_count": null, "denial_risk_count": null},
{"id": "997aecf7-4ac2-4c79-a1bd-0e8f30047b88", "client_reference": "enc-0002", "created_at": "2026-10-25T05:09:30.425542Z", "status": "succeeded", "final_cpt_code": "99214", "retryable": false, "submitted_code": "99213", "coding_change": "upgraded", "missed_revenue_count": 1, "denial_risk_count": 0}
]
}| Field | Description |
|---|---|
id | The batch ID. |
label | Your name for the batch, or null. |
source | api for batches created through the API. |
is_test | true for a batch created in test mode. |
created_at | When the batch was created. |
finished_at | When the last note finished; null while any note is queued or running. |
created_by | The label of the API key that created the batch; null if the key no longer exists. |
counts | The number of notes in each status. |
items | Every note: its analysis id, your client_reference, status, final_cpt_code once it succeeded, retryable, and the review fields submitted_code, coding_change, missed_revenue_count, and denial_risk_count (null until the note succeeds; see Analyses). |
To review a batch, start with the notes whose missed_revenue_count or denial_risk_count is above zero, and those
whose coding_change is downgraded. To find them across batches, use
Search results with "channel": "api_batch".
retryable is true for a failed note that you can still retry. If a note can't start within 6 hours, it fails, its
held credit returns to your balance, and you can retry it.
A batch that doesn't exist, belongs to another organization, or was created in the web app returns 404 not_found.
List batches
GET/v1/batches
Lists the batches created through the API in your organization, newest first, with their counts but without their
notes. It takes limit (1 to 100, default 50) and cursor.
curl -s "https://api.medchartscan.io/v1/batches?limit=10" \
-H "Authorization: Bearer $ACCESS_TOKEN"{
"items": [
{"id": "c29da7b5-0b37-4ea6-8db5-24f7d96f4c8e", "label": "Clinic day 2026-10-25", "source": "api", "is_test": false, "created_at": "2026-10-25T05:09:30.425542Z", "finished_at": "2026-10-25T05:09:30.483722Z", "item_count": 2, "counts": {"queued": 0, "running": 0, "succeeded": 1, "failed": 1, "out_of_scope": 0}}
],
"next_cursor": null
}Retry failed notes
POST/v1/batches/{id}/retry
Queues failed notes again. A failed note keeps its text for 7 days so that you can retry it; after that, it can't be
retried. Each retried note holds a new credit and gets a new analysis ID in the same position of items. Notes that
were out of scope are never retried.
The body is optional:
- No body,
{}, or{"analysis_ids": null}retries every failed note that's still retryable. {"analysis_ids": ["..."]}retries only those notes (1 to 100 IDs). Every listed note must be retryable.
curl -s -X POST https://api.medchartscan.io/v1/batches/$BATCH_ID/retry \
-H "Authorization: Bearer $ACCESS_TOKEN"The response is 200 with the batch. The retried note has a new ID and is queued again:
{
"id": "c29da7b5-0b37-4ea6-8db5-24f7d96f4c8e",
"label": "Clinic day 2026-10-25",
"source": "api",
"is_test": false,
"created_at": "2026-10-25T05:09:30.425542Z",
"finished_at": null,
"created_by": "Epic integration (prod)",
"counts": {"queued": 1, "running": 0, "succeeded": 1, "failed": 0, "out_of_scope": 0},
"items": [
{"id": "4be72810-efa0-43ad-9dfc-61b4451cf385", "client_reference": "enc-0001", "created_at": "2026-10-25T05:14:02.118407Z", "status": "queued", "final_cpt_code": null, "retryable": false, "submitted_code": null, "coding_change": null, "missed_revenue_count": null, "denial_risk_count": null},
{"id": "997aecf7-4ac2-4c79-a1bd-0e8f30047b88", "client_reference": "enc-0002", "created_at": "2026-10-25T05:09:30.425542Z", "status": "succeeded", "final_cpt_code": "99214", "retryable": false, "submitted_code": "99213", "coding_change": "upgraded", "missed_revenue_count": 1, "denial_risk_count": 0}
]
}If nothing is retryable and you didn't list IDs, the response is 200 with the batch unchanged.
| Status | Code | Cause |
|---|---|---|
400 | invalid_request | analysis_ids is empty, lists more than 100 IDs, or has an ID that isn't valid. |
402 | insufficient_credits | The balance doesn't cover every note to retry. Nothing changes. |
403 | baa_resign_required | The organization is paused until an owner signs the updated BAA. |
404 | not_found | The batch isn't visible to your keys, or a listed ID isn't in the batch. |
409 | not_retryable | A listed note didn't fail, or its 7-day window has passed. Nothing changes. |
429 | too_many_batches | The batch had finished, and retrying it would make a sixth unfinished batch. |
Delete batch results
There's no endpoint that deletes a whole batch. Delete each note with
DELETE /v1/analyses/{id}. Deleting a note that's still queued stops it from
running and returns its held credit. A note that's running can't be deleted (409 analysis_running): delete it
once it has finished.