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

> Submit up to 20 jobs at once and receive a single webhook on completion

Batch processing lets you submit multiple audio files in a single API call. All jobs process concurrently. A single `batch.complete` webhook fires when every job is done — regardless of whether individual jobs succeeded or failed.

## When to use batch vs individual jobs

| Use case                                  | Recommendation                   |
| ----------------------------------------- | -------------------------------- |
| Releasing a multi-track album             | Batch — one request, one webhook |
| Processing a catalog of 100+ songs        | Batch in groups of 20            |
| Single track with a webhook               | Individual job                   |
| Real-time single track (need result ASAP) | Individual job                   |
| Metadata-only for a set of tracks         | Batch with `align=false` per job |

## Submitting a batch

Batches are JSON-only — every job must use `audio_url`. File uploads are not supported.

```json theme={null}
POST /api/v1/batch

{
  "webhook_url": "https://api.example.com/webhooks/lyrcs",
  "jobs": [
    { "audio_url": "https://cdn.example.com/track-1.mp3", "language": "Hindi" },
    { "audio_url": "https://cdn.example.com/track-2.mp3", "language": "Tamil" },
    { "audio_url": "https://cdn.example.com/track-3.mp3", "language": "Telugu", "align": false }
  ]
}
```

* Minimum 1 job, maximum **20 jobs** per request
* Per-job fields: `audio_url` (required), `language` (required), `align` (optional, default `true`), `review` (optional, default `false`)

## Rate limit pre-flight

The rate limit check is performed **before** any jobs are created. If submitting N jobs would push any window over the limit, the entire batch is rejected with `429` and no jobs are queued.

```json theme={null}
{
  "error": "rate_limit_exceeded",
  "message": "Rate limit exceeded: minute limit would be exceeded by this batch",
  "code": "RATE_001",
  "retry_after": 60,
  "jobs_requested": 15
}
```

Check the `X-RateLimit-Remaining-*` headers on any previous response to know how much headroom you have before submitting a large batch.

## Polling GET /batch/\[id]

The batch status progresses through:

```
queued → in_progress → complete (or partial)
```

Poll every 15–30 seconds. For a 20-job batch, expect 2–3 minutes total. The batch finalizes as soon as the last job finishes — it does not wait the full polling budget.

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

## Handling partial failures

A `batch.complete` webhook with `status: "partial"` means at least one job failed. The `failed` field tells you how many. Individual job entries in `jobs[]` have their own `status` field.

```javascript theme={null}
// From a batch.complete webhook payload
const failed = payload.jobs.filter((j) => j.status === "failed");

for (const job of failed) {
  console.error(`Job ${job.job_id} (${job.language}) failed — resubmit individually`);
  // Resubmit as an individual job via POST /transcribe
}

const completed = payload.jobs.filter((j) => j.status === "complete");
for (const job of completed) {
  const lrcUrl = job.downloads?.lrc_original;
  if (lrcUrl) {
    // Download and deliver
  }
}
```

<Tip>
  Failed jobs should be resubmitted individually via `POST /transcribe`. The batch endpoint requires at least 1 job and you may only have 1 to retry.
</Tip>

## Metadata-only batch (align=false)

For catalog workflows where you need lyrics text but not LRC files, set `align=false` per job. Processing is faster (\~60s per job) and the batch completes sooner.

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

Jobs submitted with `align=false` will not include `downloads` in the `batch.complete` payload or in `GET /batch/{id}` responses.

## Processing large catalogs

The rate limit is 1,000 jobs per day. For a catalog larger than 1,000 tracks, spread submissions across multiple days or contact [support@lyrcs.ai](mailto:support@lyrcs.ai) to discuss higher limits.

For 1,000 tracks:

* 50 batches of 20 jobs each
* All within the daily limit
* Each batch submitted when the previous `batch.complete` fires (or use a queue)
