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

# Batch processing

> Send large volumes of requests asynchronously using the Batch API. Process Responses, chat completions, embeddings, and image generations in bulk within a 24-hour completion window.

The Batch API lets you send groups of requests as a single job that processes asynchronously.
This is useful when you need to run many requests and do not need immediate results — for example, running evaluations, generating embeddings for a large dataset, or classifying content in bulk.

## How batches run

A batch processes its lines one at a time, so per-request latency sets its throughput: 1,000 lines that take 3 seconds each finish in about 50 minutes.
Separate batches run in parallel, so split a large job across several batches to finish it sooner.

Batch lines do not count against your API key's [rate limits](/docs/rate-limiting).
If your organization uses judge-based [auto-routing](/docs/auto-routing), a line with `model: "auto"` still counts against the selected model's own rate limits, and a line that exceeds them fails with `rate_limit_exceeded`.

Batch requests are billed at the same rates as synchronous requests.

Plan for each batch to finish within its **24-hour completion window**.
A batch that overruns is stopped and returns no results, even for lines that completed.
Its status stays `in_progress`, so give your poller its own deadline.

## Supported endpoints

| Endpoint | Description |
| - | - |
| `/v1/responses` | Responses requests |
| `/v1/chat/completions` | Chat completion requests |
| `/v1/embeddings` | Embedding generation |
| `/v1/images/generations` | Image generation |

All requests in a single batch must target the same endpoint.

## Lifecycle

A batch moves through these statuses:

```
validating → in_progress → completed
     ↓            ↓
   failed     cancelling → cancelled
```

| Status | Description |
| - | - |
| `validating` | The input file is being checked for format and content errors. |
| `failed` | Validation failed — the input file contains errors. Check `batch.errors` for details. |
| `in_progress` | Requests are being processed. |
| `completed` | The batch finished. Download results from `output_file_id`. |
| `cancelling` | A cancel was requested. In-flight requests are finishing. |
| `cancelled` | The batch was cancelled. Partial results may be available. |

The batch object's `status` field also defines `finalizing` and `expired`, but batches do not enter either one.
A batch moves from `in_progress` straight to `completed`, and a batch that overruns its window is not marked `expired`.

## Step 1 — Prepare the input file

Create a JSONL file where each line is one request. Every line has four fields:

| Field | Type | Description |
| - | - | - |
| `custom_id` | string | Your identifier for this request. Used to match input to output. Must be unique within the file. |
| `method` | string | `"POST"` — the only supported method. |
| `url` | string | The endpoint path — must match the `endpoint` you declare when creating the batch. |
| `body` | object | The request body — the same parameters you would send to the synchronous endpoint. |

```jsonl theme={null}
{"custom_id": "request-1", "method": "POST", "url": "/v1/responses", "body": {"model": "auto", "input": "Summarize the benefits of batch processing in one sentence.", "store": false}}
{"custom_id": "request-2", "method": "POST", "url": "/v1/responses", "body": {"model": "auto", "input": "What is the capital of France?", "store": false}}
{"custom_id": "request-3", "method": "POST", "url": "/v1/responses", "body": {"model": "auto", "input": "Explain embeddings in one paragraph.", "store": false}}
```

Each `body` takes the same parameters as a synchronous [Responses request](/docs/generate-a-response), with these rules:

