# API reference

> Inferit API reference: every endpoint, authentication with an API key or x402, money in µ, errors, response headers, and the on-chain calls that have no endpoint.

Base URL: `https://api-production-c74b9.up.railway.app` (this site's current API). Inference is OpenAI-compatible; everything else is plain JSON. Agents can read the same in one file: [agents.md](https://inferit.cc/agents.md).

## Authentication

-   **Inference** takes an API key: `Authorization: Bearer ik_…` or `x-api-key: ik_…`. Without either, it takes an x402 payment instead ([below](https://inferit.cc/docs/api-reference.md#x402)).
-   **Account routes** (keys, balance, usage, seller) take the session token from sign-in as `Authorization: Bearer <session>`. Sessions last 24 h. Balance and usage also accept your API key.
-   **Public routes** (models, prices, marketplace, stats, rails) need no auth and are cacheable.

## Money

All amounts are integers in µ (fields ending in `Micro`), serialised as decimal strings. 1 µ = one millionth of an ITC (0.000001 ITC) on the testnet, where ITC (Inferit Test Credit) is a test token with no monetary value; a micro-dollar at mainnet (planned). So `"1500000"` = 1.50 ITC. API prices are in µ per 1M tokens: 84 000 µ per 1M = 0.084 µ/token = 0.084 ITC per 1M tokens. The site shows µ per token, which is the same number as ITC (test dollars) per 1M tokens. Prices are **all-in** (the seller's price plus the platform fee) unless a field says otherwise. Percentages are numbers with two decimals; a discount is rounded down so it is never overstated. What one request costs, and the rounding, is in [Core concepts](https://inferit.cc/docs.md#request-cost); the `x-inferit-buyer-cost-micro` header is the authoritative figure.

## Inference

### POST `/v1/chat/completions` Auth: API key

OpenAI Chat Completions. Supports `stream: true` (SSE relayed byte for byte). Before routing, your escrow balance (under your ceiling) must cover the worst case: input estimate plus the output limit at the top-ranked offer. The output limit is the smallest of `max_tokens`, `max_completion_tokens` and the model's `context_length` (from `GET /v1/models`), or 1,024 when you send neither, times `n` for several choices. The seller is held to it: a long answer without a limit stops at 1,024 tokens with `finish_reason: "length"`. You are then charged the metered cost. A closed model (`open_weight: false` in `/v1/models`) is a list-price-only row that no seller can list, so a request for it answers `503 no_available_offers`.

_example (model with a live offer)_

```bash
curl https://api-production-c74b9.up.railway.app/v1/chat/completions \
  -H "Authorization: Bearer $INFERIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "qwen/qwen-2.5-14b-instruct",
    "messages": [{"role": "user", "content": "Hello"}]
  }'
```

### POST `/min{N}/v1/chat/completions` Auth: API key

Same, but only routes to offers at least N% below list (0–100). Also available as the `x-min-discount` header or a per-key setting; the strictest wins.

### POST `/anthropic/v1/messages` Auth: —

Not implemented yet: returns `501` with a docs link. See [Claude Code](https://inferit.cc/docs/guides/claude-code.md).

### Response headers

On every inference call:

| Header | Meaning |
| --- | --- |
| `x-request-id` | Request id, also in your usage log |
| `x-inferit-attempts` | Offers tried (failover happens only before the first byte) |
| `x-inferit-buyer-cost-micro` | What this request cost you, in µ (a micro-ITC on the testnet). A stream's final cost is known only after its last chunk; see your usage log. |
| `x-inferit-offer` | Opaque hash of the offer that served you, not the seller's identity. On Whitechain the settlement itself is public; see [On-chain visibility](https://inferit.cc/docs/security-trust.md#on-chain) |
| `x-inferit-rail` | Rail the request is funded and settled on |
| `x-inferit-network` | Settlement network, on every response: `whitechain-sepolia` today (`whitechain-mainnet` on the planned mainnet) |
| `x-inferit-testnet` | `1` on every response from a testnet deployment: balances are test tokens with no value |

### Errors

Errors use OpenAI's shape, `{"error":{"message","type","code"}}`. Branch on `code`:

| Status | Code | When |
| --- | --- | --- |
| 400 | `invalid_request` | The body does not parse or a field is invalid (type invalid\_request\_error) |
| 400 | `invalid_address` | POST /v1/faucet or sign-in with an address that is not 0x-prefixed 20-byte hex |
| 401 | `invalid_api_key` | A wrong or revoked API key |
| 401 | `missing_credentials` | An account route (keys, balance, usage, seller) without a session or key |
| 402 | `insufficient_funds` | With a key: the worst case exceeds your escrow balance or ceiling |
| 404 | `model_not_found` | The model is not in the catalog |
| 404 | `faucet_disabled` | This server sponsors no faucet (not a testnet) |
| 429 | `rate_limit_exceeded` | Too many requests from this IP or paying address, or the faucet’s hourly server budget; honour retry-after |
| 429 | `faucet_cooldown` | This address claimed ITC less than 24 h ago; retry-after says when |
| 501 | `not_implemented` | /anthropic/v1/messages and legacy /v1/completions |
| 503 | `no_available_offers` | No healthy offer right now; honour retry-after |
| 503 | `faucet_pending` | The mint was sent but is not confirmed yet (retry-after 15); check the balance before claiming again |
| 503 | `faucet_unavailable` | The faucet cannot mint right now; retry after 60 s |
| 503 | `x402_settlement_pending` | The x402 deposit was sent but not confirmed in time; if it lands it is your balance |
| 503 | `x402_unavailable` | The x402 path failed before anything was sent; nothing was charged |

Inference with no key at all answers `402`: the x402 challenge, not an error. Rate limits (hosted defaults): 1,200 requests a minute per IP on every route, 30 a minute on sign-in; over them, `429 rate_limit_exceeded` with `retry-after`. Responses carry no RateLimit headers.

```json
{
  "error": {
    "message": "Your available balance on whitechain does not cover the worst-case cost of this request.",
    "type": "insufficient_funds",
    "code": "insufficient_funds"
  }
}
```

## x402: pay per request, no key

Standard x402 v2, `exact` scheme, settled into the escrow and credited to the payer. x402 can express 1 µ, but every x402 payment is an on-chain transaction that costs gas, so the API works as a prepaid meter: each payment is at least 10 000 µ (0.01 ITC), it is credited to the payer's own escrow balance, and each request is charged its exact µ cost against that balance. Settlement to sellers is batched on chain. The facilitator pays the gas, so an agent needs no WBT. The walkthrough and client code are on [Agents](https://inferit.cc/agents.md) and in [agents.md](https://inferit.cc/agents.md).

### POST `/v1/chat/completions` Auth: none → 402

With no `Authorization` or `x-api-key` header: `402` with a `PAYMENT-REQUIRED` header (base64 JSON) and the same document as the body. It offers one requirement: `scheme: "exact"`, `network: "eip155:<chainId>"`, `asset` = the token, `payTo` = the escrow, `amount` = the worst-case all-in cost of this exact body (at least 10 000 µ = 0.01 ITC, since each payment is an on-chain transaction; the request is then charged its exact µ cost), `maxTimeoutSeconds` and `extra: { name, version }`, the token's EIP-712 domain: `name: "Inferit Test Credit"`, `version: "1"` (the domain version, not the contract revision: the token is TestCredit contract rev. 2). The quote is pinned to the body for 120 s. The same applies to `/min{N}/v1/chat/completions`.

### POST `/v1/chat/completions` Auth: PAYMENT-SIGNATURE

The same body with a `PAYMENT-SIGNATURE` header (base64 JSON payload with an EIP-3009 authorization; `X-PAYMENT` is accepted too). The API verifies it, settles it first with `depositWithAuthorization`, then serves the request against the payer's escrow balance (streaming included). The response carries `PAYMENT-RESPONSE`, base64 of `{ success: true, transaction, network, payer }`, plus the usual `x-inferit-*` headers. A payment that does not verify gets a fresh `402` with the reason in `error`. If the request fails after the payment settled, the credit stays in the payer's escrow balance and the error says so (an `error.x402` object with `credited`, `payer`, `amount` and `transaction`; a funding `402` at that point becomes `503`, so the client does not pay again). `503 x402_settlement_pending`: the deposit was sent but its receipt did not arrive in time; if it lands it is the payer's escrow balance. `503 x402_unavailable`: nothing was sent and nothing was charged.

### POST `/v1/faucet` Auth: none

Testnets only. Inferit's ITC faucet (test credit, not WBT), free and sponsored. Body `{ address }`: the API mints 1,000 test ITC to that address with `faucetTo` and pays the gas, so the address needs no WBT. At most once per 24 h per address (`429 faucet_cooldown`), 10 calls an hour per IP and 120 sponsored mints an hour per server (`429 rate_limit_exceeded`). `503 faucet_pending` (`retry-after: 15`): the mint was sent but is not confirmed yet; check the balance before you claim again. `404 faucet_disabled` when the server does not sponsor the faucet.

_200 response_

```json
{
  "address": "0xYourAddress",
  "amountMicro": "1000000000",
  "token": { "address": "0x2E67…7fBB", "symbol": "ITC", "decimals": 6 },
  "network": "eip155:1874",
  "transaction": "0x…",
  "explorerUrl": "https://explorer.testnet.whitechain.io/tx/0x…",
  "next": "Pay for inference with x402: POST …/v1/chat/completions without credentials …"
}
```

### GET `/.well-known/x402`

Discovery: the paid resources and the requirement a client signs for (scheme, network, asset, payTo, extra), the minimum payment, the facilitator address and the faucet. `404` when x402 is off.

## Marketplace (public)

### GET `/v1/models`

OpenRouter-shaped model rows plus `pricing` (list price) and `best_price` (best all-in offer, or `null`). Two encodings of the same price sit side by side: `prompt` and `completion` are decimal strings in ITC (test dollars) per token (`"0.0000001"`), as OpenRouter writes them; `input_per_m`, `output_per_m` and `cache_read_per_m` are integer µ per 1M tokens (`"100000"`). `best_price.discount_pct` is the best all-in price (fee included) against list. The list includes list-price-only rows: `best_price: null` means no seller offers that model right now.

### GET `/v1/prices`

List and best all-in price per model.

### GET `/api/marketplace`

Per model: best all-in input/output price, discount vs list, sellers, healthy sellers, 24 h requests and volume, uptime, TTFT p50.

### GET `/api/markets/:model`

Aggregated order book: price levels with offer count, healthy count and the combined daily caps of the offers at each level. Never seller identities, endpoints or per-offer volume. URL-encode the model id.

### GET `/api/stats`

Rolling 24 h totals: requests, spend, savings vs list, tokens, offers, sellers.

### GET `/v1/rails`

Settlement on Whitechain: network, testnet flag, chain id, escrow and token addresses, operator, owner, guardian, escrow version, health.

### GET `/v1/providers/resale-allowlist`

Providers whose terms permit API-key resale, each with an evidence URL. Empty by default.

### GET `/health`

Liveness, the build's git SHA, the network, chain id and testnet flag. Answers 503 when the database or the settlement rail is unhealthy (RPC down, escrow paused, operator low on gas).

### GET `/metrics`

JSON operations metrics: settlement backlog, batches awaiting finality, chain head lag, operator gas runway, circuit-breaker headroom. May require a bearer token on hosted deployments.

### GET `/llms.txt`

The API's own summary for agents, with the live models and prices and both ways to pay. This site's index of every page as markdown is a different file: [/llms.txt](https://inferit.cc/llms.txt) on the docs host.

The two examples below are illustrative: they show the response shape, not live figures. The live values are on [/models](https://inferit.app/models) and [/analytics](https://inferit.app/analytics).

_illustrative example (annotated)_

```
GET /api/marketplace
{
  "feeBps": 500,
  "markets": [{
    "modelId": "qwen/qwen-2.5-14b-instruct",
    "name": "Qwen: Qwen2.5 14B Instruct",
    "openWeight": true,
    "licenseId": "apache-2.0",
    "listInputPerM": "100000",        // µ per 1M tokens: 0.10 ITC per 1M on the testnet
    "listOutputPerM": "200000",
    "bestInputPerM": "84000",         // all-in, healthy offers only
    "bestOutputPerM": "168000",
    "bestDiscountPct": 16,
    "sellers": 2, "healthySellers": 1,
    "requests24h": 0, "volume24hMicro": "0",
    "uptime24hPct": null,             // null until there is data
    "ttftP50Ms": null
  }]
}
```

_illustrative example (annotated)_

```
GET /api/markets/qwen%2Fqwen-2.5-14b-instruct
{
  "modelId": "qwen/qwen-2.5-14b-instruct",
  "summary": { ...same shape as a marketplace row... },
  "levels": [
    { "inputPerM": "84000", "outputPerM": "168000", "offers": 1, "healthyOffers": 1, "capacityMicro": null },
    { "inputPerM": "90000", "outputPerM": "180000", "offers": 1, "healthyOffers": 0, "capacityMicro": "1000000" }
  ]
}
```

## Sign-in

### POST `/v1/auth/evm/challenge`

Body `{ address }`. Returns `{ message, nonce, expiresAt }`: a one-time EIP-4361 message to sign with the wallet (an EIP-191 signature). It is an off-chain text signature: no transaction, no gas.

### POST `/v1/auth/evm/verify`

Body `{ message, signature }`. Verifies the signature and returns `{ token, expiresAt, account }`: a session token for the account routes, valid for 24 h, for both the Buy and the Sell workspace.

## Keys

### POST `/v1/keys` Auth: session

Body `{ name?, spendLimitMicro?, minDiscountPct? }`. Returns the key once in `key`.

### GET `/v1/keys` Auth: session

Your keys (prefix only, never the secret).

### DELETE `/v1/keys/:id` Auth: session

Revoke immediately.

## Buyer

### GET `/v1/balance` Auth: session or API key

Available, cap, spent, deposit, expiry, unsettled charges (`pendingMicro`) and usage settled on-chain but not yet final (`pendingFinalityMicro`). Available subtracts both.

### PUT `/v1/buyer/rail` Auth: session

Body `{ rail: "whitechain" }`. Selects Whitechain settlement for future requests (`"credit"` exists only on local development APIs).

### GET `/v1/usage?limit&cursor` Auth: session or API key

Per-request token counts, costs, latency, rail and settlement status. Paginate with `nextCursor`. Each row carries `finality`: `pending` until its settlement batch is finalized on-chain, then `finalized`. Rows never carry the seller's identity, a transaction hash or a batch id; your own settlements are listed with explorer links in the Buy workspace.

### GET `/v1/usage/export.csv` Auth: session or API key

The same as CSV. Never contains prompts or completions.

## Seller

### POST `/v1/seller/offers` Auth: session

Body `{ model, kind: "endpoint" | "upstream_key", endpointUrl, authToken?, upstreamProvider?, upstreamKey?, pricing, capDailyMicro?, licenseAck }`. `pricing` is `{ mode: "per_token", inputPerM, outputPerM, cacheReadPerM? }` or `{ mode: "multiplier", multiplierBps }` (10,000 = list price). The API checks the licence, the resale allowlist and an SSRF guard, sends a live 1-token probe, and requires you to be registered on your rail.

### GET `/v1/seller/offers` Auth: session

Your offers, including your own endpoint URLs and health.

### PATCH `/v1/seller/offers/:id` Auth: session

Pause/resume, reprice, change cap or endpoint.

### DELETE `/v1/seller/offers/:id` Auth: session

Delist; stored secrets are destroyed (hard delete).

### GET `/v1/seller/earnings` Auth: session

Settled, pending and withdrawable earnings, with the settled part split into `pendingFinalityMicro` and `finalizedMicro`.

## On-chain calls (no endpoint)

Moving money in or out of the escrow is a transaction on Whitechain Sepolia, not an API call. The app sends these from your wallet; a transaction you send yourself costs gas in WBT. The contract view, with every check, is on [Whitechain escrow](https://inferit.cc/docs/payments/whitechain.md#calls).

| Action | Call | Gas |
| --- | --- | --- |
| [Deposit and set a ceiling](https://inferit.cc/docs/payments/whitechain.md#deposit) | `approve(escrow, amount)` on ITC, then `depositAndSetCap(amount, cap, expiry)` | WBT, from your wallet |
| Change the ceiling | `setSpendingCap(cap, expiry)`, or the relayed `setSpendingCapBySig` | WBT, or the relayer's |
| [Withdraw a deposit](https://inferit.cc/docs/payments/whitechain.md#withdraw) | `requestWithdraw(amount)`, then `executeWithdraw()` after the delay | WBT, from your wallet |
| [Register as a seller](https://inferit.cc/docs/sellers.md#steps) | `registerSeller(payout)` from the seller wallet | WBT, from your wallet |
| [Withdraw seller payouts](https://inferit.cc/docs/sellers.md#payouts) | `withdrawEarnings()`, paid to the registered payout wallet | WBT, from your wallet |
| [Pay with x402](https://inferit.cc/docs/payments/whitechain.md#x402) | `depositWithAuthorization`, submitted by the facilitator with your signature | None for the payer |
| [Claim ITC](https://inferit.cc/docs/api-reference.md#faucet) | `POST /v1/faucet` (sponsored), or `faucet()` on ITC from your wallet | None, or WBT from your wallet |

---

Canonical page: https://inferit.cc/docs/api-reference
All pages as markdown: https://inferit.cc/llms.txt
Testnet demo: test tokens, no monetary value.
