Integrations
Request log API
List and filter your organization's requests, read one with its prompt and response, and read latency and spend. These are the reads behind Observability, and any key of your organization can call them.
Access
Send any xpl_ key of your organization as Authorization: Bearer. Every route here is a GET under https://api-pr-1144.preview.experientiallabs.ai/api and reads only that key's organization. Get its id from GET /api/whoami; another organization's id answers 404.
| Route | Returns |
|---|---|
| GET /api/whoami | Your key's organization: org_id, org_slug, org_name. |
| GET /api/orgs/{org_id}/usage/requests | One page of requests, newest first, and next_cursor. |
| GET /api/orgs/{org_id}/usage/requests/{request_id} | One request. With include_body=true, its captured prompt and response as body. |
| GET /api/orgs/{org_id}/usage/requests/{request_id}/prompt | The captured prompt and response alone; 404 when nothing was captured. |
| GET /api/orgs/{org_id}/usage/requests/{request_id}/attempts | Every provider dispatch of the request (retries and fallbacks), at most 256. |
| GET /api/orgs/{org_id}/usage/timeseries | Requests, errors, tokens and cost per time bucket, model and lane. |
| GET /api/orgs/{org_id}/usage/latency | p50 and p95 latency and time to first token over the window. |
| GET /api/orgs/{org_id}/usage/by-provider | The same totals per provider. |
| GET /api/orgs/{org_id}/usage/by-label | The same totals per end-user label. |
| GET /api/orgs/{org_id}/telemetry-settings | capture_prompt_content: whether prompts and responses are stored. |
Reads with a key are limited per organization: about 10 per second with short bursts, and a few at a time. Over the limit, a read answers 429 with Retry-After. Signed-in dashboard use is not counted.
Let an agent read it
The quickest way to use this API is the agent prompt. In Observability, choose Copy agent prompt at the top right of the Requests table. Paste the prompt into your coding agent and replace the key placeholder with one of your keys. The agent then answers questions such as "why did requests fail today?" or "what did request X send?" with links back to each request. The same prompt is on Setup prompts.
List requests
GET /api/orgs/{org_id}/usage/requests returns newest first. All parameters are optional and combine with AND.
| Parameter | Meaning |
|---|---|
| window | 24h, 7d (default) or 30d, ending now. |
| start, end | An exact range instead of window (ISO 8601; start inclusive, end exclusive). |
| model | One model slug. |
| api_key_id | One API key's requests. |
| attribution_label | One end user: the safety_identifier (or user) your request sent. |
| lane | platform (paid with credits) or byok (your own provider key). |
| status | error (anything not completed) or one of completed, failed, cancelled, incomplete, expired_before_dispatch, unknown_after_crash. |
| failure_class | One error class, as a row's failure_class shows it. |
| conversation | One conversation: the hex handle a row shows as conversation_group. |
| cache_hits_only | true: only requests with recorded cached input tokens. |
| sort | cost, latency or tokens (largest first) or oldest. Default: newest first. Not combinable with failure_class or conversation. |
| limit | 1 to 200 rows per page (default 50). Out of range answers 422. |
Pagination
The response is {requests, next_cursor}. While next_cursor is not null, send the same query again with cursor_ts, cursor_id and cursor_after set from its ts, id and after. A sorted page also returns value and before; send them as cursor_value and cursor_before. The cursor freezes the window, so pages do not shift while new requests arrive.
curl "https://api-pr-1144.preview.experientiallabs.ai/api/orgs/$ORG_ID/usage/requests?window=24h&status=error&limit=50" \-H "Authorization: Bearer $EXPLABS_API_KEY"
Fields
| Field | Meaning |
|---|---|
| request_id · created_at | The request id (the x-request-id response header) and when it settled. |
| model · provider · lane | The model slug, the provider that served it, and whose money it rode. provider and lane are null when nothing was dispatched. |
| api_key_id · key_label | The key that made the request (null when that key was deleted). |
| status · failure_class · error_message | Terminal status and, for a request that did not complete, the sanitized reason. |
| incomplete_reason | Why an incomplete request stopped (max_tokens or the engine's reason). |
| latency_ms · ttft_ms | Total latency and time to first token, in milliseconds. |
| input_tokens · cached_input_tokens · output_tokens · reasoning_tokens | Token counts. Cached input is part of input; reasoning is part of output. |
| real_cost_usd · cost_usd · estimated_cost_usd · free_usd · pricing_known | real_cost_usd is what the call cost, whoever paid: charged credits (cost_usd) plus the estimate for your own provider key (estimated_cost_usd). A free promotional request carries its list value in free_usd. With pricing_known false, 0 means unpriced, not free. |
| attempt_count | Provider dispatches; 0 means it failed before any dispatch. |
| tools_used | Distinct tool names the request called (names only). |
| prompt_group · conversation_group | Short handles: same agent configuration, same conversation. |
| attribution_label | The end-user label your request sent, or null. |
| body (one request only) | With include_body=true: messages, request (tools and settings), response and wire_request. null when nothing was captured. |
Read one request
GET /api/orgs/{org_id}/usage/requests/{request_id} returns the list row for one request. Add include_body=true to inline its captured prompt and response as body: messages, request (tools, tool choice and sampling settings), response (the JSON body, or the stream's frames) and wire_request (the request as the gateway received it, with secrets removed from headers). body is null when the request was not captured.
The request id is the x-request-id header of the response you received. A request appears here once it settles, shortly after the response ends.
curl "https://api-pr-1144.preview.experientiallabs.ai/api/orgs/$ORG_ID/usage/requests/$REQUEST_ID?include_body=true" \-H "Authorization: Bearer $EXPLABS_API_KEY"
GET .../usage/requests/{request_id}/attempts lists every provider dispatch of the request in order: provider, lane, state, failure_class, error_message, timestamps and tokens. Read it when a request retried or fell back to another provider.
When prompts and responses are stored
- Bodies are stored only while your organization's prompt capture is on. Read it with
GET /api/orgs/{org_id}/telemetry-settings(capture_prompt_content). - Requests routed under zero data retention are never stored, even with capture on.
- A prompt whose response never arrived is kept for two hours, then removed.
- Requests served on your own provider key or a connected Claude or ChatGPT plan follow the capture setting like any other request.
Everything else on this page (model, tokens, cost, latency, status, errors) is recorded for every request, whatever the capture setting. See Data controls.
Latency and spend rollups
These read the whole window, not a page of rows. Each takes window (24h, 7d or 30d). timeseries, latency and by-provider also filter by model, api_key_id and lane. A 24h window buckets hourly; 7d and 30d bucket daily. timeseries?compare=true adds the previous period's totals.
For exact grouped reports over any dates, use the Spend API (provisioning key). For a full export of settled requests, use POST /api/v1/usage/exports on the Cost API.
Errors and common mistakes
- 401: the key is wrong or revoked.
- 404 on every route: the
org_idin the path is not your key's organization. Use the one from/api/whoami. - 404 on one request: it belongs to another organization, or it has not settled yet. Retry after a few seconds.
- 400: an unknown
status,windoworlane, astartafterend, or a cursor sent with a different query. The error names the parameter. An unknown status is refused rather than returning an empty list. - 422:
limitoutside 1 to 200, or a sort too broad to read over its filters (sort_too_broad); narrow the window. - 429: your organization's keys sent too many reads at once, or the rollups are busy. Wait the
Retry-Afterseconds and retry. For more than a few pages of requests, use the usage export instead of paging the log. - body is null: capture is off, the request was routed under zero data retention, or its prompt expired before a response arrived.