# OpenRouter chat — agent guide

OpenAI-compatible chat completions for every OpenRouter model (700+), paid per
call in USDC via MPP at provider cost. No API key, no subscription. Schemas:
`/openapi.json`. Base URL: https://openrouter.withzero.xyz

## Routes

| Route | Payment | Use for |
| --- | --- | --- |
| `GET /api/v1/models` | free | model IDs, per-token USD rates, context lengths |
| `POST /api/v1/chat/completions` | fixed: one 402, pay worst-case estimate up front, no refund | short calls, fewest round-trips |
| `POST /api/v1/chat/completions/metered` | metered: escrow a ceiling, pay actual usage, difference refunded | long generations, reasoning models, pay-exactly-actual |

Both chat routes take the same OpenAI-format body (`model`, `messages`,
`max_tokens` (default 4096), `temperature`, `tools`, `response_format`,
`reasoning`, `models`, …). `stream` must be `false` (v1). Unlisted models are
priced at frontier-high fallback rates ($25/M in, $150/M out) — pick from
`/api/v1/models`.

## Pricing

Both routes price BEFORE the call; the 402 quotes the exact amount — decline
it if it exceeds your budget, nothing is charged until you sign.

**Fixed**: `price = input_estimate x in_rate + (max_tokens + reasoning_reserve)
x out_rate (+ rare per-request fee)`. Input ≈ chars/4 x1.25 (non-ASCII at UTF-8
byte length); tools/tool_calls JSON counts. You pay this even if the reply is
short — keep `max_tokens` tight. Per-model ceiling = price of a 200k-in +
16,384-out request at the model's own rates, clamped to [$0.10, $5]; estimates
above it are REJECTED (400, never clamped) → lower `max_tokens` or go metered.

**Metered**: escrow a hard worst-case reservation (ceiling $5), settle actuals,
refund the rest — your SDK handles escrow → retry-with-credential → close.
Reservation = UTF-8 byte length of `messages`+`tools` as input tokens (bounds
any tokenizer) + `(max_tokens + reasoning_reserve)` output + per-request fee.
Typically several times actual cost; safe (refunded) but your wallet must HOLD
it during the call. Settlement never exceeds it. With a `models` fallback list,
reservation prices the priciest candidate; settlement uses the model that
served. `route: "fallback"` without an explicit `models` list is rejected.

**Reasoning** (`reasoning` object, forwarded verbatim; bills as output):
- `{ max_tokens: N }` → reserves exactly N. Preferred — cheapest escrow.
- effort-only / `{enabled: true}` / `{exclude: true}` → reserves the worst
  case, `max(max_tokens, 32768)` — expensive escrow; set an explicit budget.

## The `billing` block (every paid response)

```json
"billing": { "promptTokens": 812, "completionTokens": 344,
  "reservedUsdcMicro": "412000",  // committed before the call
  "settledUsdcMicro": "9873",     // what you were CHARGED
  "actualUsdcMicro": "9873",      // best-known true cost
  "costBasis": "openrouter" }     // source of actualUsdcMicro
```

Metered: settled == actual. Fixed: settled == reserved (actual is
informational — use it to calibrate `max_tokens`). `costBasis`: `openrouter`
(provider-billed, authoritative) > `rates` > `estimated`. Micro-USDC strings
(1_000_000 = $1); successful calls settle ≥ $0.001.

## Failure settlement (metered)

| Failure | You pay |
| --- | --- |
| Upstream 4xx/5xx, pre-connect network error | $0 (escrow refunded) |
| Parseable 200 error body (`{"error":...}`) | only usage-block-evidenced work; $0 if none |
| **You time out / disconnect, or 200 body unreadable** | **the FULL reservation** |

OpenRouter bills abandoned non-streaming generations, hence the last row. **Set
your client timeout above this capability's 120s upstream timeout** (130s+).

## Cost checklist

Tight `max_tokens` (dominant price term) · explicit `reasoning.max_tokens` ·
lean `messages` on metered (byte-priced escrow) · listed models only · decline
402s over budget · fixed for small, metered for big or pay-actual.
