---
title: 배치 처리
url: https://platform.claude.com/docs/ko/build-with-claude/batch-processing
description: Message Batches API를 사용하여 대량의 Messages 요청을 비동기적으로 처리하고, 비용을 50% 절감하며 처리량을 높이세요.
---

"Batch processing"(배치 처리)은 대량의 요청을 효율적으로 처리하기 위한 강력한 접근 방식입니다. 요청을 하나씩 처리하며 즉각적인 응답을 받는 대신, 배치 처리를 사용하면 여러 요청을 함께 제출하여 비동기적으로 처리할 수 있습니다. 이 패턴은 특히 다음과 같은 경우에 유용합니다:

* 대량의 데이터를 처리해야 하는 경우
* 즉각적인 응답이 필요하지 않은 경우
* 비용 효율성을 최적화하려는 경우
* 대규모 평가 또는 분석을 실행하는 경우

Message Batches API는 이 패턴에 대한 Anthropic의 첫 번째 구현입니다.

<Note>
  "zero data retention"(제로 데이터 보존), 즉 ZDR이 이 기능에 어떻게 적용되는지 알아보려면 [API 및 데이터 보존](https://platform.claude.com/docs/ko/manage-claude/api-and-data-retention)을 참조하세요.
</Note>

# Message Batches API

Message Batches API는 대량의 [Messages](https://platform.claude.com/docs/ko/api/messages/create) 요청을 비동기적으로 처리하는 강력하고 비용 효율적인 방법입니다. 이 접근 방식은 즉각적인 응답이 필요하지 않은 작업에 적합하며, 대부분의 배치는 1시간 이내에 완료되면서 비용을 50% 절감하고 처리량을 높입니다.

이 가이드 외에도 [API 레퍼런스를 직접 살펴볼](https://platform.claude.com/docs/ko/api/messages/batches/create) 수 있습니다.

## Message Batches API의 작동 방식

Message Batches API에 요청을 보내면:

1. 시스템이 제공된 Messages 요청으로 새로운 Message Batch를 생성합니다.
2. 그런 다음 배치가 비동기적으로 처리되며, 각 요청은 독립적으로 처리됩니다.
3. 배치의 상태를 폴링하고 모든 요청의 처리가 종료되면 결과를 가져올 수 있습니다.

이는 다음과 같이 즉각적인 결과가 필요하지 않은 대량 작업에 특히 유용합니다:

* 대규모 평가: 수천 개의 테스트 케이스를 효율적으로 처리합니다.
* 콘텐츠 모더레이션: 대량의 사용자 생성 콘텐츠를 비동기적으로 분석합니다.
* 데이터 분석: 대규모 데이터셋에 대한 인사이트 또는 요약을 생성합니다.
* 대량 콘텐츠 생성: 다양한 목적(예: 제품 설명, 기사 요약)을 위해 대량의 텍스트를 생성합니다.

### 배치 제한 사항

* Message Batch는 100,000개의 Message 요청 또는 256 MB 크기 중 먼저 도달하는 쪽으로 제한됩니다.
* 시스템은 각 배치를 가능한 한 빠르게 처리하며, 대부분의 배치는 1시간 이내에 완료됩니다. 모든 메시지가 완료되었을 때 또는 24시간이 지난 후 중 먼저 도래하는 시점에 배치 결과에 접근할 수 있습니다. 24시간 이내에 처리가 완료되지 않으면 배치는 만료됩니다.
* 배치 결과는 생성 후 29일 동안 사용할 수 있습니다. 그 이후에도 Batch를 조회할 수는 있지만, 결과는 더 이상 다운로드할 수 없습니다.
* 배치는 [Workspace](https://platform.claude.com/settings/workspaces) 범위로 지정됩니다. 요청이 실행되는 Workspace 내에서 생성된 모든 배치(및 그 결과)를 조회할 수 있습니다.
* "Rate limit"(속도 제한)은 Batches API HTTP 요청과 배치 내에서 처리 대기 중인 요청 수 모두에 적용됩니다. [Message Batches API 속도 제한](https://platform.claude.com/docs/ko/api/rate-limits#message-batches-api)을 참조하세요. 또한 현재 수요와 요청량에 따라 처리 속도가 느려질 수 있습니다. 이 경우 24시간 후 만료되는 요청이 더 많아질 수 있습니다.
* 높은 처리량과 동시 처리로 인해, 배치는 Workspace에 구성된 [지출 한도](https://platform.claude.com/settings/billing)를 약간 초과할 수 있습니다.
* 배치에 포함된 각 요청은 `max_tokens`가 최소 `1` 이상이어야 합니다. `max_tokens: 0`([캐시 사전 워밍](https://platform.claude.com/docs/ko/build-with-claude/prompt-caching#pre-warming-the-cache))은 배치 내에서 지원되지 않습니다. 배치 처리 중에 작성된 임시 캐시 항목은 후속 요청이 실행되기 전에 만료될 가능성이 높기 때문입니다.

### 지원 모델

모든 [활성 모델](https://platform.claude.com/docs/ko/models/overview)이 Message Batches API를 지원합니다.

### 배치로 처리할 수 있는 항목

Messages API에 보낼 수 있는 거의 모든 요청을 배치에 포함할 수 있습니다. 여기에는 다음이 포함됩니다:

* 비전
* 모든 [서버 도구](https://platform.claude.com/docs/ko/agents-and-tools/tool-use/server-tools)(웹 검색, 웹 가져오기, 코드 실행, MCP 커넥터, 어드바이저, 도구 검색)를 포함한 "tool use"(도구 사용)
* 시스템 메시지
* 멀티턴 대화
* "Extended thinking"(확장 사고)
* 대부분의 베타 기능

배치의 각 요청은 독립적으로 처리되므로, 단일 배치 내에서 서로 다른 유형의 요청을 혼합할 수 있습니다.

소수의 Messages API 매개변수는 배치 요청에서 지원되지 **않습니다**. 이 중 하나라도 포함하면 유효성 검사 오류가 반환됩니다:

| 매개변수                                                                                   | 이유                                                                                                           |
| -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| `stream: true`                                                                         | 배치 결과는 스트림이 아닌 단일 파일로 반환됩니다.                                                                                 |
| `speed` ([Fast mode](https://platform.claude.com/docs/ko/build-with-claude/fast-mode)) | Fast mode는 동기식 지연 시간을 조정하는 기능으로, 비동기 배치 처리에는 적용되지 않습니다.                                                      |
| `max_tokens: 0`                                                                        | [배치 제한 사항](https://platform.claude.com/docs/ko/build-with-claude/batch-processing#batch-limitations)을 참조하세요. |

<Tip>
  배치 처리에는 5분 이상 걸릴 수 있으므로, 공유 컨텍스트가 있는 배치를 처리할 때 캐시 적중률을 높이려면 "prompt caching"(프롬프트 캐싱)과 함께 [1시간 캐시 지속 시간](https://platform.claude.com/docs/ko/build-with-claude/prompt-caching#1-hour-cache-duration)을 사용하는 것을 고려하세요.
</Tip>

## 가격

Batches API는 상당한 비용 절감을 제공합니다. 모든 사용량은 표준 API 가격의 50%로 청구됩니다.

| Model                                                                                                                                | Batch input  | Batch output  |
| :----------------------------------------------------------------------------------------------------------------------------------- | :----------- | :------------ |
| Claude Fable 5.1                                                                                                                     | $5 / MTok    | $25 / MTok    |
| Claude Mythos 5.1 ([limited availability](https://support.claude.com/en/articles/14604842))                                          | $5 / MTok    | $25 / MTok    |
| Claude Fable 5                                                                                                                       | $5 / MTok    | $25 / MTok    |
| Claude Mythos 5 ([limited availability](https://support.claude.com/en/articles/14604842))                                            | $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 ([retired, except on Bedrock and Google Cloud](https://platform.claude.com/docs/ko/about-claude/model-deprecations)) | $7.50 / MTok | $37.50 / MTok |
| Claude Opus 4 ([retired, except on Google Cloud](https://platform.claude.com/docs/ko/about-claude/model-deprecations))               | $7.50 / MTok | $37.50 / MTok |
| Claude Sonnet 5.5                                                                                                                    | $1 / MTok    | $5 / 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 ([retired, except on Bedrock and Google Cloud](https://platform.claude.com/docs/ko/about-claude/model-deprecations)) | $1.50 / MTok | $7.50 / MTok  |
| Claude Haiku 5.5 (for prompts up to 100,000 tokens)                                                                                  | $0.05 / MTok | $0.25 / MTok  |
| Claude Haiku 5.5 (for prompts over 100,000 tokens)                                                                                   | $0.25 / MTok | $1.25 / MTok  |
| Claude Haiku 4.5                                                                                                                     | $0.50 / MTok | $2.50 / MTok  |
| Claude Haiku 3.5 ([retired, except on Google Cloud](https://platform.claude.com/docs/ko/about-claude/model-deprecations))            | $0.40 / MTok | $2 / MTok     |

* **MTok:** Million tokens. $5 / MTok is $5 for every million tokens.
* **Limited access:** Available only to organizations verified through Anthropic’s verification programs, such as the [Cyber Verification Program](https://support.claude.com/en/articles/14604842). To request access, apply to the program that covers your use case.
* **Retired:** May still be available on other cloud platforms. See [Model deprecations](https://platform.claude.com/docs/ko/about-claude/model-deprecations) for more.

## Message Batches API 사용 방법

### 배치 준비 및 생성

Message Batch는 Message를 생성하기 위한 요청 목록으로 구성됩니다. 개별 요청의 형태는 다음으로 이루어집니다:

* Messages 요청을 식별하기 위한 고유한 `custom_id`. 1\~64자여야 하며 영숫자, 하이픈, 밑줄만 포함해야 합니다(`^[a-zA-Z0-9_-]{1,64}$`와 일치).
* 표준 [Messages API](https://platform.claude.com/docs/ko/api/messages/create) 매개변수가 포함된 `params` 객체

이 목록을 `requests` 매개변수에 전달하여 [배치를 생성](https://platform.claude.com/docs/ko/api/messages/batches/create)할 수 있습니다:

<CodeGroup>
  ```bash cURL
  curl https://api.anthropic.com/v1/messages/batches \
       --header "x-api-key: $ANTHROPIC_API_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"}
                  ]
              }
          }
      ]
  }'
  ```

  ```bash CLI
  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
  ```

  ```python Python
  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)
  ```

  ```typescript TypeScript
  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);
  ```

  ```csharp C#
  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);
  ```

  ```go Go
  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)
  ```

  ```java Java
  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);
  ```

  ```php PHP
  $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;
  ```

  ```ruby Ruby
  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
  ```
</CodeGroup>

이 예시에서는 두 개의 개별 요청이 비동기 처리를 위해 함께 배치로 묶입니다. 각 요청에는 고유한 `custom_id`가 있으며 Messages API 호출에 사용하는 표준 매개변수가 포함되어 있습니다.

<Tip>
  **Messages API로 배치 요청 테스트하기**

  각 메시지 요청의 `params` 객체에 대한 유효성 검사는 비동기적으로 수행되며, 유효성 검사 오류는 전체 배치의 처리가 종료되었을 때 반환됩니다. 먼저 [Messages API](https://platform.claude.com/docs/ko/api/messages/create)로 요청 형태를 검증하여 입력을 올바르게 구성하고 있는지 확인할 수 있습니다.
</Tip>

배치가 처음 생성되면 응답의 처리 상태는 `in_progress`입니다.

```json Output
{
  "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](https://platform.claude.com/settings/workspaces/default/batches)을 방문하거나 [조회 엔드포인트](https://platform.claude.com/docs/ko/api/messages/batches/retrieve)를 사용하여 배치 상태를 모니터링할 수 있습니다.

#### Message Batch 완료 폴링

Message Batch를 폴링하려면 배치 생성 시 응답에서 제공되거나 배치 목록 조회를 통해 얻을 수 있는 `id`가 필요합니다. 처리가 종료될 때까지 주기적으로 배치 상태를 확인하는 폴링 루프를 구현할 수 있습니다:

<CodeGroup>
  ```bash cURL
  #!/bin/sh
  # ...
  # 상태를 확인합니다. processing_status가 "ended"가 될 때까지 반복하세요
  curl -s "https://api.anthropic.com/v1/messages/batches/$MESSAGE_BATCH_ID" \
    --header "x-api-key: $ANTHROPIC_API_KEY" \
    --header "anthropic-version: 2023-06-01" \
    | jq -r '.processing_status'
  ```

  ```bash CLI
  #!/bin/bash
  # ...
  # 상태를 확인합니다. processing_status가 "ended"가 될 때까지 반복하세요
  ant messages:batches retrieve \
    --message-batch-id "$MESSAGE_BATCH_ID" \
    --transform processing_status --raw-output
  ```

  ```python Python
  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)
  ```

  ```typescript TypeScript
  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);
  ```

  ```csharp C#
  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);
  ```

  ```go Go
  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)
  ```

  ```java Java
  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);
  ```

  ```php PHP
  $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);
  ```

  ```ruby Ruby
  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
  ```
</CodeGroup>

### 모든 Message Batch 목록 조회

[목록 엔드포인트](https://platform.claude.com/docs/ko/api/messages/batches/list)를 사용하여 Workspace의 모든 Message Batch를 나열할 수 있습니다. API는 페이지네이션을 지원하며, 필요에 따라 추가 페이지를 자동으로 가져옵니다:

<CodeGroup>
  ```bash cURL
  #!/bin/sh
  # 한 페이지를 가져옵니다. 응답의 has_more가 true인 동안 해당 응답의
  # last_id를 after_id로 전달하여 다음 페이지를 가져오세요. (SDK와 CLI는
  # 자동 페이지네이션을 수행합니다.)
  curl -s "https://api.anthropic.com/v1/messages/batches?limit=20" \
    --header "x-api-key: $ANTHROPIC_API_KEY" \
    --header "anthropic-version: 2023-06-01"
  ```

  ```bash CLI
  # 필요에 따라 추가 페이지를 자동으로 가져옵니다
  ant messages:batches list --limit 20
  ```

  ```python Python
  client = anthropic.Anthropic()

  # 필요에 따라 추가 페이지를 자동으로 가져옵니다.
  for message_batch in client.messages.batches.list(limit=20):
      print(message_batch)
  ```

  ```typescript TypeScript
  const client = new Anthropic();

  // 필요에 따라 추가 페이지를 자동으로 가져옵니다.
  for await (const messageBatch of client.messages.batches.list({
    limit: 20
  })) {
    console.log(messageBatch);
  }
  ```

  ```csharp C#
  AnthropicClient client = new();

  var parameters = new BatchListParams
  {
      Limit = 20
  };

  // 필요에 따라 추가 페이지를 자동으로 가져옵니다
  var page = await client.Messages.Batches.List(parameters);
  await foreach (var messageBatch in page.Paginate())
  {
      Console.WriteLine(messageBatch);
  }
  ```

  ```go Go
  client := anthropic.NewClient()

  // 필요에 따라 추가 페이지를 자동으로 가져옵니다
  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)
  }
  ```

  ```java Java
  AnthropicClient client = AnthropicOkHttpClient.fromEnv();

  // 필요에 따라 추가 페이지를 자동으로 가져옵니다
  for (MessageBatch messageBatch : client
    .messages()
    .batches()
    .list(BatchListParams.builder().limit(20).build())
    .autoPager()) {
    System.out.println(messageBatch);
  }
  ```

  ```php PHP
  $client = new Client();

  // 필요에 따라 추가 페이지를 자동으로 가져옵니다
  foreach ($client->messages->batches->list(limit: 20)->pagingEachItem() as $messageBatch) {
      echo $messageBatch->id . "\n";
  }
  ```

  ```ruby Ruby
  client = Anthropic::Client.new

  # 필요에 따라 추가 페이지를 자동으로 가져옵니다
  client.messages.batches.list(limit: 20).auto_paging_each do |message_batch|
    puts message_batch
  end
  ```
</CodeGroup>

### 배치 결과 가져오기

배치 처리가 종료되면 배치의 각 Messages 요청에는 결과가 있습니다. 결과 유형은 네 가지입니다:

| 결과 유형       | 설명                                                                                           |
| ----------- | -------------------------------------------------------------------------------------------- |
| `succeeded` | 요청이 성공했습니다. 메시지 결과가 포함됩니다.                                                                   |
| `errored`   | 요청에 오류가 발생하여 메시지가 생성되지 않았습니다. 가능한 오류에는 잘못된 요청 및 내부 서버 오류가 포함됩니다. 이러한 요청에 대해서는 요금이 청구되지 않습니다. |
| `canceled`  | 이 요청이 모델로 전송되기 전에 사용자가 배치를 취소했습니다. 이러한 요청에 대해서는 요금이 청구되지 않습니다.                               |
| `expired`   | 이 요청이 모델로 전송되기 전에 배치가 24시간 만료 시점에 도달했습니다. 이러한 요청에 대해서는 요금이 청구되지 않습니다.                        |

배치의 `request_counts`는 결과의 개요를 보여주며, 이 네 가지 상태 각각에 도달한 요청 수를 나타냅니다.

배치 결과는 Message Batch의 `results_url` 속성에서 다운로드할 수 있으며, 조직 권한이 허용하는 경우 Console에서도 다운로드할 수 있습니다. 결과의 크기가 클 수 있으므로, 한 번에 모두 다운로드하기보다는 [결과를 스트리밍](https://platform.claude.com/docs/ko/api/messages/batches/results)하는 것이 권장됩니다.

<CodeGroup>
  ```bash cURL
  #!/bin/sh
  # 배치의 results_url을 가져온 다음, 해당 URL이 가리키는 .jsonl 결과를
  # 스트리밍합니다. 결과별 처리(재시도, 유효성 검사 오류)는
  # 다른 탭의 SDK 예제를 사용하세요.
  RESULTS_URL=$(curl -s "https://api.anthropic.com/v1/messages/batches/msgbatch_01HkcTjaV5uDC8jWR4ZsDV8d" \
    --header "anthropic-version: 2023-06-01" \
    --header "x-api-key: $ANTHROPIC_API_KEY" \
    | jq -r '.results_url')

  curl -s "$RESULTS_URL" \
    --header "anthropic-version: 2023-06-01" \
    --header "x-api-key: $ANTHROPIC_API_KEY" \
    | jq -r '"\(.result.type): \(.custom_id)"'
  ```

  ```bash CLI
  # 결과당 한 줄씩 출력합니다. 예: `{"custom_id":"test-1","type":"succeeded",…}`.
  # 결과별 처리(재시도, 유효성 검사 오류)가 필요하면 다른 탭의
  # SDK 예제를 사용하세요.
  ant messages:batches results \
    --message-batch-id msgbatch_01HkcTjaV5uDC8jWR4ZsDV8d \
    --transform '{custom_id,"type":result.type,"error":result.error.error.type}' \
    --format jsonl
  ```

  ```python Python
  client = anthropic.Anthropic()

  # 결과 파일을 메모리 효율적인 청크 단위로 스트리밍하여 한 번에 하나씩 처리합니다
  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":
                  # 요청을 다시 보내기 전에 요청 본문을 수정해야 합니다
                  print(f"Validation error {result.custom_id}")
              else:
                  # 요청을 그대로 재시도할 수 있습니다
                  print(f"Server error {result.custom_id}")
          case "expired":
              print(f"Request expired {result.custom_id}")
  ```

  ```typescript TypeScript
  const client = new Anthropic();

  // 결과 파일을 메모리 효율적인 청크 단위로 스트리밍하여 한 번에 하나씩 처리합니다
  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") {
          // 요청을 다시 보내기 전에 요청 본문을 수정해야 합니다
          console.log(`Validation error: ${result.custom_id}`);
        } else {
          // 요청을 바로 재시도할 수 있습니다
          console.log(`Server error: ${result.custom_id}`);
        }
        break;
      case "expired":
        console.log(`Request expired: ${result.custom_id}`);
        break;
    }
  }
  ```

  ```csharp C#
  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;
      }
  }
  ```

  ```go Go
  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)
  }
  ```

  ```java Java
  import com.anthropic.core.http.StreamResponse;
  import com.anthropic.models.messages.batches.BatchResultsParams;
  import com.anthropic.models.messages.batches.MessageBatchIndividualResponse;
  // ...
      AnthropicClient client = AnthropicOkHttpClient.fromEnv();

      // 결과 파일을 메모리 효율적인 청크 단위로 스트리밍하여 한 번에 하나씩 처리합니다
      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()) {
                  // 요청을 다시 보내기 전에 요청 본문을 수정해야 합니다
                  System.out.println("Validation error: " + result.customId());
                } else {
                  // 요청을 바로 재시도할 수 있습니다
                  System.out.println("Server error: " + result.customId());
                }
              }
              case EXPIRED -> System.out.println("Request expired: " + result.customId());
            }
          });
      }
  ```

  ```php PHP
  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;
      }
  }
  ```

  ```ruby Ruby
  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
  ```
</CodeGroup>

결과는 `.jsonl` 형식이며, 각 줄은 Message Batch의 단일 요청 결과를 나타내는 유효한 JSON 객체입니다. 스트리밍된 각 결과에 대해 `custom_id`와 결과 유형에 따라 서로 다른 작업을 수행할 수 있습니다. 다음은 결과 세트의 예시입니다:

```jsonl .jsonl file
{"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`는 표준 [오류 형태](https://platform.claude.com/docs/ko/api/errors#error-shapes)로 설정됩니다.

<Tip>
  **배치 결과는 입력 순서와 일치하지 않을 수 있습니다**

  배치 결과는 임의의 순서로 반환될 수 있으며, 배치가 생성되었을 때의 요청 순서와 일치하지 않을 수 있습니다. 앞의 예시에서는 두 번째 배치 요청의 결과가 첫 번째보다 먼저 반환됩니다. 결과를 해당 요청과 올바르게 매칭하려면 항상 `custom_id` 필드를 사용하세요.
</Tip>

### Message Batch 취소

[취소 엔드포인트](https://platform.claude.com/docs/ko/api/messages/batches/cancel)를 사용하여 현재 처리 중인 Message Batch를 취소할 수 있습니다. 취소 직후 배치의 `processing_status`는 `canceling`이 됩니다. 앞서 설명한 것과 동일한 폴링 기법을 사용하여 취소가 완료될 때까지 기다릴 수 있습니다. 취소된 배치는 최종적으로 `ended` 상태가 되며, 취소 전에 처리된 요청에 대한 부분 결과를 포함할 수 있습니다.

<CodeGroup>
  ```bash cURL
  #!/bin/sh
  # ...
  curl --request POST https://api.anthropic.com/v1/messages/batches/$MESSAGE_BATCH_ID/cancel \
      --header "x-api-key: $ANTHROPIC_API_KEY" \
      --header "anthropic-version: 2023-06-01"
  ```

  ```bash CLI
  #!/bin/bash
  # ...
  ant messages:batches cancel --message-batch-id "$MESSAGE_BATCH_ID"
  ```

  ```python Python
  client = anthropic.Anthropic()

  MESSAGE_BATCH_ID = "msgbatch_01HkcTjaV5uDC8jWR4ZsDV8d"

  message_batch = client.messages.batches.cancel(
      MESSAGE_BATCH_ID,
  )
  print(message_batch)
  ```

  ```typescript TypeScript
  const client = new Anthropic();

  const messageBatch = await client.messages.batches.cancel(MESSAGE_BATCH_ID);
  console.log(messageBatch);
  ```

  ```csharp C#
  AnthropicClient client = new();
  string messageBatchId = Environment.GetEnvironmentVariable("MESSAGE_BATCH_ID");

  var messageBatch = await client.Messages.Batches.Cancel(messageBatchId);
  Console.WriteLine(messageBatch);
  ```

  ```go Go
  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)
  ```

  ```java Java
  import com.anthropic.models.messages.batches.*;
  // ...
      AnthropicClient client = AnthropicOkHttpClient.fromEnv();

      MessageBatch messageBatch = client
        .messages()
        .batches()
        .cancel("msgbatch_01HkcTjaV5uDC8jWR4ZsDV8d");
      System.out.println(messageBatch);
  ```

  ```php PHP
  $client = new Client();

  $messageBatch = $client->messages->batches->cancel(
      messageBatchID: 'msgbatch_example_id',
  );
  echo $messageBatch;
  ```

  ```ruby Ruby
  client = Anthropic::Client.new

  message_batch_id = ENV.fetch("MESSAGE_BATCH_ID")
  message_batch = client.messages.batches.cancel(message_batch_id)
  puts message_batch
  ```
</CodeGroup>

응답은 `canceling` 상태의 배치를 보여줍니다:

```json Output
{
  "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% 범위의 캐시 적중률을 경험합니다.

배치 요청에서 캐시 적중 가능성을 극대화하려면:

1. 배치 내 모든 Message 요청에 동일한 `cache_control` 블록을 포함하세요.
2. 캐시 항목이 5분 수명 이후 만료되지 않도록 꾸준한 요청 흐름을 유지하세요.
3. 가능한 한 많은 캐시된 콘텐츠를 공유하도록 요청을 구성하세요.

배치에서 프롬프트 캐싱을 구현하는 예시:

<CodeGroup>
  ```bash cURL
  curl https://api.anthropic.com/v1/messages/batches \
       --header "x-api-key: $ANTHROPIC_API_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."}
                  ]
              }
          }
      ]
  }'
  ```

  ```bash CLI
  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
  ```

  ```python Python
  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.",
                      }
                  ],
              ),
          ),
      ]
  )
  ```

  ```typescript TypeScript
  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." }]
        }
      }
    ]
  });
  ```

  ```csharp C#
  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." }
                  ]
              }
          }
      ]
  });
  ```

  ```go Go
  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)
  ```

  ```java Java
  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);
  ```

  ```php PHP
  $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.']
                  ]
              ]
          ]
      ],
  );
  ```

  ```ruby Ruby
  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." }
          ]
        }
      }
    ]
  )
  ```
</CodeGroup>

이 예시에서 배치의 두 요청 모두 캐시 적중 가능성을 높이기 위해 `cache_control`로 표시된 동일한 시스템 메시지와 오만과 편견(Pride and Prejudice) 전문을 포함합니다.

### 서버 도구와 에이전트 루프

모든 [서버 도구](https://platform.claude.com/docs/ko/agents-and-tools/tool-use/server-tools)(웹 검색, 웹 가져오기, 코드 실행, MCP 커넥터, 어드바이저, 도구 검색)는 배치 요청에서 작동합니다. 배치 워커는 동기식 Messages API와 동일한 서버 측 에이전트 루프를 실행합니다.

유지해야 할 열린 연결이 없기 때문에, 배치 루프는 `stop_reason: "pause_turn"`을 반환하기 전에 동기식 요청보다 **턴당 더 많은 반복**을 실행합니다. 배치 결과가 `pause_turn`과 함께 반환되면 해당 턴이 완료되지 않은 것입니다. [pause\_turn 계속 패턴](https://platform.claude.com/docs/ko/agents-and-tools/tool-use/server-tools#the-server-side-loop-and-pause-turn)에 표시된 것과 정확히 동일하게, 일시 중지된 어시스턴트 콘텐츠를 후속 요청(배치 또는 동기식)에 제출하여 계속할 수 있습니다.

배치 워커는 추가로 조직별로 `web_search`를 스로틀링하여, 동시성이 높은 배치 처리가 조직의 웹 검색 속도 제한을 소진하지 않도록 합니다. 배치는 스로틀링된 요청을 자동으로 재시도하므로 직접 처리할 필요는 없지만, 매우 큰 웹 검색 배치는 완료하는 데 더 오래 걸릴 수 있습니다.

### 확장 출력 (베타)

`output-300k-2026-03-24` 베타 헤더는 다음 모델을 사용하는 배치 요청의 `max_tokens` 상한을 300,000으로 높입니다: Claude Opus 5.5, Claude Opus 5, Claude Opus 4.8, Claude Opus 4.7, Claude Opus 4.6, Claude Sonnet 5.5, Claude Sonnet 5, Claude Sonnet 4.6, 또는 Claude Haiku 5.5. 이 헤더를 포함하면 단일 턴에서 표준 128k `max_tokens` 제한보다 훨씬 긴 출력을 생성할 수 있습니다.

<Note>
  확장 출력은 동기식 Messages API가 아닌 Message Batches API에서만 사용할 수 있습니다. Claude API 및 Claude Platform on AWS에서 지원되며, 현재 Amazon Bedrock, Google Cloud 또는 Microsoft Foundry에서는 사용할 수 없습니다.
</Note>

책 분량의 초안 및 기술 문서와 같은 장문 생성, 포괄적인 구조화 데이터 추출, 대규모 코드 생성 스캐폴드, 긴 추론 체인에 확장 출력을 사용하세요.

단일 300k 토큰 생성은 완료하는 데 1시간 이상 걸릴 수 있으므로, 24시간 처리 기간을 염두에 두고 배치 제출을 계획하세요. 표준 배치 가격(표준 API 가격의 50%)이 적용됩니다.

<CodeGroup>
  ```bash cURL
  curl https://api.anthropic.com/v1/messages/batches \
       --header "x-api-key: $ANTHROPIC_API_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."}
                  ]
              }
          }
      ]
  }'
  ```

  ```bash CLI
  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
  ```

  ```python Python
  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)
  ```

  ```typescript TypeScript
  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);
  ```

  ```csharp C#
  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);
  ```

  ```go Go
  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)
  ```

  ```java Java
  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);
  }
  ```

  ```php PHP
  $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;
  ```

  ```ruby Ruby
  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
  ```
</CodeGroup>

### 효과적인 배치 처리를 위한 모범 사례

Batches API를 최대한 활용하려면:

* 배치 처리 상태를 정기적으로 모니터링하고 실패한 요청에 대해 적절한 재시도 로직을 구현하세요.
* 순서가 보장되지 않으므로, 결과를 요청과 쉽게 매칭할 수 있도록 의미 있는 `custom_id` 값을 사용하세요.
* 관리 용이성을 높이기 위해 매우 큰 데이터셋은 여러 배치로 나누는 것을 고려하세요.
* 유효성 검사 오류를 피하기 위해 Messages API로 단일 요청 형태를 사전 테스트(dry run)하세요.

### 일반적인 문제 해결

예상치 못한 동작이 발생하는 경우:

* 전체 배치 요청 크기가 256 MB를 초과하지 않는지 확인하세요. 요청 크기가 너무 크면 413 `request_too_large` 오류가 발생할 수 있습니다.
* 배치의 모든 요청에 [지원 모델](https://platform.claude.com/docs/ko/build-with-claude/batch-processing#supported-models)을 사용하고 있는지 확인하세요.
* 배치의 각 요청에 고유한 `custom_id`가 있는지 확인하세요.
* 배치 `created_at`(처리 `ended_at`이 아님) 시간으로부터 29일이 지나지 않았는지 확인하세요. 29일이 지났다면 결과를 더 이상 볼 수 없습니다.
* 배치가 취소되지 않았는지 확인하세요.

배치 내 한 요청의 실패는 다른 요청의 처리에 영향을 미치지 않습니다.

## 배치 저장 및 개인정보 보호

* **Workspace 격리**: 배치는 생성된 Workspace 내에서 격리됩니다. 동일한 Workspace의 API 요청 또는 Console에서 Workspace 배치를 볼 수 있는 권한이 있는 사용자만 접근할 수 있습니다.

* **결과 가용성**: 배치 결과는 배치 생성 후 29일 동안 사용할 수 있어, 조회 및 처리에 충분한 시간을 제공합니다.

## 데이터 보존

배치 처리는 배치 생성 후 최대 29일 동안 요청 및 응답 데이터를 저장합니다. 처리 후 언제든지 `DELETE /v1/messages/batches/{batch_id}` 엔드포인트를 사용하여 메시지 배치를 삭제할 수 있습니다. 진행 중인 배치를 삭제하려면 먼저 취소하세요. 비동기 처리에는 배치 완료 및 결과 조회 시까지 입력과 출력 모두의 서버 측 저장이 필요합니다.

모든 기능에 대한 ZDR 적격성은 [API 및 데이터 보존](https://platform.claude.com/docs/ko/manage-claude/api-and-data-retention)을 참조하세요.

## FAQ

<AccordionGroup>
  <Accordion title="배치 처리에는 얼마나 걸리나요?">
    배치 처리에는 최대 24시간이 걸릴 수 있지만, 많은 경우 더 빨리 완료됩니다. 실제 처리 시간은 배치 크기, 현재 수요 및 요청량에 따라 달라집니다. 배치가 만료되어 24시간 이내에 완료되지 않을 수도 있습니다.
  </Accordion>

  <Accordion title="Batches API는 모든 모델에서 사용할 수 있나요?">
    지원되는 모델 목록은 [지원 모델](https://platform.claude.com/docs/ko/build-with-claude/batch-processing#supported-models)을 참조하세요.
  </Accordion>

  <Accordion title="Message Batches API를 다른 API 기능과 함께 사용할 수 있나요?">
    예, Message Batches API는 대부분의 베타 기능을 포함하여 Messages API에서 사용할 수 있는 거의 모든 기능을 지원합니다. 소수의 매개변수(`stream`, `speed`, `max_tokens: 0`)는 지원되지 않습니다. 전체 목록은 [배치로 처리할 수 있는 항목](https://platform.claude.com/docs/ko/build-with-claude/batch-processing#what-can-be-batched)을 참조하세요.
  </Accordion>

  <Accordion title="Message Batches API는 가격에 어떤 영향을 미치나요?">
    Message Batches API는 표준 API 가격 대비 모든 사용량에 대해 50% 할인을 제공합니다. 이는 입력 토큰, 출력 토큰 및 모든 특수 토큰에 적용됩니다. 가격에 대한 자세한 내용은 [가격](https://claude.com/pricing#anthropic-api)을 방문하세요.
  </Accordion>

  <Accordion title="제출한 후 배치를 업데이트할 수 있나요?">
    아니요, 배치가 제출되면 수정할 수 없습니다. 변경이 필요한 경우 현재 배치를 취소하고 새 배치를 제출해야 합니다. 취소가 즉시 적용되지 않을 수 있다는 점에 유의하세요.
  </Accordion>

  <Accordion title="Message Batches API 속도 제한이 있나요? 그리고 Messages API 속도 제한과 상호작용하나요?">
    Message Batches API에는 처리가 필요한 요청 수에 대한 제한 외에도 HTTP 요청 기반 속도 제한이 있습니다. [Message Batches API 속도 제한](https://platform.claude.com/docs/ko/api/rate-limits#message-batches-api)을 참조하세요. Batches API 사용은 Messages API의 속도 제한에 영향을 미치지 않습니다.
  </Accordion>

  <Accordion title="배치 요청의 오류는 어떻게 처리하나요?">
    결과를 가져오면 각 요청에는 `succeeded`, `errored`, `canceled` 또는 `expired` 여부를 나타내는 `result` 필드가 있습니다. `errored` 결과의 경우 추가 오류 정보가 제공됩니다. [API 레퍼런스](https://platform.claude.com/docs/ko/api/messages/batches/create)에서 오류 응답 객체를 확인하세요.
  </Accordion>

  <Accordion title="Message Batches API는 개인정보 보호와 데이터 분리를 어떻게 처리하나요?">
    Message Batches API는 강력한 개인정보 보호 및 데이터 분리 조치를 갖추고 설계되었습니다:

    1. 배치와 그 결과는 생성된 Workspace 내에서 격리됩니다. 즉, 동일한 Workspace의 API 요청으로만 접근할 수 있습니다.
    2. 배치 내 각 요청은 독립적으로 처리되며, 요청 간 데이터 유출이 없습니다.
    3. 결과는 제한된 기간(29일) 동안만 사용할 수 있으며, Anthropic의 [데이터 보존 정책](https://support.claude.com/en/articles/7996866-how-long-do-you-store-personal-data)을 따릅니다.
    4. Console에서의 배치 결과 다운로드는 조직 수준 또는 Workspace별로 비활성화할 수 있습니다.
  </Accordion>

  <Accordion title="Message Batches API에서 프롬프트 캐싱을 사용할 수 있나요?">
    예, Message Batches API에서 프롬프트 캐싱을 사용할 수 있습니다. 그러나 비동기 배치 요청은 동시에 임의의 순서로 처리될 수 있으므로, 캐시 적중은 최선 노력(best-effort) 방식으로 제공됩니다.
  </Accordion>
</AccordionGroup>

## 다음 단계

<CardGroup cols={2}>
  <Card title="검색 결과" icon="magnifying-glass" href="https://platform.claude.com/docs/ko/build-with-claude/search-results">
    출처 표기가 포함된 검색 결과를 제공하여 RAG 애플리케이션에서 자연스러운 인용을 활성화하세요.
  </Card>

  <Card title="프롬프트 캐싱" icon="database" href="https://platform.claude.com/docs/ko/build-with-claude/prompt-caching">
    배치 내 요청 간에 공유되는 프롬프트 접두사를 캐싱하여 비용과 지연 시간을 줄이세요.
  </Card>
</CardGroup>
