API reference
One OpenAI-compatible API for every text, image and video model, paid in crypto on Robinhood Chain. Base URL:
https://omnirail.org/api/v1
Quickstart
- Humans: open the dashboard, connect a wallet, top up with USDG or ETH, create a key.
- Agents: read /agents.md. It installs the MCP server and picks a payment mode.
- No account at all: pay each call with x402.
curl https://omnirail.org/api/v1/chat/completions \
-H "Authorization: Bearer $OMNIRAIL_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"anthropic/claude-opus-5.5","messages":[{"role":"user","content":"Hello"}],"max_tokens":300}'Authentication
Send Authorization: Bearer orl_…. Keys belong to the wallet that created them, are shown once, stored hashed, and can carry a cumulative spend limit. Revoking is immediate. Without a key, paid endpoints answer 402 with an x402 price when x402 is enabled, otherwise 401.
Pricing & holds
You pay the provider's reported cost plus the markup (15% by default, lower while the $RAIL promotion is funded). Before a call runs we reserve its worst case (input estimate + max_tokens, or the image/video estimate). After the call we charge the real cost and release the rest. In the rare case the provider bills more than the estimate (a price change mid-call, extra searches), the difference comes out of your free balance, never below zero.
- If you omit
max_tokens, the gateway sets it to the largest completion your balance covers (up to 8192). - Provider errors, filtered images and failed videos cost nothing.
- A non-streamed call still running after 5 minutes is cut and charged its reserved maximum, because the provider may already have billed it. Use
stream: truefor long generations: a stream stopped early is charged only for what was generated. - Every JSON response has
usage.cost(USD charged) andusage.promo_discount. Headers:X-OmniRail-Charge,X-Request-Id.
Chat completions
POST /chat/completions, OpenAI format. Supports stream, tools, tool_choice, response_format, reasoning, images in messages, and the usual sampling parameters. Not supported: n > 1, plugins, web search, model fallback lists.
import OpenAI from "openai";
const ai = new OpenAI({ baseURL: "https://omnirail.org/api/v1", apiKey: process.env.OMNIRAIL_KEY });
const stream = await ai.chat.completions.create({
model: "google/gemini-3.8-flash",
messages: [{ role: "user", content: "Summarise this repo" }],
stream: true,
});
for await (const chunk of stream) process.stdout.write(chunk.choices[0]?.delta?.content ?? "");In streams, the final chunk carries usage with the amount charged.
Images & edits
POST /images (alias /images/generations) generates; POST /images/edits requires at least one source image. Images come back as b64_json.
| Field | Notes |
|---|---|
model, prompt | Required. Pick an image model from /models?kind=image. |
n | 1 to 4, capped by the model. |
aspect_ratio, resolution, quality, background, output_format, seed | Validated against what the model supports; unsupported values return 400 with the allowed list. |
input_references / image | http(s) or data:image/… URLs, up to the model's limit. |
curl https://omnirail.org/api/v1/images/edits -H "Authorization: Bearer $OMNIRAIL_KEY" -H "Content-Type: application/json" \
-d '{"model":"openai/gpt-image-2","prompt":"make the background lime","image":"https://example.com/logo.png"}'Videos
Asynchronous. POST /videos returns 202 with id, token and poll_url. Poll GET /videos/{id} (with your key, or ?token=) every 30 seconds or so. When status is completed the real cost is charged and content_urls stream the MP4. Failed jobs are not charged.
{ "model": "google/veo-3.1-fast", "prompt": "a lime switchboard, slow dolly in",
"duration": 4, "resolution": "720p", "aspect_ratio": "16:9", "generate_audio": false,
"first_frame": "https://…/frame.png" }Models & balance
GET /models is public: ?kind=text|image|video, ?q=, ?limit=. Prices include the current markup. GET /balance returns the key's wallet balance, holds and live markup. GET https://omnirail.org/api/status lists the chain, payment asset, treasury and whether x402 is on.
x402 payments
Every paid endpoint speaks x402 v2. Call it without a key: the response is 402 with a PAYMENT-REQUIRED header asking for USDG on Robinhood Chain. Sign it with any x402 v2 client and retry with PAYMENT-SIGNATURE. We verify the EIP-3009 authorization, settle it on-chain ourselves (no third-party facilitator, no gas for you), then run the call. The unused part of the hold stays as balance on the paying wallet: sign in with that wallet on the dashboard to see or use it.
| Scheme | exact (EIP-3009 transferWithAuthorization) |
| Network | eip155:4663 |
| Asset | USDG 0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168, 6 decimals |
| EIP-712 domain | name Global Dollar, version 1 |
Official clients only sign known assets by default, so allow USDG explicitly and cap each payment:
import { wrapFetchWithPayment, x402Client } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";
const client = new x402Client()
.register("eip155:4663", new ExactEvmScheme(privateKeyToAccount(process.env.AGENT_KEY)))
.setSpendControls({ allowedAssets: [{ network: "eip155:4663", asset: "0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168", maxAmountPerPayment: "2000000" }] });
const pay = wrapFetchWithPayment(fetch, client);Buying credit in bulk
POST /credits?amount=5 charges exactly that many USDG through x402 and credits the paying wallet. The first time (no Authorization header) it also returns a new API key bound to that wallet; send the key on later top-ups to credit the same account. This is what the local MCP server does: fewer on-chain transactions, exact per-call metering.
MCP server
Tools: list_models, chat, generate_image, edit_image, generate_video, get_video, account_balance.
Remote (Streamable HTTP, key)
claude mcp add --transport http omnirail https://omnirail.org/api/mcp --header "Authorization: Bearer orl_YOUR_KEY"
{
"mcpServers": {
"omnirail": {
"url": "https://omnirail.org/api/mcp",
"headers": {
"Authorization": "Bearer orl_YOUR_KEY"
}
}
}
}Local (npx, key or wallet)
Adds file paths: save generated images and videos with save_to, pass local images to edits. With OMNIRAIL_WALLET_KEY it buys credit itself via x402, capped by OMNIRAIL_TOPUP_USD (default 5) and OMNIRAIL_DAILY_BUDGET_USD (default 20). The minted key is kept in ~/.config/omnirail/ with owner-only permissions. Use a dedicated wallet.
claude mcp add omnirail -e OMNIRAIL_WALLET_KEY=0xAGENT_WALLET_PRIVATE_KEY -e OMNIRAIL_DAILY_BUDGET_USD=20 -- npx -y https://omnirail.org/downloads/omnirail-mcp.tgz
Wallet deposits
On the dashboard: connect, sign in (a signature, not a transaction), choose USDG or ETH and an amount. The site sends a plain transfer to the treasury and claims it automatically after 2 confirmations. USDG is credited 1:1; ETH at the market price of the block's minute. A transaction can only be claimed by the wallet that sent it, and only once. If the page closed mid-way, paste the transaction hash to claim it.
Errors & limits
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_request, unsupported_parameter | Body or parameter rejected; the message says what is allowed. |
| 401 | invalid_api_key | Missing, malformed or revoked key. |
| 402 | insufficient_balance or x402 challenge | Top up, lower max_tokens, or pay via x402. |
| 404 | not_found | Unknown model or job. |
| 409 | conflict, tx_confirming | Payment already used, deposit already credited, or still confirming. |
| 429 | rate_limited | 120 requests/minute per key (per IP without a key). Respect Retry-After. |
| 502/504 | upstream_error | The provider failed or timed out. The hold is released. |
Request bodies are limited to 4 MB (use image URLs rather than large data URLs). Streams and generations may run up to 5 minutes.