У ваших менеджеров уходит по 4 часа на ручную обработку отзывов или заполнение карточек товаров - а половина всё равно с ошибками. Если нужно обработать 10 000 заявок, документов или отзывов, платить за каждый запрос в реальном времени дорого. Batch API (интерфейс пакетной обработки) решает это: скидка 50% на обработку, результаты через пару часов, а вы в это время занимаетесь другими делами. Никаких программистов в штате - достаточно менеджера, который умеет запускать готовый скрипт.
Зачем это бизнесу
Batch API - это когда вы отправляете пачку запросов, а ответ получаете через несколько часов, но платите вдвое меньше. Разница с обычными запросами в режиме реального времени - 50% скидка на каждый токен.
Для 1M токенов в обычных запросах gpt-4o обойдётся $12.50. В батче - $6.25. Если ваша компания ежемесячно обрабатывает 10M токенов (примерно 10 000 документов по одной странице), экономия - $62.50 в месяц. При 100M токенов - $625 ежемесячно. На масштабе реального бизнеса это тысячи долларов в месяц.
Разберём на примере стройфирмы (это пример, не реальный кейс). У вас есть 3000 отзывов с сайта и из соцсетей. Нужно разобрать их по тональности (позитив, негатив, нейтраль) для ежеквартального отчёта. Если делать руками - менеджер потратит неделю. Если через обычный API - запросы будут идти по одному, каждый по 2 секунды, итого 1.5 часа непрерывной работы, плюс платите за каждый запрос. Batch API: скинули файл, через час забрали готовую таблицу, заплатили $3 вместо $6.
Другие примеры для пакетной обработки: генерация SEO-описаний для каталога из 5000 товаров, перевод документации на три языка, извлечение ключевых условий из архива договоров, анализ данных звонков в колл-центре для еженедельного отчёта.
Важно: Batch API не для задач, где нужен ответ прямо сейчас. Чат-бот, который должен ответить пользователю за секунду - не подходит. Ночная обработка базы документов для утреннего отчёта - идеально.
Когда батч выгоден: сравнение с обычными запросами
Обычный запрос (синхронный): отправили - подождали - получили ответ. Ответ гарантированно придёт за несколько секунд. Используйте, когда результат нужен немедленно для отображения пользователю или для следующего шага обработки.
Batch API (асинхронный): отправили пакет - через 1-4 часа (максимум 24 часа) забрали все результаты разом. Используйте, когда задача офлайн, объём большой, и экономия важна. Асинхронный - значит не ждёте ответа сразу, продолжаете другую работу.
Ограничение: время выполнения до 24 часов. Обычно батч завершается за 1-4 часа, но гарантии нет. Планируйте задачи с учётом этого окна - не запускайте батч за час до того, как нужны результаты.
OpenAI Batch: формат JSONL
JSONL (JSON Lines) - формат файла, где каждая строка - отдельный JSON-объект. Удобен для больших файлов: не нужно загружать весь файл в память, можно читать построчно.
Вот как выглядит код для создания такого файла. Если у вас нет программиста - попросите знакомого IT-специалиста или фрилансера написать этот скрипт один раз, а дальше будете только менять список отзывов.
Этот код создаёт JSONL-файл с запросами на классификацию отзывов: каждый отзыв из списка превращается в отдельный запрос с уникальным идентификатором.
import json
requests = [
{
"custom_id": f"item-{i}",
"method": "POST",
"url": "/v1/chat/completions",
"body": {
"model": "gpt-4o-mini",
"messages": [
{"role": "system", "content": "Определи тональность текста: positive/negative/neutral"},
{"role": "user", "content": review_text}
],
"max_tokens": 10
}
}
for i, review_text in enumerate(reviews)
]
# Сохраняем в JSONL
with open("batch_requests.jsonl", "w") as f:
for req in requests:
f.write(json.dumps(req, ensure_ascii=False) + "\n")
custom_id - ваш идентификатор для сопоставления результатов. Может быть любой строкой до 64 символов. Используйте реальные ID из базы данных (product-42819, review-uuid-...) - удобнее при разборе результатов. Без custom_id вы не свяжете результат с исходным элементом.
url - адрес конечной точки API. Для генерации текста это всегда /v1/chat/completions, для векторных представлений (эмбеддингов) - /v1/embeddings.
OpenAI Batch: загрузка и запуск
Процесс состоит из двух шагов: сначала загружаем файл на серверы OpenAI, потом создаём батч-задание из этого файла.
Этот код загружает JSONL-файл на серверы OpenAI и создаёт пакетное задание, которое будет выполнено в течение 24 часов.
from openai import OpenAI
client = OpenAI()
# Шаг 1: загружаем файл
with open("batch_requests.jsonl", "rb") as f:
file_obj = client.files.create(
file=f,
purpose="batch"
)
print(f"File ID: {file_obj.id}")
# Шаг 2: создаём батч
batch = client.batches.create(
input_file_id=file_obj.id,
конечная точка API="/v1/chat/completions",
completion_window="24h"
)
print(f"Batch ID: {batch.id}, Status: {batch.status}")
# Batch ID: batch_abc123, Status: validating
Сохраните batch.id - он нужен для проверки статуса. После создания статус сначала validating (проверка формата файла), потом in_progress (выполнение).
OpenAI: проверка статуса и получение результатов
Получить статус можно вручную через API. Для автоматического ожидания нужен цикл периодического опроса.
Этот код периодически запрашивает статус батча (раз в минуту по умолчанию), показывает прогресс и когда батч завершается - скачивает и разбирает все результаты по идентификаторам.
import time
batch_id = "batch_abc123"
def wait_for_batch(client, batch_id, poll_interval=60):
while True:
batch = client.batches.retrieve(batch_id)
status = batch.status
completed = batch.request_counts.completed
total = batch.request_counts.total
print(f"[{status}] {completed}/{total} запросов")
if status == "completed":
return batch
elif status in ("failed", "expired", "cancelled"):
raise RuntimeError(f"Batch {status}: {batch.errors}")
time.sleep(poll_interval)
batch = wait_for_batch(client, batch_id)
# Скачиваем результаты
result_content = client.files.content(batch.output_file_id)
results = {}
for line in result_content.text.splitlines():
result = json.loads(line)
custom_id = result["custom_id"]
if result["error"] is None:
text = result["response"]["body"]["choices"][0]["message"]["content"]
results[custom_id] = text
else:
results[custom_id] = None
print(f"Error for {custom_id}: {result['error']}")
Статусы батча: validating - проверка файла, in_progress - выполнение, finalizing - сборка результатов, completed - готово, failed - ошибка, expired - не уложился в 24 часа.
Лимиты OpenAI: до 2000 батчей в очереди одновременно. Лимит на токены в очереди зависит от уровня аккаунта (смотрите в Platform Settings, Batches). Для векторных представлений - до 1 млн запросов на батч.
Anthropic Message Batches
Anthropic (компания-разработчик Claude) предлагает аналогичный инструмент с двумя ключевыми отличиями: не нужно загружать файл, и лимит запросов в одном батче выше.
Этот код создаёт батч на стороне Anthropic, передавая запросы напрямую в теле вызова без предварительной загрузки файла.
import anthropic
client = anthropic.Anthropic()
# Создаём батч напрямую без JSONL-файла
batch = client.messages.batches.create(
requests=[
{
"custom_id": f"doc-{doc_id}",
"params": {
"model": "claude-haiku-4-5",
"max_tokens": 256,
"messages": [
{
"role": "user",
"content": f"Извлеки ключевые слова из текста: {doc_text}"
}
]
}
}
for doc_id, doc_text in documents.items()
]
)
print(f"Batch ID: {batch.id}")
Отличие от OpenAI: Anthropic принимает запросы напрямую в JSON, без загрузки файла. Лимит 100 000 запросов на батч - больше, чем у OpenAI. Для больших объёмов это удобнее.
Для claude-opus-4-7 и claude-sonnet-4-6 доступен расширенный вывод через бета-заголовок:
batch = client.messages.batches.create(
requests=[...],
betas=["output-300k-2026-03-24"] # до 300K токенов на выход в каждом запросе
)
Anthropic: проверка статуса и разбор результатов
Этот код ждёт завершения батча и затем разбирает результаты, разделяя успешные ответы от ошибочных.
import time
def wait_for_anthropic_batch(client, batch_id, poll_interval=60):
while True:
batch = client.messages.batches.retrieve(batch_id)
counts = batch.request_counts
print(f"[{batch.processing_status}] processing:{counts.processing} "
f"succeeded:{counts.succeeded} errored:{counts.errored}")
if batch.processing_status == "ended":
return batch
time.sleep(poll_interval)
batch = wait_for_anthropic_batch(client, batch.id)
# Разбор результатов
results = {}
errored_ids = []
for result in client.messages.batches.results(batch.id):
if result.result.type == "succeeded":
text = result.result.message.content[0].text
results[result.custom_id] = text
elif result.result.type == "errored":
errored_ids.append(result.custom_id)
print(f"Error {result.custom_id}: {result.result.error}")
# type может быть: 'succeeded', 'errored', 'canceled', 'expired'
Стратегия повтора упавших запросов
Часть запросов в батче может вернуть ошибку: превышение контекста (слишком длинный текст), временная перегрузка сервера, некорректный формат. Создавать новый батч полностью заново дорого - правильнее повторить только упавшие.
Этот код берёт только те запросы из оригинального списка, которые вернули ошибку, и создаёт из них новый батч для повторной попытки.
def retry_failed_requests(client, original_requests, errored_ids, max_retries=2):
failed_set = set(errored_ids)
retry_requests = [
req for req in original_requests
if req["custom_id"] in failed_set
]
if not retry_requests:
return {}
print(f"Повторяем {len(retry_requests)} упавших запросов...")
retry_batch = client.messages.batches.create(requests=retry_requests)
retry_batch = wait_for_anthropic_batch(client, retry_batch.id)
retry_results = {}
new_errors = []
for result in client.messages.batches.results(retry_batch.id):
if result.result.type == "succeeded":
retry_results[result.custom_id] = result.result.message.content[0].text
else:
new_errors.append(result.custom_id)
return retry_results, new_errors
# Объединяем результаты основного и повторного батча
all_results = {**main_results}
retry_results, still_failed = retry_failed_requests(
client, original_requests, errored_ids
)
all_results.update(retry_results)
print(f"Итого успешных: {len(all_results)}, неудачных: {len(still_failed)}")
Типичный сценарий: обработка каталога товаров
Представьте задачу: интернет-магазин с 8000 товаров хочет сгенерировать SEO-описания. Каждое описание - 300 слов, входной контекст с характеристиками товара - около 200 токенов, выход - около 400 токенов.
В обычном режиме: 8000 запросов x $0.15/1M (gpt-4o-mini input) x 200 токенов = $0.24 на входные токены. Плюс выход: 8000 x $0.60/1M x 400 = $1.92. Итого около $2.16. В батче - $1.08 за счёт скидки 50%.
По времени: если делать запросы последовательно со скоростью 2 секунды на один - это 16 000 секунд, около 4.5 часов. Если параллельно через батч - запустили батч, через 1-2 часа все 8000 готовы разом.
Дополнительная выгода: батч не занимает вашу квоту запросов в минуту, можно одновременно продолжать обычную работу с API.
Ограничение: если нужна проверка и утверждение каждого описания перед следующим - батч не подходит. Батч для задач, где результат нужен как массив, а не как последовательность шагов.
Сравнение OpenAI Batch и Anthropic Message Batches
Оба провайдера предлагают скидку 50%, но детали отличаются.
По формату: OpenAI требует загрузку JSONL-файла в два шага (сначала файл, потом батч). Anthropic принимает запросы напрямую в JSON-теле вызова без промежуточного файла. Для небольших батчей до 1000 запросов Anthropic удобнее.
По лимитам: OpenAI ограничивает один батч без указания точного максимума запросов (зависит от уровня аккаунта), Anthropic - явный лимит 100 000 запросов на батч. Для действительно больших объёмов Anthropic выигрывает.
По моделям внутри батча: в одном батче OpenAI используется одна конечная точка API, то есть одна модель. У Anthropic разные запросы в одном батче могут использовать разные модели - можно смешать Haiku и Sonnet в зависимости от сложности задачи.
По скорости: оба выполняют батч за 1-4 часа типично, гарантия - 24 часа. Разницы на практике нет.
По выходному формату: OpenAI возвращает JSONL-файл, который нужно скачать. Anthropic - итератор через API без промежуточного файла. При больших результатах (миллионы строк) файловый подход OpenAI может быть удобнее для дальнейшей обработки.
Типичные подводные камни
Потерян идентификатор батча: сохраняйте его сразу в базу данных или в файл. Без него нельзя проверить статус или получить результаты. Список всех батчей за последние 30 дней доступен через client.batches.list(), но это запасной вариант.
Файл не проходит валидацию: самые частые причины - нарушен формат JSONL (лишняя запятая, незакрытая скобка), custom_id не уникален, или url содержит опечатку. Проверьте первые 5 строк файла вручную перед загрузкой.
Батч истёк за 24 часа: статус expired. Частично выполненные результаты доступны в output_file_id у OpenAI. Причина чаще всего - слишком большой объём или пиковая нагрузка на серверах. Разбейте батч на меньшие части по 1000-5000 запросов.
Путаница с лимитами: Batch API использует отдельный пул лимитов от обычных запросов. Обычные лимиты на запросы и токены в минуту батч не расходует - можно запустить батч и продолжать обычные запросы параллельно.
Частые вопросы
Можно ли отменить батч после запуска?
Да. OpenAI: client.batches.cancel(batch_id). Anthropic: client.messages.batches.cancel(batch_id). Запросы в обработке ещё могут завершиться, но новые не запустятся. Оплачиваются только фактически выполненные запросы.
Что происходит, если батч не завершился за 24 часа?
Статус становится expired. Частично выполненные результаты доступны в output_file_id (OpenAI). У Anthropic в результатах появятся записи с result.type == 'expired'. Оплачиваются только завершённые запросы.
Поддерживает ли Batch API потоковую передачу или вызов инструментов?
Потоковая передача ответа - нет, это асинхронная обработка, ответ приходит разом. Вызов инструментов (tool use) - да, для OpenAI можно передать tools в body каждого запроса. Для Anthropic - в params. Но полный цикл агента из нескольких шагов не поддерживается: батч выполняет один обмен «запрос-ответ».
Можно ли смешивать разные модели в одном батче?
OpenAI - нет, батч привязан к одной конечной точке API. Anthropic - да, разные запросы внутри одного батча могут использовать разные модели (Haiku и Sonnet одновременно). Это удобно, если часть документов простая (Haiku), а часть требует более глубокого анализа (Sonnet).
Что дальше
Если батч стал основным инструментом - следующий шаг - Prompt Caching: кэширование повторяющихся system prompt внутри батча даёт дополнительную экономию поверх скидки 50%. Полезно совмещать: батч для объёма + кэширование для одинаковых инструкций внутри батча.
Конкретный следующий шаг: возьмите одну задачу - например, классификацию 100 отзывов. Попросите знакомого разработчика или фрилансера настроить скрипт для OpenAI Batch (код выше). Запустите тестовый батч на 10 запросах, убедитесь, что всё работает. Потом масштабируйте на весь объём. Бесплатного плана у OpenAI нет, но на старте хватит $5 на балансе аккаунта - этого хватит на тысячи запросов через gpt-4o-mini.
AI Компас (t.me/kosmoslab_ai) - канал для предпринимателей в РФ и СНГ, которые применяют AI в своём бизнесе без программиста. Разбираем инструменты и схемы - без курсов и теории.