Docs
Ostraka is an OpenAI-compatible API for hundreds of models. Any client that speaks Chat Completions works with a base URL and a key.
- Open the app and sign in with an Ethereum wallet. Signing in is a free message signature.
- Add funds: deposit USDG on Robinhood Chain into the vault.
- Under API keys, create a key with a spending limit. The key is shown once.
- Call
https://ostraka.xyz/api/v1withAuthorization: Bearer osk_….
curl https://ostraka.xyz/api/v1/chat/completions \
-H "Authorization: Bearer $OSTRAKA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek/deepseek-chat",
"stream": true,
"messages": [{"role": "user", "content": "Write a haiku about receipts."}]
}'
No wallet yet? POST /api/demo/keys returns a demo key: 24 hours, a small limit, a few low-cost models, up to 1,024 output tokens per request, three per visitor per day.
Keys and limits
- Every key has a lifetime spending limit, set when you create it. When a request would go past it, the API answers
402 key_limit_reached. - A key can be limited to a list of models. Other models answer
403 model_not_allowed. - Keys can expire after 1 to 90 days, and you can revoke one at any time. Revoking takes effect within seconds.
- Each key handles up to 120 requests a minute.
- We store only a hash of each key. If you lose one, revoke it and create another.
GET /api/v1/key returns the calling key's limit, what it has spent, what's left, and the account's available balance. Agents can use it to check their budget.
Models and prices
GET /api/v1/models is public and uses the OpenAI list format. Each model's pricing shows both prices in USD per million tokens:
{
"id": "anthropic/claude-sonnet-4.5",
"context_length": 200000,
"capabilities": { "tools": true, "reasoning": true, "vision": true },
"pricing": {
"unit": "usd_per_million_tokens",
"prompt": 3.15, "completion": 15.75,
"upstream_prompt": 3, "upstream_completion": 15,
"markup_bps": 500
}
}
When the provider reports the exact cost of a request, we charge that cost plus the markup. Otherwise we charge the tokens used times the listed price. Either way, the receipt records both our charge and the provider's cost.
Chat completions
POST /api/v1/chat/completions follows the OpenAI Chat Completions format: messages, stream, tools, tool_choice, response_format, temperature, max_tokens or max_completion_tokens, and the model's other parameters pass through.
- Before a request runs, we hold its largest possible cost against your balance and key. If your balance covers less output than you asked for,
max_tokensis lowered to what it covers, and thex-ostraka-max-tokensheader says what was used. - If the provider fails, nothing is charged.
- If you stop reading a stream, the request is cancelled and you pay only for what was delivered.
nabove 1 isn't supported.
Non-streaming answers include an ostraka object:
"ostraka": {
"receipt": "0x3f1c…",
"cost_usd": 0.004213,
"upstream_cost_usd": 0.004012,
"verify": "https://ostraka.xyz/verify#0x3f1c…"
}
Receipts
Every paid request gets a receipt. Its id is in the x-ostraka-receipt response header, streaming or not. GET /api/v1/receipts/{id} returns:
{
"receipt": {
"v": 1, "id": "0x…", "account": "0x…", "model": "deepseek/deepseek-chat",
"in": 412, "out": 188, "cost": 251, "upstream": 239, "ts": 1793102400,
"leaf": "0x…", "signature": "0x…", "signer": "0x…"
},
"status": "settled",
"epoch": { "n": 41, "root": "0x…", "tx": "0x…" },
"proof": ["0x…", "0x…"],
"vault": "0x…"
}
cost and upstream are in USDG base units: millionths of a dollar. A receipt never contains your prompt or the answer.
Verifying a receipt
The verify page does all of this in your browser. To do it yourself:
- Leaf.
keccak256(keccak256(abi.encode(bytes32 id, address account, bytes32 keccak256(model), uint64 in, uint64 out, uint128 cost, uint128 upstream, uint64 ts)))must equalleaf. - Signature. Recover the signer of
signatureas an EIP-191 personal signature over the 32 leaf bytes. It must be the receipt signer published on the ledger and in/llms.txt. - Inclusion. Hash the leaf up the
proof, sorting each pair (OpenZeppelinMerkleProofrules). The result is the epoch root. - On chain.
vault.roots(epoch)on Robinhood Chain must return that root.
// with viem const ok = await recoverMessageAddress({ message: { raw: r.leaf }, signature: r.signature }) === SIGNER; const root = await client.readContract({ address: vault, abi, functionName: "roots", args: [BigInt(epoch.n)] });
The settlement charges each account the sum of its receipts in the epoch, so your receipts add up to your charge.
Balance and settlement
- Your balance is held by the vault contract, in USDG on Robinhood Chain.
- Requests are metered as they happen. At most once per the vault's minimum gap (an hour at launch), the gateway closes an epoch and settles it: one transaction with each account's total and the epoch's Merkle root.
- The vault never charges an account more than it holds, or more than its per-settlement cap.
- Withdrawing: request an amount, wait the withdrawal delay (24 hours at launch), then collect. You get the amount, or everything still withdrawable if you spent some in the meantime. Nothing can pause withdrawals.
- Any change that would raise what the operator can charge (a new operator, a higher cap, a shorter gap, a longer delay) and any change to the credits share waits 8 days on chain.
Holder credits
A fixed share of settled revenue (shown on the ledger) goes to the credits pool when anyone calls sweep(). Each round:
- We take a snapshot of token balances at a block.
- Holders above the minimum get a share of the round in proportion to their balance, capped per wallet.
- The round's Merkle root is posted on chain. Holders claim in the app until the deadline. Unclaimed amounts return to the pool for later rounds.
Claimed credits are added to your vault balance and are spent before your cash. Credits are for inference only and can't be withdrawn. The pool contract has no other way to pay out.
Coding agents
Any tool that accepts an OpenAI-compatible provider works. Use base URL https://ostraka.xyz/api/v1, your key, and a model id from the catalog.
Cline, Roo Code, Kilo Code
API Provider: OpenAI Compatible. Base URL as above, API key, Model ID from the catalog.
Continue
models:
- name: Ostraka
provider: openai
model: deepseek/deepseek-chat
apiBase: https://ostraka.xyz/api/v1
apiKey: osk_…
Aider
export OPENAI_API_BASE=https://ostraka.xyz/api/v1 export OPENAI_API_KEY=osk_… aider --model openai/deepseek/deepseek-chat
OpenAI SDK (Node)
import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://ostraka.xyz/api/v1", apiKey: process.env.OSTRAKA_API_KEY });
Errors
Errors use the OpenAI envelope, { "error": { "message", "type", "param", "code" } }.
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_value, invalid_json, unsupported_parameter | The request is malformed or asks for something unsupported. |
| 401 | invalid_api_key, key_expired | The key is missing, unknown, revoked or expired. |
| 402 | insufficient_balance | Your balance doesn't cover the request. Add funds. |
| 402 | key_limit_reached | The key reached its spending limit. |
| 403 | model_not_allowed | The key isn't allowed to use that model. |
| 404 | model_not_found | No model with that id. See GET /api/v1/models. |
| 429 | rate_limit_exceeded, upstream_rate_limited | Too many requests on this key, or the provider is busy. |
| 502, 503 | upstream_unreachable, upstream_unavailable | The provider failed. Nothing was charged. |
Privacy
- Prompts and answers pass through Ostraka and are never stored or logged.
- The provider that runs the model reads your prompt in order to answer. We ask the router to use only providers that don't store or train on prompts. The provider's own policy still applies.
- We store your wallet address, your keys (hashed), and receipts: model, token counts, charge, provider cost, time.
- Receipts go on chain only as part of a Merkle root, which reveals nothing about any single request.
Network and contracts
Robinhood Chain mainnet (chain ID 4663). Balances are in USDG. Contract addresses and the receipt signer are on the ledger, and the source is verified on the explorer.
- OstrakaVault holds balances, takes settlements and roots, pays withdrawals, and sweeps revenue.
- CreditsPool holds the credits share and pays it out as credits, round by round.