Developer platform

Build on Raeto credits

One OpenAI-compatible API for 400+ models, paid from credits your tokenized stocks earn. Plug it into your code, your agent over MCP, or let your users sign in with Raeto and spend their own credits.

Quick start

  1. Open the app and sign in with your wallet.
  2. Make sure you have credits. Deposit into a vault to earn them, or buy them with a card.
  3. Go to Developers, then Create key. Copy the key; it's shown once.
  4. Point any OpenAI SDK at https://api.raeto.cc/v1 with your key.
from openai import OpenAI

client = OpenAI(
    base_url="https://api.raeto.cc/v1",
    api_key="YOUR_RAETO_KEY",
)

reply = client.chat.completions.create(
    model="openai/gpt-4o-mini",
    messages=[{"role": "user", "content": "Hello"}],
)
print(reply.choices[0].message.content)
Base URL: https://api.raeto.cc/v1. The API lives on the api. subdomain; raeto.cc is the website. Opening the base URL in a browser shows a short guide; requests go to paths under it, like /v1/models and /v1/chat/completions.

Anything that speaks the OpenAI chat format works the same way: LangChain, the Vercel AI SDK, LlamaIndex, Cursor-style tools that take a custom base URL, and so on.

API keys

Create keys in the app under Developers. Give each app or environment its own key so you can see what each one spends and shut one off without touching the others.

SettingWhat it does
NameA label for you, like "Discord bot" or "staging". It shows in the activity log.
Spending limitOptional. Once the key has spent this much in credits, it's turned away with key_limit_reached until you raise or remove the limit. It's checked before each request, so a final request can finish slightly past it.
Allowed modelsOptional. A list of model ids the key may use, such as openai/gpt-4o-mini or raeto/auto. Anything else returns model_not_allowed.
PauseTurns the key off right away without deleting it. Resume it any time.
DeleteRevokes the key for good.

Each wallet can have up to 20 keys. Keys are stored only as hashes, so we can't show one again. If you lose a key, delete it and make another.

API keys can call models but can't manage your account, create other keys, or buy credits. Keep them on your server, never in a browser or app bundle.

Models

Use any model id from the list endpoint. It returns 400+ models from OpenAI, Anthropic, Google, Meta, DeepSeek, Mistral and more, with their per-token prices.

curl https://api.raeto.cc/v1/models

Auto routing

Set model to raeto/auto and our routing engine picks the cheapest model that can handle each request: quick questions go to a fast model, code to a stronger one, and hard reasoning to the top tier. That's how credits go up to 2–3x further.

Auto routing is for wallets holding at least 1,000 $RAETO. The response tells you what was picked in the x-raeto-model and x-raeto-tier headers. To make auto start from a stronger tier, send x-route-floor: mid or top.

Using your own provider account instead? See Routing only.

Routing only NEW

Already have your own OpenAI, Anthropic or OpenRouter account? Keep it. Send Raeto the request you're about to make, and we tell you which model to use. Then you call that model yourself, with your own key. Raeto never sees your provider key or the model's reply.

Each routing decision costs a flat $0.0002 from your Raeto balance ($0.20 per 1,000 requests), whatever the request's size. It works with any developer key, and there's no $RAETO requirement.

import OpenAI from "openai";
import { Raeto } from "./raeto.mjs";

const raeto = new Raeto({ apiKey: process.env.RAETO_API_KEY });
const openai = new OpenAI();                     // your own provider key

const request = { messages: [{ role: "user", content: "Summarize this ticket in one line" }] };
const pick = await raeto.route({ ...request, models: { fast: "gpt-4o-mini", mid: "gpt-4.1", top: "o3" } });

const reply = await openai.chat.completions.create({ ...request, model: pick.model });
// pick.tier = "fast", pick.reasons = ["simple request"]

Request

FieldMeaning
messagesRequired. The chat you're about to send. Also send tools or images if the real request has them; they affect the pick.
modelsOptional. Your own model name for each tier: fast, mid, top. Leave out a tier and the next stronger one you gave is used. Without models, you get Raeto's picks as OpenRouter ids.
floorOptional. mid or top to never go below that tier.
max_tokensOptional. Only used for the cost estimate.

Response

FieldMeaning
modelThe model to call.
tier, reasonsWhich tier was picked, and why (for example code or long input).
fallbacksStronger models to try, in order, if the pick fails or the answer isn't good enough.
estimateOnly with Raeto's model list: estimated cost of this request, the top model's cost and the percent saved.
fee_usd, balance_usdWhat this decision cost, and your balance after it.

Routing decisions show up in Developers → Activity as route → model. Key spending limits apply to them too.

Overview, savings calculator and pricing: raeto.cc/routing.

Requests

POST /v1/chat/completions takes the standard OpenAI body. Streaming ("stream": true), tools, JSON mode, images and other provider options pass straight through to the model.

Response headers

HeaderMeaning
x-raeto-modelThe model that answered.
x-raeto-tierfast, mid or top. Only when auto routing chose the model.
x-raeto-costWhat this request took from your credits, in dollars. Non-streaming requests only; streamed requests show their cost in the activity log.

Who am I

GET /v1/me returns what the key can still spend (available_usd), plus its name and limit. Called with a Sign in with Raeto token, it returns the user's user_id for your app instead of their wallet.

SDKs

The OpenAI SDKs above work as they are. If you'd rather have something smaller, our clients for JavaScript and Python have no dependencies, and they include helpers for Sign in with Raeto. Download one file and drop it into your project:

