본문 바로가기
카테고리 없음

Anthropic Batch API (비용 절감, custom_id, rate limit)

by BOOST YOUR INFORMATION 2026. 5. 24.

Anthropic Batch API (비용 절감, custom_id, rate limit) 참조 이미지
Anthropic Batch API

 

50,000개 상품 설명을 실시간 API로 분류하면 비용이 얼마나 나올까요. 저도 처음엔 그냥 루프 돌리면 되겠다 싶었는데, 견적을 뽑아보고 생각이 완전히 바뀌었습니다. 그날 이후로 대량 LLM 작업에서 Batch API는 선택이 아니라 기본 전제가 됐습니다.

50,000건 분류 작업, 실시간 API로 돌리다가 멈춘 이유

처음 이 프로젝트를 맡았을 때, 상품 설명 텍스트를 카테고리별로 분류하는 파이프라인을 짜야 했습니다. 규모는 약 50,000건. 초반에는 동기(synchronous) API 호출 방식으로 접근했습니다. 동기 호출이란 요청을 하나 보내고, 응답이 올 때까지 기다렸다가, 다음 요청을 보내는 방식입니다. 구조가 단순하고 결과를 즉시 확인할 수 있어서 처음 프로토타입을 짤 때는 편했습니다.

문제는 규모가 커지면서 바로 드러났습니다. rate limit에 계속 걸렸습니다. rate limit이란 API 제공자가 단위 시간당 허용하는 요청 수의 상한선을 말합니다. 분당 요청 수 제한을 피하려면 요청 사이마다 인위적인 지연을 넣어야 했고, 그러다 보니 전체 처리 시간이 걷잡을 수 없이 늘어났습니다. 코드 절반이 time.sleep()과 재시도 로직으로 채워지는 상황이 됐는데, 솔직히 이건 예상 밖이었습니다. 그 시점에서 접근 방식 자체를 바꿔야 한다는 걸 직감했습니다.

비용 문제도 있었습니다. 실시간 API 단가로 50,000건을 처리하면 만만치 않은 금액이 나옵니다. 이 작업이 꼭 실시간이어야 하는지 스스로에게 물어봤을 때, 답은 명확히 "아니오"였습니다. 카테고리 분류 결과가 1시간 뒤에 나와도 전혀 문제없는 작업이었으니까요.

Batch API 전환, 비용 절반에 rate limit 걱정도 사라진 이유

Anthropic의 Message Batches API는 비동기(asynchronous) 방식으로 동작합니다. 비동기란 요청을 한꺼번에 제출해두고 결과를 나중에 수령하는 방식으로, 처리 중에 다른 작업을 이어갈 수 있습니다. 요청을 JSONL 형식으로 묶어 하나의 배치 잡(batch job)으로 제출하면, Anthropic 측에서 이를 백그라운드로 처리한 뒤 결과를 돌려주는 구조입니다. JSONL이란 JSON Lines의 약자로, 각 줄이 독립적인 JSON 객체인 파일 형식입니다. 대용량 데이터를 줄 단위로 스트리밍하기에 적합해서 배치 처리에 자주 쓰입니다.

공식 가격 정책 기준으로 Batch API를 쓰면 실시간 API 대비 50% 할인이 적용됩니다(출처: Anthropic Pricing). 50,000건 기준으로 계산하면 비용이 절반으로 줄어드는 셈인데, 제가 직접 써봤는데 이 수치는 실제로 정확하게 체감됩니다. 50% 절감이라는 게 작은 숫자처럼 느껴질 수도 있지만, 건수가 수만 단위를 넘어가면 절대 금액 차이가 꽤 커집니다.

rate limit 압박이 사라진 것도 체감 효과가 컸습니다. 실시간 호출에서는 분당 요청 수와 토큰 사용량을 모니터링하면서 인위적으로 속도를 조절해야 했는데, 배치 방식에서는 그런 관리가 불필요했습니다. 배치 잡 안에서 개별 요청 간의 처리 속도는 Anthropic이 알아서 분산 처리하기 때문입니다. 코드가 훨씬 단순해졌고, 운영 중에 rate limit 에러가 뜰까봐 신경 쓸 일도 없어졌습니다.

처리 완료까지 최대 24시간이 소요될 수 있다고 공식 문서에 명시돼 있습니다(출처: Anthropic Message Batches API). 처음 이 부분을 봤을 때 솔직히 부담스러웠습니다. 그런데 막상 50,000건 규모로 실제로 돌려보니 2~4시간 내에 완료됐습니다. 24시간은 최악의 경우를 가정한 상한선으로 보는 게 맞고, 제 경험상 이건 좀 과하게 보수적인 수치입니다.

