Skip to main content
Lesson 2 of 12 · Batch API: Bulk Processing

Four-step workflow

Lesson 2 of 12 Updated November 2, 2026 Developer
On this page
  1. The lifecycle of a batch
  2. Step 1: Create a batch
  3. Step 2: Add requests
  4. Step 3: Monitor progress
  5. Step 4: Retrieve results
  6. The workflow in practice
  7. Error handling in the workflow
  8. Key takeaways

Updated November 2, 2026 based on xAI documentation: the batch discount is 20% on eligible text models (not 50%); the examples use the REST API and the official xai-sdk (client.batch.*; xAI’s Batch API does not follow OpenAI’s format, so the OpenAI SDK does not apply); and the Responses API takes precedence over Chat Completions, a legacy endpoint at xAI. Sources: Batch API · REST reference · Batch pricing.

The lifecycle of a batch

Every batch follows the same cycle: create, add requests, process, retrieve results. Understanding this cycle is essential before you move to implementation, because each step has its own constraints and best practices.

Step 1: Create a batch

Everything starts with a POST /v1/batches call, with one required field: the batch name. The API returns a unique identifier (batch_id) that you use for all subsequent operations.

curl -X POST https://api.x.ai/v1/batches \
  -H "Authorization: Bearer $XAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "customer_review_analysis"}'

The response also contains the batch creation and expiration dates (create_time, expire_time) and a state object whose counters are all still at zero.

Step 2: Add requests

There are two methods:

Through the API: you send requests with POST /v1/batches/{batch_id}/requests. Each call carries a list of requests, and you can keep adding them as long as the batch is not canceled. This is the path for batches built incrementally.

Through a JSONL file: you prepare a file containing all your requests, upload it through the Files API, then create the batch from that file. The batch is then sealed: no more additions are possible. This is the path for large volumes prepared in advance.

Each request carries a unique identifier within the batch: batch_request_id in the API, custom_id in the JSONL file. If you omit it in the API, xAI generates a UUID; give your own instead (a database ID, for example): it serves as the matching key between your data and the results, and guarantees that a request is processed only once.

Step 3: Monitor progress

Processing starts as soon as requests arrive; there is no “start” step. Meanwhile, GET /v1/batches/{batch_id} returns the state object and its counters: num_requests (total), num_pending (pending), num_success (successful), num_error (failed), and num_cancelled (canceled).

curl https://api.x.ai/v1/batches/$BATCH_ID \
  -H "Authorization: Bearer $XAI_API_KEY"

When num_pending drops to zero, all requests have been processed, successfully or not. Periodic polling is enough: a few seconds for a small test batch, a few minutes for a large batch.

Step 4: Retrieve results

Results are read with GET /v1/batches/{batch_id}/results, without waiting for the batch to finish: each request is available as soon as it is processed. The response is paginated: limit sets the page size (1,000 maximum), and the returned pagination_token is used to request the next page; it is empty on the last page.

curl "https://api.x.ai/v1/batches/$BATCH_ID/results?limit=100" \
  -H "Authorization: Bearer $XAI_API_KEY"

Each result carries the batch_request_id of its request: this lets you reconstruct the mapping between your input data and Grok’s responses.

The workflow in practice

In a production pipeline, the workflow generally translates into two separate processes:

  • A submission process that creates the batch, prepares and adds requests, then stores the batch_id
  • A retrieval process that periodically checks the counters and downloads results as they become available

This separation lets you integrate the Batch API into event-driven architectures or existing data pipelines (cron jobs, Apache Airflow, queues).

Error handling in the workflow

A batch has no global “succeeded” or “failed” state: you read it through its counters. A failed request increments num_error and appears in the results with its error message, without blocking the others. You therefore need to examine each result and handle failures case by case.

Two events affect the entire batch. The first is cancellation, by you or by xAI, which then explains it in the cancel_by_xai_message field. This happens in particular when a line in a JSONL file is invalid. The other is expiration (expire_time), after which results are no longer accessible: retrieve them promptly.

Key takeaways

  • The workflow follows the order: create (with a name), add, monitor, retrieve
  • The request identifier (batch_request_id, or custom_id in JSONL) is your matching key
  • Progress is read from the state counters; everything is processed when num_pending is zero
  • Results are paginated (limit, pagination_token) and available as they are processed
  • Errors are handled request by request; cancellation and expiration affect the entire batch