Prostor Docs
Public API

Пачки (Batch API)

POST /v1/batches — тысячи запросов одной отправкой, ответ в течение суток и вдвое дешевле, когда провайдер берёт их в свою отложенную очередь.

Пачка — для работы, которой некуда спешить: ночная разметка, пересчёт описаний, классификация накопленного за день. Вы отправляете список обычных chat-completion запросов одним вызовом и забираете ответы позже.

Взамен вы получаете цену: элементы, которые уходят в отложенную очередь самого провайдера, стоят примерно вдвое дешевле обычного вызова. Платите вы за это временем — ответ приходит в течение суток, а не секунд.

Важно

Пачка — не способ «послать много запросов быстрее». Если ответ нужен сейчас, используйте POST /v1/chat/completions: он и быстрее, и предсказуемее. Пачка выгодна ровно тогда, когда сутки ожидания вас устраивают.

Когда это ваш инструмент

ЗадачаПачка?
Разметить 20 тыс. отзывов к утруда
Пересчитать описания каталога после импортада
Сгенерировать заголовок чата, который пользователь сейчас видитнет
Ответить в интерфейсенет

Отправить

bash
curl -s "$PROSTOR_API_BASE_URL/batches" \
  -H "Authorization: Bearer $PROSTOR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d "{
    \"requests\": [
      {
        \"custom_id\": \"review-1041\",
        \"body\": {
          \"model\": \"$PROSTOR_MODEL\",
          \"max_tokens\": 200,
          \"messages\": [{\"role\": \"user\", \"content\": \"Оцени тональность: ...\"}]
        }
      },
      {
        \"custom_id\": \"review-1042\",
        \"body\": {
          \"model\": \"$PROSTOR_MODEL\",
          \"max_tokens\": 200,
          \"messages\": [{\"role\": \"user\", \"content\": \"Оцени тональность: ...\"}]
        }
      }
    ]
  }" | jq .
ПолеОписание
requestsОбязательно. От 1 до 1000 элементов.
requests[].custom_idОбязательно. Ваш ключ элемента, уникальный внутри пачки. По нему вы найдёте результат — порядок ответов не гарантируется.
requests[].bodyОбязательно. Обычное тело /v1/chat/completions. Переносить вызов в пачку не значит переписывать его.

Что делает шлюз с телом элемента:

  • stream принудительно выключается — смотреть на токены отложенной генерации некому;
  • max_tokens приводится к потолку, доступному вашему ключу (см. max_output_tokens

в GET /v1/key). Элемент не может сгенерировать больше, чем под него зарезервировано.

Ответ — 200 с объектом пачки; все элементы в статусе queued.

Заметка

custom_id разумно делать идентификатором вашего задания. Тогда повторная отправка потерянной пачки идемпотентна с вашей стороны, а результаты ложатся на ваши записи без сопоставления по порядку.

Опросить и забрать результаты

bash
curl -s "$PROSTOR_API_BASE_URL/batches/$BATCH_ID" \
  -H "Authorization: Bearer $PROSTOR_API_KEY" | jq .
json
{
  "id": "batch_9f1c...",
  "object": "batch",
  "status": "completed",
  "mode": "mixed",
  "created_at": 1785000000,
  "completed_at": 1785031200,
  "counts": { "total": 2, "queued": 0, "running": 0, "succeeded": 2, "failed": 0, "cancelled": 0 },
  "results": [
    {
      "custom_id": "review-1041",
      "status": "succeeded",
      "mode": "native",
      "response": {
        "id": "gen-...", "object": "chat.completion",
        "choices": [{ "index": 0, "message": { "role": "assistant", "content": "..." }, "finish_reason": "stop" }],
        "usage": { "prompt_tokens": 120, "completion_tokens": 48, "total_tokens": 168, "cost": 0.0021 }
      }
    },
    {
      "custom_id": "review-1042",
      "status": "succeeded",
      "mode": "degraded",
      "response": { "...": "..." }
    }
  ]
}
  • status пачки: in_progress → completed | cancelled.
  • status элемента: queued, running, succeeded, failed, cancelled.
  • response — ровно тот объект, который вернул бы одиночный /v1/chat/completions,

вместе с usage и вашей стоимостью в usage.cost.

  • error у элемента — тот же тип и сообщение, что у одиночного запроса. Обработка

ошибок не раздваивается на «пакетную» и «обычную».

Результаты появляются по мере готовности: опрос до завершения пачки уже отдаёт то, что успело посчитаться. Ошибка одного элемента не валит остальные.

Разумный интервал опроса — раз в несколько минут. Пачка живёт часами; опрос раз в секунду ничего не ускорит, но упрётся в лимит запросов вашего ключа.

mode: за что вы заплатили

Это поле есть и у пачки, и у каждого элемента, и оно — единственное, что честно отвечает на вопрос «сэкономил ли я».

modeЧто произошлоЦена
nativeэлемент исполнила отложенная очередь провайдера≈ вдвое дешевле
emulatedобычные вызовы: отложенная очередь для элемента даже не планироваласьобычная
degradedочередь взяла элемент, но не отдала результат вовремя — доделали обычным путёмобычная
refusedочередь не взяла элемент вовсе: у модели нет отложенного эндпоинтаобычная
mixedтолько у пачки: элементы пошли разными путями—

