← All guides

Claude Batch API 가이드: 대량 처리로 비용 50% 절감하기

Claude Batch API로 최대 10,000건 일괄 처리 — 실시간 API보다 50% 저렴. Python 코드, 폴링 패턴, 콘텐츠 배치 실전 예제.

Claude Batch API 가이드: 대량 처리로 비용 50% 절감하기

Claude Batch API는 실시간 API 대비 50% 저렴한 단가로 최대 10,000건의 요청을 비동기 일괄 처리한다. 결과는 24시간 이내(대부분 수 분~수 시간)에 JSONL 파일로 제공된다. 즉각적인 응답이 필요 없는 작업 — 콘텐츠 분류, 번역, 데이터 정제, SEO 메타 생성 — 에서 비용을 절반으로 줄이는 가장 직접적인 방법이다. Python SDK 한 줄로 배치를 생성하고, 폴링으로 완료를 확인하며, 스트리밍 JSONL로 결과를 소비한다.


언제 Batch API를 써야 하나?

Batch API와 실시간 API의 선택 기준은 단순하다. 응답 지연을 사용자가 체감하는 작업이면 실시간, 그렇지 않으면 배치다.

구분 실시간 API Batch API
응답 시간 수 초 이내 수 분~24시간
비용 기준 단가 기준 단가의 50%
최대 요청 수 제한 없음 (Rate Limit 범위) 배치당 10,000건
적합한 작업 챗봇, 실시간 번역, API 응답 배치 분류, 일괄 번역, 야간 파이프라인

Batch API가 유리한 대표 시나리오:

반면 사용자가 텍스트 입력 후 즉시 응답을 기다리는 챗봇이나, 실시간 스트리밍이 필요한 인터페이스에서는 실시간 API를 유지해야 한다.


Python 코드 예시

1. 배치 생성

import anthropic

client = anthropic.Anthropic()

# 처리할 요청 목록 구성
requests = []
texts = [
    "이 제품은 정말 훌륭합니다. 배송도 빠르고 품질도 최고예요.",
    "기대했던 것과 달랐습니다. 색상이 사진과 많이 달라요.",
    "보통이에요. 가격 대비 나쁘지 않은 것 같습니다.",
    # ... 최대 10,000건까지 추가 가능
]

for i, text in enumerate(texts):
    requests.append(
        anthropic.types.beta.messages.MessageCreateParamsNonStreaming(
            model="claude-haiku-4-5",
            max_tokens=100,
            messages=[
                {
                    "role": "user",
                    "content": f"다음 리뷰의 감성을 '긍정', '부정', '중립' 중 하나로만 답하세요.\n\n리뷰: {text}"
                }
            ]
        )
    )

# 배치 생성
batch = client.beta.messages.batches.create(requests=requests)
print(f"배치 ID: {batch.id}")
print(f"상태: {batch.processing_status}")
# 출력 예: 배치 ID: batch_01ABC123...
#          상태: in_progress

2. 완료 대기 (폴링)

import time

def wait_for_batch(client, batch_id: str, poll_interval: int = 30):
    """배치 완료까지 폴링하며 대기."""
    while True:
        batch = client.beta.messages.batches.retrieve(batch_id)
        status = batch.processing_status

        if status == "ended":
            print(f"완료! 성공: {batch.request_counts.succeeded}건, "
                  f"실패: {batch.request_counts.errored}건")
            return batch
        elif status == "canceling":
            print("배치가 취소 중입니다.")
            return None

        print(f"처리 중... ({batch.request_counts.processing}건 남음)")
        time.sleep(poll_interval)

# 폴링 실행 (30초 간격 권장)
completed_batch = wait_for_batch(client, batch.id)

3. 결과 처리 (JSONL 스트리밍)

results = {}

# 결과 스트리밍 — 메모리 효율적으로 한 줄씩 처리
for result in client.beta.messages.batches.results(completed_batch.id):
    custom_id = result.custom_id
    
    if result.result.type == "succeeded":
        text = result.result.message.content[0].text
        results[custom_id] = text
    elif result.result.type == "errored":
        error = result.result.error
        print(f"요청 {custom_id} 실패: {error.type} — {error.message}")

