Docs
Status
OverviewQuickstartSetup promptsThe core loopAuthenticationOverviewModelsThe waterfallPlansAdding modelsData controlsOpenAI compatibilityEmbeddingsAnthropic APIErrorsIntegrate the gatewayCost APIAccount APICoding agentsCredits & billingSpend & intelligenceSpend APITelemetryBecome a providerProvider guideAPI reference

Get started

  • Overview
  • Quickstart
  • Setup prompts
  • The core loop
  • Authentication

Guides

  • Overview
  • Models
  • The waterfall
  • Plans
  • Adding models
  • Data controls
  • OpenAI compatibility
  • Embeddings
  • Anthropic API
  • Errors

Integrations

  • Integrate the gateway
  • Cost API
  • Account API
  • Coding agents

Billing & usage

  • Credits & billing
  • Spend & intelligence
  • Spend API
  • Telemetry

Providers

  • Become a provider
  • Provider guide

Reference

  • API reference

Integrations

Cost API

Read what every request cost: inline on the response, per request by id, the credit balance, or as a settled export you poll into your own billing system to rebill end-customers at your margin.

Base URL

The per-request cost and account reads below take an ordinary xpl_ inference key, under one host. Organization Spend reports, linked separately below, require a provisioning key:

  • Serving: https://api-pr-143.preview.experientiallabs.ai/v1 (also https://api-pr-143.preview.experientiallabs.ai/api/v1).
  • Cost & account: https://api-pr-143.preview.experientiallabs.ai/api/v1.

New to the surface? Start with Integrate the gateway.

Aggregated reports use the Spend API

For exact grouped reports, arbitrary dates, frozen pagination and matching charts, use the Spend API at /api/orgs/{org_id}/spend. It requires a provisioning key and shares its reporting stores with the dashboard. Start with POST /analytics, retain a loading id and poll that id; a ready report supplies the snapshot for every later read.

Spend's all-in value is paid credits plus BYOK estimates plus free promotional list value. Only the paid part is charged; it is not the same quantity as this page's real_cost_usd, which excludes free list value. Spend CSV contains aggregate groups, not the per-request rows of GET /api/v1/usage. The existing Cost API activity and usage export retain their documented org scope and plan-bound identity restrictions; the new provisioning requirement is not a blanket own-key-only rule for inference reads.

Customer-plan allowances

GET /api/orgs/{org_id}/identities/{identity_id}/allowances reads the same counters native admission enforces. Inference keys read their own identity only. Org admins and provisioning management keys can inspect their organization. Plan-bound keys cannot read the org wallet; usage, generation and activity reads are scoped server-side to their identity, including rotated keys.

The response carries assignment, budget_period, period_start, period_end, pricing_basis: frozen_list_value, groups and aggregate. Each group and the shared aggregate report limit_nano_usd, settled_nano_usd, reserved_nano_usd, pending_nano_usd and remaining_nano_usd. The aggregate includes all groups for this identity, even removed groups. Its limit and remaining are null when no aggregate cap is authored; zero denies. Both group and aggregate caps must admit a request. An unassigned identity has no aggregate allowance. Values are integers; $1 = 1,000,000,000 nano-USD. Read remaining directly, without subtracting pending twice. Each group and aggregate also carries exact: decimal integer strings for settled, reserved, pending and remaining (null when unlimited). Browser displays use these withBigInt because accumulated exposure can exceed JavaScript's safe-integer range. Periods are recurring UTC calendar months or explicit source intervals with timezone-aware bounds. An explicit interval expires without auto-renewal; rolling windows are unsupported. Native and imported unresolved exposure survive period changes without erasing settled history.

Allowances measure attributed inference list value at the frozen selected rate schedule, independently of platform charges, promotions and house discounts. BYOK uses the allowance without debiting platform credits; its value is an estimate, never a provider invoice. Unknown prices or missing enforceable request bounds fail closed. Actual overages are recorded and stop further admission, not hidden behind an absolute invoice-ceiling claim.

Failed or disconnected calls without trustworthy complete usage retain pending allowance. The current engine cannot always prove that a provider rejection was unbillable. Measured zero usage releases exposure; missing usage does not. An operator must reconcile it or release it with evidence. Removing a group with outstanding exposure blocks new admission until resolution; the removed group remains visible with a zero current limit. This does not release platform-credit holds or change the wallet.