Ни degraded, ни refused не ошибка. Ответ у вас есть, и он пришёл в обещанный срок; не случилось только скидки. Мы предпочитаем отдать вам ответ по обычной цене, чем уложиться в скидку и не отдать ничего.

Различать их стоит: они требуют разного. degraded означает, что очередь у модели есть и сегодня подвела — тут нечего менять, разве что следить за долей. refused означает, что спрашивать было бессмысленно: провайдер отказал сразу, и ожидание ничего бы не изменило. Если refused держится у модели постоянно — берите для пачек другую модель, эта отложенную очередь не поддерживает.

Какие модели попадают в native, зависит от провайдера и меняется без предупреждения с его стороны. Планируйте бюджет по обычной цене, а экономию считайте постфактум — по GET /v1/batches.

Отменить

bash
curl -s -X POST "$PROSTOR_API_BASE_URL/batches/$BATCH_ID/cancel" \
  -H "Authorization: Bearer $PROSTOR_API_KEY" | jq .

Элементы, которые ещё не начались, отменяются и не оплачиваются. Уже исполняющиеся доводятся до конца и оплачиваются: их вызов к провайдеру уже сделан. Отмена завершённой пачки ничего не меняет.

Список пачек и сводка расхода

bash
curl -s "$PROSTOR_API_BASE_URL/batches?limit=20" \
  -H "Authorization: Bearer $PROSTOR_API_KEY" | jq .
json
{
  "object": "list",
  "data": [
    {
      "id": "batch_9f1c...",
      "object": "batch",
      "items": 500,
      "billing_status": "settled",
      "created_at": 1785000000,
      "completed_at": 1785031200,
      "reserved_credits": 4200,
      "billed_credits": 1180,
      "modes": { "native": 460, "emulated": 0, "degraded": 38, "refused": 0, "failed": 2, "cancelled": 0 }
    }
  ],
  "usage": {
    "batches": 12,
    "items": 5400,
    "by_mode": [
      { "mode": "native", "items": 4900, "total_tokens": 812000, "billed_credits": 9800, "cost_usd": "0.98" },
      { "mode": "degraded", "items": 400, "total_tokens": 66000, "billed_credits": 1600, "cost_usd": "0.16" },
      { "mode": "refused", "items": 100, "total_tokens": 17000, "billed_credits": 400, "cost_usd": "0.04" }
    ]
  }
}

usage — сводка за последние 30 дней по этому ключу. Доля native и есть ответ на вопрос, окупается ли отложенная отправка на вашем профиле нагрузки, а degraded и refused говорят, почему она не окупилась: очередь подвела или её нет вовсе.

billing_status — состояние расчёта, а не генерации: held (резерв стоит, пачка ещё идёт), settled (посчитано), released (ничего не доставлено, резерв возвращён), expired (вы перестали опрашивать, мы закрыли расчёт сами — см. ниже).

Деньги

  1. На отправке резервируется худший случай — сумма по всем элементам, если каждый

выберет свой max_tokens целиком. Резерв виден в заголовке X-Prostor-Cost-Credits ответа на отправку.

  1. На завершении списывается факт. Неиспользованный резерв возвращается. Списание

происходит один раз, на том опросе, который первым увидел пачку завершённой, — опрашивать в цикле безопасно.

  1. Ошибочные и отменённые элементы не оплачиваются. Они всё равно видны в

результатах: вам нужно знать, что с ними случилось.

  1. Больше резерва не спишется никогда.
Важно

Если вы перестали опрашивать пачку, расчёт мы закроем сами — примерно через полтора суток. Оплачены будут те элементы, которые провайдер успел выдать: бросить пачку — не способ получить генерации бесплатно, но и не способ заморозить свои кредиты навсегда.

Что может пойти не так

КодКогдаЧто делать
400 invalid_request_errorпустой список, нет custom_id или body.model, дубль custom_idпоправить тело
400 too_many_itemsбольше 1000 элементовразбить на несколько пачек
402 insufficient_creditsбаланса не хватает на худший случай всей пачкипополнить или уменьшить max_tokens/размер пачки
403 model_not_allowedмодель элемента не в белом списке ключасм. allowed_models в GET /v1/key
404 not_foundпачки с таким id у этого ключа нетпроверить id; чужие пачки не видны
404 batch_lostпачка потеряна на нашей стороне (перезапуск, истёк срок хранения)отправить заново; доставленное уже оплачено, остальное возвращено
429лимит запросов ключареже опрашивать
Заметка

404 batch_lost — единственная неприятность, специфичная для пачек. Незавершённые пачки не переживают перезапуск нашего узла, поэтому неизвестный id значит «отправить заново», а не «продолжать опрашивать». Держите custom_id привязанным к своим заданиям, и повторная отправка обойдётся вам только в то, что не успело посчитаться.

Ограничения

  • До 1000 элементов в пачке.
  • Только chat completions. Изображения, видео и эмбеддинги в пачку не принимаются.
  • stream игнорируется.
  • Пачка не переживает перезапуск узла (см. batch_lost).
  • Срок ответа — до 24 часов. Обычно быстрее, но закладывайтесь на сутки.