custom_id 매핑과 예외 처리, 놓치면 결과가 뒤섞인다

배치 처리에서 가장 주의해야 할 점이 하나 있습니다. 결과가 제출 순서와 다르게 반환된다는 점입니다. 처음에 이걸 모르고 결과를 순서대로 받아서 원본 데이터와 매핑했다가 분류 결과가 전부 뒤섞이는 상황이 생겼습니다. 제가 직접 겪어보니 이 부분이 실제로 가장 많은 실수가 생기는 지점이었습니다.

해결책은 각 요청에 custom_id를 붙이는 것입니다. custom_id란 요청마다 고유하게 부여하는 식별자로, 결과가 어떤 순서로 반환되더라도 이 ID를 기준으로 원본 데이터와 1:1 매핑할 수 있습니다. 저는 상품의 고유 ID를 그대로 custom_id로 사용했고, 결과 JSONL을 파싱할 때 이 ID로 딕셔너리를 만들어서 매핑했습니다.

예외 처리 파이프라인도 별도로 구축해야 했습니다. 배치 결과 안에는 성공한 요청뿐 아니라 에러로 반환된 요청도 섞여 있을 수 있습니다. 제 경우에는 실패 항목을 추출해 별도 JSONL로 저장해두고, 이를 다시 소규모 배치 잡으로 재제출하는 방식으로 처리했습니다. 전체 건수 대비 실패율은 낮았지만, 0%가 아닌 이상 이 로직은 필수입니다.

대용량 결과 파일을 다룰 때도 한 가지 함정이 있었습니다. 수만 건의 결과가 담긴 JSONL 파일을 한꺼번에 메모리에 올리면 메모리 부족이 발생했습니다. 스트리밍 방식 파서, 즉 파일을 줄 단위로 읽어가며 처리하는 방식을 써야 이 문제를 피할 수 있었습니다. 처음에 이 부분을 간과하고 json.load()로 전체를 올리려다 프로세스가 죽는 경험을 했습니다.

Batch API 전환 시 챙겨야 할 핵심 구현 포인트를 정리하면 다음과 같습니다.

  • custom_id를 요청마다 반드시 부여하고, 결과 파싱 시 이를 기준으로 원본과 매핑
  • 성공/실패 항목을 분리하고, 실패 항목만 재처리하는 예외 처리 로직 구축
  • 결과 JSONL 파일은 스트리밍 방식(줄 단위 읽기)으로 파싱해 메모리 이슈 방지
  • 처리 완료 여부는 폴링(polling) 또는 웹훅으로 확인

Batch API가 적합한 상황과 적합하지 않은 상황

제가 이 경험을 통해 얻은 결론은 단순합니다. 실시간 응답이 필요 없는 LLM 작업이라면, Batch API를 기본 선택지로 놓고 시작하는 게 맞습니다. 분류, 요약, 데이터 정제, 레이블링처럼 배치 처리에 완벽히 적합한 작업들이 실제로 매우 많은데, 많은 개발자들이 습관적으로 동기 API를 씁니다. OpenAI도 유사한 배치 처리 인터페이스를 제공하고 있어, 이 방식이 업계에서 대량 처리의 표준적인 접근법으로 자리 잡아가고 있습니다(출처: OpenAI Batch API).

단, Batch API가 맞지 않는 상황도 분명합니다. 실시간 사용자 인터랙션, 시간에 민감한 알림, 완료 시점을 정확히 보장해야 하는 SLA(서비스 수준 협약, 즉 서비스가 응답해야 하는 최대 허용 시간을 정한 약속)가 있는 서비스에서는 배치 방식이 치명적인 단점이 됩니다. 챗봇 응답, 실시간 추천 시스템 같은 경우가 여기에 해당합니다.

결국 Batch API는 "빠름"보다 "저렴함"을 택하는 트레이드오프입니다. 트레이드오프란 한쪽을 얻으면 다른 쪽을 포기해야 하는 상충 관계를 말합니다. 이 트레이드오프를 팀 안에서 명확히 인식하고 워크플로를 설계하는 것이 아키텍처적으로 중요한 판단입니다. 실시간이 필요 없는 작업에 굳이 실시간 비용을 지불하는 건, 제 경험상 가장 흔하고 가장 조용하게 비용을 갉아먹는 실수입니다.

대량 LLM 처리 파이프라인을 처음 설계한다면, 작업의 시간 민감도를 먼저 따져보시길 권합니다. 그 답이 "몇 시간 내라도 괜찮다"라면, Batch API로 시작하는 것이 가장 합리적인 선택입니다.


참고:


소개 및 문의 · 개인정보처리방침 · 면책조항

© 2026 ⚡ 정보 부스터 🚀