The settled export remains your billing reconciliation feed, not an admission mechanism. Request labels are attribution, never customer-plan authority. Use a trusted identity mapping and issue the customer's inference keys under that identity. Your application owns subscription events, markup and invoices. See the Account API for immutable policy versions, counter-preserving changes and bounded assignment delegation.

Current-period migration uses the administrator-only GET /api/orgs/{org_id}/identities/{identity_id}/allowance-cutover. It exports allowances, immutable source receipts, native outstanding attempts andsettled_periodswithout content. Historical period rows carry period_start, period_end, nullable source_period_id, group_key and settled_nano_usd. More than 500 external receipts, 500 native outstanding rows or 1,000 settled-period rows sets export_overflow; a partial export is never accepted as a complete handoff. The web host accepts a signed-in administrator's browser session and forwards its verified actor to the API host. Ordinary customer keys, including provisioning keys, cannot use it. Prepare accepts at most 500 distinct source-owned request receipts, not cumulative totals or duplicate destination usage. On activation, their settled and pending legs enter the same counters. Evidence-backed resolution replaces the pending leg only.

Handoff totals can exceed 2^53 - 1. Consume and produce JSON losslessly: Python integers or exact decimals, or a JavaScript lossless JSON parser/serializer withBigInt support. The session proxy preserves raw JSON without parsing and reserializing money. Never use ordinary JSON.parse, response.json() or Number amounts for rollback reconciliation. Preserve the original export and server-issued digest; send exact JSON integers, not rounded numbers or numeric strings.

Rollback requires frozen admission, resolved native reserved and pending exposure, resolved imported pending exposure, and a complete fresh export. export_overflow:true or a changed export_sha256 blocks acknowledgment. Resolve all uncertainty while active or frozen; unresolved native or imported exposure blocks acknowledgment. Exporting and acknowledging never reset history or infer zero usage. The endpoint does not stop or resume Cloudflare. See the handoff lifecycle.

Enforcement ships inactive until operator activation after fleet verification. A prepared handoff nevertheless closes its destination identity immediately. These APIs do not prove deployment or real-provider parity. Unsupported reset rules, missing current-period migration decisions and unverified provider protocol, auth, usage or prices block cutover.

Three ways to read cost

Cheapest-to-wire to most complete:

  • Inline, per response. Read usage.cost (USD) off the response. It is stamped on every Chat/Responses/Messages reply, no request flag needed. Good for showing a cost immediately; not a durable feed on its own.
  • One request by id. Pass the response’s x-request-id to GET /api/v1/generation?id= for cost and token detail.
  • The settled-usage export (recommended for billing). GET /api/v1/usage is a cursor-paginated feed of settled per-request rows with attribution, real cost, and token counts; the feed a billing sync job should poll.

Inline cost on every response

Experiential stamps the settled cost inline on the usage object of every Chat Completions, Responses and Anthropic Messages reply, in USD, as an extension of the standard usage object. No request flag is needed: usage.cost is stamped whenever the engine emits a usage object, so usage is always included (both usage: { include: true } and stream_options: { include_usage: true } are accepted as no-ops). The one exception is a request sent with an Idempotency-Key: it carries no usage.cost on the original or on a replay, so the bytes stay identical; treat the field as optional and read the export for those.

  • provider names the rung that answered. A model's waterfall can fail over to a sibling rung without any change to the reply, so the body carries the serving provider (tencent, openrouter, your own provider on a BYOK request) as a top-level field beside id/model, where OpenRouter puts it. It is the catalog provider name (tencent), and the x-gateway-provider response header carries the same public name.
  • Non-streaming Chat/Responses/Messages bodies always carry usage (and cost); on a stream the cost rides the final usage chunk when the engine emits one (the message_delta event on the Messages surface), so request that terminal frame the way you already do for OpenAI.
  • A gateway-run web search is a flat per-search fee inside cost. When a request asks for web search on a route without native search (Chat web_search_options / plugins web / :online, Responses web_search, Messages web_search_*), the gateway runs one search (Exa) and charges a flat fee per search (default $0.007) on top of the model's token price, on the finalizing settle of a finished request only. It is never charged on a failed request or on BYOK. It is already included in usage.costand in the export's settled cost; the count is usage.server_tool_use_details.web_search_requests.
  • BYOK settles cost 0. A bring-your-own-key request reports cost: 0, sets is_byok: true, and puts the upstream provider’s own attributed charge on usage.cost_details.upstream_inference_cost (present for BYOK only; omitted entirely for platform-funded traffic rather than reported as a wrong or zero value).
  • Keyed / idempotent replays are byte-exact. A replay of an Idempotency-Keyed completion returns the original bytes with no injected fields; Experiential skips annotation on those requests entirely. (X-Client-Request-Id is correlation/affinity, not a replay key, and is still annotated.)

