Quick start
- Open the app and sign in with your wallet.
- Make sure you have credits. Deposit into a vault to earn them, or buy them with a card.
- Go to Developers, then Create key. Copy the key; it's shown once.
- Point any OpenAI SDK at
https://velastocks.online/api/v1with your key.
from openai import OpenAI
client = OpenAI(
base_url="https://velastocks.online/api/v1",
api_key="YOUR_VELA_KEY",
)
reply = client.chat.completions.create(
model="openai/gpt-4o-mini",
messages=[{"role": "user", "content": "Hello"}],
)
print(reply.choices[0].message.content)
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://velastocks.online/api/v1",
apiKey: "YOUR_VELA_KEY",
});
const reply = await client.chat.completions.create({
model: "openai/gpt-4o-mini",
messages: [{ role: "user", content: "Hello" }],
});
console.log(reply.choices[0].message.content);
curl https://velastocks.online/api/v1/chat/completions \ -H "Authorization: Bearer YOUR_VELA_KEY" \ -H "Content-Type: application/json" \ -d '{"model": "openai/gpt-4o-mini", "messages": [{"role": "user", "content": "Hello"}]}'
https://velastocks.online/api/v1. The API has its own host, separate from this 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.
| Setting | What it does |
|---|---|
| Name | A label for you, like "Discord bot" or "staging". It shows in the activity log. |
| Spending limit | Optional. 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 models | Optional. A list of model ids the key may use, such as openai/gpt-4o-mini or vela/auto. Anything else returns model_not_allowed. |
| Pause | Turns the key off right away without deleting it. Resume it any time. |
| Delete | Revokes 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.
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://velastocks.online/api/v1/modelsAuto routing
Set model to vela/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 $VELA. The response tells you what was picked in the x-vela-model and x-vela-tier headers. To make auto start from a stronger tier, send x-route-floor: mid or top.
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
| Header | Meaning |
|---|---|
x-vela-model | The model that answered. |
x-vela-tier | fast, mid or top. Only when auto routing chose the model. |
x-vela-cost | What 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 Vela 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 Vela. Download one file and drop it into your project:
import { Vela } from "./vela.mjs";
const vela = new Vela({ apiKey: process.env.VELA_API_KEY });
console.log(await vela.ask("Explain Morpho in one line", { model: "openai/gpt-4o-mini" }));
console.log(vela.last); // { model, tier, costUsd }
for await (const t of vela.stream({ messages: [{ role: "user", content: "Count to 5" }] }))
process.stdout.write(t);
const me = await vela.me(); // { available_usd, ... }
import os
from vela import Vela
vela = Vela(api_key=os.environ["VELA_API_KEY"])
print(vela.ask("Explain Morpho in one line", model="openai/gpt-4o-mini"))
print(vela.last) # {"model", "tier", "cost_usd"}
for t in vela.stream(messages=[{"role": "user", "content": "Count to 5"}]):
print(t, end="")
me = vela.me() # {"available_usd", ...}
Both raise VelaError 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)
Vela 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.
| Setting | Value |
|---|---|
| URL | https://velastocks.online/api/mcp |
| Transport | Streamable HTTP |
| Auth | Header Authorization: Bearer YOUR_VELA_KEY |
claude mcp add --transport http vela https://velastocks.online/api/mcp \ --header "Authorization: Bearer YOUR_VELA_KEY"
// ~/.cursor/mcp.json { "mcpServers": { "vela": { "url": "https://velastocks.online/api/mcp", "headers": { "Authorization": "Bearer YOUR_VELA_KEY" } } } }
// Settings → Developer → Edit Config (claude_desktop_config.json). Needs Node.js. { "mcpServers": { "vela": { "command": "npx", "args": ["-y", "mcp-remote", "https://velastocks.online/api/mcp", "--header", "Authorization:${VELA_AUTH}"], "env": { "VELA_AUTH": "Bearer YOUR_VELA_KEY" } } } }
Tools
| Tool | What it does |
|---|---|
ask_model | Sends 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 vela/auto for auto routing. |
list_models | Searches models by name, with prices per million tokens. |
get_balance | How many credits the key can still spend. |
Sign in with Vela
Add a "Sign in with Vela" 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.
- In the app, go to Developers → Sign in with Vela → Create an app. Add your redirect URL. Server apps get a client secret; browser and mobile apps use PKCE only.
- Send the user to
https://velastocks.online/authorizewithresponse_type=code,client_id,redirect_uri,state,code_challengeandcode_challenge_method=S256. - They come back to your redirect URL with
?code=…&state=…. Check thatstatematches. - Swap the code at
POST https://velastocks.online/api/oauth/tokenfor an access token, then use it exactly like an API key.
import { Vela, createPkce, authorizeUrl, exchangeCode } from "./vela.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.vela = t.access_token; // keep it server-side
const vela = new Vela({ apiKey: t.access_token });
res.send(await vela.ask("Write a welcome line for " + t.user_id));
});
from vela import Vela, create_pkce, authorize_url, exchange_code verifier, challenge = create_pkce() # store verifier + state in the session login_link = authorize_url(CLIENT_ID, REDIRECT_URI, state, challenge) # on your redirect URL: t = exchange_code(CLIENT_ID, code, verifier, REDIRECT_URI, client_secret=CLIENT_SECRET) vela = Vela(api_key=t["access_token"]) print(vela.ask("Hello"))
curl -X POST https://velastocks.online/api/oauth/token \ -d grant_type=authorization_code \ -d code=CODE_FROM_REDIRECT \ -d redirect_uri=https://yourapp.com/callback \ -d client_id=vela_app_… \ -d client_secret=rs_… \ -d code_verifier=YOUR_PKCE_VERIFIER # → {"access_token": "rat-…", "token_type": "Bearer", "user_id": "u_…", "limit_usd": 10}
What your app gets
- An access token that works on
/v1/chat/completions,/v1/models,/v1/meand the MCP server, up to the limit the user chose. - A
user_idthat 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
S256is required for every app. Redirect URLs must match exactly and usehttps://(orhttp://localhostwhile 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 Vela credit balance. You're charged for the tokens the model actually used, at the provider's price plus Vela's service fee. Every charge shows up in Developers → Activity, with tokens in and out, cost, time taken, and which key made it.
Credits come from two places: interest your vault deposits earn, and credits you buy with a card. 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.
| Status | Type | What to do |
|---|---|---|
| 401 | authentication_error | The key is wrong or was deleted. |
| 401 | key_disabled | The key is paused. Resume it in the app. |
| 402 | insufficient_balance | Out of credits. Deposit or buy more. |
| 402 | key_limit_reached | This key hit its spending limit. Raise or remove it. |
| 403 | model_not_allowed | The key isn't allowed to use that model. |
| 403 | auto_routing_locked | Auto routing needs 1,000 $VELA in the key's wallet. Name a model instead. |
| 429 | rate_limit_error | Too many requests. Back off and retry. |
| 502 | upstream_error | The 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 @VelaStocks.