Advanced API Usage
Batch API
The Batch API lets you process large volumes of requests asynchronously with reduced pricing and higher rate limits. For pricing details, see Batch API Pricing. If you need lower latency on real-time requests instead, see Priority Processing.
Not every model accepts Batch API requests. See Details on each model page. Unsupported models reject batch requests.
What is the Batch API?
When you make a standard API call to Grok, you send a request and wait for an immediate response. This approach is perfect for interactive applications like chatbots, real-time assistants, or any use case where users are waiting for a response.
The Batch API takes a different approach. Instead of processing requests immediately, you submit them to a queue where they're processed in the background. You don't get an instant response—instead, you check back later to retrieve your results.
Key differences from real-time API requests:
| Real-time API | Batch API | |
|---|---|---|
| Response time | Immediate (seconds) | Typically within 24 hours* |
| Cost | Standard pricing | Reduced pricing (see details) |
| Rate limits | Per-minute limits apply | Requests don't count towards rate limits |
| Use case | Interactive, real-time | Background processing, bulk jobs |
* Processing time: Most batch requests complete within 24 hours, though processing time may vary depending on system load and batch size. Completion time is best effort and not guaranteed.
You can also create, monitor, and manage batches through the xAI Console. The Console provides a visual interface for tracking batch progress and viewing results.
When to use the Batch API
The Batch API is ideal when you don't need immediate results and want to reduce your API costs:
- Running evaluations and benchmarks - Test model performance across thousands of prompts
- Processing large datasets - Analyze customer feedback, classify support tickets, extract entities
- Content moderation at scale - Review backlogs of user-generated content
- Document summarization - Process reports, research papers, or legal documents in bulk
- Data enrichment pipelines - Add AI-generated insights to database records
- Scheduled overnight jobs - Generate daily reports or prepare data for dashboards
How it works
The Batch API workflow consists of four main steps:
- Create a batch - A batch is a container that groups related requests together
- Add requests - Submit your inference requests to the batch queue
- Monitor progress - Poll the batch status to track completion
- Retrieve results - Fetch responses for all processed requests
Let's walk through each step.
Step 1: Create a batch
A batch acts as a container for your requests. Think of it as a folder that groups related work together—you might create separate batches for different datasets, experiments, or job types.
When you create a batch, you receive a batch_id that you'll use to add requests and retrieve results.
curl -X POST https://api.x.ai/v1/batches \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $XAI_API_KEY" \
-d '{
"name": "customer_feedback_analysis"
}'Step 2: Add requests to the batch
With your batch created, you can now add requests to it. Each request will be processed asynchronously.
With the xAI SDK, adding batch requests is simple: use chat.create() for text, image.prepare() for images, video.prepare() for videos, or video.prepare_extension() for video extensions, then pass them as a list. You can also upload a JSONL file if you prefer.
Important: Assign a unique batch_request_id to each request. This ID lets you match results back to their original requests, which becomes important when you're processing hundreds or thousands of items. If you don't provide an ID, we generate a UUID for you. Using your own IDs is useful for idempotency (ensuring a request is only processed once) and for linking batch requests to records in your own system.
curl -X POST https://api.x.ai/v1/batches/{batch_id}/requests \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $XAI_API_KEY" \
-d '{
"batch_requests": [
{
"batch_request_id": "feedback_001",
"batch_request": {
"responses": {
"input": [
{"role": "system", "content": "Classify the sentiment as positive, negative, or neutral."},
{"role": "user", "content": "The product exceeded my expectations!"}
],
"model": "grok-4.3"
}
}
},
{
"batch_request_id": "feedback_002",
"batch_request": {
"responses": {
"input": [
{"role": "system", "content": "Classify the sentiment as positive, negative, or neutral."},
{"role": "user", "content": "Shipping took way too long."}
],
"model": "grok-4.3"
}
}
}
]
}'Step 3: Monitor batch progress
After adding requests, they begin processing in the background. Since batch processing is asynchronous, you need to poll the batch status to know when results are ready.
The batch state includes counters for pending, successful, and failed requests. Poll periodically until num_pending reaches zero, which indicates all requests have been processed (either successfully or with errors).
# Check batch status
curl https://api.x.ai/v1/batches/{batch_id} \
-H "Authorization: Bearer $XAI_API_KEY"
# Response includes state with request counts:
# {
# "state": {
# "num_requests": 100,
# "num_pending": 25,
# "num_success": 70,
# "num_error": 5
# }
# }Understanding batch states
The Batch API tracks state at two levels: the batch level and the individual request level.
Batch-level state shows aggregate progress across all requests in a given batch,
accessible through the batch.state object returned by the client.batch.get() method:
| Counter | Description |
|---|---|
num_requests | Total number of requests added to the batch |
num_pending | Requests waiting to be processed |
num_success | Requests that completed successfully |
num_error | Requests that failed with an error |
num_cancelled | Requests that were cancelled |
When num_pending reaches zero, all requests have been processed (either successfully, with errors, or cancelled).
Individual request states describe where each request is in its lifecycle, accessible through the batch_request_metadata object returned by the client.batch.list_batch_requests() method:
| State | Description |
|---|---|
pending | Request is queued and waiting to be processed |
succeeded | Request completed successfully, result is available |
failed | Request encountered an error during processing |
cancelled | Request was cancelled (e.g., when the batch was cancelled before this request was processed) |
Batch lifecycle: A batch can also be cancelled or expire. If you cancel a batch, pending requests won't be processed, but already-completed results remain available. Batches have an expiration time after which results are no longer accessible—check the expires_at field when retrieving batch details.
Step 4: Retrieve results
You can retrieve results at any time, even before the entire batch completes. Results are available as soon as individual requests finish processing, so you can start consuming completed results while other requests are still in progress.
Each result is linked to its original request via the batch_request_id you assigned earlier. For chat completions, use result.response which has the familiar fields: .content, .usage, .finish_reason, and more. For image requests, use result.image_response which provides .url, .base64, .usage, and .model. For video requests, use result.video_response which provides .url, .duration, .usage, and .model. These are the same response types returned by the regular client.image.sample() and client.video.generate() methods.
The SDK provides convenient .succeeded and .failed properties to separate successful responses from errors.
Pagination: Results are returned in pages. Use the limit parameter to control page size and pagination_token to fetch subsequent pages. When pagination_token is None, you've reached the end.
# Fetch first page
curl "https://api.x.ai/v1/batches/{batch_id}/results?limit=100" \
-H "Authorization: Bearer $XAI_API_KEY"
# Use pagination_token from response to fetch next page
curl "https://api.x.ai/v1/batches/{batch_id}/results?limit=100&pagination_token={token}" \
-H "Authorization: Bearer $XAI_API_KEY"Additional operations
Beyond the core workflow, the Batch API provides additional operations for managing your batches.
Cancel a batch
You can cancel a batch before all requests complete. Already-processed requests remain available in the results, but pending requests will not be processed. You cannot add more requests to a cancelled batch.
curl -X POST https://api.x.ai/v1/batches/{batch_id}:cancel \
-H "Authorization: Bearer $XAI_API_KEY"List all batches
View all batches belonging to your team. Batches are retained until they expire (check the expires_at field). This endpoint supports the same limit and pagination_token parameters for paginating through large lists.
curl "https://api.x.ai/v1/batches?limit=20" \
-H "Authorization: Bearer $XAI_API_KEY"Check individual request status
For detailed tracking, you can inspect the metadata for each request in a batch. This shows the status, timing, and other details for individual requests. This endpoint supports the same limit and pagination_token parameters for paginating through large batches.
curl "https://api.x.ai/v1/batches/{batch_id}/requests?limit=50" \
-H "Authorization: Bearer $XAI_API_KEY"Track costs
Each batch tracks the total processing cost. Access the cost breakdown after processing to understand your spending. For pricing details, see Batch API Pricing on the Pricing page.
# Get batch with cost information
curl -s "https://api.x.ai/v1/batches/{batch_id}/results?limit=100" \
-H "Authorization: Bearer $XAI_API_KEY"
# Cost per result can be found on response.results[].batch_result.response.chat_get_completion.usage.cost_in_usd_ticks
# Cost is returned in ticks (1e-10 USD) for precisionComplete example
This end-to-end example demonstrates a realistic batch workflow: analyzing customer feedback at scale. It creates a batch, submits feedback items for sentiment analysis, waits for processing, and outputs the results. For simplicity, this example doesn't paginate results—see Step 4 for pagination when processing larger batches.
const BASE_URL = "https://api.x.ai/v1";
const headers = { "Content-Type": "application/json", Authorization: `Bearer ${process.env.XAI_API_KEY}` };
// Sample dataset: customer feedback to analyze
const feedbackData = [
{ id: "fb_001", text: "Absolutely love this product! Best purchase ever." },
{ id: "fb_002", text: "Delivery was late and the packaging was damaged." },
{ id: "fb_003", text: "Works fine, nothing special to report." },
{ id: "fb_004", text: "Customer support was incredibly helpful!" },
{ id: "fb_005", text: "The app keeps crashing on my phone." },
];
// Step 1: Create a batch
console.log("Creating batch...");
const batchRes = await fetch(`${BASE_URL}/batches`, {
method: "POST",
headers,
body: JSON.stringify({ name: "feedback_sentiment_analysis" }),
});
const batch = await batchRes.json();
const batchId = batch.batch_id;
console.log(`Batch created: ${batchId}`);
// Step 2: Build and add requests
console.log("\nAdding requests...");
const response = await fetch(`${BASE_URL}/batches/${batchId}/requests`, {
method: "POST",
headers,
body: JSON.stringify({
batch_requests: feedbackData.map((item) => ({
batch_request_id: item.id,
batch_request: {
chat_get_completion: {
model: "grok-4.3",
messages: [
{
role: "system",
content: "Analyze the sentiment of the customer feedback. Respond with exactly one word: positive, negative, or neutral.",
},
{ role: "user", content: item.text },
],
},
},
})),
}),
});
if (!response.ok) throw new Error(`Failed to add requests: ${await response.text()}`);
console.log(`Added ${feedbackData.length} requests`);
// Step 3: Wait for completion
console.log("\nProcessing...");
const interval = setInterval(async () => {
const statusRes = await fetch(`${BASE_URL}/batches/${batchId}`, { headers });
const status = await statusRes.json();
const { num_pending, num_success, num_error, num_requests } = status.state;
console.log(` ${num_success + num_error}/${num_requests} complete`);
if (num_requests > 0 && num_pending === 0) {
clearInterval(interval);
// Step 4: Retrieve and display results
console.log("\n--- Results ---");
const resultsRes = await fetch(`${BASE_URL}/batches/${batchId}/results?limit=100`, { headers });
const { results } = await resultsRes.json();
// Create a lookup for original feedback text
const feedbackLookup = Object.fromEntries(feedbackData.map((item) => [item.id, item.text]));
const succeeded = results.filter((r) => r.batch_result?.response?.chat_get_completion);
const failed = results.filter((r) => !r.batch_result?.response?.chat_get_completion);
for (const result of succeeded) {
const originalText = feedbackLookup[result.batch_request_id] ?? "";
const sentiment = result.batch_result.response.chat_get_completion.choices[0].message.content.trim().toLowerCase();
console.log(`[${sentiment.toUpperCase()}] ${originalText.slice(0, 50)}...`);
}
// Report any failures
if (failed.length > 0) {
console.log("\n--- Errors ---");
for (const result of failed) {
console.log(`[${result.batch_request_id}] ${result.error_message}`);
}
}
// Display cost
let totalTicks = 0;
for (const r of results) {
totalTicks += r.batch_result?.response?.chat_get_completion?.usage?.cost_in_usd_ticks ?? 0;
}
console.log(`\nTotal cost: $${(totalTicks / 1e10).toFixed(4)}`);
}
}, 2000);JSONL File Upload
As an alternative to adding requests via the SDK, you can create batches by uploading a JSONL file. This is useful when generating requests from scripts, pipelines, or external tools.
Each line in the file is a JSON object with four fields: custom_id (unique identifier, maps to batch_request_id), method (always "POST"), url (API endpoint path), and body (the JSON request payload matching the REST API reference for that endpoint).
JSON
{"custom_id": "chat-1", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "grok-4.3", "messages": [{"role": "user", "content": "Classify this as positive, negative, or neutral: The product exceeded my expectations!"}]}}
{"custom_id": "search-1", "method": "POST", "url": "/v1/responses", "body": {"model": "grok-4.3", "tools": [{"type": "web_search"}, {"type": "x_search"}], "input": [{"role": "user", "content": "What are the latest SpaceX launches?"}]}}
{"custom_id": "mcp-1", "method": "POST", "url": "/v1/responses", "body": {"model": "grok-4.3", "tools": [{"type": "mcp", "server_label": "deepwiki", "server_url": "https://mcp.deepwiki.com/mcp"}], "input": [{"role": "user", "content": "What does the xai-sdk-python repo do?"}]}}
{"custom_id": "img-1", "method": "POST", "url": "/v1/images/generations", "body": {"model": "grok-imagine-image-2.0", "prompt": "A futuristic city skyline at sunset"}}
{"custom_id": "img-edit-1", "method": "POST", "url": "/v1/images/edits", "body": {"model": "grok-imagine-image-2.0", "prompt": "Add a rainbow", "image": {"url": "https://picsum.photos/800"}}}
{"custom_id": "vid-1", "method": "POST", "url": "/v1/videos/generations", "body": {"model": "grok-imagine-video-1.5", "prompt": "A rocket launching from Mars", "duration": 8}}
{"custom_id": "vid-edit-1", "method": "POST", "url": "/v1/videos/edits", "body": {"model": "grok-imagine-video", "prompt": "Make it slow motion", "video": {"url": "https://lorem.video/cat_360p_3s"}}}
{"custom_id": "vid-ext-1", "method": "POST", "url": "/v1/videos/extensions", "body": {"model": "grok-imagine-video", "prompt": "The camera slowly pans to reveal a sunset", "video": {"url": "https://lorem.video/cat_360p_3s"}, "duration": 6}}
You can mix different endpoints in the same file. Each request is routed independently.
Supported url values:
| URL | Description |
|---|---|
/v1/chat/completions | Chat completions |
/v1/responses | Model responses |
/v1/images/generations | Image generation |
/v1/images/edits | Image editing |
/v1/videos/generations or /v1/videos | Video generation |
/v1/videos/edits | Video editing |
/v1/videos/extensions | Video extension |
Only batch-enabled models are accepted. Refer to the relevant model pages for the most up-to-date information; models that are not batch-enabled are rejected with "not supported for batch processing".
Upload the file via the Files API, then create a batch referencing it:
# Upload the JSONL file
curl -X POST https://api.x.ai/v1/files \
-H "Authorization: Bearer $XAI_API_KEY" \
-F file="@batch_requests.jsonl"
# Create a batch with the file ID
curl -X POST https://api.x.ai/v1/batches \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $XAI_API_KEY" \
-d '{
"name": "sentiment_analysis",
"input_file_id": "file-abc123"
}'The file is processed asynchronously in the background. If any line is invalid, the batch is cancelled with an error message. Monitor progress and retrieve results the same way as inline batches.
File-based batches are sealed after creation — you cannot add more requests via AddBatchRequests. Maximum file size is 200 MB with up to 50,000 requests. Each custom_id must be unique within the file.
Limitations
Batches
- A team can have an unlimited number of batches.
- Maximum batch creation rate: 2 batch creations per second per team.
Batch Requests
- A batch can contain an unlimited number of requests in theory, but extremely large batches (>100,000 requests) may be throttled for processing stability.
- Each individual request that can be added to a batch has a maximum payload size of 25MB.
- A team can send up to 1000 add-batch-requests API calls every 30 seconds (this is a rolling limit shared across all batches in the team).
- Image and video results contain signed URLs that expire after 1 hour. Download the media promptly after retrieving results.
Tool Use
Both server-side tools and client-side function tools are supported in batch requests.
- Server-side tools (web search, code execution, MCP, etc.) work the same as in the real-time API — they are executed during processing and the final response is returned.
- Client-side function tools are supported: the model returns
tool_callsin the response for you to handle offline. Multi-turn tool calling requires submitting a new batch request with the tool result messages included in the conversation.
Related
Last updated: October 8, 2026