import { Raeto } from "./raeto.mjs";

const raeto = new Raeto({ apiKey: process.env.RAETO_API_KEY });

console.log(await raeto.ask("Explain Morpho in one line", { model: "openai/gpt-4o-mini" }));
console.log(raeto.last);            // { model, tier, costUsd }

for await (const t of raeto.stream({ messages: [{ role: "user", content: "Count to 5" }] }))
  process.stdout.write(t);

const me = await raeto.me();        // { available_usd, ... }

Both raise RaetoError with status and type from the error table. The JavaScript client runs in Node 18+, Deno, Bun and browsers; the Python one needs 3.8+.

Agents (MCP)

Raeto runs a remote MCP server, so Claude, Cursor and other agents can call any model with your credits: for a second opinion, to hand off to a cheaper model, or to reach one they don't have.

SettingValue
URLhttps://api.raeto.cc/mcp
TransportStreamable HTTP
AuthHeader Authorization: Bearer YOUR_RAETO_KEY
claude mcp add --transport http raeto https://api.raeto.cc/mcp \
  --header "Authorization: Bearer YOUR_RAETO_KEY"

Tools

ToolWhat it does
ask_modelSends a prompt (and optional system message) to a model and returns the reply, with the model used and what it cost. Leave model empty or use raeto/auto for auto routing.
list_modelsSearches models by name, with prices per million tokens.
get_balanceHow many credits the key can still spend.
Give your agent its own key with a spending limit. Every call it makes shows up in your activity log under that key's name.

Sign in with Raeto

Add a "Sign in with Raeto" button to your app. Your users approve it once with their wallet, choose how much your app may spend, and from then on your app calls models with their credits. You never handle API keys or payments for them.

It's standard OAuth 2.0 (authorization code with PKCE), so most OAuth libraries work.

  1. In the app, go to Developers → Sign in with Raeto → Create an app. Add your redirect URL. Server apps get a client secret; browser and mobile apps use PKCE only.
  2. Send the user to https://raeto.xyz/authorize with response_type=code, client_id, redirect_uri, state, code_challenge and code_challenge_method=S256.
  3. They come back to your redirect URL with ?code=…&state=…. Check that state matches.
  4. Swap the code at POST https://api.raeto.cc/oauth/token for an access token, then use it exactly like an API key.
import { Raeto, createPkce, authorizeUrl, exchangeCode } from "./raeto.mjs";

app.get("/login", async (req, res) => {
  const { verifier, challenge } = await createPkce();
  req.session.verifier = verifier;
  req.session.state = crypto.randomUUID();
  res.redirect(authorizeUrl({ clientId: CLIENT_ID, redirectUri: REDIRECT_URI, state: req.session.state, codeChallenge: challenge }));
});

app.get("/callback", async (req, res) => {
  if (req.query.state !== req.session.state) return res.status(400).send("Bad state");
  if (req.query.error) return res.send("Sign-in cancelled");
  const t = await exchangeCode({ clientId: CLIENT_ID, clientSecret: CLIENT_SECRET, code: req.query.code,
                                 codeVerifier: req.session.verifier, redirectUri: REDIRECT_URI });
  req.session.raeto = t.access_token;   // keep it server-side
  const raeto = new Raeto({ apiKey: t.access_token });
  res.send(await raeto.ask("Write a welcome line for " + t.user_id));
});

What your app gets

  • An access token that works on /v1/chat/completions, /v1/models, /v1/me and the MCP server, up to the limit the user chose.
  • A user_id that stays the same for that user in your app. It's different in every app, and apps never see the user's wallet address.

What users control

  • They set your app's spending limit when they sign in, and can change it later.
  • They can disconnect your app any time under Developers → Connected apps. The token stops working at once.
  • Signing in again replaces the old token.

Rules

  • PKCE with S256 is required for every app. Redirect URLs must match exactly and use https:// (or http://localhost while testing).
  • Codes last 10 minutes and work once. If a code is used twice, the token it produced is revoked.
  • Keep client secrets and access tokens on your server. Deleting an app signs every user out of it.
  • Token errors use OAuth's shape: {"error": "invalid_grant", "error_description": "…"}.

Billing

Every request is paid from your Raeto credit balance. You're charged for the tokens the model actually used, at the provider's price plus Raeto's service fee. Routing-only decisions cost a flat fee each instead, since no model runs on our side. Every charge shows up in Developers → Activity, with tokens in and out, cost, time taken, and which key made it.

Credits come from interest your vault deposits earn, credits you buy with USDG or a card, and rCREDIT you use. When your balance runs out, requests return insufficient_balance until you top up.

Errors

Errors use the OpenAI shape, {"error": {"message", "type", "code"}}, so existing SDK error handling keeps working. Check type to tell them apart.

StatusTypeWhat to do
401authentication_errorThe key is wrong or was deleted.
401key_disabledThe key is paused. Resume it in the app.
402insufficient_balanceOut of credits. Deposit or buy more.
402key_limit_reachedThis key hit its spending limit. Raise or remove it.
403model_not_allowedThe key isn't allowed to use that model.
403auto_routing_lockedAuto routing needs 1,000 $RAETO in the key's wallet. Name a model instead.
429rate_limit_errorToo many requests. Back off and retry.
502upstream_errorThe model provider didn't answer. Retry, or try another model. You aren't charged.

Rate limits

Each wallet can make up to 120 requests a minute across all its keys. Need more? Reach out on X at @RaetoCC.