Идемпотентность
Сеть рвётся, ответ теряется, клиент повторяет запрос — и задача ставится дважды. Заголовок
Idempotency-Key делает повтор безопасным: действие выполняется один раз, повтор получает первый ответ.
Idempotency-Key: 6f1c0a5e-8d1b-4b4e-9a53-2c7d0e1f9a10
Где нужен
Настоятельно рекомендуется там, где повтор дорог: без заголовка запрос выполнится, но повтор после потерянного ответа поставит вторую задачу или подписку и спишет кредиты ещё раз.
POST /v1/jobs— поставить задачу;POST /v1/tg/subscriptions— создать подписку;POST /v1/tg/leave— выйти из чата.
Обязателен (без него — 400 caller_error, code=idempotency_key_required): POST /v1/tg/join,
POST /v1/tg/send и пишущие методы POST /v1/tg/call.
На читающих путях (посты, сообщения, карточки, ВКонтакте) заголовок ничего не меняет: каждый вызов с данными выполняется и списывается.
Правила
- Ключ — 1–128 символов
A-Z a-z 0-9 _ - : .; UUID подходит. Один ключ — одно действие: для новой задачи — новый ключ, для повтора той же — тот же. - Повтор с тем же ключом и тем же телом → тот же ответ, что в первый раз, с заголовком
Idempotent-Replayed: true. Действие не повторяется, кредиты второй раз не списываются. Секрет подписки (webhook_secret) в повторном ответе не возвращается: он показывается один раз. Потеряли — выпустите новый (rotate_webhook_secret: true). - Тот же ключ с другим телом →
409 conflict, code=idempotency_mismatch. - Первый запрос ещё выполняется →
409 conflict, code=idempotency_in_progress,retry_after: 1. - Неудачный запрос ключ не занимает: повтор выполнится заново.
- Ключи хранятся сутки и у каждого ключа API свои.
Пример
curl
KEY=$(uuidgen) # один раз на действие
curl -s "https://soroka.tools/v1/jobs" \
-H "Authorization: Bearer $API_KEY" \
-H "Idempotency-Key: $KEY" \
-H "Content-Type: application/json" \
-d '{"kind": "parser.preset", "params": {"preset": "tg.channel_posts", "params": {"ref": "@telegram", "limit": 100}}}'
# ответ потерялся — повторите ту же команду с тем же $KEY
Python
import os
import uuid
import httpx
key = str(uuid.uuid4()) # один раз на действие
body = {"kind": "parser.preset",
"params": {"preset": "tg.channel_posts", "params": {"ref": "@telegram", "limit": 100}}}
headers = {"Authorization": f"Bearer {os.environ['API_KEY']}", "Idempotency-Key": key}
for attempt in range(3):
try:
r = httpx.post("https://soroka.tools/v1/jobs", headers=headers, json=body, timeout=30)
break
except httpx.TransportError:
continue # повтор с тем же ключом: вторая задача не появится
else:
raise RuntimeError("сеть недоступна")
print(r.json()["job_id"])