Skip to content

Batches

Message Batches let you send large volumes of message requests and process them asynchronously. Instead of making individual API calls, you submit a batch of requests that Anthropic processes in the background, at 50% of the cost, with results available within 24 hours.

Creating a batch

Submit multiple message requests at once. Each request gets a custom_id that you use later to match results:

$response = $client->batches()->create([
    'requests' => [
        [
            'custom_id' => 'request-1',
            'params' => [
                'model' => 'claude-sonnet-4-6',
                'max_tokens' => 1024,
                'messages' => [
                    ['role' => 'user', 'content' => 'What is the capital of France?'],
                ],
            ],
        ],
        [
            'custom_id' => 'request-2',
            'params' => [
                'model' => 'claude-sonnet-4-6',
                'max_tokens' => 1024,
                'messages' => [
                    ['role' => 'user', 'content' => 'What is the capital of Germany?'],
                ],
            ],
        ],
    ],
]);

$response->id;                        // 'msgbatch_04Rka1yCsMLGPnR7kfPdgR8x'
$response->type;                      // 'message_batch'
$response->processingStatus;          // 'in_progress'
$response->requestCounts->processing; // 2
$response->requestCounts->succeeded;  // 0
$response->createdAt;                 // '2025-04-01T12:00:00Z'
$response->expiresAt;                 // '2025-04-02T12:00:00Z'
$response->endedAt;                   // null
$response->cancelInitiatedAt;         // null
$response->archivedAt;                // null
$response->resultsUrl;                // null

The params object inside each request takes the same parameters you'd pass to $client->messages()->create(). You can use any feature that the Messages API supports: tool use, thinking, system messages, and so on.

Checking batch status

Poll for completion using retrieve():

$response = $client->batches()->retrieve('msgbatch_04Rka1yCsMLGPnR7kfPdgR8x');

$response->processingStatus;              // 'ended'
$response->requestCounts->processing;    // 0
$response->requestCounts->succeeded;     // 95
$response->requestCounts->errored;       // 3
$response->requestCounts->canceled;      // 0
$response->requestCounts->expired;       // 2
$response->resultsUrl; // 'https://api.anthropic.com/v1/messages/batches/msgbatch_.../results'

Batch processing statuses:

Status Meaning
in_progress Batch is being processed
canceling Cancellation requested, finishing in-progress requests
ended All requests have completed (check requestCounts for breakdown)

A canceled batch ends up with status ended, not a separate canceled status. The requestCounts breakdown tells you how many individual requests succeeded, errored, were canceled, or expired.

Getting results

Once a batch has ended, stream the results with results(). Each result contains the original custom_id and the outcome:

$response = $client->batches()->results('msgbatch_04Rka1yCsMLGPnR7kfPdgR8x');

foreach ($response as $individual) {
    $individual->customId;     // 'request-1'
    $individual->result->type; // 'succeeded', 'errored', 'canceled', or 'expired'

    if ($individual->result->type === 'succeeded') {
        $individual->result->message->id;               // 'msg_014VwiXbi91y3JMjcpyGBHX2'
        $individual->result->message->content[0]->text; // 'The capital of France is Paris.'
    }

    if ($individual->result->type === 'errored') {
        $individual->result->error->type;    // 'invalid_request_error'
        $individual->result->error->message; // 'max_tokens: Field required'
    }
}

$response->meta(); // [rate limit headers](https://mozex.dev/docs/anthropic-php/v1/reference/meta-information)

Results are streamed as JSONL, so you can process them one at a time without loading everything into memory.

For succeeded requests, $individual->result->message is a full message response with the same structure you'd get from $client->messages()->create().

Listing batches

List your batches with cursor-based pagination:

$response = $client->batches()->list(['limit' => 10]);

foreach ($response->data as $batch) {
    $batch->id;               // 'msgbatch_04Rka1yCsMLGPnR7kfPdgR8x'
    $batch->processingStatus; // 'ended'
}

$response->hasMore; // true
$response->firstId; // 'msgbatch_04Rka1yCsMLGPnR7kfPdgR8x'
$response->lastId;  // 'msgbatch_07V2nm5PqB3bP8szLgTmn1EG'

Canceling a batch

Cancel a batch that's still processing. Requests that are already in-progress will finish, but no new requests will start:

$response = $client->batches()->cancel('msgbatch_04Rka1yCsMLGPnR7kfPdgR8x');

$response->processingStatus; // 'canceling'

Deleting a batch

Delete a batch after it has completed. Only batches with ended status can be deleted:

$response = $client->batches()->delete('msgbatch_04Rka1yCsMLGPnR7kfPdgR8x');

$response->id;   // 'msgbatch_04Rka1yCsMLGPnR7kfPdgR8x'
$response->type; // 'message_batch_deleted'

Typical workflow

Here's the full lifecycle for a batch job:

// 1. Create the batch
$batch = $client->batches()->create(['requests' => $requests]);

// 2. Poll until complete
do {
    sleep(30);
    $batch = $client->batches()->retrieve($batch->id);
} while ($batch->processingStatus !== 'ended');

// 3. Process results
$results = $client->batches()->results($batch->id);

foreach ($results as $individual) {
    if ($individual->result->type === 'succeeded') {
        processResult($individual->customId, $individual->result->message);
    }
}

// 4. Clean up
$client->batches()->delete($batch->id);

For batch limits, pricing, and the full API specification, see the Batch processing guide on the Anthropic docs.

Scroll to top