Batch
Submit several videos in one request and follow their progress.
Examples use VIDEO_ID and clp_live_... as placeholders.
Submit a batch
Send POST /v1/batch with a JSON body:
curl -X POST "https://clypso-eta.vercel.app/v1/batch" \
-H "Authorization: Bearer clp_live_..." \
-H "Content-Type: application/json" \
-d '{"urls":["https://www.youtube.com/watch?v=VIDEO_ID_1","https://www.youtube.com/watch?v=VIDEO_ID_2"]}'| Field | Required | Meaning |
|---|---|---|
urls | Yes | The YouTube video URLs. Each one takes the same forms as url in Extraction. |
formats | No | As in Extraction, for every item. |
lang | No | As in Extraction, for every item. |
An accepted batch returns a 202 with one job for each unique video. The Location header holds the batch's path.
{
"batch_id": "BATCH_ID",
"status": "running",
"items": [
{
"url": "https://www.youtube.com/watch?v=VIDEO_ID_1",
"job_id": "JOB_ID_1"
},
{
"url": "https://www.youtube.com/watch?v=VIDEO_ID_2",
"job_id": "JOB_ID_2"
}
]
}- Items keep the order you sent them in.
- Repeats of a video collapse onto its first URL, so
itemscan be shorter thanurls. - Each
urlin the response is the video's standard watch URL, whatever form you sent.
Limits and rejections
During early access, a batch can hold 10 URLs. Duplicates count toward that, because the check runs before they collapse.
A batch is accepted or rejected whole. When it's rejected, no batch or job is created.
| Problem | You get |
|---|---|
| More than 10 URLs | A 400 with batch_too_large. |
| A URL that isn't a YouTube video | A 400 with invalid_batch_item. The message lists the indexes of the bad URLs. |
Malformed JSON, an unknown field, an unknown format name, an empty urls list, a URL over 2,048 characters, or more than 1,000 URLs | A 400 with invalid_request. |
| Fewer monthly videos left than unique videos in the batch | A 429 with quota_exceeded. Queued work is subtracted first. Every unique video needs room here, even one that turns out to be cached. |
| Your queued and running API and batch jobs, plus this batch, would pass twice your batch limit | A 429 with rate_limited and a Retry-After of 60 seconds. Try again when some have finished. |
Follow its progress
Poll GET /v1/batch/:batchId. In the example, replace the batchId placeholder with the batch_id you got:
curl -X GET "https://clypso-eta.vercel.app/v1/batch/{batchId}" \
-H "Authorization: Bearer clp_live_..."{
"batch_id": "BATCH_ID",
"status": "running",
"created_at": "2026-11-05T09:30:00.000Z",
"completed_at": null,
"counts": {
"total": 2,
"pending": 0,
"running": 1,
"succeeded": 1,
"failed": 0
},
"items": [
{
"job_id": "JOB_ID_1",
"url": "https://www.youtube.com/watch?v=VIDEO_ID_1",
"status": "succeeded",
"error_code": null
},
{
"job_id": "JOB_ID_2",
"url": "https://www.youtube.com/watch?v=VIDEO_ID_2",
"status": "running",
"error_code": null
}
]
}status | What to do |
|---|---|
running | Items are still in progress. Poll again. |
completed | Stop. Every item has finished, which doesn't mean every item worked. Check counts.failed. |
This response has no results. To read one, poll GET /v1/extract/:jobId with the item's job_id, as in Extraction.
Wait a few seconds between polls. Each poll counts toward the rate limit of 60 a minute, and none of them use your monthly limit. To be told when a batch finishes instead, use the batch.completed event in Webhooks.
A batch is visible only to the account that created it. An unknown batch and another account's batch both get a 404 with batch_not_found.
How items run
- Each item is an ordinary job that runs in the background, and they don't all start at once.
- Each one counts against your monthly limit as a single extraction would. A cached result or a failed run doesn't count.
- One failure doesn't stop the others. An item fails with its own code in
error_code, such asno_captionsorvideo_too_long. See Errors. - An item that works also lands in your library in the dashboard.
- To try a failed item again, send it in a new request.
Retry safely
Send an Idempotency-Key header so a retry after a network error can't queue the batch twice. It works as it does for Extraction: the same key and body return the first 202 again, a different body gets a 409 with idempotency_key_reused, and a first request still running gets a 409 with request_in_progress.
A 400 or the queued work 429 doesn't use up the key, so you can retry with it. A quota_exceeded response is stored and replayed, so use a new key after that one.