Look up one request by id

Every completion response carries an x-request-id header, the gateway request id. Read it off the response and pass it back to GET /api/v1/generation?id=to pull that one request’s settled cost and token detail. The endpoint accepts both the bare id and the gen-<request_id> form it echoes:

GET /api/v1/generation?id=
# The completion response returns the id in the x-request-id header;
# pass it (bare, or as gen-<id>) to /generation.
curl "https://api-pr-143.preview.experientiallabs.ai/api/v1/generation?id=$REQUEST_ID" \
-H "Authorization: Bearer xpl_..."

The read is org-scoped: a request id from another organization 404s exactly like an unknown one. The Anthropic Messages surface sets the same x-request-idheader (read it with the Anthropic SDK's with_raw_response); Claude Code exposes no response headers, so read a Claude Code run's cost from the usage export filtered on the key the run used.

Credit balance & activity

Two account-level reads for the running totals:

  • GET /api/v1/credits: {data:{total_credits, total_usage}} in USD; the remaining balance is total_credits - total_usage.
  • GET /api/v1/activity: a recent activity rollup for the org.

The settled-usage export

GET /api/v1/usage returns settled rows, newest first, with a keyset cursor, the feed a billing sync job should poll:

GET /api/v1/usage
curl "https://api-pr-143.preview.experientiallabs.ai/api/v1/usage?limit=1000" \
-H "Authorization: Bearer xpl_..."

The response is { "data": [ ... ], "next_cursor": { ... } | null }. Each row in data carries:

FieldMeaning
idThe request id.
created_atSettlement timestamp (ISO 8601).
modelThe model slug.
providerThe winning provider (nullable).
attribution_labelThe safety_identifier / user you set on the request; null when neither was sent.
real_cost_usdAlways-real per-call cost in USD (charged credits + attributed BYOK pass-through). 0 with pricing_known false means “unpriced”, not free.
cost_usdCharged platform-credit cost in USD.
estimated_cost_usdEstimated / attributed cost in USD.
pricing_knownWhether a price was known for the request.
input_tokens · output_tokens · cached_input_tokens · reasoning_tokensToken counts.
statusTerminal status of the request.
api_key_idThe key that made the request (nullable).

Filter by ?attribution_label=<id> (plus optional window, model, api_key_id, status, limit) to pull one end-customer’s requests. When a full page comes back, next_cursor is { cursor_ts, cursor_id, cursor_after }; pass those three values back as the cursor_ts, cursor_id, and cursor_after query params for the next page, and persist them so the next run resumes where you stopped. next_cursor is null on the last page. This is a pull/poll model today; there is no push webhook yet.

Attribute each request to your end-customer

Pass the OpenAI-standard safety_identifier on every request (legacy alias: user). Experiential records it as the request’s attribution label and rolls usage up per end-user. No per-customer key required.

safety_identifier
curl https://api-pr-143.preview.experientiallabs.ai/v1/chat/completions \
-H "Authorization: Bearer xpl_..." \
-H "Content-Type: application/json" \
-d '{
"model": "claude-fable-5.1",
"messages": [{"role": "user", "content": "..."}],
"safety_identifier": "cust_8842"
}'

The label then appears as attribution_label on the matching /api/v1/usage row, so you can group cost and tokens by your own customer id without running a separate key per customer. safety_identifier is one of two attribution models; see key-per-customer on the Account API.

