Polling
Follow a request with GET /v1/requests/{request_id}, optionally long-polling with ?wait.
Poll a request with its status_url:
GET /v1/requests/{request_id}
GET /v1/requests/{request_id}/status (alias, same answer)Each call returns the request object. While the request is open, the call checks the run's current state first, so every answer is up to date. A final request (completed, partial, failed, cancelled) is answered from storage and never changes.
#Long-polling with ?wait
Add ?wait=N (an integer from 0 to 60, in seconds) to hold the call until the request is final or N seconds have passed, whichever comes first. Between checks the server waits at least 2 seconds, or longer when the model reports a later check time.
curl "https://api-live.aitopia.ai/v1/requests/$REQUEST_ID?wait=30" \
-H "Authorization: Key $AITOPIA_KEY"waitoutside 0–60, or not an integer, answers400 INVALID_INPUTwithfield: "wait".- An account can hold 3 waiting calls at a time (shared with the AITOPIA MCP server). A call made while all 3 are busy checks once and answers immediately, as if
waitwere 0. - If you close the connection, the wait ends.
The submit endpoints take ?wait=N too: the submit answers with the state after up to N seconds, still with status code 202.
#A polling loop
Loop on ?wait=30 until the status is final. Each long-poll counts as one request against your rate limits.
STATUS=queued
while [ "$STATUS" = "queued" ] || [ "$STATUS" = "running" ]; do
BODY=$(curl -s "https://api-live.aitopia.ai/v1/requests/$REQUEST_ID?wait=30" \
-H "Authorization: Key $AITOPIA_KEY")
STATUS=$(echo "$BODY" | jq -r .status)
done
echo "$BODY" | jq .outputconst TERMINAL = new Set(["completed", "partial", "failed", "cancelled"]);
async function waitForResult(statusUrl) {
const headers = { Authorization: `Key ${process.env.AITOPIA_KEY}` };
for (;;) {
const res = await fetch(`${statusUrl}?wait=30`, { headers });
const body = await res.json();
if (!res.ok) {
if (!body.error?.retryable) throw new Error(`${body.error.code}: ${body.error.message}`);
const delay = (body.error.retry_after_seconds ?? 5) * 1000;
await new Promise((r) => setTimeout(r, delay));
continue;
}
if (TERMINAL.has(body.status)) return body;
}
}import os, time, requests
TERMINAL = {"completed", "partial", "failed", "cancelled"}
HEADERS = {"Authorization": f"Key {os.environ['AITOPIA_KEY']}"}
def wait_for_result(status_url):
while True:
r = requests.get(status_url, params={"wait": 30}, headers=HEADERS, timeout=90)
body = r.json()
if not r.ok:
err = body["error"]
if not err.get("retryable"):
raise RuntimeError(f"{err['code']}: {err['message']}")
time.sleep(err.get("retry_after_seconds", 5))
continue
if body["status"] in TERMINAL:
return bodySet your HTTP client timeout above the wait value (the examples use 90 seconds for wait=30).
#Polling without wait
Plain GET calls work as well; poll every few seconds rather than in a tight loop.
#Errors while polling
| Status | Code | Meaning |
|---|---|---|
| 400 | INVALID_INPUT | Bad wait value. |
| 404 | NOT_FOUND | No request with this id in your account (including ids that are not UUIDs). |
| 401 | UNAUTHENTICATED, KEY_SESSION_EXPIRED | See Authentication. |
| 429 | RATE_LIMIT | Slow down; see retry_after_seconds. |
A 200 answer for an open request may carry an error with retryable: true (for example STATUS_CHECK_FAILED). The request is still open; keep polling. See open requests with an error.
Instead of polling, you can have the result sent to you: see Webhooks.