Skip to main content
The Impossibl AI API keeps one request log row for every gateway request, success or failure, unlike the usage ledger which only records billed successes. The list returns rows alone; the captured request and response bodies come from the single-request endpoint. Three endpoints read this data:
  • GET /v1/requests — recent rows, newest first, with filters
  • GET /v1/requests/{id} — one row plus its captured, redacted payload
  • GET /v1/usage/stream — a live SSE feed of rows as they settle
Authenticate with your gateway API key (Authorization: Bearer imp-rt-..., or the x-api-key header). See Authentication for how keys are minted.

List requests

Returns request log rows for your account, ts descending.
Response (row abbreviated)
A failed request logs the same row shape with the token and cost fields null, an errorMessage, and an attempts failover trail. Costs are in credits, where 1 credit = $0.000001.

Filters

Errors only, one model

Paging with before_id

Page backward with a keyset cursor: pass the id of the oldest row you already hold as before_id to get the rows strictly older than it. The cursor keys off the row’s id, not its timestamp, so rows that share a millisecond are never skipped and the boundary row does not come back:
since and before_id compose. Keep a since filter on every page to bound how far back paging runs: the window is at/after since and strictly older than the oldest row you hold.

Export a window to CSV

Paging to the end of a filtered window and writing the rows to a file is common enough that the console does it for you. In the logs room, set the status, model, and time filters you want, then press export csv. The export walks the filtered window, not the rows you have scrolled to. It pages /v1/requests with the before_id cursor above until the window runs out, so the file is what the filters describe.
  • Up to 10,000 rows. The newest 10,000 in the window. If the window holds more, the console says so under the filters — narrow the model or the time range to reach older rows.
  • Filenames carry the filters, so two exports do not collide: impossibl-requests-20260805-142230-ok-since-2026-07-24.csv.
  • UTF-8 with a byte-order mark, CRLF line endings, RFC 4180 quoting. Excel and Sheets open it directly.

Columns

One row per request, in this order:
The file holds raw values, not the readings the console shows you. Timestamps are ISO, durations are bare integers of milliseconds, and money is a plain decimal with no currency symbol, so a summed column is the figure you were charged.
A null field exports as an empty cell, never 0. A request that failed before it billed and a request that cost nothing are different facts, and a spreadsheet averaging the column has to be able to tell them apart.
cost_usd and provider_cost_usd are dollars at six decimal places — the full precision of the credit, since 1 credit = $0.000001. A request costing 8 credits exports as 0.000008, not a cent it was rounded into. audio_input_seconds is the metered duration for transcription requests, which bill on audio length rather than tokens — input_tokens and output_tokens are 0 on those rows, so this is the column that explains the charge.

The same export over the API

There is no export endpoint. The console walks the same public API you have, so an agent can collect the same rows and write whatever format it wants:
Walk a filtered window into rows.json
When you write your own CSV, either keep costCredits as an integer column and divide in the spreadsheet, or format to six decimal places as you write it:
Credits to fixed-decimal dollars
Do not divide inside jq. .costCredits / 1000000 returns a float that jq prints in scientific notation — 8 credits becomes 8e-06, which most spreadsheets will not read as a number.
Keep the six places whichever route you take. Rounding a single request to the cent prints most sub-cent calls as 0.00.

One request with its payload

Fetch a single row plus the captured request and response bodies. An id that does not exist, or belongs to another account, returns 404 (never leaks across accounts).
Response
The payload is null if no bodies were captured for that request. What the gateway captures is scrubbed before it is stored:
  • Sensitive headers are redacted. authorization, x-api-key, x-goog-api-key, cookie, and set-cookie are stored as [redacted].
  • Inline media is stripped. data: URIs and large base64 blobs (images, files) are replaced with a [media omitted (N chars)] placeholder.
  • Oversized bodies are truncated. Bodies are capped near 256 KB; when a body is shrunk to fit, truncated is true. The stored JSON is always valid.

Live stream

GET /v1/usage/stream is a long-lived text/event-stream. On connect it sends one snapshot event, then one request event per settled or failed request. Pass -N to curl so it prints frames as they arrive.
Every frame is JSON with v: 1. Payloads are never streamed, only log rows.
Stream (abridged)
  • snapshot — the last 20 rows, the current balanceCredits, the server-authoritative month-to-date totals (tokens, spentCredits, requests, ok-only), and cutoff, the read time the totals were computed at. Gate any streamed rows against cutoff so live ticks never re-add rows the snapshot already counted.
  • request (live) — a newly settled or failed row plus balanceCredits, the account balance after that request.
  • request (buffered replay) — a request that settled between subscribe and the snapshot read is replayed once right after the snapshot without balanceCredits (its balance could be staler than the snapshot’s). The snapshot balance stands until the next live frame ticks it forward.
  • : hb — a comment heartbeat about every 25s that keeps an idle connection open. It carries no data.
All amounts (balanceCredits, spentCredits, a row’s costCredits) are in credits, where 1 credit = $0.000001.