# 결과 출력
for req_id, sentiment in results.items():
    print(f"{req_id}: {sentiment.strip()}")

4. 전체 파이프라인 (프로덕션 패턴)

import anthropic
import json
import time
from pathlib import Path

def run_sentiment_batch(texts: list[str], output_path: str = "results.jsonl"):
    client = anthropic.Anthropic()

    # 요청 구성 (custom_id로 추적)
    requests = [
        anthropic.types.beta.messages.MessageCreateParamsNonStreaming(
            custom_id=f"req_{i}",
            model="claude-haiku-4-5",
            max_tokens=50,
            messages=[{
                "role": "user",
                "content": f"감성 분석 (긍정/부정/중립만 답할 것):\n{text}"
            }]
        )
        for i, text in enumerate(texts)
    ]

    # 배치 제출
    batch = client.beta.messages.batches.create(requests=requests)
    print(f"배치 제출 완료: {batch.id} ({len(requests)}건)")

    # 완료 대기
    while True:
        batch = client.beta.messages.batches.retrieve(batch.id)
        if batch.processing_status == "ended":
            break
        time.sleep(60)

    # 결과 저장
    with open(output_path, "w", encoding="utf-8") as f:
        for result in client.beta.messages.batches.results(batch.id):
            f.write(json.dumps({
                "id": result.custom_id,
                "sentiment": result.result.message.content[0].text.strip()
                    if result.result.type == "succeeded" else "ERROR"
            }, ensure_ascii=False) + "\n")

    print(f"결과 저장 완료: {output_path}")
    return output_path

비용 계산 예시 (10,000건 기준)

Claude Haiku 기준으로 리뷰 감성 분석 10,000건을 처리하는 비용을 비교한다.

요청 구조 가정

입력 토큰 합계: (50 + 200) × 10,000 = 2,500,000 토큰
출력 토큰 합계: 5 × 10,000 = 50,000 토큰

실시간 API vs Batch API

구분 실시간 API Batch API 절감액
입력 단가 (Haiku) $0.80 / 1M $0.40 / 1M
출력 단가 (Haiku) $4.00 / 1M $2.00 / 1M
입력 비용 $2.00 $1.00 -$1.00
출력 비용 $0.20 $0.10 -$0.10
총합 $2.20 $1.10 $1.10 (50%)
원화 환산 약 3,080원 약 1,540원 약 1,540원

10,000건에서 $1.10 절감은 작아 보이지만, 동일 파이프라인을 매일 실행하면 월 $33(약 46,200원) 이 절약된다.

Sonnet으로 더 복잡한 작업(입력 평균 1,000토큰, 출력 500토큰, 10,000건)을 처리하는 경우:

구분 실시간 Batch
입력 비용 ($3.00/$1.50 per 1M) $30.00 $15.00
출력 비용 ($15.00/$7.50 per 1M) $75.00 $37.50
총합 $105.00 $52.50

Sonnet 수준 작업에서 10,000건당 $52.50 절감이 발생한다. 월 1회 실행만 해도 연간 $630(약 882,000원)을 아낄 수 있다.

자세한 비용 시뮬레이션은 Claude API 비용 계산 가이드에서 확인할 수 있다.


실전 사용 패턴

패턴 1: 콘텐츠 분류 파이프라인

블로그 운영자나 미디어 팀에서 수백~수천 편의 기사를 카테고리별로 자동 분류할 때 유용하다.

# 기사 제목 + 본문 첫 단락으로 카테고리 분류
def classify_articles(articles: list[dict]) -> list[dict]:
    requests = [
        anthropic.types.beta.messages.MessageCreateParamsNonStreaming(
            custom_id=str(article["id"]),
            model="claude-haiku-4-5",
            max_tokens=30,
            messages=[{
                "role": "user",
                "content": (
                    "다음 기사를 카테고리로 분류하세요. "
                    "카테고리: [기술, 경제, 스포츠, 문화, 정치, 사회]\n"
                    f"제목: {article['title']}\n"
                    f"요약: {article['summary']}"
                )
            }]
        )
        for article in articles
    ]
    # ... 배치 실행 및 결과 매핑

