Skip to main content

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.