> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lyrcs.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Batch

> Submit up to 20 transcription jobs in a single request

## 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

```
POST https://lyrcs.ai/api/v1/batch
```

**JSON only** — file upload is not supported. Every job must use `audio_url`.

### Body — two accepted shapes

**Shape A: plain array**

```json theme={null}
[
  { "audio_url": "https://cdn.example.com/song-1.mp3", "language": "Hindi" },
  { "audio_url": "https://cdn.example.com/song-2.mp3", "language": "Tamil" },
  { "audio_url": "https://cdn.example.com/song-3.mp3", "language": "Telugu", "align": false }
]
```

**Shape B: object with webhook**

```json theme={null}
{
  "webhook_url": "https://api.example.com/webhooks/lyrcs",
  "jobs": [
    { "audio_url": "https://cdn.example.com/song-1.mp3", "language": "Hindi" },
    { "audio_url": "https://cdn.example.com/song-2.mp3", "language": "Tamil" }
  ]
}
```

### Per-job fields

| Field        | Type    | Required | Default |
| ------------ | ------- | -------- | ------- |
| `audio_url`  | string  | Yes      | —       |
| `language`   | string  | Yes      | —       |
| `align`      | boolean | No       | `true`  |
| `word_align` | boolean | No       | `true`  |
| `review`     | boolean | No       | `false` |

### 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](/errors#rate-limit-headers).

### Response — 202 Accepted

```json theme={null}
{
  "batch_id": "b1a2t3c4-...",
  "job_count": 5,
  "status": "queued",
  "message": "Batch queued. Use GET /api/v1/batch/{batch_id} to poll status."
}
```

***

## GET /api/v1/batch/\[id]

Poll for batch status and per-job results.

### Endpoint

```
GET https://lyrcs.ai/api/v1/batch/{batch_id}
```

### Batch status values

| Status        | Meaning                                         |
| ------------- | ----------------------------------------------- |
| `queued`      | Received — coordinator not yet started          |
| `in_progress` | Jobs are fanning out and processing             |
| `complete`    | All jobs finished successfully                  |
| `partial`     | Done — but at least one job failed or timed out |

### Response

```json theme={null}
{
  "batch_id": "b1a2t3c4-...",
  "status": "complete",
  "job_count": 3,
  "completed": 2,
  "failed": 1,
  "pending": 0,
  "created_at": "2024-01-01T00:00:00.000Z",
  "completed_at": "2024-01-01T00:02:15.000Z",
  "jobs": [
    {
      "job_id": "a1b2c3d4-...",
      "language": "Hindi",
      "status": "complete",
      "align_requested": true,
      "review_required": false,
      "review_url": null,
      "review_approved_at": null,
      "completed_at": "2024-01-01T00:01:45.000Z",
      "studio_url": "https://lyrcs.ai/studio/a1b2c3d4-...",
      "downloads": {
        "lrc_original": "https://lyrcs.ai/api/v1/jobs/a1b2c3d4-.../download/lrc/original",
        "lrc_transliterated": "https://lyrcs.ai/api/v1/jobs/a1b2c3d4-.../download/lrc/transliterated",
        "srt_original": "https://lyrcs.ai/api/v1/jobs/a1b2c3d4-.../download/srt/original",
        "srt_transliterated": "https://lyrcs.ai/api/v1/jobs/a1b2c3d4-.../download/srt/transliterated",
        "words_original": "https://lyrcs.ai/api/v1/jobs/a1b2c3d4-.../download/words/original",
        "words_transliterated": "https://lyrcs.ai/api/v1/jobs/a1b2c3d4-.../download/words/transliterated"
      }
    },
    {
      "job_id": "b2c3d4e5-...",
      "language": "Tamil",
      "status": "failed",
      "align_requested": true,
      "review_required": false,
      "review_url": null,
      "review_approved_at": null,
      "completed_at": null,
      "studio_url": "https://lyrcs.ai/studio/b2c3d4e5-..."
    }
  ]
}
```

<Note>
  `downloads` is only present on jobs where `status === "complete"` AND `align_requested === true`. Failed jobs and `align=false` jobs do not include it.
</Note>

## Examples

<CodeGroup>
  ```bash curl theme={null}
  # Submit batch
  curl -X POST https://lyrcs.ai/api/v1/batch \
    -H "Authorization: Bearer $LYRCS_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "webhook_url": "https://api.example.com/webhooks/lyrcs",
      "jobs": [
        { "audio_url": "https://cdn.example.com/song-1.mp3", "language": "Hindi" },
        { "audio_url": "https://cdn.example.com/song-2.mp3", "language": "Tamil" },
        { "audio_url": "https://cdn.example.com/song-3.mp3", "language": "Telugu" }
      ]
    }'

  # Poll batch status
  curl https://lyrcs.ai/api/v1/batch/b1a2t3c4-... \
    -H "Authorization: Bearer $LYRCS_API_KEY"
  ```

  ```javascript Node.js theme={null}
  // Submit
  const res = await fetch("https://lyrcs.ai/api/v1/batch", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.LYRCS_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      webhook_url: "https://api.example.com/webhooks/lyrcs",
      jobs: [
        { audio_url: "https://cdn.example.com/song-1.mp3", language: "Hindi" },
        { audio_url: "https://cdn.example.com/song-2.mp3", language: "Tamil" },
      ],
    }),
  });
  const { batch_id } = await res.json();

  // Poll
  async function waitForBatch(batchId, apiKey, { intervalMs = 15000, timeoutMs = 600000 } = {}) {
    const deadline = Date.now() + timeoutMs;
    while (Date.now() < deadline) {
      const r = await fetch(`https://lyrcs.ai/api/v1/batch/${batchId}`, {
        headers: { Authorization: `Bearer ${apiKey}` },
      });
      const batch = await r.json();
      if (batch.status === "complete" || batch.status === "partial") return batch;
      await new Promise((res) => setTimeout(res, intervalMs));
    }
    throw new Error("Batch timed out");
  }

  const batch = await waitForBatch(batch_id, process.env.LYRCS_API_KEY);
  const failed = batch.jobs.filter((j) => j.status === "failed");
  if (failed.length) console.warn(`${failed.length} jobs failed`);
  ```

  ```python Python theme={null}
  import os, time, requests

  api_key = os.environ["LYRCS_API_KEY"]
  headers = {"Authorization": f"Bearer {api_key}"}

  # Submit
  r = requests.post(
      "https://lyrcs.ai/api/v1/batch",
      headers=headers,
      json={
          "webhook_url": "https://api.example.com/webhooks/lyrcs",
          "jobs": [
              {"audio_url": "https://cdn.example.com/song-1.mp3", "language": "Hindi"},
              {"audio_url": "https://cdn.example.com/song-2.mp3", "language": "Tamil"},
          ],
      },
  )
  batch_id = r.json()["batch_id"]

  # Poll
  def wait_for_batch(batch_id, interval=15, timeout=600):
      deadline = time.time() + timeout
      while time.time() < deadline:
          r = requests.get(f"https://lyrcs.ai/api/v1/batch/{batch_id}", headers=headers)
          batch = r.json()
          if batch["status"] in ("complete", "partial"):
              return batch
          time.sleep(interval)
      raise TimeoutError("Batch timed out")

  batch = wait_for_batch(batch_id)
  failed = [j for j in batch["jobs"] if j["status"] == "failed"]
  if failed:
      print(f"{len(failed)} jobs failed")
  ```
</CodeGroup>

See the [Batch Processing guide](/guides/batch-processing) for patterns on handling partial failures and polling strategy.
