이 가이드에서는 OpenAI 배치 API를 사용해 수천 개의 모델 요청을 하나의 비동기 작업으로 실행하고, 결과를 50% 할인된 토큰 비용으로 가져오는 구현 흐름을 다룹니다. JSONL 파일을 만들고, 배치를 제출하고, 상태를 폴링하고, 결과를 다운로드한 뒤, 프로덕션에 연결하기 전에 Apidog에서 각 단계를 검증합니다. 작업이 사용자와 실시간으로 상호작용해야 한다면 배치 대신 동기 API를 사용하고, Apidog로 ChatGPT API를 테스트하는 흐름이 더 적합합니다.
배치 API란 무엇이며 언제 사용해야 할까요?
배치 API는 지연을 허용할 수 있는 대량 모델 호출을 처리하기 위한 비동기 엔드포인트입니다.
일반적인 동기 호출은 프롬프트마다 HTTP 요청을 보냅니다. 배치 API는 여러 요청을 하나의 JSONL 파일로 묶어 업로드한 뒤, 하나의 배치 작업으로 제출합니다. 이후 작업 상태를 폴링하고, 완료되면 출력 파일을 다운로드합니다.
배치 API의 핵심 이점은 두 가지입니다.
- 동기 API 대비 입력 및 출력 토큰 모두 50% 할인
- 실시간 트래픽과 분리된 별도 속도 제한 풀 사용
단점은 지연 시간입니다. OpenAI는 배치가 24시간 이내에 완료된다고 보장합니다. 많은 작업은 더 빨리 끝날 수 있지만, 시스템은 24시간 상한을 기준으로 설계해야 합니다.
배치 API는 다음과 같은 오프라인 대량 작업에 적합합니다.
- 과거 데이터 백로그 분류 또는 태그 지정
- 전체 코퍼스에 대한 임베딩 생성
- 제품 설명, 요약, 번역 등 대량 콘텐츠 생성
- 데이터셋 기반 평가 스위트 또는 모델 비교 실행
반대로 사용자가 응답을 기다리는 기능에는 사용하지 마세요. 채팅 UI, 자동 완성, 라이브 에이전트는 동기식 엔드포인트가 필요합니다. 여러 모델 또는 에이전트 구성을 한 번에 생성하는 경우에는 배치 처리가 잘 맞습니다. 관련 예시는 배치 처리로 100개 이상의 에이전트 구성 생성 가이드를 참고하세요.
전체 흐름
배치 API 구현은 크게 네 단계입니다.
| 단계 | 엔드포인트 | 수행 작업 |
|---|---|---|
| 1. 업로드 | POST /v1/files |
purpose: "batch"와 함께 .jsonl 파일을 업로드하고 파일 ID를 받습니다 |
| 2. 생성 | POST /v1/batches |
파일 ID, 대상 엔드포인트, 완료 기간을 제출합니다 |
| 3. 폴링 | GET /v1/batches/{id} |
status가 completed가 될 때까지 확인합니다 |
| 4. 검색 | GET /v1/files/{id}/content |
output_file_id로 결과 파일을 다운로드합니다 |
준비물은 다음과 같습니다.
- OpenAI API 키
- 요청을 담은 JSONL 파일
-
curl, Apidog 등 HTTP 요청을 실행하고 응답을 확인할 도구
환경 변수로 API 키를 설정합니다.
export OPENAI_API_KEY="your_api_key"
1단계: JSONL 요청 파일 만들기
입력 파일은 JSONL 형식입니다. 각 줄은 하나의 독립적인 요청입니다.
각 줄에는 다음 필드가 필요합니다.
| 필드 | 설명 |
|---|---|
custom_id |
결과와 원본 요청을 매칭하기 위한 고유 ID |
method |
일반적으로 POST
|
url |
실행할 대상 엔드포인트. 예: /v1/chat/completions
|
body |
실제 API 요청 본문 |
예를 들어 감정 분류 작업을 배치로 실행하려면 requests.jsonl 파일을 다음처럼 만들 수 있습니다.
{"custom_id": "req-1", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "gpt-4.1-mini", "messages": [{"role": "user", "content": "Classify the sentiment of: 'shipping was slow but the product is great'"}]}}
{"custom_id": "req-2", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "gpt-4.1-mini", "messages": [{"role": "user", "content": "Classify the sentiment of: 'returned it the same day'"}]}}
중요한 점은 custom_id입니다. 출력 결과는 입력 순서대로 반환된다는 보장이 없습니다. 따라서 결과를 원본 요청과 매칭할 때 줄 번호가 아니라 custom_id를 기준으로 처리해야 합니다.
제약 조건도 확인하세요.
- 단일 배치: 최대 50,000개 요청
- 파일 크기: 최대 200MB
- 파일 내
custom_id: 고유해야 함
2단계: JSONL 파일 업로드
파일 API에 purpose="batch"로 JSONL 파일을 업로드합니다.
curl https://api.openai.com/v1/files \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-F purpose="batch" \
-F file="@requests.jsonl"
응답에서 id를 확인합니다.
{
"id": "file-abc123",
"object": "file",
"purpose": "batch"
}
이 id가 다음 단계에서 사용할 input_file_id입니다.
3단계: 배치 생성
업로드한 파일 ID로 배치 작업을 생성합니다.
curl https://api.openai.com/v1/batches \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input_file_id": "file-abc123",
"endpoint": "/v1/chat/completions",
"completion_window": "24h",
"metadata": {
"job": "sentiment-backfill"
}
}'
endpoint 값은 JSONL 각 줄의 url과 일치해야 합니다.
예를 들어 JSONL에서 url이 /v1/chat/completions라면 배치 생성 요청의 endpoint도 /v1/chat/completions여야 합니다.
지원되는 대상에는 다음이 포함됩니다.
/v1/chat/completions/v1/responses/v1/embeddings/v1/completions/v1/moderations
metadata는 선택 사항입니다. 최대 16개의 키-값 쌍을 넣을 수 있으며, 작업 추적이나 비용 분류에 유용합니다.
배치 생성 응답 예시는 다음과 같습니다.
{
"id": "batch_abc123",
"object": "batch",
"endpoint": "/v1/chat/completions",
"input_file_id": "file-abc123",
"completion_window": "24h",
"status": "validating",
"output_file_id": null,
"error_file_id": null,
"request_counts": {
"total": 0,
"completed": 0,
"failed": 0
},
"created_at": 1733452800,
"metadata": {
"job": "sentiment-backfill"
}
}
여기서 저장해야 할 값은 id입니다. 이후 상태 조회에 사용합니다.
export BATCH_ID="batch_abc123"
4단계: 배치 상태 폴링
배치는 처음에 validating 상태로 시작합니다. 이후 처리 상태에 따라 값이 변경됩니다.
curl https://api.openai.com/v1/batches/$BATCH_ID \
-H "Authorization: Bearer $OPENAI_API_KEY"
주요 상태는 다음과 같습니다.
| 상태 | 의미 |
|---|---|
validating |
실행 전 입력 파일을 검증 중 |
in_progress |
요청 처리 중 |
finalizing |
실행이 끝났고 출력 파일 준비 중 |
completed |
완료. 결과 다운로드 가능 |
failed |
유효성 검사 실패. 요청은 실행되지 않음 |
expired |
24시간 내 모든 요청이 완료되지 않음 |
cancelling / cancelled
|
취소 요청 중 또는 취소 완료 |
in_progress 상태에서는 request_counts로 진행률을 확인할 수 있습니다.
{
"request_counts": {
"total": 50000,
"completed": 31500,
"failed": 12
}
}
폴링은 너무 자주 하지 마세요. 웹훅을 기다리는 방식이 아니므로, 몇 초 단위가 아니라 몇 분 간격으로 확인하는 패턴이 적합합니다.
간단한 폴링 스크립트 예시는 다음과 같습니다.
while true; do
curl -s https://api.openai.com/v1/batches/$BATCH_ID \
-H "Authorization: Bearer $OPENAI_API_KEY" | jq '{id, status, request_counts, output_file_id, error_file_id}'
sleep 120
done
잘못 제출한 배치는 취소할 수 있습니다.
curl -X POST https://api.openai.com/v1/batches/$BATCH_ID/cancel \
-H "Authorization: Bearer $OPENAI_API_KEY"
5단계: 결과 파일 다운로드
status가 completed가 되면 배치 객체에 output_file_id가 포함됩니다.
{
"status": "completed",
"output_file_id": "file-output456",
"error_file_id": "file-error789"
}
출력 파일을 다운로드합니다.
curl https://api.openai.com/v1/files/file-output456/content \
-H "Authorization: Bearer $OPENAI_API_KEY" > results.jsonl
결과도 JSONL 형식입니다. 각 줄에는 원래 요청의 custom_id와 응답 정보가 들어 있습니다.
출력 예시는 다음과 비슷합니다.
{"custom_id":"req-1","response":{"status_code":200,"request_id":"request-1","body":{"choices":[{"message":{"role":"assistant","content":"Mixed positive"}}]}}}
{"custom_id":"req-2","response":{"status_code":200,"request_id":"request-2","body":{"choices":[{"message":{"role":"assistant","content":"Negative"}}]}}}
오류가 있는 요청은 error_file_id가 가리키는 파일에서 확인합니다.
curl https://api.openai.com/v1/files/file-error789/content \
-H "Authorization: Bearer $OPENAI_API_KEY" > errors.jsonl
결과 처리 시에는 반드시 custom_id를 기준으로 원본 데이터와 조인하세요. 출력 줄 순서를 신뢰하면 안 됩니다.
비용과 처리 시간 설계
배치 API의 비용 모델은 단순합니다.
- 입력 토큰 50% 할인
- 출력 토큰 50% 할인
- 최대 24시간 처리 시간 허용
따라서 다음 작업에는 적합합니다.
- 야간 배치
- 일회성 백필
- 비동기 리포트 생성
- 대량 평가 작업
- 대량 임베딩 생성
반대로 제품의 핵심 실시간 경로에는 적합하지 않습니다.
운영 시 고려할 점은 다음과 같습니다.
- 할인은 지원되는 모델의 입력 및 출력 토큰 모두에 적용됩니다.
- 24시간은 목표 시간이 아니라 상한선입니다.
-
expired가 되면 완료된 요청은 반환 및 청구되고, 완료되지 않은 요청은 반환되지 않습니다. - 배치 작업은 별도 대기열 토큰 제한을 사용하므로 실시간 API 속도 제한을 직접 잠식하지 않습니다.
- 대량 작업은 반값이어도 총액이 커질 수 있으므로
metadata로 작업별 비용을 추적하세요.
동기식 API의 속도 제한을 함께 다뤄야 한다면 GPT API 속도 제한 및 테스트 방법을 참고하세요. 작업별 비용 할당이 필요하다면 OpenAI 지출에 대한 비용 할당 플레이북을 확인할 수 있습니다.
Apidog에서 배치 API 테스트하기
배치 API는 일반적인 단일 채팅 호출보다 실패 지점이 많습니다.
- JSONL 형식 오류
- 누락된
custom_id - 잘못된
url - 배치
endpoint와 JSONLurl불일치 - 파일 업로드 실패
- 폴링 로직 오류
- 결과 파일 파싱 오류
자동화하기 전에 Apidog에서 전체 수명 주기를 수동으로 검증하면 불필요한 24시간 왕복을 줄일 수 있습니다. Apidog는 OpenAI SDK가 아니라 API 요청을 구성, 실행, 확인, 모의할 수 있는 API 플랫폼입니다.
권장 테스트 흐름은 다음과 같습니다.
1. JSONL 형식 검증
업로드 전에 각 줄이 다음 필드를 포함하는지 확인합니다.
{
"custom_id": "req-1",
"method": "POST",
"url": "/v1/chat/completions",
"body": {}
}
특히 다음을 확인하세요.
-
custom_id가 비어 있지 않은지 - 파일 내
custom_id가 중복되지 않는지 -
method가POST인지 -
url이 배치 생성 시 사용할endpoint와 같은지 -
body.model이 있는지 -
messages또는 대상 엔드포인트에 필요한 필드가 있는지
2. 파일 업로드 요청 만들기
Apidog에서 POST /v1/files 요청을 만들고 multipart form-data로 설정합니다.
| 키 | 값 |
|---|---|
purpose |
batch |
file |
requests.jsonl |
응답의 id를 환경 변수로 저장합니다.
예:
input_file_id = file-abc123
3. 배치 생성 요청 실행
POST /v1/batches 요청을 만들고 다음 본문을 보냅니다.
{
"input_file_id": "{{input_file_id}}",
"endpoint": "/v1/chat/completions",
"completion_window": "24h",
"metadata": {
"job": "sentiment-backfill"
}
}
응답에서 다음 필드를 확인합니다.
-
status가validating인지 -
endpoint가 기대한 값인지 -
input_file_id가 업로드한 파일 ID인지 -
id가 반환되었는지
반환된 배치 ID도 환경 변수로 저장합니다.
batch_id = batch_abc123
4. 상태 폴링 요청 구성
GET /v1/batches/{{batch_id}} 요청을 만들고 주기적으로 실행합니다.
확인할 필드는 다음입니다.
statusrequest_counts.totalrequest_counts.completedrequest_counts.failedoutput_file_iderror_file_id
status가 completed가 되면 output_file_id를 저장합니다.
5. 결과 다운로드 요청 만들기
GET /v1/files/{{output_file_id}}/content 요청을 실행해 결과 JSONL을 다운로드합니다.
오류 파일이 있으면 error_file_id도 동일하게 다운로드합니다.
6. 취소 및 실패 경로 테스트
정상 경로만 테스트하지 마세요. 다음 케이스도 미리 확인하는 것이 좋습니다.
- 일부러 잘못된 JSONL 파일을 업로드해
failed상태 확인 -
endpoint와 JSONLurl을 다르게 설정해 오류 처리 확인 -
POST /v1/batches/{id}/cancel로 취소 요청 확인 -
error_file_id가 있는 경우 파싱 로직 확인
출력 파일은 나중에 도착하므로, 개발 중에는 모의 API를 만들어 샘플 배치 객체와 결과 파일을 반환하도록 구성할 수 있습니다. 이렇게 하면 실제 24시간 작업을 기다리거나 토큰을 소비하지 않고 결과 다운로드 및 파싱 로직을 구현할 수 있습니다.
팀이 사양 우선 방식으로 작업한다면 OpenAPI 사양에서 직접 테스트 컬렉션을 생성하고 CI에서 배치 엔드포인트를 회귀 테스트 범위에 포함할 수도 있습니다.
자주 묻는 질문
실제로 배치는 얼마나 걸리나요?
OpenAI는 배치가 24시간 이내에 완료된다고 보장합니다. 실제로는 더 빨리 끝나는 작업도 많지만, 시스템은 24시간 상한을 기준으로 설계해야 합니다.
작업이 기간 내 완료되지 않으면 배치는 expired 상태가 됩니다. 이 경우 완료된 요청만 반환되고 청구됩니다.
할인은 어느 정도인가요?
배치 API는 동기식 엔드포인트 대비 입력 및 출력 토큰 모두 50% 고정 할인을 제공합니다.
기능이나 작업 단위로 비용을 나누어 추적하려면 metadata를 사용해 배치 작업을 태깅하세요. 비용 분할 방식은 비용 할당 플레이북에서 확인할 수 있습니다.
배치에서 어떤 엔드포인트를 실행할 수 있나요?
JSONL의 url과 배치 생성 요청의 endpoint가 모두 같은 대상을 가리켜야 합니다.
지원되는 대상에는 다음이 포함됩니다.
/v1/chat/completions/v1/responses/v1/embeddings/v1/completions/v1/moderations- 이미지 및 비디오 관련 엔드포인트
지원 목록은 시간이 지나면서 바뀔 수 있으므로 OpenAI의 최신 문서를 확인하세요.
결과 순서가 입력 순서와 다른 이유는 무엇인가요?
의도된 동작입니다. 출력 JSONL은 입력 줄 순서를 보장하지 않습니다.
그래서 모든 요청에 고유한 custom_id가 필요합니다. 결과 처리 로직은 반드시 custom_id를 기준으로 원본 요청과 응답을 매칭해야 합니다.
결론
OpenAI 배치 API의 구현 흐름은 다음과 같습니다.
- 요청을 JSONL 파일로 작성
-
POST /v1/files로 업로드 -
POST /v1/batches로 배치 생성 -
GET /v1/batches/{id}로 상태 폴링 -
output_file_id로 결과 다운로드 -
custom_id기준으로 결과 매칭
배치 API는 실시간 응답이 필요 없는 대량 작업에서 토큰 비용을 절반으로 줄일 수 있는 실용적인 방법입니다. 다만 JSONL 형식과 폴링, 오류 파일 처리까지 포함해 전체 수명 주기를 검증해야 안정적으로 운영할 수 있습니다.
자동화 전에 Apidog를 다운로드해 업로드, 생성, 폴링, 취소, 결과 다운로드 흐름을 먼저 실행해 보세요. 잘못된 JSONL 한 줄 때문에 24시간을 낭비하는 상황을 줄일 수 있습니다.
Top comments (0)