배치 처리
배치 처리 (Batch processing)
배치 처리는 대량의 요청을 효율적으로 처리하는 강력한 방법이에요. 즉시 응답이 필요한 요청을 하나씩 처리하는 대신, 여러 요청을 함께 제출해 비동기로 처리할 수 있어요. 이 패턴은 특히 다음 상황에서 유용해요.
- 대량의 데이터를 처리해야 할 때
- 즉시 응답이 필요 없을 때
- 비용 효율을 최적화하고 싶을 때
- 대규모 평가나 분석을 실행할 때
Message Batches API는 Anthropic이 이 패턴을 처음 구현한 것이에요.
참고: ZDR(제로 데이터 보존)이 이 기능에 어떻게 적용되는지는 API와 데이터 보존을 참고해 주세요.
출처: 문서
본문
Message Batches API
Message Batches API는 대량의 Messages 요청을 비동기로 처리하는 강력하고 비용 효율적인 방법이에요. 즉시 응답이 필요 없는 작업에 잘 맞아요. 대부분의 배치가 1시간 안에 끝나면서도 비용은 50% 줄고 처리량은 높아져요.
이 가이드 외에도 API 레퍼런스를 직접 살펴볼 수 있어요.
Message Batches API 동작 방식
Message Batches API에 요청을 보내면:
- 시스템이 제공된 Messages 요청으로 새 Message Batch를 만들어요.
- 배치가 비동기로 처리되며, 각 요청은 독립적으로 처리돼요.
- 배치 상태를 폴링하고, 모든 요청의 처리가 끝나면 결과를 검색할 수 있어요.
즉시 결과가 필요 없는 대량 작업에 특히 유용해요. 예를 들면:
- 대규모 평가: 수천 개의 테스트 케이스를 효율적으로 처리.
- 콘텐츠 중재: 대량의 사용자 생성 콘텐츠를 비동기로 분석.
- 데이터 분석: 대용량 데이터세트에 대한 인사이트나 요약 생성.
- 대량 콘텐츠 생성: 다양한 목적(예: 제품 설명, 기사 요약)으로 많은 텍스트 생성.
배치 제한
- Message Batch는 100,000개의 Message 요청 또는 256MB 크기 중 먼저 도달하는 쪽으로 제한돼요.
- 시스템은 각 배치를 최대한 빨리 처리하며, 대부분 1시간 안에 완료돼요. 모든 메시지가 완료되거나 24시간이 지난 뒤, 둘 중 먼저 도달하는 시점에 배치 결과에 접근할 수 있어요. 배치는 24시간 안에 처리가 끝나지 않으면 만료돼요.
- 배치 결과는 생성 후 29일 동안 사용할 수 있어요. 그 후에도 배치는 볼 수 있지만, 결과는 더 이상 다운로드할 수 없어요.
- 배치는 워크스페이스로 범위가 제한돼요. 요청이 실행되는 워크스페이스 안에서 생성된 모든 배치(와 그 결과)를 볼 수 있어요.
- 속도 제한은 Batches API HTTP 요청과 배치 안에서 처리를 기다리는 요청 수 모두에 적용돼요. Message Batches API 속도 제한 참고. 또한 현재 수요와 요청량에 따라 처리가 느려질 수 있어요. 그 경우 24시간 후 만료되는 요청이 더 많아질 수 있어요.
- 높은 처리량과 동시 처리 때문에 배치가 워크스페이스의 구성된 지출 한도를 약간 초과할 수 있어요.
- 각 배치 요청은
max_tokens가 최소1이어야 해요.max_tokens: 0(캐시 프리워밍)은 배치 안에서 지원되지 않아요. 배치 처리 중에 작성된 일시적 캐시 항목이 후속 요청이 실행되기 전에 만료될 가능성이 높기 때문이에요.
지원 모델
모든 활성 모델이 Message Batches API를 지원해요.
배치할 수 있는 것
Messages API에 할 수 있는 거의 모든 요청을 배치에 포함할 수 있어요. 여기에는 다음이 포함돼요.
- Vision
- 모든 서버 도구(웹 검색, 웹 fetch, 코드 실행, MCP 커넥터, advisor, 도구 검색)를 포함한 도구 사용
- 시스템 메시지
- 다중 턴 대화
- 확장 thinking
- 대부분의 베타 기능
배치의 각 요청이 독립적으로 처리되므로, 단일 배치 안에서 다양한 유형의 요청을 섞을 수 있어요.
소수의 Messages API 파라미터는 배치 요청에서 지원되지 않아요. 이 중 하나라도 포함하면 검증 오류가 반환돼요.
| 파라미터 | 이유 |
|---|---|
stream: true |
배치 결과는 스트림이 아니라 단일 파일로 돌아와요. |
speed (Fast mode) |
Fast mode는 동기 지연 시간을 조정하는데, 비동기 배치 처리에는 적용되지 않아요. |
max_tokens: 0 |
배치 제한 참고. |
팁: 배치는 처리에 5분 이상 걸릴 수 있으므로, 공유 컨텍스트가 있는 배치를 처리할 때 프롬프트 캐싱과 함께 1시간 캐시 지속 시간을 쓰면 캐시 적중률이 더 좋아져요.
요금
Batches API는 상당한 비용 절감을 제공해요. 모든 사용량이 표준 API 요금의 50%로 청구돼요.
| 모델 | 배치 입력 | 배치 출력 |
|---|---|---|
| Claude Fable 5.1 | $5 / MTok | $25 / MTok |
| Claude Mythos 5.1 (제한적 제공) | $5 / MTok | $25 / MTok |
| Claude Fable 5 | $5 / MTok | $25 / MTok |
| Claude Mythos 5 (제한적 제공) | $5 / MTok | $25 / MTok |
| Claude Opus 5.5 | $2 / MTok | $10 / MTok |
| Claude Opus 5 | $2.50 / MTok | $12.50 / MTok |
| Claude Opus 4.8 | $2.50 / MTok | $12.50 / MTok |
| Claude Opus 4.7 | $2.50 / MTok | $12.50 / MTok |
| Claude Opus 4.6 | $2.50 / MTok | $12.50 / MTok |
| Claude Opus 4.5 | $2.50 / MTok | $12.50 / MTok |
| Claude Opus 4.1 (은퇴, Bedrock과 Google Cloud 제외) | $7.50 / MTok | $37.50 / MTok |
| Claude Opus 4 (은퇴, Google Cloud 제외) | $7.50 / MTok | $37.50 / MTok |
| Claude Sonnet 5 | $1 / MTok | $5 / MTok |
| Claude Sonnet 4.6 | $1.50 / MTok | $7.50 / MTok |
| Claude Sonnet 4.5 | $1.50 / MTok | $7.50 / MTok |
| Claude Sonnet 4 (은퇴, Bedrock과 Google Cloud 제외) | $1.50 / MTok | $7.50 / MTok |
| Claude Haiku 4.5 | $0.50 / MTok | $2.50 / MTok |
| Claude Haiku 3.5 (은퇴, Bedrock과 Google Cloud 제외) | $0.40 / MTok | $2 / MTok |
- MTok: 백만 토큰. $5 / MTok은 백만 토큰당 $5예요.
- 제한적 접근: Project Glasswing의 일부로 초대 전용으로 별도 제공돼요. 접근하려면 Anthropic, AWS, Google Cloud 계정 팀에 문의하세요.
- 은퇴: 다른 클라우드 플랫폼에서는 여전히 사용 가능할 수 있어요. 자세한 내용은 모델 은퇴 참고.
Message Batches API 사용 방법
배치 준비하고 만들기
Message Batch는 Message를 만드는 요청 목록으로 구성돼요. 개별 요청의 모양은 다음과 같아요.
- Messages 요청을 식별하는 고유
custom_id. 1~64자여야 하고 영숫자, 하이픈, 밑줄만 포함해야 해요(^[a-zA-Z0-9_-]{1,64}$와 일치). - 표준 Messages API 파라미터가 있는
params객체.
requests 파라미터에 이 목록을 넘겨 배치를 만들 수 있어요.
curl https://api.anthropic.com/v1/messages/batches \
--header "x-api-key: $ANTHR...KEY" \
--header "anthropic-version: 2023-06-01" \
--header "content-type: application/json" \
--data \
'{
"requests": [
{
"custom_id": "my-first-request",
"params": {
"model": "claude-opus-5-5",
"max_tokens": 1024,
"messages": [
{"role": "user", "content": "Hello, world"}
]
}
},
{
"custom_id": "my-second-request",
"params": {
"model": "claude-opus-5-5",
"max_tokens": 1024,
"messages": [
{"role": "user", "content": "Hi again, friend"}
]
}
}
]
}'
ant messages:batches create <<'YAML'
requests:
- custom_id: my-first-request
params:
model: claude-opus-5-5
max_tokens: 1024
messages:
- role: user
content: Hello, world
- custom_id: my-second-request
params:
model: claude-opus-5-5
max_tokens: 1024
messages:
- role: user
content: Hi again, friend
YAML
from anthropic.types.message_create_params import MessageCreateParamsNonStreaming
from anthropic.types.messages.batch_create_params import Request
client = anthropic.Anthropic()
message_batch = client.messages.batches.create(
requests=[
Request(
custom_id="my-first-request",
params=MessageCreateParamsNonStreaming(
model="claude-opus-5-5",
max_tokens=1024,
messages=[
{
"role": "user",
"content": "Hello, world",
}
],
),
),
Request(
custom_id="my-second-request",
params=MessageCreateParamsNonStreaming(
model="claude-opus-5-5",
max_tokens=1024,
messages=[
{
"role": "user",
"content": "Hi again, friend",
}
],
),
),
]
)
print(message_batch)
const client = new Anthropic();
const messageBatch = await client.messages.batches.create({
requests: [
{
custom_id: "my-first-request",
params: {
model: "claude-opus-5-5",
max_tokens: 1024,
messages: [{ role: "user", content: "Hello, world" }]
}
},
{
custom_id: "my-second-request",
params: {
model: "claude-opus-5-5",
max_tokens: 1024,
messages: [{ role: "user", content: "Hi again, friend" }]
}
}
]
});
console.log(messageBatch);
using Anthropic;
using Anthropic.Models.Messages;
using Anthropic.Models.Messages.Batches;
AnthropicClient client = new();
var batch = await client.Messages.Batches.Create(new BatchCreateParams
{
Requests =
[
new()
{
CustomID = "my-first-request",
Params = new()
{
Model = Model.ClaudeOpus5_5,
MaxTokens = 1024,
Messages =
[
new() { Role = Role.User, Content = "Hello, world" }
]
}
},
new()
{
CustomID = "my-second-request",
Params = new()
{
Model = Model.ClaudeOpus5_5,
MaxTokens = 1024,
Messages =
[
new() { Role = Role.User, Content = "Hi again, friend" }
]
}
}
]
});
Console.WriteLine(batch);
client := anthropic.NewClient()
batch, _ := client.Messages.Batches.New(context.Background(),
anthropic.MessageBatchNewParams{
Requests: []anthropic.MessageBatchNewParamsRequest{
{
CustomID: "my-first-request",
Params: anthropic.MessageBatchNewParamsRequestParams{
Model: anthropic.ModelClaudeOpus5_5,
MaxTokens: 1024,
Messages: []anthropic.MessageParam{
anthropic.NewUserMessage(
anthropic.NewTextBlock("Hello, world"),
),
},
},
},
{
CustomID: "my-second-request",
Params: anthropic.MessageBatchNewParamsRequestParams{
Model: anthropic.ModelClaudeOpus5_5,
MaxTokens: 1024,
Messages: []anthropic.MessageParam{
anthropic.NewUserMessage(
anthropic.NewTextBlock("Hi again, friend"),
),
},
},
},
},
})
fmt.Println(batch.ID)
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
BatchCreateParams params = BatchCreateParams.builder()
.addRequest(
BatchCreateParams.Request.builder()
.customId("my-first-request")
.params(
BatchCreateParams.Request.Params.builder()
.model(Model.CLAUDE_OPUS_5_5)
.maxTokens(1024)
.addUserMessage("Hello, world")
.build()
)
.build()
)
.addRequest(
BatchCreateParams.Request.builder()
.customId("my-second-request")
.params(
BatchCreateParams.Request.Params.builder()
.model(Model.CLAUDE_OPUS_5_5)
.maxTokens(1024)
.addUserMessage("Hi again, friend")
.build()
)
.build()
)
.build();
MessageBatch messageBatch = client.messages().batches().create(params);
System.out.println(messageBatch);
$client = new Client();
$batch = $client->messages->batches->create(
requests: [
[
'custom_id' => 'my-first-request',
'params' => [
'model' => 'claude-opus-5-5',
'max_tokens' => 1024,
'messages' => [
['role' => 'user', 'content' => 'Hello, world']
]
]
],
[
'custom_id' => 'my-second-request',
'params' => [
'model' => 'claude-opus-5-5',
'max_tokens' => 1024,
'messages' => [
['role' => 'user', 'content' => 'Hi again, friend']
]
]
]
],
);
echo $batch->id;
client = Anthropic::Client.new
batch = client.messages.batches.create(
requests: [
{
custom_id: "my-first-request",
params: {
model: "claude-opus-5-5",
max_tokens: 1024,
messages: [
{ role: "user", content: "Hello, world" }
]
}
},
{
custom_id: "my-second-request",
params: {
model: "claude-opus-5-5",
max_tokens: 1024,
messages: [
{ role: "user", content: "Hi again, friend" }
]
}
}
]
)
puts batch
이 예제에서는 두 개의 개별 요청이 비동기 처리를 위해 함께 배치돼요. 각 요청은 고유한 custom_id를 가지며, Messages API 호출에 쓰는 표준 파라미터를 포함해요.
팁: 배치 요청을 Messages API로 테스트하기
각 메시지 요청의
params객체 검증은 비동기로 수행되며, 검증 오류는 전체 배치 처리가 끝났을 때 반환돼요. 먼저 Messages API로 요청 형태를 확인해 입력을 올바르게 만들고 있는지 확인할 수 있어요.
배치가 처음 생성되면 응답의 처리 상태는 in_progress예요.
{
"id": "msgbatch_01HkcTjaV5uDC8jWR4ZsDV8d",
"type": "message_batch",
"processing_status": "in_progress",
"request_counts": {
"processing": 2,
"succeeded": 0,
"errored": 0,
"canceled": 0,
"expired": 0
},
"ended_at": null,
"created_at": "2024-09-24T18:37:24.100435Z",
"expires_at": "2024-09-25T18:37:24.100435Z",
"cancel_initiated_at": null,
"results_url": null
}
배치 추적
Message Batch의 processing_status 필드는 배치가 처리 중인 단계를 나타내요. in_progress로 시작하고, 배치의 모든 요청 처리가 끝나면 ended로 업데이트되며 결과가 준비돼요. Console을 방문하거나 검색 엔드포인트를 사용해 배치 상태를 모니터링할 수 있어요.
Message Batch 완료 폴링하기
Message Batch를 폴링하려면 id가 필요해요. 배치를 만들 때 응답에서 제공되거나 배치를 나열해 얻을 수 있어요. 처리가 끝날 때까지 배치 상태를 주기적으로 확인하는 폴링 루프를 구현할 수 있어요.
#!/bin/sh
# ...
# Check the status; repeat until processing_status is "ended"
curl -s "https://api.anthropic.com/v1/messages/batches/$MESSAGE_BATCH_ID" \
--header "x-api-key: $ANTHR...KEY" \
--header "anthropic-version: 2023-06-01" \
| jq -r '.processing_status'
#!/bin/bash
# ...
# Check the status; repeat until processing_status is "ended"
ant messages:batches retrieve \
--message-batch-id "$MESSAGE_BATCH_ID" \
--transform processing_status --raw-output
import time
client = anthropic.Anthropic()
MESSAGE_BATCH_ID = "msgbatch_01HkcTjaV5uDC8jWR4ZsDV8d"
message_batch = None
while True:
message_batch = client.messages.batches.retrieve(MESSAGE_BATCH_ID)
if message_batch.processing_status == "ended":
break
print(f"Batch {MESSAGE_BATCH_ID} is still processing...")
time.sleep(60)
print(message_batch)
const client = new Anthropic();
const messageBatchId = "msgbatch_01HkcTjaV5uDC8jWR4ZsDV8d";
let messageBatch;
while (true) {
messageBatch = await client.messages.batches.retrieve(messageBatchId);
if (messageBatch.processing_status === "ended") {
break;
}
console.log(`Batch ${messageBatchId} is still processing... waiting`);
await new Promise((resolve) => setTimeout(resolve, 60_000));
}
console.log(messageBatch);
AnthropicClient client = new();
string messageBatchId = Environment.GetEnvironmentVariable("MESSAGE_BATCH_ID");
MessageBatch messageBatch = null;
while (true)
{
messageBatch = await client.Messages.Batches.Retrieve(messageBatchId);
if (messageBatch.ProcessingStatus == "ended")
{
break;
}
Console.WriteLine($"Batch {messageBatchId} is still processing...");
await Task.Delay(60000);
}
Console.WriteLine(messageBatch);
client := anthropic.NewClient()
messageBatchID := os.Getenv("MESSAGE_BATCH_ID")
var messageBatch *anthropic.MessageBatch
for {
var err error
messageBatch, err = client.Messages.Batches.Get(context.TODO(), messageBatchID, anthropic.MessageBatchGetParams{})
if err != nil {
log.Fatal(err)
}
if messageBatch.ProcessingStatus == "ended" {
break
}
fmt.Printf("Batch %s is still processing...\n", messageBatchID)
time.Sleep(60 * time.Second)
}
fmt.Println(messageBatch)
import com.anthropic.models.messages.batches.MessageBatch;
// ...
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
String messageBatchId = "msgbatch_01HkcTjaV5uDC8jWR4ZsDV8d";
MessageBatch messageBatch = null;
while (true) {
messageBatch = client.messages().batches().retrieve(messageBatchId);
if (messageBatch.processingStatus().equals(MessageBatch.ProcessingStatus.ENDED)) {
break;
}
System.out.println("Batch " + messageBatchId + " is still processing...");
Thread.sleep(60000);
}
System.out.println(messageBatch);
$client = new Client();
$messageBatchId = getenv("MESSAGE_BATCH_ID");
$messageBatch = null;
while (true) {
$messageBatch = $client->messages->batches->retrieve(
messageBatchID: $messageBatchId,
);
if ($messageBatch->processingStatus === "ended") {
break;
}
echo "Batch {$messageBatchId} is still processing...\n";
sleep(60);
}
echo json_encode($messageBatch, JSON_PRETTY_PRINT);
client = Anthropic::Client.new
message_batch_id = ENV["MESSAGE_BATCH_ID"]
message_batch = nil
loop do
message_batch = client.messages.batches.retrieve(message_batch_id)
break if message_batch.processing_status == :ended
puts "Batch #{message_batch_id} is still processing..."
sleep 60
end
puts message_batch
모든 Message Batch 나열하기
list 엔드포인트로 워크스페이스의 모든 Message Batch를 나열할 수 있어요. API는 페이지네이션을 지원하며 필요에 따라 추가 페이지를 자동으로 가져와요.
#!/bin/sh
# Fetches one page. While the response's has_more is true, pass its
# last_id as after_id to fetch the next page. (The SDKs and the CLI
# perform automatic pagination.)
curl -s "https://api.anthropic.com/v1/messages/batches?limit=20" \
--header "x-api-key: $ANTHR...KEY" \
--header "anthropic-version: 2023-06-01"
# Automatically fetches more pages as needed
ant messages:batches list --limit 20
client = anthropic.Anthropic()
# Automatically fetches more pages as needed.
for message_batch in client.messages.batches.list(limit=20):
print(message_batch)
const client = new Anthropic();
// Automatically fetches more pages as needed.
for await (const messageBatch of client.messages.batches.list({
limit: 20
})) {
console.log(messageBatch);
}
AnthropicClient client = new();
var parameters = new BatchListParams
{
Limit = 20
};
// Automatically fetches more pages as needed
var page = await client.Messages.Batches.List(parameters);
await foreach (var messageBatch in page.Paginate())
{
Console.WriteLine(messageBatch);
}
client := anthropic.NewClient()
// Automatically fetches more pages as needed
iter := client.Messages.Batches.ListAutoPaging(context.TODO(), anthropic.MessageBatchListParams{
Limit: anthropic.Int(20),
})
for iter.Next() {
messageBatch := iter.Current()
fmt.Println(messageBatch)
}
if err := iter.Err(); err != nil {
log.Fatal(err)
}
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
// Automatically fetches more pages as needed
for (MessageBatch messageBatch : client
.messages()
.batches()
.list(BatchListParams.builder().limit(20).build())
.autoPager()) {
System.out.println(messageBatch);
}
$client = new Client();
// Automatically fetches more pages as needed
foreach ($client->messages->batches->list(limit: 20)->pagingEachItem() as $messageBatch) {
echo $messageBatch->id . "\n";
}
client = Anthropic::Client.new
# Automatically fetches more pages as needed
client.messages.batches.list(limit: 20).auto_paging_each do |message_batch|
puts message_batch
end
배치 결과 검색
배치 처리가 끝나면 배치 안의 각 Messages 요청에 결과가 있어요. 네 가지 결과 유형이 있어요.
| 결과 유형 | 설명 |
|---|---|
succeeded |
요청이 성공했어요. 메시지 결과를 포함해요. |
errored |
요청에서 오류가 발생했고 메시지가 생성되지 않았어요. 가능한 오류에는 잘못된 요청과 내부 서버 오류가 있어요. 이 요청들에는 청구되지 않아요. |
canceled |
이 요청이 모델로 보내지기 전에 사용자가 배치를 취소했어요. 이 요청들에는 청구되지 않아요. |
expired |
이 요청이 모델로 보내지기 전에 배치가 24시간 만료에 도달했어요. 이 요청들에는 청구되지 않아요. |
배치의 request_counts는 결과 개요를 보여주며, 각 네 가지 상태에 도달한 요청 수를 나타내요.
배치 결과는 Message Batch의 results_url 속성에서 다운로드할 수 있고, 조직 권한이 허용하면 Console에서도 볼 수 있어요. 결과가 잠재적으로 크기 때문에, 한 번에 모두 다운로드하기보다 결과를 스트리밍하는 걸 권장해요.
#!/bin/sh
# Fetch the batch's results_url, then stream the .jsonl results it
# points to. For per-result handling (retries, validation errors),
# use the SDK examples in the other tabs.
RESULTS_URL=$(curl -s "https://api.anthropic.com/v1/messages/batches/msgbatch_01HkcTjaV5uDC8jWR4ZsDV8d" \
--header "anthropic-version: 2023-06-01" \
--header "x-api-key: $ANTHR...KEY" \
| jq -r '.results_url')
curl -s "$RESULTS_URL" \
--header "anthropic-version: 2023-06-01" \
--header "x-api-key: $ANTHR...KEY" \
| jq -r '"\(.result.type): \(.custom_id)"'
# Prints one line per result, e.g. `{"custom_id":"test-1","type":"succeeded",…}`.
# For per-result handling (retries, validation errors), use the SDK
# examples in the other tabs.
ant messages:batches results \
--message-batch-id msgbatch_01HkcTjaV5uDC8jWR4ZsDV8d \
--transform '{custom_id,"type":result.type,"error":result.error.error.type}' \
--format jsonl
client = anthropic.Anthropic()
# Stream results file in memory-efficient chunks, processing one at a time
for result in client.messages.batches.results(
"msgbatch_01HkcTjaV5uDC8jWR4ZsDV8d",
):
outcome = result.result
match outcome.type:
case "succeeded":
print(f"Success! {result.custom_id}")
case "errored":
if outcome.error.error.type == "invalid_request_error":
# Request body must be fixed before re-sending request
print(f"Validation error {result.custom_id}")
else:
# Request can be retried directly
print(f"Server error {result.custom_id}")
case "expired":
print(f"Request expired {result.custom_id}")
const client = new Anthropic();
// Stream results file in memory-efficient chunks, processing one at a time
for await (const result of await client.messages.batches.results(
"msgbatch_01HkcTjaV5uDC8jWR4ZsDV8d"
)) {
switch (result.result.type) {
case "succeeded":
console.log(`Success! ${result.custom_id}`);
break;
case "errored":
if (result.result.error.type === "invalid_request_error") {
// Request body must be fixed before re-sending request
console.log(`Validation error: ${result.custom_id}`);
} else {
// Request can be retried directly
console.log(`Server error: ${result.custom_id}`);
}
break;
case "expired":
console.log(`Request expired: ${result.custom_id}`);
break;
}
}
AnthropicClient client = new();
await foreach (var result in client.Messages.Batches.ResultsStreaming("msgbatch_01HkcTjaV5uDC8jWR4ZsDV8d"))
{
switch (result.Result.Type)
{
case "succeeded":
Console.WriteLine($"Success! {result.CustomID}");
break;
case "errored":
if (result.Result.Error?.Type == "invalid_request")
{
Console.WriteLine($"Validation error: {result.CustomID}");
}
else
{
Console.WriteLine($"Server error: {result.CustomID}");
}
break;
case "expired":
Console.WriteLine($"Request expired: {result.CustomID}");
break;
}
}
client := anthropic.NewClient()
stream := client.Messages.Batches.ResultsStreaming(context.TODO(), "msgbatch_01HkcTjaV5uDC8jWR4ZsDV8d", anthropic.MessageBatchResultsParams{})
for stream.Next() {
result := stream.Current()
switch variant := result.Result.AsAny().(type) {
case anthropic.MessageBatchSucceededResult:
fmt.Printf("Success! %s\n", result.CustomID)
case anthropic.MessageBatchErroredResult:
fmt.Printf("Error: %s - %s\n", result.CustomID, variant.Error.Error.Message)
case anthropic.MessageBatchExpiredResult:
fmt.Printf("Request expired: %s\n", result.CustomID)
}
}
if err := stream.Err(); err != nil {
log.Fatal(err)
}
import com.anthropic.core.http.StreamResponse;
import com.anthropic.models.messages.batches.BatchResultsParams;
import com.anthropic.models.messages.batches.MessageBatchIndividualResponse;
// ...
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
// Stream results file in memory-efficient chunks, processing one at a time
try (
StreamResponse<MessageBatchIndividualResponse> streamResponse = client
.messages()
.batches()
.resultsStreaming(
BatchResultsParams.builder()
.messageBatchId("msgbatch_01HkcTjaV5uDC8jWR4ZsDV8d")
.build()
)
) {
streamResponse
.stream()
.forEach(result -> {
switch (result.result().type().value()) {
case SUCCEEDED -> System.out.println("Success! " + result.customId());
case ERRORED -> {
if (result.result().asErrored().error().error().isInvalidRequestError()) {
// Request body must be fixed before re-sending request
System.out.println("Validation error: " + result.customId());
} else {
// Request can be retried directly
System.out.println("Server error: " + result.customId());
}
}
case EXPIRED -> System.out.println("Request expired: " + result.customId());
}
});
}
use Anthropic\Messages\Batches\MessageBatchErroredResult;
use Anthropic\Messages\Batches\MessageBatchExpiredResult;
use Anthropic\Messages\Batches\MessageBatchSucceededResult;
$client = new Client();
foreach ($client->messages->batches->resultsStream(messageBatchID: 'msgbatch_01HkcTjaV5uDC8jWR4ZsDV8d') as $result) {
switch (true) {
case $result->result instanceof MessageBatchSucceededResult:
echo "Success! {$result->customID}\n";
break;
case $result->result instanceof MessageBatchErroredResult:
if ($result->result->error->error->type === "invalid_request_error") {
echo "Validation error: {$result->customID}\n";
} else {
echo "Server error: {$result->customID}\n";
}
break;
case $result->result instanceof MessageBatchExpiredResult:
echo "Request expired: {$result->customID}\n";
break;
}
}
client = Anthropic::Client.new
client.messages.batches.results_streaming("msgbatch_01HkcTjaV5uDC8jWR4ZsDV8d").each do |result|
outcome = result.result
case outcome
when Anthropic::Models::Messages::MessageBatchSucceededResult
puts "Success! #{result.custom_id}"
when Anthropic::Models::Messages::MessageBatchErroredResult
if outcome.error.type == :invalid_request
puts "Validation error: #{result.custom_id}"
else
puts "Server error: #{result.custom_id}"
end
when Anthropic::Models::Messages::MessageBatchExpiredResult
puts "Request expired: #{result.custom_id}"
end
end
결과는 .jsonl 형식이에요. 각 줄은 Message Batch 안의 단일 요청 결과를 나타내는 유효한 JSON 객체예요. 각 스트리밍 결과에 대해 custom_id와 결과 유형에 따라 다르게 처리할 수 있어요. 다음은 결과 예제 세트예요.
{"custom_id":"my-second-request","result":{"type":"succeeded","message":{"id":"msg_014VwiXbi91y3JMjcpyGBHX5","type":"message","role":"assistant","model":"claude-opus-5-5","content":[{"type":"text","text":"Hello again! It's nice to see you. How can I assist you today? Is there anything specific you'd like to chat about or any questions you have?"}],"stop_reason":"end_turn","stop_sequence":null,"usage":{"input_tokens":11,"output_tokens":36}}}}
{"custom_id":"my-first-request","result":{"type":"succeeded","message":{"id":"msg_01FqfsLoHwgeFbguDgpz48m7","type":"message","role":"assistant","model":"claude-opus-5-5","content":[{"type":"text","text":"Hello! How can I assist you today? Feel free to ask me any questions or let me know if there's anything you'd like to chat about."}],"stop_reason":"end_turn","stop_sequence":null,"usage":{"input_tokens":10,"output_tokens":34}}}}
결과에 오류가 있으면 result.error가 표준 오류 모양으로 설정돼요.
팁: 배치 결과가 입력 순서와 맞지 않을 수 있어요
배치 결과는 어떤 순서로든 반환될 수 있으며, 배치 생성 시 요청 순서와 일치하지 않을 수 있어요. 앞선 예제에서 두 번째 배치 요청의 결과가 첫 번째보다 먼저 반환됐죠. 결과를 해당 요청과 정확히 맞추려면 항상
custom_id필드를 사용하세요.
Message Batch 취소하기
처리 중인 Message Batch는 cancel 엔드포인트로 취소할 수 있어요. 취소 직후 배치의 processing_status는 canceling이 돼요. 앞서 설명한 것과 같은 폴링 기법으로 취소가 완료될 때까지 기다릴 수 있어요. 취소된 배치는 ended 상태로 끝나며, 취소 전에 처리된 요청의 부분 결과를 포함할 수 있어요.
#!/bin/sh
# ...
curl --request POST https://api.anthropic.com/v1/messages/batches/$MESSAGE_BATCH_ID/cancel \
--header "x-api-key: $ANTHR...KEY" \
--header "anthropic-version: 2023-06-01"
#!/bin/bash
# ...
ant messages:batches cancel --message-batch-id "$MESSAGE_BATCH_ID"
client = anthropic.Anthropic()
MESSAGE_BATCH_ID = "msgbatch_01HkcTjaV5uDC8jWR4ZsDV8d"
message_batch = client.messages.batches.cancel(
MESSAGE_BATCH_ID,
)
print(message_batch)
const client = new Anthropic();
const messageBatch = await client.messages.batches.cancel(MESSAGE_BATCH_ID);
console.log(messageBatch);
AnthropicClient client = new();
string messageBatchId = Environment.GetEnvironmentVariable("MESSAGE_BATCH_ID");
var messageBatch = await client.Messages.Batches.Cancel(messageBatchId);
Console.WriteLine(messageBatch);
client := anthropic.NewClient()
messageBatchID := os.Getenv("MESSAGE_BATCH_ID")
messageBatch, err := client.Messages.Batches.Cancel(context.TODO(), messageBatchID, anthropic.MessageBatchCancelParams{})
if err != nil {
log.Fatal(err)
}
fmt.Println(messageBatch)
import com.anthropic.models.messages.batches.*;
// ...
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
MessageBatch messageBatch = client
.messages()
.batches()
.cancel("msgbatch_01HkcTjaV5uDC8jWR4ZsDV8d");
System.out.println(messageBatch);
$client = new Client();
$messageBatch = $client->messages->batches->cancel(
messageBatchID: 'msgbatch_example_id',
);
echo $messageBatch;
client = Anthropic::Client.new
message_batch_id = ENV.fetch("MESSAGE_BATCH_ID")
message_batch = client.messages.batches.cancel(message_batch_id)
puts message_batch
응답은 canceling 상태의 배치를 보여줘요.
{
"id": "msgbatch_013Zva2CMHLNnXjNJJKqJ2EF",
"type": "message_batch",
"processing_status": "canceling",
"request_counts": {
"processing": 2,
"succeeded": 0,
"errored": 0,
"canceled": 0,
"expired": 0
},
"ended_at": null,
"created_at": "2024-09-24T18:37:24.100435Z",
"expires_at": "2024-09-25T18:37:24.100435Z",
"cancel_initiated_at": "2024-09-24T18:39:03.114875Z",
"results_url": null
}
Message Batches와 함께 프롬프트 캐싱 사용하기
Message Batches API는 프롬프트 캐싱을 지원해요. 배치 요청의 비용과 처리 시간을 줄일 수 있어요. 프롬프트 캐싱과 Message Batches의 요금 할인은 누적되므로, 두 기능을 함께 쓰면 비용이 더 절감돼요. 다만 배치 요청이 비동기로 동시에 처리되므로 캐시 적중은 최선(best-effort) 방식으로 제공돼요. 사용자들은 트래픽 패턴에 따라 보통 30%~98%의 캐시 적중률을 경험해요.
배치 요청에서 캐시 적중 가능성을 최대화하려면:
- 배치 안의 모든 Message 요청에 동일한
cache_control블록을 포함해요. - 캐시 항목이 5분 수명 후 만료되지 않도록 꾸준한 요청 흐름을 유지해요.
- 가능한 한 많은 캐시된 콘텐츠를 공유하도록 요청을 구성해요.
배치에서 프롬프트 캐싱을 구현하는 예제:
curl https://api.anthropic.com/v1/messages/batches \
--header "x-api-key: $ANTHR...KEY" \
--header "anthropic-version: 2023-06-01" \
--header "content-type: application/json" \
--data \
'{
"requests": [
{
"custom_id": "my-first-request",
"params": {
"model": "claude-opus-5-5",
"max_tokens": 1024,
"system": [
{
"type": "text",
"text": "You are an AI assistant tasked with analyzing literary works. Your goal is to provide insightful commentary on themes, characters, and writing style.\n"
},
{
"type": "text",
"text": "<the entire contents of Pride and Prejudice>",
"cache_control": {"type": "ephemeral"}
}
],
"messages": [
{"role": "user", "content": "Analyze the major themes in Pride and Prejudice."}
]
}
},
{
"custom_id": "my-second-request",
"params": {
"model": "claude-opus-5-5",
"max_tokens": 1024,
"system": [
{
"type": "text",
"text": "You are an AI assistant tasked with analyzing literary works. Your goal is to provide insightful commentary on themes, characters, and writing style.\n"
},
{
"type": "text",
"text": "<the entire contents of Pride and Prejudice>",
"cache_control": {"type": "ephemeral"}
}
],
"messages": [
{"role": "user", "content": "Write a summary of Pride and Prejudice."}
]
}
}
]
}'
ant messages:batches create <<'YAML'
requests:
- custom_id: my-first-request
params:
model: claude-opus-5-5
max_tokens: 1024
system:
- type: text
text: >
You are an AI assistant tasked with analyzing literary works. Your
goal is to provide insightful commentary on themes, characters, and
writing style.
- type: text
text: "<the entire contents of Pride and Prejudice>"
cache_control:
type: ephemeral
messages:
- role: user
content: Analyze the major themes in Pride and Prejudice.
- custom_id: my-second-request
params:
model: claude-opus-5-5
max_tokens: 1024
system:
- type: text
text: >
You are an AI assistant tasked with analyzing literary works. Your
goal is to provide insightful commentary on themes, characters, and
writing style.
- type: text
text: "<the entire contents of Pride and Prejudice>"
cache_control:
type: ephemeral
messages:
- role: user
content: Write a summary of Pride and Prejudice.
YAML
from anthropic.types.message_create_params import MessageCreateParamsNonStreaming
from anthropic.types.messages.batch_create_params import Request
client = anthropic.Anthropic()
message_batch = client.messages.batches.create(
requests=[
Request(
custom_id="my-first-request",
params=MessageCreateParamsNonStreaming(
model="claude-opus-5-5",
max_tokens=1024,
system=[
{
"type": "text",
"text": "You are an AI assistant tasked with analyzing literary works. Your goal is to provide insightful commentary on themes, characters, and writing style.\n",
},
{
"type": "text",
"text": "<the entire contents of Pride and Prejudice>",
"cache_control": {"type": "ephemeral"},
},
],
messages=[
{
"role": "user",
"content": "Analyze the major themes in Pride and Prejudice.",
}
],
),
),
Request(
custom_id="my-second-request",
params=MessageCreateParamsNonStreaming(
model="claude-opus-5-5",
max_tokens=1024,
system=[
{
"type": "text",
"text": "You are an AI assistant tasked with analyzing literary works. Your goal is to provide insightful commentary on themes, characters, and writing style.\n",
},
{
"type": "text",
"text": "<the entire contents of Pride and Prejudice>",
"cache_control": {"type": "ephemeral"},
},
],
messages=[
{
"role": "user",
"content": "Write a summary of Pride and Prejudice.",
}
],
),
),
]
)
const client = new Anthropic();
const messageBatch = await client.messages.batches.create({
requests: [
{
custom_id: "my-first-request",
params: {
model: "claude-opus-5-5",
max_tokens: 1024,
system: [
{
type: "text",
text: "You are an AI assistant tasked with analyzing literary works. Your goal is to provide insightful commentary on themes, characters, and writing style.\n"
},
{
type: "text",
text: "<the entire contents of Pride and Prejudice>",
cache_control: { type: "ephemeral" }
}
],
messages: [
{ role: "user", content: "Analyze the major themes in Pride and Prejudice." }
]
}
},
{
custom_id: "my-second-request",
params: {
model: "claude-opus-5-5",
max_tokens: 1024,
system: [
{
type: "text",
text: "You are an AI assistant tasked with analyzing literary works. Your goal is to provide insightful commentary on themes, characters, and writing style.\n"
},
{
type: "text",
text: "<the entire contents of Pride and Prejudice>",
cache_control: { type: "ephemeral" }
}
],
messages: [{ role: "user", content: "Write a summary of Pride and Prejudice." }]
}
}
]
});
using Anthropic;
using Anthropic.Models.Messages;
using Anthropic.Models.Messages.Batches;
AnthropicClient client = new()
{
ApiKey = Environment.GetEnvironmentVariable("ANTHROPIC_API_KEY")
};
var messageBatch = await client.Messages.Batches.Create(new BatchCreateParams
{
Requests =
[
new()
{
CustomID = "my-first-request",
Params = new()
{
Model = Model.ClaudeOpus5_5,
MaxTokens = 1024,
System = new List<TextBlockParam>
{
new()
{
Text = "You are an AI assistant tasked with analyzing literary works. Your goal is to provide insightful commentary on themes, characters, and writing style.\n"
},
new()
{
Text = "<the entire contents of Pride and Prejudice>",
CacheControl = new()
}
},
Messages =
[
new() { Role = Role.User, Content = "Analyze the major themes in Pride and Prejudice." }
]
}
},
new()
{
CustomID = "my-second-request",
Params = new()
{
Model = Model.ClaudeOpus5_5,
MaxTokens = 1024,
System = new List<TextBlockParam>
{
new()
{
Text = "You are an AI assistant tasked with analyzing literary works. Your goal is to provide insightful commentary on themes, characters, and writing style.\n"
},
new()
{
Text = "<the entire contents of Pride and Prejudice>",
CacheControl = new()
}
},
Messages =
[
new() { Role = Role.User, Content = "Write a summary of Pride and Prejudice." }
]
}
}
]
});
client := anthropic.NewClient()
messageBatch, err := client.Messages.Batches.New(context.TODO(), anthropic.MessageBatchNewParams{
Requests: []anthropic.MessageBatchNewParamsRequest{
{
CustomID: "my-first-request",
Params: anthropic.MessageBatchNewParamsRequestParams{
Model: anthropic.ModelClaudeOpus5_5,
MaxTokens: 1024,
System: []anthropic.TextBlockParam{
{
Text: "You are an AI assistant tasked with analyzing literary works. Your goal is to provide insightful commentary on themes, characters, and writing style.\n",
},
{
Text: "<the entire contents of Pride and Prejudice>",
CacheControl: anthropic.NewCacheControlEphemeralParam(),
},
},
Messages: []anthropic.MessageParam{
anthropic.NewUserMessage(anthropic.NewTextBlock("Analyze the major themes in Pride and Prejudice.")),
},
},
},
{
CustomID: "my-second-request",
Params: anthropic.MessageBatchNewParamsRequestParams{
Model: anthropic.ModelClaudeOpus5_5,
MaxTokens: 1024,
System: []anthropic.TextBlockParam{
{
Text: "You are an AI assistant tasked with analyzing literary works. Your goal is to provide insightful commentary on themes, characters, and writing style.\n",
},
{
Text: "<the entire contents of Pride and Prejudice>",
CacheControl: anthropic.NewCacheControlEphemeralParam(),
},
},
Messages: []anthropic.MessageParam{
anthropic.NewUserMessage(anthropic.NewTextBlock("Write a summary of Pride and Prejudice.")),
},
},
},
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(messageBatch)
import com.anthropic.models.messages.CacheControlEphemeral;
// ...
import com.anthropic.models.messages.batches.*;
// ...
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
BatchCreateParams createParams = BatchCreateParams.builder()
.addRequest(
BatchCreateParams.Request.builder()
.customId("my-first-request")
.params(
BatchCreateParams.Request.Params.builder()
.model(Model.CLAUDE_OPUS_5_5)
.maxTokens(1024)
.systemOfTextBlockParams(
List.of(
TextBlockParam.builder()
.text(
"You are an AI assistant tasked with analyzing literary works. Your goal is to provide insightful commentary on themes, characters, and writing style.\n"
)
.build(),
TextBlockParam.builder()
.text("<the entire contents of Pride and Prejudice>")
.cacheControl(CacheControlEphemeral.builder().build())
.build()
)
)
.addUserMessage("Analyze the major themes in Pride and Prejudice.")
.build()
)
.build()
)
.addRequest(
BatchCreateParams.Request.builder()
.customId("my-second-request")
.params(
BatchCreateParams.Request.Params.builder()
.model(Model.CLAUDE_OPUS_5_5)
.maxTokens(1024)
.systemOfTextBlockParams(
List.of(
TextBlockParam.builder()
.text(
"You are an AI assistant tasked with analyzing literary works. Your goal is to provide insightful commentary on themes, characters, and writing style.\n"
)
.build(),
TextBlockParam.builder()
.text("<the entire contents of Pride and Prejudice>")
.cacheControl(CacheControlEphemeral.builder().build())
.build()
)
)
.addUserMessage("Write a summary of Pride and Prejudice.")
.build()
)
.build()
)
.build();
MessageBatch messageBatch = client.messages().batches().create(createParams);
$client = new Client();
$messageBatch = $client->messages->batches->create(
requests: [
[
'custom_id' => 'my-first-request',
'params' => [
'model' => 'claude-opus-5-5',
'max_tokens' => 1024,
'system' => [
[
'type' => 'text',
'text' => 'You are an AI assistant tasked with analyzing literary works. Your goal is to provide insightful commentary on themes, characters, and writing style.\n'
],
[
'type' => 'text',
'text' => '<the entire contents of Pride and Prejudice>',
'cache_control' => ['type' => 'ephemeral']
]
],
'messages' => [
['role' => 'user', 'content' => 'Analyze the major themes in Pride and Prejudice.']
]
]
],
[
'custom_id' => 'my-second-request',
'params' => [
'model' => 'claude-opus-5-5',
'max_tokens' => 1024,
'system' => [
[
'type' => 'text',
'text' => 'You are an AI assistant tasked with analyzing literary works. Your goal is to provide insightful commentary on themes, characters, and writing style.\n'
],
[
'type' => 'text',
'text' => '<the entire contents of Pride and Prejudice>',
'cache_control' => ['type' => 'ephemeral']
]
],
'messages' => [
['role' => 'user', 'content' => 'Write a summary of Pride and Prejudice.']
]
]
]
],
);
client = Anthropic::Client.new
message_batch = client.messages.batches.create(
requests: [
{
custom_id: "my-first-request",
params: {
model: "claude-opus-5-5",
max_tokens: 1024,
system: [
{
type: "text",
text: "You are an AI assistant tasked with analyzing literary works. Your goal is to provide insightful commentary on themes, characters, and writing style.\n"
},
{
type: "text",
text: "<the entire contents of Pride and Prejudice>",
cache_control: { type: "ephemeral" }
}
],
messages: [
{ role: "user", content: "Analyze the major themes in Pride and Prejudice." }
]
}
},
{
custom_id: "my-second-request",
params: {
model: "claude-opus-5-5",
max_tokens: 1024,
system: [
{
type: "text",
text: "You are an AI assistant tasked with analyzing literary works. Your goal is to provide insightful commentary on themes, characters, and writing style.\n"
},
{
type: "text",
text: "<the entire contents of Pride and Prejudice>",
cache_control: { type: "ephemeral" }
}
],
messages: [
{ role: "user", content: "Write a summary of Pride and Prejudice." }
]
}
}
]
)
이 예제에서는 배치의 두 요청 모두 동일한 시스템 메시지와 cache_control로 표시된 Pride and Prejudice 전체 텍스트를 포함해 캐시 적중 가능성을 높여요.
서버 도구와 에이전틱 루프
모든 서버 도구(웹 검색, 웹 fetch, 코드 실행, MCP 커넥터, advisor, 도구 검색)가 배치 요청에서 동작해요. 배치 워커는 동기 Messages API와 같은 서버 측 에이전틱 루프를 실행해요.
유지할 열린 연결이 없으므로, 배치 루프는 stop_reason: "pause_turn"을 반환하기 전에 동기 요청보다 턴당 반복을 더 많이 실행해요. 배치 결과가 pause_turn과 함께 돌아오면 턴이 끝나지 않은 거예요. pause_turn 연속 패턴에서 보여준 대로 후속 요청(배치 또는 동기)에서 일시 중지된 어시스턴트 콘텐츠를 제출하면 턴을 계속할 수 있어요.
배치 워커는 조직별로 web_search를 추가로 제한(throttle)해서, 고도로 동시적인 배치 처리가 조직의 웹 검색 속도 제한을 소진하지 않게 해요. 배치는 제한된 요청을 자동으로 재시도해요. 직접 처리할 필요는 없지만, 매우 큰 웹 검색 배치는 완료하는 데 더 오래 걸릴 수 있어요.
확장 출력 (Extended output, 베타)
output-300k-2026-03-24 베타 헤더는 Claude Opus 5.5, Claude Opus 5, Claude Opus 4.8, Claude Opus 4.7, Claude Opus 4.6, Claude Sonnet 5, Claude Sonnet 4.6을 사용하는 배치 요청의 max_tokens 상한을 300,000으로 올려요. 표준 128k max_tokens 한도보다 훨씬 긴 출력을 단일 턴으로 생성하려면 이 헤더를 포함하세요.
참고: 확장 출력은 동기 Messages API가 아니라 Message Batches API에서만 사용할 수 있어요. Claude API와 Claude Platform on AWS에서 지원되며, 현재 Amazon Bedrock, Google Cloud, Microsoft Foundry에서는 사용할 수 없어요.
도서 길이의 초안, 기술 문서 같은 장문 생성, 철저한 구조화 데이터 추출, 대규모 코드 생성 스캐폴드, 긴 추론 체인에 확장 출력을 사용해요.
단일 300k 토큰 생성은 완료하는 데 한 시간이 넘게 걸릴 수 있으므로, 24시간 처리 창을 염두에 두고 배치 제출을 계획하세요. 표준 배치 요금(표준 API 요금의 50%)이 적용돼요.
curl https://api.anthropic.com/v1/messages/batches \
--header "x-api-key: $ANTHR...KEY" \
--header "anthropic-version: 2023-06-01" \
--header "anthropic-beta: output-300k-2026-03-24" \
--header "content-type: application/json" \
--data \
'{
"requests": [
{
"custom_id": "long-form-request",
"params": {
"model": "claude-opus-5-5",
"max_tokens": 300000,
"messages": [
{"role": "user", "content": "Write a comprehensive technical guide to building distributed systems, covering architecture patterns, consistency models, fault tolerance, and operational best practices."}
]
}
}
]
}'
ant beta:messages:batches create --beta output-300k-2026-03-24 <<'YAML'
requests:
- custom_id: long-form-request
params:
model: claude-opus-5-5
max_tokens: 300000
messages:
- role: user
content: >-
Write a comprehensive technical guide to building distributed
systems, covering architecture patterns, consistency models,
fault tolerance, and operational best practices.
YAML
from anthropic.types.beta.message_create_params import MessageCreateParamsNonStreaming
from anthropic.types.beta.messages.batch_create_params import Request
client = anthropic.Anthropic()
message_batch = client.beta.messages.batches.create(
betas=["output-300k-2026-03-24"],
requests=[
Request(
custom_id="long-form-request",
params=MessageCreateParamsNonStreaming(
model="claude-opus-5-5",
max_tokens=300_000,
messages=[
{
"role": "user",
"content": "Write a comprehensive technical guide to building distributed systems, covering architecture patterns, consistency models, fault tolerance, and operational best practices.",
}
],
),
),
],
)
print(message_batch)
const client = new Anthropic();
const messageBatch = await client.beta.messages.batches.create({
betas: ["output-300k-2026-03-24"],
requests: [
{
custom_id: "long-form-request",
params: {
model: "claude-opus-5-5",
max_tokens: 300000,
messages: [
{
role: "user",
content:
"Write a comprehensive technical guide to building distributed systems, covering architecture patterns, consistency models, fault tolerance, and operational best practices."
}
]
}
}
]
});
console.log(messageBatch);
using Anthropic;
using Anthropic.Models.Beta.Messages;
using Anthropic.Models.Beta.Messages.Batches;
using Model = Anthropic.Models.Messages.Model;
AnthropicClient client = new();
var batch = await client.Beta.Messages.Batches.Create(new BatchCreateParams
{
Betas = ["output-300k-2026-03-24"],
Requests =
[
new()
{
CustomID = "long-form-request",
Params = new()
{
Model = Model.ClaudeOpus5_5,
MaxTokens = 300_000,
Messages =
[
new() { Role = Role.User, Content = "Write a comprehensive technical guide to building distributed systems, covering architecture patterns, consistency models, fault tolerance, and operational best practices." }
]
}
}
]
});
Console.WriteLine(batch);
client := anthropic.NewClient()
batch, err := client.Beta.Messages.Batches.New(context.Background(),
anthropic.BetaMessageBatchNewParams{
Betas: []anthropic.AnthropicBeta{"output-300k-2026-03-24"},
Requests: []anthropic.BetaMessageBatchNewParamsRequest{
{
CustomID: "long-form-request",
Params: anthropic.BetaMessageBatchNewParamsRequestParams{
Model: anthropic.ModelClaudeOpus5_5,
MaxTokens: 300_000,
Messages: []anthropic.BetaMessageParam{
anthropic.NewBetaUserMessage(
anthropic.NewBetaTextBlock("Write a comprehensive technical guide to building distributed systems, covering architecture patterns, consistency models, fault tolerance, and operational best practices."),
),
},
},
},
},
})
if err != nil {
panic(err)
}
fmt.Println(batch.ID)
import com.anthropic.models.beta.messages.batches.*;
void main() {
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
BatchCreateParams params = BatchCreateParams.builder()
.addBeta("output-300k-2026-03-24")
.addRequest(
BatchCreateParams.Request.builder()
.customId("long-form-request")
.params(
BatchCreateParams.Request.Params.builder()
.model(Model.CLAUDE_OPUS_5_5)
.maxTokens(300_000L)
.addUserMessage("Write a comprehensive technical guide to building distributed systems, covering architecture patterns, consistency models, fault tolerance, and operational best practices.")
.build()
)
.build()
)
.build();
BetaMessageBatch messageBatch = client.beta().messages().batches().create(params);
IO.println(messageBatch);
}
$client = new Client();
$batch = $client->beta->messages->batches->create(
betas: ['output-300k-2026-03-24'],
requests: [
[
'custom_id' => 'long-form-request',
'params' => [
'model' => 'claude-opus-5-5',
'max_tokens' => 300_000,
'messages' => [
['role' => 'user', 'content' => 'Write a comprehensive technical guide to building distributed systems, covering architecture patterns, consistency models, fault tolerance, and operational best practices.']
]
]
]
],
);
echo $batch->id;
client = Anthropic::Client.new
batch = client.beta.messages.batches.create(
betas: ["output-300k-2026-03-24"],
requests: [
{
custom_id: "long-form-request",
params: {
model: "claude-opus-5-5",
max_tokens: 300_000,
messages: [
{ role: "user", content: "Write a comprehensive technical guide to building distributed systems, covering architecture patterns, consistency models, fault tolerance, and operational best practices." }
]
}
}
]
)
puts batch
효과적인 배칭을 위한 모범 사례
Batches API를 최대한 활용하려면:
- 배치 처리 상태를 정기적으로 모니터링하고 실패한 요청에 대한 적절한 재시도 로직을 구현해요.
- 순서가 보장되지 않으므로 의미 있는
custom_id값을 사용해 결과를 요청과 쉽게 일치시켜요. - 더 나은 관리성을 위해 매우 큰 데이터세트를 여러 배치로 나누는 걸 고려해요.
- 검증 오류를 피하기 위해 Messages API로 단일 요청 형태를 드라이 런해요.
흔한 문제 해결
예상치 못한 동작이 발생하면:
- 총 배치 요청 크기가 256MB를 넘지 않는지 확인해요. 요청이 너무 크면 413
request_too_large오류가 나올 수 있어요. - 배치 안의 모든 요청에 지원되는 모델을 사용하고 있는지 확인해요.
- 배치 안의 각 요청이 고유한
custom_id를 가지는지 확인해요. - 배치
created_at(처리ended_at이 아니라) 시점부터 29일 미만이 지났는지 확인해요. 29일이 넘으면 결과를 더 이상 볼 수 없어요. - 배치가 취소되지 않았는지 확인해요.
배치 안의 한 요청 실패는 다른 요청의 처리에 영향을 주지 않는다는 점을 기억해 주세요.
배치 저장과 프라이버시
- 워크스페이스 격리: 배치는 생성된 워크스페이스 안에서 격리돼요. 같은 워크스페이스의 API 요청이나, Console에서 워크스페이스 배치를 볼 권한이 있는 사용자만 접근할 수 있어요.
- 결과 가용성: 배치 결과는 배치 생성 후 29일 동안 사용할 수 있어 검색과 처리에 충분한 시간을 제공해요.
데이터 보존
배치 처리는 요청·응답 데이터를 배치 생성 후 최대 29일 동안 저장해요. 처리 후 언제든지 DELETE /v1/messages/batches/{batch_id} 엔드포인트로 메시지 배치를 삭제할 수 있어요. 진행 중인 배치를 삭제하려면 먼저 취소하세요. 비동기 처리는 배치 완료와 결과 검색까지 입력·출력 양쪽을 서버 측에 저장해야 해요.
모든 기능의 ZDR 자격은 API와 데이터 보존을 참고해 주세요.
FAQ
배치 처리는 얼마나 걸리나요?
배치는 처리에 최대 24시간까지 걸릴 수 있지만, 많은 경우 더 빨리 끝나요. 실제 처리 시간은 배치 크기, 현재 수요, 요청량에 따라 달라져요. 배치가 만료되어 24시간 안에 완료하지 못할 수도 있어요.
Batches API는 모든 모델에서 사용할 수 있나요?
지원 모델 목록은 지원되는 모델을 참고해 주세요.
Message Batches API를 다른 API 기능과 함께 쓸 수 있나요?
네, Message Batches API는 대부분의 베타 기능을 포함해 Messages API에서 사용 가능한 거의 모든 기능을 지원해요. 소수의 파라미터(stream, speed, max_tokens: 0)는 지원되지 않아요. 전체 목록은 배치할 수 있는 것을 참고해 주세요.
Message Batches API가 요금에 어떤 영향을 주나요?
Message Batches API는 모든 사용량에 표준 API 요금 대비 50% 할인을 제공해요. 입력 토큰, 출력 토큰, 특수 토큰 모두에 적용돼요. 요금에 대한 자세한 내용은 요금을 방문해 주세요.
제출한 후 배치를 업데이트할 수 있나요?
아니요, 배치가 제출되면 수정할 수 없어요. 변경이 필요하면 현재 배치를 취소하고 새 배치를 제출해야 해요. 취소가 즉시 적용되지 않을 수 있다는 점을 유의하세요.
Message Batches API 속도 제한이 있고, Messages API 속도 제한과 상호작용하나요?
Message Batches API는 처리 대기 요청 수에 대한 제한 외에도 HTTP 요청 기반 속도 제한이 있어요. Message Batches API 속도 제한 참고. Batches API 사용은 Messages API의 속도 제한에 영향을 주지 않아요.
배치 요청에서 오류를 어떻게 처리하나요?
결과를 검색하면 각 요청에 succeeded, errored, canceled, expired 중 무엇인지 나타내는 result 필드가 있어요. errored 결과에는 추가 오류 정보가 제공돼요. API 레퍼런스에서 오류 응답 객체를 확인하세요.
Message Batches API는 프라이버시와 데이터 분리를 어떻게 처리하나요?
Message Batches API는 강력한 프라이버시와 데이터 분리 조치로 설계됐어요.
- 배치와 그 결과는 생성된 워크스페이스 안에서 격리돼요. 같은 워크스페이스의 API 요청만 접근할 수 있어요.
- 배치 안의 각 요청은 독립적으로 처리되며, 요청 사이에 데이터 누출이 없어요.
- 결과는 제한된 시간(29일) 동안만 사용할 수 있으며, Anthropic의 데이터 보존 정책을 따릅니다.
- Console에서 배치 결과 다운로드는 조직 수준 또는 워크스페이스별로 비활성화할 수 있어요.
Message Batches API에서 프롬프트 캐싱을 쓸 수 있나요?
네, Message Batches API에서 프롬프트 캐싱을 사용할 수 있어요. 다만 비동기 배치 요청이 동시에 어떤 순서로든 처리될 수 있으므로, 캐시 적중은 최선 방식으로 제공돼요.
다음 단계
- 검색 결과 — 소스 귀속이 있는 검색 결과를 제공해 RAG 애플리케이션에서 자연스러운 인용을 활성화.
- 프롬프트 캐싱 — 배치 안 요청들 사이에 공유되는 프롬프트 접두사를 캐싱해 비용과 지연 시간 절감.