POST /api/v1/batch
Submit a batch of jobs. All jobs are processed concurrently. A single batch.complete webhook fires when all jobs are done.
Endpoint
JSON only — file upload is not supported. Every job must use audio_url.
Body — two accepted shapes
Shape A: plain array
Shape B: object with webhook
Per-job fields
external_id is an arbitrary string (max 255 characters) used to map each job back to a track in your own system. It is echoed back in GET /api/v1/batch/{id}, GET /api/v1/jobs/{job_id}, and the job.complete webhook payload for each job.
review_stage, review_delivery and end_user behave exactly as on
POST /transcribe, and both review fields require
review: true on the same job. All of them are per job, not per batch — one
batch can carry songs belonging to several of your customers, and a customer sent
on twenty jobs produces one record, not twenty.
Limits
- Minimum 1 job, maximum 20 jobs per request
- Rate limit check is pre-flight: if submitting N jobs would push any window over the limit, the entire batch is rejected with
429 before any jobs are created. See rate limit headers.
Response — 202 Accepted
Retrying safely
Send an Idempotency-Key header and reuse it on every retry of the same batch. A retried batch would otherwise create duplicate jobs and duplicate charges for every song in it.
The hash covers the whole job list, so re-sending the same batch replays the original batch_id, while a different list under a reused key is refused with 409 IDEM_002 rather than silently processed. See POST /transcribe for the full behaviour table.
GET /api/v1/batch/[id]
Poll for batch status and per-job results.
Endpoint
Batch status values
Per-job status
Each entry in jobs carries the same status and stage as
GET /jobs/{id}, derived identically — the two endpoints
never disagree about the same job.
processing is the normal state for several minutes after submission, and on a
large batch for considerably longer, since jobs queue behind one another. A
job in processing has not failed. failed is returned only when the
pipeline recorded a failure, and carries an error object saying which stage
and why.
Don’t treat “not yet complete” as failure. A 20-song batch can take 15 minutes
or more end to end, and individual jobs move through transcribing and
aligning on their own schedule. Wait for the batch to reach complete or
partial, or use the batch.complete webhook.
Response
downloads is only present on jobs where status === "complete" AND align_requested === true. Failed jobs and align=false jobs do not include it. words_original and words_transliterated appear only when the job was submitted with word_align=true.
Examples
See the Batch Processing guide for patterns on handling partial failures and polling strategy.
Quality signals in a batch
Each job carries a summary of the two quality blocks rather than the full
detail returned by GET /jobs/{id}. A batch is a triage
view: the counts tell you which songs deserve a look, and you fetch those jobs
individually to see what was actually found.
Both fields are null when the check did not run — which is not the same
as a zero count. A zero suggestion_count means the listener ran and agreed
with every line. See Quality Signals for the full
contract and for what the flags do and do not catch.