Meter your customers on inference cost

The common shape: you sell consumption-based credits, you charge each customer for what their inference actually cost you (plus your margin), and a billing system such as Metronome, Orb, or Stripe metered billing turns metered usage into invoices. The usual integration is a pull-based sync off the settled usage export, not bookkeeping on each response. This is how it fits together.

  1. Tag every request with your customer. Send your customer’s stable id as safety_identifier (alias user) on each call. One org key serves all of them; the id comes back as attribution_label on the export row. If you need per-customer revocation or daily caps as well, mint a key per customer instead (Account API) and group by api_key_id.
  2. Bill from the export, not the response. usage.cost on the reply is for showing the number; it is not a durable ledger (a client can disconnect mid-stream, a retry can double-count, and a request sent with an Idempotency-Key carries no usage.cost at all, on the original or a replay). The export is the settled ledger: every request settles exactly once, rows never change after they settle, and failed requests appear with their status and whatever cost settled for them.
  3. Run a scheduled sync, newest first. The export is newest-first inside a lookback window (window=24h|7d|30d, default 7d), and next_cursor pages older within that one call; it is not a checkpoint to resume from on the next run. Each run therefore starts at the top: call GET /api/v1/usage?limit=1000, follow next_cursorpage by page, and stop once a whole page is older than your last successful run’s start time minus a grace period that covers your longest request (a few minutes is plenty). Dedupe on the row id. Run at least daily: rows older than the 30-day window are no longer exportable.
  4. Land rows in a local outbox first. Insert each row into a table keyed on the request id (insert, on conflict do nothing) in one local transaction, then have a separate delivery step push unsent rows to the meter and mark them sent. A meter call is an external HTTP write and cannot share your database transaction; the outbox is what makes a crash between the two safe to replay.
  5. Emit one usage event per row. For Metronome: customer_id from your mapping of attribution_label, transaction_id = the row id (the ingest API deduplicates on it, so a redelivery is a no-op), timestamp = created_at, and properties inference_cost_usd = real_cost_usd plus model, provider, the token counts, status, pricing_known, and whether the row was BYOK. Orb and Stripe meters take the same shape: idempotency key = row id, value = cost.
  6. Price it in the billing system. Define a billable metric that sums inference_cost_usd per customer per period, and put your credit rate (the margin) on that metric. The sync job ships raw cost; the markup and the conversion to your credits live where your pricing already lives, so a price change never touches the integration.
  7. Reconcile. The export’s cost_usd column (charged credits only) summed over a day should agree with the movement of total_usage on GET /api/v1/credits over that day; both are settled charged spend. Rows with a null attribution_label are requests your code did not tag: surface them, do not drop them. To audit one customer, pull ?attribution_label=<id>.

Which quantity to bill: cost_usd is the platform credits the request was charged; estimated_cost_usdis the attributed value of a BYOK request at catalog list rates, which is an estimate and never the provider’s invoice; and real_cost_usd is the two added together, so a BYOK row shows its estimated spend instead of a $0 charge. Bill real_cost_usd only if you accept that the BYOK part is estimated; otherwise bill cost_usd and meter BYOK rows on tokens (they carry is_byok, tokens, and attribution). A row with pricing_known: false carries a cost of 0 because no price was known, not because it was free; decide up front whether to bill those on tokens or skip them. Because the feed is settled, you can rebill at any margin.

A new org receives a welcome credit grant at signup, but spending stays locked until the founder proves inbox ownership (verification email) or a card charge settles. Until then GET /v1/models and telemetry work, but a paid completion is refused.

Credits & funding

  • Platform-funded (credits). Your org holds credit balance; each request draws from it and usage.cost / real_cost_usd is the real USD cost. This is the default and what you rebill against.
  • BYOK (bring-your-own-key). A model served through your own provider connection is billed by the provider directly, so Experiential settles cost: 0 (inline is_byok: true, with the upstream charge on cost_details.upstream_inference_cost) and the export row’s real_cost_usdis the attributed pass-through cost. Those rows still carry tokens and attribution, so you can still meter your customers on usage; just don’t expect a platform charge on them.
PreviousIntegrate the gatewayNextAccount API