Gemini 2.5 Pro Batch API 사용기 - 반값에 AI 돌리는 법
실시간이 필요 없다면, 배치로 돌려라. 폴링 기반 비동기 처리 이야기.

들어가며
LLM API를 쓰다 보면 비용이 슬슬 신경 쓰이기 시작한다. 특히 Gemini 2.5 Pro처럼 고성능 모델을 반복적으로 호출하면 비용이 꽤 누적된다.
그런데 생각해보면, 우리가 LLM API를 쓰는 작업 중 실시간 응답이 진짜 필요한 건 얼마 안 된다. 채팅봇이나 실시간 어시스턴트가 아닌 이상, 대부분의 작업은 결과를 몇 분 뒤에 받아도 상관없다. 전사, 번역, 요약, 데이터 전처리, 평가... 이런 건 다 그렇다.
Google도 이걸 알았는지, 2025년 7월에 Gemini Batch API를 공개했다. 똑같은 모델, 똑같은 결과, 가격만 반값. 대신 실시간 응답 대신 비동기로 처리된다.
나는 개인적으로 미팅 노트 앱을 만들어서 쓰고 있는데, 미팅 녹음을 Gemini로 전사하는 부분에 이 Batch API를 적용했다. 오늘은 Gemini Batch API가 뭔지, 어떻게 쓰는 건지, 그리고 실제로 써본 경험을 정리해보려 한다.
Gemini Batch API가 뭔가?
한 마디로 비동기 대량 처리 API다.
일반 API(Standard API)는 요청하면 바로 응답이 온다. Batch API는 요청을 모아서 던지면 Google이 알아서 스케줄링하고, 처리가 끝나면 결과를 가져갈 수 있게 해준다. 대신 **비용이 정가의 50%**다.
| 구분 | Standard API | Batch API |
|---|---|---|
| 응답 방식 | 동기 (즉시 응답) | 비동기 (최대 24시간) |
| Input 가격 (1M 토큰) | $1.25 | $0.625 |
| Output 가격 (1M 토큰) | $10.00 | $5.00 |
| 적합한 용도 | 채팅, 실시간 기능 | 대량 처리, 전사, 평가 |
가격은 Gemini 2.5 Pro 기준 (200K 컨텍스트 이하)
핵심은 심플하다. 실시간 응답이 필요 없으면 반값에 쓸 수 있다.
Standard API와 기능 차이는 없다. 텍스트 생성, 이미지 처리, 임베딩, 구조화된 출력, 함수 호출 등 Standard API에서 되는 건 Batch API에서도 다 된다. 차이는 오직 동기 vs 비동기뿐이다.
어떻게 동작하나?
흐름 자체는 간단하다.
요청 제출 (batches.create)
→ Google이 스케줄링
→ 처리 중 (PENDING → RUNNING)
→ 완료 (SUCCEEDED)
→ 결과 조회 (batches.get)
개발자가 할 일은 세 가지다:
- 배치 작업을 생성한다
- 주기적으로 상태를 확인한다 (폴링)
- 완료되면 결과를 가져간다
클라이언트 사이드에서 복잡한 큐 관리나 재시도 로직을 구현할 필요가 없다. Google이 알아서 스케줄링하고 처리해준다. 우리는 그냥 던지고, 물어보고, 가져가면 된다.
제출 방식
배치 작업은 두 가지 방식으로 제출할 수 있다.
- 인라인 요청:
GenerateContentRequest객체를 직접 전달 (20MB 이하) - JSONL 파일 업로드: 요청을 JSONL 파일로 묶어서 File API로 업로드 (최대 2GB)
소규모 배치는 인라인이 편하고, 수천~수만 건의 대량 처리는 JSONL 파일이 적합하다. Google 공식 권장도 "작은 작업 여러 개보다 큰 작업 하나로 묶는 게 처리량이 좋다"는 쪽이다.
작업 상태
배치 작업은 다음 상태를 거친다:
PENDING → RUNNING → SUCCEEDED / FAILED / CANCELLED / EXPIRED
SUCCEEDED: 정상 완료FAILED: 처리 실패CANCELLED: 사용자가 취소EXPIRED: 48시간 경과로 자동 만료
실제 코드로 보기
SDK는 Python과 JavaScript(Node.js)를 지원한다. @google/genai 패키지를 사용한 예시를 보자.
배치 작업 생성
import { GoogleGenAI } from "@google/genai";
const client = new GoogleGenAI({ apiKey: GEMINI_API_KEY });
const batchJob = await client.batches.create({
model: "gemini-2.5-pro",
src: [
{
contents: [
{
role: "user",
parts: [{ text: "이 텍스트를 한국어로 번역해줘: Hello world" }],
},
],
},
// 요청을 여러 개 넣을 수 있다
],
config: {
generationConfig: {
maxOutputTokens: 65536,
},
},
});
console.log(batchJob.name); // 작업 ID폴링으로 상태 확인
async function pollBatchJob(jobName: string) {
let interval = 10_000; // 처음엔 10초
while (true) {
const job = await client.batches.get({ name: jobName });
switch (job.state) {
case "JOB_STATE_SUCCEEDED":
return job; // 완료 - 결과 반환
case "JOB_STATE_FAILED":
throw new Error(`Batch job failed: ${job.error}`);
case "JOB_STATE_CANCELLED":
throw new Error("Batch job was cancelled");
default:
// PENDING 또는 RUNNING - 계속 대기
await sleep(interval);
interval = Math.min(interval * 1.5, 30_000); // 점진적으로 30초까지
}
}
}폴링 간격을 처음엔 10초로 시작해서 점진적으로 30초까지 늘리는 전략이다. 초반에는 빠르게 완료를 감지하고, 시간이 지나면 불필요한 API 호출을 줄인다.
그 외에도 client.batches.list()로 최근 작업 목록 조회, client.batches.cancel()로 작업 취소, client.batches.delete()로 작업 삭제가 가능하다.
실제 처리 시간은?
Google 공식 SLO는 24시간 이내 완료다. 하지만 실제로는 훨씬 빠르다.
Google 공식 블로그에서도 "대부분의 작업은 24시간보다 훨씬 빨리 완료된다"고 명시하고 있다. 내 경험 기준으로도 그렇다.
미팅 전사 작업에 적용했을 때, 빠르면 몇 분 안에 끝나기도 하고 느릴 때는 20분 넘게 걸리기도 했다. Google 쪽 사용량에 따라 오락가락하는 느낌이다. 같은 분량의 오디오를 돌려도 어떤 날은 금방 끝나고, 어떤 날은 한참 걸린다.
확실한 건, 실시간 API처럼 즉시 결과가 오는 건 아니라는 점이다. "몇 분이면 되겠지"라고 기대하기보다 넉넉하게 잡아두고 다른 일을 하는 게 맞다. SLO가 24시간인 데는 이유가 있다. 48시간이 지나면 미완료 작업은 자동 만료(EXPIRED)되고, 완료된 요청분만 과금된다.
Google의 팁도 있다: "작은 작업 1,000개보다 큰 작업 1개로 묶는 게 처리량이 좋다." 예를 들어 200건씩 1,000개 작업보다 200,000건짜리 1개 작업이 더 빠르다는 이야기다.
비용, 진짜 반값인가?
진짜 반값이다. 모든 유료 Gemini 모델에서 동일하게 50% 할인이 적용된다.
Gemini 2.5 Pro 기준으로 1시간 미팅 전사를 대략 산정하면:
| 항목 | Standard API | Batch API |
|---|---|---|
| Input (오디오 + 프롬프트) | ~$0.50 | ~$0.25 |
| Output (전사 결과) | ~$2.00 | ~$1.00 |
| 합계 | ~$2.50 | ~$1.25 |
하루에 2-3개 미팅을 전사한다고 치면, 월간으로 수십 달러 차이가 난다.
추가로 알아두면 좋은 것: Gemini에는 implicit caching이라는 기능이 있다. 같은 입력이 반복되면 캐시 히트로 입력 토큰에 90% 할인이 적용된다. 다만 캐시 할인과 배치 할인은 중복 적용되지 않고, 둘 중 더 큰 할인이 적용된다. 동일 프롬프트를 반복 사용하는 경우 캐시 할인이 더 클 수 있다.
내가 적용한 사례: 미팅 전사
간단히 내 사례를 소개하면, 미팅 노트 앱을 Electron으로 직접 만들어서 쓰고 있다. 녹음된 미팅 오디오를 Gemini 2.5 Pro로 전사하는데, 이 부분을 Batch API로 교체했다.
미팅 노트 앱 전사 화면
구조는 이렇다:
- 오디오 파일을 Gemini File API로 업로드
- Batch 작업 생성
- SQLite에 작업 상태 저장 (폴링 추적용)
- 백그라운드에서 폴링
- 완료 시 결과 파싱 → 세션 저장 → OS 네이티브 알림
UI는 블로킹되지 않고, 사용자는 다른 작업을 하다가 알림을 받으면 된다. 실시간 API였으면 결과 올 때까지 기다려야 했을 텐데, 배치라서 "던져놓고 잊어버리기"가 가능하다.
전사 품질도 프롬프트 엔지니어링으로 꽤 쓸만하게 뽑았다. 화자 분리, 타임스탬프, 동일 화자 발화 자동 병합, 7개국어(한/영/일/중/스/프/독) 지원까지. Standard API든 Batch API든 모델은 같으니 결과 품질은 동일하다.
누가 Batch API를 써야 할까
정리하면, 이런 경우에 Batch API를 강력히 추천한다:
- 결과를 즉시 안 봐도 되는 작업 - 전사, 번역, 요약, 데이터 전처리, 리포트 생성
- 대량 처리가 필요한 작업 - 수백~수만 건의 요청을 한 번에 돌려야 할 때
- 비용에 민감한 경우 - 같은 작업을 반값에 할 수 있다
- 높은 rate limit이 필요한 경우 - 배치 모드는 Standard API보다 높은 처리량 제한이 적용됨
실제로 Reforged Labs는 대량 비디오 광고 분석에, Vals AI는 법률/금융/의료 분야 모델 벤치마크 평가에 Batch API를 쓰고 있다.
떠오르는 유즈 케이스를 몇 가지 더 나열하면:
- PDF 문서 분석 - 계약서, 논문, 보고서 수백 건을 요약하거나 핵심 조항 추출
- 프로필 사진 규격 체크 - 업로드된 이미지가 가이드라인(해상도, 배경, 얼굴 비율 등)에 맞는지 일괄 검수
- 상품 리뷰 감성 분석 - 수만 건의 리뷰를 긍정/부정/중립으로 분류
- 다국어 번역 - 앱이나 웹사이트의 i18n 문자열을 한 번에 여러 언어로 번역
- 코드 리뷰 자동화 - PR 수백 개에 대해 코드 품질, 보안 취약점 일괄 분석
- 이력서 스크리닝 - 대량의 이력서에서 주요 경력, 기술 스택 추출 및 정리
공통점은 하나다. 결과를 지금 당장 안 봐도 되고, 건수가 많을수록 50% 할인의 효과가 커진다.
반대로 이런 경우는 Standard API를 써야 한다:
- 채팅봇처럼 즉시 응답이 필요한 경우
- 사용자가 화면에서 결과를 기다리고 있는 경우
- 스트리밍 응답이 필요한 경우
마치며
Batch API를 쓰면서 느낀 건, **"실시간이 아니어도 되는 작업이 생각보다 많다"**는 것이다.
우리는 습관적으로 API를 호출하면 바로 결과를 받으려 한다. 하지만 잠깐 생각해보면, 전사, 번역, 데이터 분석, 평가 같은 작업은 몇 분 기다려도 전혀 문제가 없다. 그 기다림의 대가로 비용이 반이 된다.
비용 절감은 단순히 돈을 아끼는 것만이 아니다. 같은 예산으로 더 많은 작업을 돌릴 수 있다는 뜻이기도 하다. 반값이면 두 배를 돌릴 수 있으니까.
실시간이 필요 없다면, 배치로 돌려라. 진심이다.