# Опрос и повтор

`GET /v1/tasks/{id}`

**Опрос.** Задача не успела за `wait`: ответ `202` со `status` `queued` или `in_progress`, с `poll_after` в теле и `Retry-After` в заголовке. Спрашивайте `GET /v1/tasks/{id}` через это число секунд, пока `status` не станет `completed` или `failed`. Пример продолжает пример создания: `$TASK_ID` и `task` оттуда. Опрос не бесконечен: к `deadline_at` задача гарантированно завершается, после него это `failed` с `task_expired`. `GET` читает задачу из базы и к провайдеру не ходит; лимиты частоты на опрос не распространяются.

**Повтор без двойной оплаты.** Любой ответ 5xx, обрыв или не-JSON на `POST /v1/tasks` значит «неизвестно, создана ли задача». Повторите тот же запрос с тем же `id`: вы получите ту же задачу (200 или 202 по её состоянию), а не ошибку и не вторую оплату. Два одновременных запроса с одним `id`, например двойной клик, дают одну задачу и один резерв.

- `Тот же запрос` — Совпадает тело без `id` и `wait`: kind, model, input, options, metadata. Порядок ключей и форма чисел не важны. Значения по умолчанию в сравнение не подставляются: `count: 1` и отсутствие `count` разные запросы
- `Другой запрос с тем же id` (409) — Ответ 409 `task_id_conflict`, даже если новое тело с ошибкой. То же, если id занят задачей другого аккаунта или запросом чата
- `Цена` — Не сравнивается: повтор возвращает ту же задачу по старой цене. Новая задача списывается по текущей
- `Ответ 400, 401, 402, 404, 429` — Задача не создана, id свободен: можно повторить с ним же. Повторить `failed`: новый id
- `Без id` — Повтор после обрыва создаст вторую задачу. Для безопасных повторов ставьте свой случайный UUID, v4 или v7

**Пример запроса**

curl:

```bash
curl https://api.polyapi.ru/v1/tasks/$TASK_ID \
  -H "Authorization: Bearer $POLYAPI_KEY"
```

Python:

```python
import time

headers = {"Authorization": "Bearer " + os.environ["POLYAPI_KEY"]}
url = "https://api.polyapi.ru/v1/tasks/" + task["id"]
while task["status"] in ("queued", "in_progress"):
    time.sleep(task["poll_after"])
    task = requests.get(url, headers=headers).json()
print(task["output"][0]["url"] if task["status"] == "completed" else task["error"])
```

Node.js:

```js
const headers = { Authorization: `Bearer ${process.env.POLYAPI_KEY}` };
const url = "https://api.polyapi.ru/v1/tasks/" + task.id;
while (["queued", "in_progress"].includes(task.status)) {
  await new Promise(r => setTimeout(r, task.poll_after * 1000));
  task = await (await fetch(url, { headers })).json();
}
console.log(task.status === "completed" ? task.output[0].url : task.error);
```

**Ответ, пока задача идёт**

```json
{
  "id": "0f6e4c1a-8a7b-4d2e-9c55-3b1f2a7d9e10",
  "kind": "image/generate",
  "model": "google/gemini-3-pro-image",
  "status": "in_progress",
  "events": [
    {
      "type": "task.queued",
      "at": "2026-11-07T12:00:00Z"
    },
    {
      "type": "attempt.started",
      "at": "2026-11-07T12:00:00Z",
      "attempt": 1
    }
  ],
  "usage": {
    "reserved": {
      "amount": "17.18",
      "currency": "rub"
    }
  },
  "deadline_at": "2026-11-07T12:07:30Z",
  "poll_after": 2
}
```

**Хранение.** Файлы хранятся 30 дней с завершения задачи, срок в `retained_until` каждой части. Ссылка `url` выдаётся заново при каждом чтении задачи; в пределах одного часа UTC она та же, а `expires_at`, её срок, приходится на начало часа через один, то есть через 1–2 часа. Хранить ссылку не нужно, достаточно `id`. После `retained_until` часть остаётся с метаданными, без `url` и `expires_at`, с `"expired": true`.

Задача читается, пока живут её события: 90 дней после последнего. Позже 410 `task_purged`, и в списке её нет. `POST` с id удалённой задачи и тем же запросом тоже даёт 410, а с другим запросом 409 `task_id_conflict`: id занять снова нельзя. Задачи другого аккаунта не видны: для них 404 `not_found`. Id не в форме UUID: 400.

Источник: https://polyapi.ru/docs/tasks-poll
