Skip to content

Request Logs

GET /logs?range=<range>&after=<ts>&after_id=<id>&errors=<bool>&limit=<n>

Returns the full per-request log for your organization, ordered newest-first.

You can also retrieve and inspect the same logs in the console under Analytics → Logs. Use the console for interactive troubleshooting and the API for programmatic retrieval or export.

From a terminal, the ai& CLI reads the same logs: aiand logs --range 1h --errors shows recent failures, and aiand logs --follow tails new requests.

ParameterTypeRequiredDescription
rangestringNo15m, 1h, 6h, 24h, 7days, 30days. Default: 24h.
afterstringNoPagination cursor — next_after from the previous page. Requires after_id.
after_idstringNoPagination cursor — next_after_id from the previous page. Requires after.
errorsbooleanNoIf true, only non-2xx requests.
endpointstringNoOnly requests to this /v1 path, e.g. /v1/responses.
content_loggedbooleanNoIf true, only requests where capture was attempted (see below). The page is then ordered by request_at.
limitintegerNoPage size. Capped at 100. Default: 50.
{
"data": [
{
"id": "...",
"model": "...",
"api_key": "sk-...abcd",
"status_code": 200,
"ttft_ms": 210,
"latency_ms": 420,
"input_tokens": 123,
"output_tokens": 45,
"cached_tokens": 64,
"cost": "0.00420000",
"currency": "usd",
"created_at": "2026-05-18 12:34:56.123456+00",
"request_at": "2026-05-18 12:34:55.901234+00",
"endpoint": "/v1/chat/completions",
"content_logged": true
}
],
"has_more": true,
"next_after": "2026-05-18T12:34:55.654321Z",
"next_after_id": "..."
}

cost is denominated in currency — the org’s billing currency (usd or jpy); it is never a bare USD figure. When has_more is false, you’ve reached the end of the available range.

ttft_ms is the time to first token: milliseconds from receiving a streamed request to the first generated output you receive, whether text, reasoning or a tool call. A chunk that carries only metadata, such as the opening role announcement, does not count, and neither does output the endpoint does not send you, such as reasoning on /v1/messages without thinking. It is null for a non-streamed request and for a stream that ended before any output.

cached_tokens is the portion of input_tokens billed at the discounted cached input rate. It is 0 when there was no cache hit and null on models without a cached rate.

request_at is when the request was received; created_at is when the row was settled, a moment later. Rows are ordered and paged by created_at, or by request_at when content_logged=true; next_after always carries the matching value, so pass it back unchanged. endpoint is the /v1 path, and content_logged says whether capture was attempted for this request — not that storage necessarily succeeded. A rare miss surfaces as unavailable in the detail response, never as content_logged: false (see Content logging).

Pass next_after and next_after_id from the previous page as after and after_id, and stop when has_more is false. Send both: after_id breaks ties between rows that share a timestamp, so either one alone is rejected with 400 invalid_request_error naming the missing parameter.

next_after is an opaque cursor, not an RFC 3339 timestamp: pass it back unchanged and don’t parse it.

API key or JWT + X-Org-ID. See Authentication.

GET /logs/{id}

Returns one request’s metadata plus its captured content (the request body and the response), when content logging is enabled for your organization (see Content logging). Use the id from the list, or the X-Request-ID header returned on every /v1 response.

{
"id": "...",
"state": "ready",
"metadata": {
"model": "...",
"endpoint": "/v1/chat/completions",
"api_key": "sk-...abcd",
"status_code": 200,
"input_tokens": 123,
"output_tokens": 45,
"cost": "0.00420000",
"currency": "usd",
"request_at": "2026-05-18 12:34:56.123456+00",
"created_at": "2026-05-18 12:34:58.000000+00"
},
"content": {
"endpoint": "/v1/chat/completions",
"streamed": false,
"capturedAt": "2026-05-18T12:34:56.123Z",
"responseId": "chatcmpl_...",
"request": "{ ...the request you sent... }",
"response": "{ ...the response... }",
"error": null,
"truncated": false
}
}

request and response are stored as text — a JSON string for a non-streamed response, and the captured SSE event stream for a streamed one — so you parse them client-side. The stored values retain the endpoint’s native shape (/v1/messages uses the Anthropic shape, for example), but they are processed for safe storage and are not guaranteed to be byte-for-byte identical to the original wire content. Inline-media placeholders and size truncation are described below.

For streamed responses, inline-media replacement is applied only to JSON payloads in the stored copy’s data: events. The SSE framing and non-media events remain parseable in their original form, and the live stream delivered to your client is never modified by request logging.

As a general rule, each captured log record is limited to 10 MiB (approximately 10 MB) across the request, response, and envelope metadata. Content beyond that limit is truncated rather than rejected: truncated is true, and the stored text includes a truncation marker. Inline base64 media is replaced with an omitted:<media-type>;bytes=<n> placeholder before the size limit is applied.

For a failed request, response is the error body you received and error carries its message plus status — the upstream status when there was one, otherwise the status you received. The upstream body itself is never stored.

Client-supplied request ids must match [A-Za-z0-9._:@=+-]{1,36}. If your X-Request-ID is outside that shape, the service replaces it with a generated id; use the X-Request-ID returned in the response to retrieve the metadata and content.

content may be absent; state says why, so the response is never an error unless the id is unknown:

StateMeaning
readyMetadata and content are both present.
processingThe request just completed; metadata is still settling.
not_loggedContent logging was off for this request.
expiredOlder than 30 days — the content has been deleted.
unavailableContent was expected but is missing.

A 404 is returned only when neither metadata nor content exists.

GET /logs/export

Organization admins only. Downloads the stored request/response bodies of the logged requests in a window as one gzip-compressed JSON Lines file, request-logs.jsonl.gz, newest first: one line per request, each in the shape of content in Retrieve one request. It takes the list’s range, errors and endpoint parameters, with the same 24h default.

One file holds at most the newest 5,000 requests in the window. For older ones, page through the list with content_logged=true (see Pagination) and retrieve each request. A request whose content has already been deleted is left out of the file.

The console offers this export when an admin switches the organization to zero data retention.

DELETE /logs

Organization admins only. Starts deleting every stored request/response body for your organization and returns 202 { "accepted": true }; the purge finishes in the background within a few minutes. Metadata rows are kept, and affected requests then open as unavailable. Deleting does not change the logging switch: while logging is on, requests made after the purge are captured again. Turning logging off first is not a clean cut either, since capture can continue for up to about a minute after the switch (see Content logging).

Capturing request/response bodies is opt-in and off by default. Metadata (the list above) is always recorded; request logs store content (the actual prompts and responses) only while content logging is on. Other features store content of their own whether or not it is on, such as inline PDFs and video jobs; Zero data retention lists them.

GET /logs/settings
PATCH /logs/settings { "log_content_enabled": true }

GET returns { "log_content_enabled": <bool>, "available": <bool>, "data_retention": "standard" | "zero" } and reflects a PATCH immediately. When available is false, hide the content-capture control; deletion remains available. PATCH (organization admins only) toggles logging when available. An organization on zero data retention reports "data_retention": "zero", and where capture is available a PATCH that switches logging on returns 403 zero_data_retention.

Organization admins can also turn content logging on or off in the console under Settings → Organizations → Request Logging. The switch is shown only when content capture is available in the current environment; when it is unavailable, the console still allows admins to delete previously stored content.