패턴 2: SEO 메타 생성 배치

기존 기사 500편의 메타 디스크립션을 일괄 생성하거나 개선할 때 사용한다. 야간에 배치를 제출하면 다음날 아침 결과를 적용할 수 있다.

# 기사 본문 → SEO 메타 디스크립션 자동 생성
meta_prompt = """다음 기사 본문을 읽고 SEO 메타 디스크립션을 작성하세요.
조건: 155자 이내, 핵심 키워드 포함, 클릭 유도 문구 포함.
본문만 답하고 다른 설명은 하지 마세요."""

패턴 3: 다국어 번역 파이프라인

한국어 제품 설명 1,000건을 영어·일본어·중국어로 동시에 번역할 때, 언어별 배치를 병렬로 제출하면 처리 시간을 크게 줄일 수 있다.

# 언어별 배치를 동시에 제출 (병렬 처리)
target_languages = ["English", "Japanese", "Chinese"]
batch_ids = {}

for lang in target_languages:
    batch = client.beta.messages.batches.create(
        requests=build_translation_requests(texts, lang)
    )
    batch_ids[lang] = batch.id
    print(f"{lang} 배치 제출: {batch.id}")

# 모든 배치 완료 대기
for lang, batch_id in batch_ids.items():
    wait_for_batch(client, batch_id)

Batch API와 프롬프트 캐싱을 함께 적용하면 비용을 더 줄일 수 있다. 캐싱 전략에 대한 자세한 내용은 Claude 프롬프트 캐싱 완전 가이드를 참고하라.


Batch API 운영 팁

배치 크기 최적화: 10,000건 한도를 채울 필요는 없다. 1,000~5,000건 단위로 나누면 부분 실패 시 재처리 범위가 좁아진다.

폴링 간격: 30~60초가 적당하다. 소규모 배치(수백 건)는 수 분 내 완료되지만, 10,000건 대용량은 수 시간이 걸릴 수 있다.

실패 재처리: result.result.type == "errored" 인 요청만 추출해 별도 배치로 재제출하면 된다. 전체를 다시 실행할 필요가 없다.

비용 최적화 조합: Batch API(50% 절감) + 프롬프트 캐싱(최대 90% 절감)을 동시에 적용하면 동일 작업량 대비 총 비용을 90% 이상 줄이는 것도 가능하다.


Claude API 비용 최적화 완전판 — Batch API, 프롬프트 캐싱, 모델 라우팅을 모두 다루는 P5 Cost Optimization Masterclass를 확인하세요. 계산기 포함, 실전 파이프라인 코드 제공.

P5 Cost Optimization Masterclass 보기 →


Frequently Asked Questions

Batch API는 어떤 모델을 지원하나요?

Claude 3.5 Haiku, Claude Sonnet 4.5, Claude Opus 4.5 등 현재 Anthropic API에서 지원하는 주요 모델을 모두 Batch API로 사용할 수 있다. 배치 내 요청별로 서로 다른 모델을 지정할 수도 있다.

24시간 내 결과가 안 나오면 어떻게 되나요?

Anthropic은 24시간 내 결과 제공을 보장한다. 만약 24시간이 지나도 완료되지 않은 배치는 자동으로 취소되며, 과금되지 않은 요청은 환불된다. 실제로는 대부분의 배치가 수 분~수 시간 내에 완료된다.

배치 중간에 취소할 수 있나요?

client.beta.messages.batches.cancel(batch_id)로 진행 중인 배치를 취소할 수 있다. 이미 처리된 요청에 대해서는 비용이 청구되고, 처리되지 않은 요청은 과금되지 않는다.

실시간 API와 Batch API를 같은 프로젝트에서 혼용할 수 있나요?

가능하다. 사용자 대면 기능(챗봇, 실시간 응답)은 실시간 API를, 야간 데이터 처리나 백그라운드 분류 파이프라인은 Batch API로 분리하는 것이 이상적인 구조다. 동일한 API 키와 클라이언트 인스턴스를 재사용할 수 있다.

도구와 자료