* Leave out `stream` and `background`, or set them to `false`. A line that sets either one to `true` fails.
* To set reasoning effort, send both keys of the `reasoning` object, for example `"reasoning": {"effort": "low", "summary": null}`. See [Control reasoning](/docs/generate-a-response#control-reasoning).

These lines set `store: false` because the output file already holds each result. Leave it out if you also want to retrieve each response by ID later.

A single batch can contain up to **10,000 requests**.
The input file can be up to **25 MB**; a larger upload is rejected with HTTP `413`.

## Step 2 — Upload the input file

Upload the JSONL file using the Files API with `purpose: "batch"`.

<CodeGroup>
  ```ts TypeScript SDK theme={null}
  import { SDK } from '@meetkai/mka1';

  const sdk = new SDK({ bearerAuth: 'Bearer <mka1-api-key>' });

  const file = await sdk.llm.files.upload({
    requestBody: {
      file: new File([jsonlContent], 'batch_input.jsonl', { type: 'application/jsonl' }),
      purpose: 'batch',
    },
  });

  console.log(file.id);     // "file-abc123"
  console.log(file.status); // "processed"
  ```

  ```ts OpenAI SDK theme={null}
  import OpenAI from 'openai';

  const openai = new OpenAI({
    apiKey: '<mka1-api-key>',
    baseURL: 'https://apigw.mka1.com/api/v1/llm/',
    defaultHeaders: { 'X-On-Behalf-Of': '<end-user-id>' },
  });

  const file = await openai.files.create({
    file: new File([jsonlContent], 'batch_input.jsonl', { type: 'application/jsonl' }),
    purpose: 'batch',
  });

  console.log(file.id);     // "file-abc123"
  console.log(file.status); // "processed"
  ```

  ```python Python SDK theme={null}
  from meetkai_mka1 import SDK
  import time
  import json

  sdk = SDK(bearer_auth="Bearer <mka1-api-key>")

  file = sdk.llm.files.upload(
      file={"file_name": "batch_input.jsonl", "content": open("batch_input.jsonl", "rb")},
      purpose="batch",
  )

  print(file.id)      # "file-abc123"
  print(file.status)  # "processed"
  ```

  ```csharp C# SDK theme={null}
  using System.Text;
  using MeetKai.MKA1;
  using MeetKai.MKA1.Types.Components;
  using MeetKai.MKA1.Types.Requests;

  var sdk = new SDK(bearerAuth: "Bearer <mka1-api-key>");

  var file = await sdk.Llm.Files.UploadAsync(new UploadFileRequestBody()
  {
      File = new UploadFileFile()
      {
          FileName = "batch_input.jsonl",
          Content = Encoding.UTF8.GetBytes(jsonlContent),
      },
      Purpose = UploadFilePurpose.Batch,
  });

  Console.WriteLine(file.File!.Id);     // "file-abc123"
  ```

  ```bash CLI theme={null}
  mka1 llm files upload \
    --file ./batch_input.jsonl \
    --purpose batch \
    -H 'X-On-Behalf-Of: <end-user-id>'
  ```

  ```bash Bash theme={null}
  curl https://apigw.mka1.com/api/v1/llm/files \
    --request POST \
    --header 'Authorization: Bearer <mka1-api-key>' \
    --header 'X-On-Behalf-Of: <end-user-id>' \
    --form 'file=@batch_input.jsonl;type=application/jsonl' \
    --form 'purpose=batch'
  ```
</CodeGroup>

## Step 3 — Create the batch

Pass the uploaded file ID, the target endpoint, and the completion window.

<CodeGroup>
  ```ts TypeScript SDK theme={null}
  const batch = await sdk.llm.batches.create({
    createBatchRequest: {
      inputFileId: file.id,
      endpoint: '/v1/responses',
      completionWindow: '24h',
    },
  });

  console.log(batch.id);            // "batch_abc123"
  console.log(batch.status);        // "validating" or "in_progress"
  console.log(batch.requestCounts); // { total: 3, completed: 0, failed: 0 }
  ```

  ```ts OpenAI SDK theme={null}
  const batch = await openai.batches.create({
    input_file_id: file.id,
    endpoint: '/v1/responses',
    completion_window: '24h',
  });

  console.log(batch.id);              // "batch_abc123"
  console.log(batch.status);          // "validating" or "in_progress"
  console.log(batch.request_counts);  // { total: 3, completed: 0, failed: 0 }
  ```

  ```python Python SDK theme={null}
  batch = sdk.llm.batches.create(
      input_file_id=file.id,
      endpoint="/v1/responses",
  )

  print(batch.id)              # "batch_abc123"
  print(batch.status)          # "validating" or "in_progress"
  print(batch.request_counts)  # { total: 3, completed: 0, failed: 0 }
  ```

  ```csharp C# SDK theme={null}
  var batch = await sdk.Llm.Batches.CreateAsync(new CreateBatchRequest()
  {
      InputFileId = file.File!.Id,
      Endpoint = BatchEndpoint.RootV1Responses,
  });

  Console.WriteLine(batch.BatchObject!.Id);            // "batch_abc123"
  Console.WriteLine(batch.BatchObject!.Status);        // "validating" or "in_progress"
  Console.WriteLine(batch.BatchObject!.RequestCounts); // { Total: 3, Completed: 0, Failed: 0 }
  ```

  ```bash CLI theme={null}
  mka1 llm batches create --body '{
    "input_file_id": "file-abc123",
    "endpoint": "/v1/responses",
    "completion_window": "24h"
  }'
  ```

  ```bash Bash theme={null}
  curl https://apigw.mka1.com/api/v1/llm/batches \
    --request POST \
    --header 'Content-Type: application/json' \
    --header 'Authorization: Bearer <mka1-api-key>' \
    --header 'X-On-Behalf-Of: <end-user-id>' \
    --data '{
      "input_file_id": "file-abc123",
      "endpoint": "/v1/responses",
      "completion_window": "24h"
    }'
  ```
</CodeGroup>

You can also attach metadata for your own tracking:

<CodeGroup>
  ```ts TypeScript SDK theme={null}
  const batch = await sdk.llm.batches.create({
    createBatchRequest: {
      inputFileId: file.id,
      endpoint: '/v1/responses',
      completionWindow: '24h',
      metadata: {
        description: 'nightly evaluation run',
        run_id: 'eval-2026-03-31',
      },
    },
  });
  ```

  ```ts OpenAI SDK theme={null}
  const batch = await openai.batches.create({
    input_file_id: file.id,
    endpoint: '/v1/responses',
    completion_window: '24h',
    metadata: {
      description: 'nightly evaluation run',
      run_id: 'eval-2026-03-31',
    },
  });
  ```

  ```python Python SDK theme={null}
  batch = sdk.llm.batches.create(
      input_file_id=file.id,
      endpoint="/v1/responses",
      metadata={
          "description": "nightly evaluation run",
          "run_id": "eval-2026-03-31",
      },
  )
  ```

  ```csharp C# SDK theme={null}
  var batch = await sdk.Llm.Batches.CreateAsync(new CreateBatchRequest()
  {
      InputFileId = file.File!.Id,
      Endpoint = BatchEndpoint.RootV1Responses,
      Metadata = new Dictionary<string, string>
      {
          { "description", "nightly evaluation run" },
          { "run_id", "eval-2026-03-31" },
      },
  });
  ```

  ```bash CLI theme={null}
  mka1 llm batches create --body '{
    "input_file_id": "file-abc123",
    "endpoint": "/v1/responses",
    "completion_window": "24h",
    "metadata": {
      "description": "nightly evaluation run",
      "run_id": "eval-2026-03-31"
    }
  }'
  ```
</CodeGroup>

## Step 4 — Check batch status

Poll the batch until it reaches a terminal status.

<CodeGroup>
  ```ts TypeScript SDK theme={null}
  const batch = await sdk.llm.batches.get({ batchId: 'batch_abc123' });

  console.log(batch.status);                // "completed"
  console.log(batch.requestCounts.completed); // 3
  console.log(batch.requestCounts.failed);    // 0
  console.log(batch.outputFileId);           // "file_xyz789"
  ```

  ```ts OpenAI SDK theme={null}
  const batch = await openai.batches.retrieve('batch_abc123');

  console.log(batch.status);                  // "completed"
  console.log(batch.request_counts.completed); // 3
  console.log(batch.request_counts.failed);    // 0
  console.log(batch.output_file_id);           // "file_xyz789"
  ```

  ```python Python SDK theme={null}
  batch = sdk.llm.batches.get(batch_id="batch_abc123")

  print(batch.status)                  # "completed"
  print(batch.request_counts.completed)  # 3
  print(batch.request_counts.failed)     # 0
  print(batch.output_file_id)           # "file_xyz789"
  ```

  ```csharp C# SDK theme={null}
  var batch = await sdk.Llm.Batches.GetAsync("batch_abc123");

  Console.WriteLine(batch.BatchObject!.Status);                // "completed"
  Console.WriteLine(batch.BatchObject!.RequestCounts);         // { Total: 3, Completed: 3, Failed: 0 }
  Console.WriteLine(batch.BatchObject!.OutputFileId);          // "file_xyz789"
  ```

  ```bash CLI theme={null}
  mka1 llm batches get --batch-id batch_abc123
  ```

  ```bash Bash theme={null}
  curl https://apigw.mka1.com/api/v1/llm/batches/batch_abc123 \
    --header 'Authorization: Bearer <mka1-api-key>' \
    --header 'X-On-Behalf-Of: <end-user-id>'
  ```
</CodeGroup>

Here is a polling helper that waits for the batch to finish, up to a deadline:

<CodeGroup>
  ```ts TypeScript SDK theme={null}
  async function waitForBatch(batchId: string, timeoutMs = 120_000) {
    const terminal = ['completed', 'failed', 'cancelled'];
    const start = Date.now();

    while (Date.now() - start < timeoutMs) {
      const batch = await sdk.llm.batches.get({ batchId });
      if (terminal.includes(batch.status)) return batch;
      await new Promise((r) => setTimeout(r, 2000));
    }

    throw new Error(`Batch ${batchId} did not complete within ${timeoutMs}ms`);
  }

  const completed = await waitForBatch(batch.id);
  ```

  ```ts OpenAI SDK theme={null}
  async function waitForBatch(batchId: string, timeoutMs = 120_000) {
    const terminal = ['completed', 'failed', 'cancelled'];
    const start = Date.now();

    while (Date.now() - start < timeoutMs) {
      const batch = await openai.batches.retrieve(batchId);
      if (terminal.includes(batch.status)) return batch;
      await new Promise((r) => setTimeout(r, 2000));
    }

    throw new Error(`Batch ${batchId} did not complete within ${timeoutMs}ms`);
  }

  const completed = await waitForBatch(batch.id);
  ```

  ```python Python SDK theme={null}
  def wait_for_batch(sdk, batch_id, timeout_ms=120_000):
      terminal = {"completed", "failed", "cancelled"}
      start = time.time() * 1000

      while (time.time() * 1000) - start < timeout_ms:
          batch = sdk.llm.batches.get(batch_id=batch_id)
          if batch.status in terminal:
              return batch
          time.sleep(2)

      raise TimeoutError(f"Batch {batch_id} did not complete within {timeout_ms}ms")

  completed = wait_for_batch(sdk, batch.id)
  ```

  ```csharp C# SDK theme={null}
  async Task<BatchObject> WaitForBatch(SDK sdk, string batchId, int timeoutMs = 300_000)
  {
      var terminal = new HashSet<BatchObjectStatus>
      {
          BatchObjectStatus.Completed,
          BatchObjectStatus.Failed,
          BatchObjectStatus.Cancelled,
      };
      var start = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();

      while (DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() - start < timeoutMs)
      {
          var batch = await sdk.Llm.Batches.GetAsync(batchId);
          if (terminal.Contains(batch.BatchObject!.Status))
              return batch.BatchObject;
          await Task.Delay(2000);
      }

      throw new TimeoutException($"Batch {batchId} did not complete within {timeoutMs}ms");
  }

  var completed = await WaitForBatch(sdk, batch.BatchObject!.Id);
  Console.WriteLine(completed.Status); // BatchObjectStatus.Completed
  ```

  ```bash CLI theme={null}
  # Poll a batch until it reaches a terminal status or 2 minutes pass, using --jq and a shell loop.
  BATCH_ID=batch_abc123
  DEADLINE=$((SECONDS + 120))
  while (( SECONDS < DEADLINE )); do
    STATUS=$(mka1 llm batches get --batch-id "$BATCH_ID" --jq '.status' --output-format json | tr -d '"')
    echo "status: $STATUS"
    case "$STATUS" in
      completed|failed|cancelled) break ;;
    esac
    sleep 2
  done
  ```
</CodeGroup>

## Step 5 — Download the results

Once the batch is `completed`, download the output file. It is a JSONL file where each line contains the `custom_id` you provided, the response, and any error.

<CodeGroup>
  ```ts TypeScript SDK theme={null}
  const stream = await sdk.llm.files.content({ fileId: completed.outputFileId! });
  const reader = stream.getReader();
  const chunks: Uint8Array[] = [];
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    chunks.push(value);
  }
  const text = new TextDecoder().decode(Buffer.concat(chunks));

  const results = text
    .split('\n')
    .filter((line) => line.trim())
    .map((line) => JSON.parse(line));

  for (const result of results) {
    console.log(`${result.custom_id}: status=${result.response.status_code}`);
    console.log(`  body:`, result.response.body);
  }
  ```

  ```ts OpenAI SDK theme={null}
  const content = await openai.files.content(completed.output_file_id!);
  const text = await content.text();

  const results = text
    .split('\n')
    .filter((line) => line.trim())
    .map((line) => JSON.parse(line));

  for (const result of results) {
    console.log(`${result.custom_id}: status=${result.response.status_code}`);
    console.log(`  body:`, result.response.body);
  }
  ```

  ```python Python SDK theme={null}
  content = sdk.llm.files.content(file_id=completed.output_file_id)
  text = content.decode("utf-8")

  results = [json.loads(line) for line in text.strip().split("\n") if line.strip()]

  for result in results:
      print(f"{result['custom_id']}: status={result['response']['status_code']}")
      print(f"  body: {result['response']['body']}")
  ```

  ```csharp C# SDK theme={null}
  var content = await sdk.Llm.Files.ContentAsync(completed.OutputFileId!);

  var bytes = content.TwoHundredTextPlainBytes
      ?? content.TwoHundredApplicationJsonlBytes
      ?? content.TwoHundredApplicationJsonBytes;
  var text = Encoding.UTF8.GetString(bytes!);

  Console.WriteLine(text); // JSONL with one line per request
  ```

  ```bash CLI theme={null}
  # Download the JSONL output file
  mka1 llm files content \
    --file-id file_xyz789 \
    --output-file ./batch_output.jsonl

  # Inspect the results inline with jq
  mka1 llm files content --file-id file_xyz789 \
    --jq '"\(.custom_id): status=\(.response.status_code)"'
  ```

  ```bash Bash theme={null}
  curl https://apigw.mka1.com/api/v1/llm/files/file_xyz789/content \
    --header 'Authorization: Bearer <mka1-api-key>' \
    --header 'X-On-Behalf-Of: <end-user-id>'
  ```
</CodeGroup>

Each line in the output file has this structure:

```json theme={null}
{
  "id": "req_abc123",
  "custom_id": "request-1",
  "response": {
    "status_code": 200,
    "request_id": "req_abc123",
    "body": { "...": "same shape as the synchronous endpoint response" }
  },
  "error": null
}
```

If a request failed, `response` is `null` and `error` contains the details:

```json theme={null}
{
  "id": "req_def456",
  "custom_id": "request-2",
  "response": null,
  "error": {
    "code": "processing_error",
    "message": "The request could not be processed."
  }
}
```

If any requests failed, the batch also provides an `error_file_id` containing only the failed entries.

## Cancel a batch

Cancel a batch that is still in progress. Requests that have already completed remain in the output.

<CodeGroup>
  ```ts TypeScript SDK theme={null}
  const cancelled = await sdk.llm.batches.cancel({ batchId: 'batch_abc123' });
  console.log(cancelled.status); // "cancelling"
  ```

  ```ts OpenAI SDK theme={null}
  const cancelled = await openai.batches.cancel('batch_abc123');
  console.log(cancelled.status); // "cancelling"
  ```

  ```python Python SDK theme={null}
  cancelled = sdk.llm.batches.cancel(batch_id="batch_abc123")
  print(cancelled.status)  # "cancelling"
  ```

  ```csharp C# SDK theme={null}
  var cancelled = await sdk.Llm.Batches.CancelAsync("batch_abc123");
  Console.WriteLine(cancelled.BatchObject!.Status); // "cancelling"
  ```

  ```bash CLI theme={null}
  mka1 llm batches cancel --batch-id batch_abc123
  ```

  ```bash Bash theme={null}
  curl https://apigw.mka1.com/api/v1/llm/batches/batch_abc123/cancel \
    --request POST \
    --header 'Authorization: Bearer <mka1-api-key>' \
    --header 'X-On-Behalf-Of: <end-user-id>'
  ```
</CodeGroup>

The batch transitions to `cancelling` while in-flight requests finish, then to `cancelled`.

## List batches

Retrieve all batches for the current account, newest first. Supports pagination.

<CodeGroup>
  ```ts TypeScript SDK theme={null}
  const page = await sdk.llm.batches.list({ limit: 20 });

  for (const batch of page.data) {
    console.log(`${batch.id}: ${batch.status} (${batch.requestCounts?.completed}/${batch.requestCounts?.total})`);
  }
  ```

  ```ts OpenAI SDK theme={null}
  const page = await openai.batches.list({ limit: 20 });

  for (const batch of page.data) {
    console.log(`${batch.id}: ${batch.status} (${batch.request_counts?.completed}/${batch.request_counts?.total})`);
  }
  ```

  ```python Python SDK theme={null}
  page = sdk.llm.batches.list(limit=20)

  for batch in page.data:
      print(f"{batch.id}: {batch.status} ({batch.request_counts.completed}/{batch.request_counts.total})")
  ```

  ```csharp C# SDK theme={null}
  var page = await sdk.Llm.Batches.ListAsync(limit: 20);

  foreach (var batch in page.ListBatchesResponseValue!.Data!)
  {
      Console.WriteLine($"{batch.Id}: {batch.Status} ({batch.RequestCounts?.Completed}/{batch.RequestCounts?.Total})");
  }
  ```

  ```bash CLI theme={null}
  mka1 llm batches list --limit 20
  ```

  ```bash Bash theme={null}
  curl 'https://apigw.mka1.com/api/v1/llm/batches?limit=20' \
    --header 'Authorization: Bearer <mka1-api-key>' \
    --header 'X-On-Behalf-Of: <end-user-id>'
  ```
</CodeGroup>

Use the `after` parameter with a batch ID to page through results.

## Example: batch chat completions

The same flow works for chat completions. Change the `url` in each JSONL line and the `endpoint` when creating the batch.

```jsonl theme={null}
{"custom_id": "chat-1", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "auto", "messages": [{"role": "user", "content": "What is the capital of France?"}], "max_tokens": 100}}
{"custom_id": "chat-2", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "auto", "messages": [{"role": "user", "content": "Explain embeddings in one paragraph."}], "max_tokens": 100}}
```

<CodeGroup>
  ```ts TypeScript SDK theme={null}
  const file = await sdk.llm.files.upload({
    requestBody: {
      file: new File([jsonlContent], 'chat_batch.jsonl', { type: 'application/jsonl' }),
      purpose: 'batch',
    },
  });

  const batch = await sdk.llm.batches.create({
    createBatchRequest: {
      inputFileId: file.id,
      endpoint: '/v1/chat/completions',
      completionWindow: '24h',
    },
  });

  const completed = await waitForBatch(batch.id);
  const stream = await sdk.llm.files.content({ fileId: completed.outputFileId! });
  // ... read stream as shown in Step 5
  ```

  ```ts OpenAI SDK theme={null}
  const file = await openai.files.create({
    file: new File([jsonlContent], 'chat_batch.jsonl', { type: 'application/jsonl' }),
    purpose: 'batch',
  });

  const batch = await openai.batches.create({
    input_file_id: file.id,
    endpoint: '/v1/chat/completions',
    completion_window: '24h',
  });

  const completed = await waitForBatch(batch.id);
  const content = await openai.files.content(completed.output_file_id!);
  const results = (await content.text()).split('\n').filter(Boolean).map((line) => JSON.parse(line));

  for (const r of results) {
    console.log(`${r.custom_id}: ${r.response.body.choices[0].message.content}`);
  }
  ```

  ```python Python SDK theme={null}
  file = sdk.llm.files.upload(
      file={"file_name": "chat_batch.jsonl", "content": open("chat_batch.jsonl", "rb")},
      purpose="batch",
  )

  batch = sdk.llm.batches.create(
      input_file_id=file.id,
      endpoint="/v1/chat/completions",
  )

  completed = wait_for_batch(sdk, batch.id)
  # Download and parse results as shown in Step 5
  ```

  ```csharp C# SDK theme={null}
  var file = await sdk.Llm.Files.UploadAsync(new UploadFileRequestBody()
  {
      File = new UploadFileFile()
      {
          FileName = "chat_batch.jsonl",
          Content = Encoding.UTF8.GetBytes(jsonlContent),
      },
      Purpose = UploadFilePurpose.Batch,
  });

  var batch = await sdk.Llm.Batches.CreateAsync(new CreateBatchRequest()
  {
      InputFileId = file.File!.Id,
      Endpoint = BatchEndpoint.RootV1ChatCompletions,
  });

  var completed = await WaitForBatch(sdk, batch.BatchObject!.Id);
  // Download and parse results as shown in Step 5
  ```

  ```bash CLI theme={null}
  # Upload the chat completions JSONL input
  FILE_ID=$(mka1 llm files upload \
    --file ./chat_batch.jsonl \
    --purpose batch \
    --jq '.id' --output-format json | tr -d '"')

  # Create the batch against the chat completions endpoint
  mka1 llm batches create --body "{
    \"input_file_id\": \"$FILE_ID\",
    \"endpoint\": \"/v1/chat/completions\",
    \"completion_window\": \"24h\"
  }"

  # Poll, then download the results — see Steps 4 and 5
  ```
</CodeGroup>

## Example: batch embeddings

The same flow works for embeddings. Change the `url` in each JSONL line and the `endpoint` when creating the batch.

```jsonl theme={null}
{"custom_id": "embed-1", "method": "POST", "url": "/v1/embeddings", "body": {"model": "auto", "input": "The quick brown fox"}}
{"custom_id": "embed-2", "method": "POST", "url": "/v1/embeddings", "body": {"model": "auto", "input": "jumps over the lazy dog"}}
```

<CodeGroup>
  ```ts TypeScript SDK theme={null}
  const file = await sdk.llm.files.upload({
    requestBody: {
      file: new File([jsonlContent], 'embed_batch.jsonl', { type: 'application/jsonl' }),
      purpose: 'batch',
    },
  });

  const batch = await sdk.llm.batches.create({
    createBatchRequest: {
      inputFileId: file.id,
      endpoint: '/v1/embeddings',
      completionWindow: '24h',
    },
  });

  const completed = await waitForBatch(batch.id);
  const stream = await sdk.llm.files.content({ fileId: completed.outputFileId! });
  // ... read stream as shown in Step 5
  ```

  ```ts OpenAI SDK theme={null}
  const file = await openai.files.create({
    file: new File([jsonlContent], 'embed_batch.jsonl', { type: 'application/jsonl' }),
    purpose: 'batch',
  });

  const batch = await openai.batches.create({
    input_file_id: file.id,
    endpoint: '/v1/embeddings',
    completion_window: '24h',
  });

  const completed = await waitForBatch(batch.id);
  const content = await openai.files.content(completed.output_file_id!);
  const results = (await content.text()).split('\n').filter(Boolean).map((line) => JSON.parse(line));

  for (const r of results) {
    console.log(`${r.custom_id}: ${r.response.body.data[0].embedding.length} dimensions`);
  }
  ```

  ```python Python SDK theme={null}
  file = sdk.llm.files.upload(
      file={"file_name": "embed_batch.jsonl", "content": open("embed_batch.jsonl", "rb")},
      purpose="batch",
  )

  batch = sdk.llm.batches.create(
      input_file_id=file.id,
      endpoint="/v1/embeddings",
  )

  completed = wait_for_batch(sdk, batch.id)
  # Download and parse results as shown in Step 5
  ```

  ```csharp C# SDK theme={null}
  var file = await sdk.Llm.Files.UploadAsync(new UploadFileRequestBody()
  {
      File = new UploadFileFile()
      {
          FileName = "embed_batch.jsonl",
          Content = Encoding.UTF8.GetBytes(jsonlContent),
      },
      Purpose = UploadFilePurpose.Batch,
  });

  var batch = await sdk.Llm.Batches.CreateAsync(new CreateBatchRequest()
  {
      InputFileId = file.File!.Id,
      Endpoint = BatchEndpoint.RootV1Embeddings,
  });

  var completed = await WaitForBatch(sdk, batch.BatchObject!.Id);
  // Download and parse results as shown in Step 5
  ```

  ```bash CLI theme={null}
  # Upload the embeddings JSONL input
  FILE_ID=$(mka1 llm files upload \
    --file ./embed_batch.jsonl \
    --purpose batch \
    --jq '.id' --output-format json | tr -d '"')

  # Create the batch against the embeddings endpoint
  mka1 llm batches create --body "{
    \"input_file_id\": \"$FILE_ID\",
    \"endpoint\": \"/v1/embeddings\",
    \"completion_window\": \"24h\"
  }"

  # Poll, then download the results — see Steps 4 and 5
  ```
</CodeGroup>

## Validation errors

If the input file has formatting issues, the batch moves to `failed` immediately.
Common causes:

* **Invalid JSON** — a line is not valid JSON.
* **Missing fields** — a line is missing `custom_id`, `method`, `url`, or `body`.
* **Wrong method** — `method` must be `"POST"`.
* **URL mismatch** — the `url` in a line does not match the `endpoint` declared when creating the batch.
* **Duplicate `custom_id`** — each `custom_id` must be unique within the file.

Check `batch.errors.data` for the specific error messages and line numbers.

<CodeGroup>
  ```ts TypeScript SDK theme={null}
  const batch = await sdk.llm.batches.get({ batchId: 'batch_abc123' });

  if (batch.status === 'failed' && batch.errors) {
    for (const err of batch.errors.data ?? []) {
      console.log(`Line ${err.line}: [${err.code}] ${err.message}`);
    }
  }
  ```

  ```ts OpenAI SDK theme={null}
  const batch = await openai.batches.retrieve('batch_abc123');

  if (batch.status === 'failed' && batch.errors) {
    for (const err of batch.errors.data ?? []) {
      console.log(`Line ${err.line}: [${err.code}] ${err.message}`);
    }
  }
  ```

  ```python Python SDK theme={null}
  batch = sdk.llm.batches.get(batch_id="batch_abc123")

  if batch.status == "failed" and batch.errors:
      for err in batch.errors.data or []:
          print(f"Line {err.line}: [{err.code}] {err.message}")
  ```

  ```csharp C# SDK theme={null}
  var batch = await sdk.Llm.Batches.GetAsync("batch_abc123");

  if (batch.BatchObject!.Status == BatchObjectStatus.Failed && batch.BatchObject.Errors != null)
  {
      foreach (var err in batch.BatchObject.Errors.Data ?? new List<BatchObjectErrorsData>())
      {
          Console.WriteLine($"Line {err.Line}: [{err.Code}] {err.Message}");
      }
  }
  ```

  ```bash CLI theme={null}
  mka1 llm batches get --batch-id batch_abc123 \
    --jq '.errors.data[] | "Line \(.line): [\(.code)] \(.message)"'
  ```
</CodeGroup>

## See also

* [Generate a response](/docs/generate-a-response) for the synchronous request that each Responses line mirrors.
* [Files and vector stores](/docs/files-and-vector-stores) for the Files API used to upload batch inputs.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.