Skip to contentx402 is live on Robinhood Chain: agents can now pay for any model, per call, in USDG →
OmniRail

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: true for long generations: a stream stopped early is charged only for what was generated.
  • Every JSON response has usage.cost (USD charged) and usage.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.

FieldNotes
model, promptRequired. Pick an image model from /models?kind=image.
n1 to 4, capped by the model.
aspect_ratio, resolution, quality, background, output_format, seedValidated against what the model supports; unsupported values return 400 with the allowed list.
input_references / imagehttp(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.

Schemeexact (EIP-3009 transferWithAuthorization)
Networkeip155:4663
AssetUSDG 0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168, 6 decimals
EIP-712 domainname 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

StatusCodeMeaning
400invalid_request, unsupported_parameterBody or parameter rejected; the message says what is allowed.
401invalid_api_keyMissing, malformed or revoked key.
402insufficient_balance or x402 challengeTop up, lower max_tokens, or pay via x402.
404not_foundUnknown model or job.
409conflict, tx_confirmingPayment already used, deposit already credited, or still confirming.
429rate_limited120 requests/minute per key (per IP without a key). Respect Retry-After.
502/504upstream_errorThe 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.