Getting started

Authentication

Every API request sends a key as a Bearer token. Keys start with rw_live_, belong to your account and can be limited by scope, IP address, request rate, daily and monthly credits, and expiry date.

Header
Authorization: Bearer rw_live_…
Format
rw_live_ + 64 hex characters
New keys
scopes read + chat · 600/min
Per account
up to 20 active keys

API Keys

Create keys under API keys. The full key is shown once, when you create it; Railwail stores only a hash, so copy it right away. There is one kind of key: every request is real and billed (there are no test keys). For testing, use a separate key with narrow scopes, a low max_tokens and a monthly cap under Spend limits.

Keep your key secret

Never put a key in client-side code, a public repository or a frontend bundle: anyone with it spends your balance. Read it from an environment variable on your server. If a key leaks, revoke or rotate it at once.

Store the key

All examples in these docs read RAILWAIL_API_KEY from the environment:

export RAILWAIL_API_KEY="rw_live_..."

Test your key (free)

GET /models?limit=1
curl -s "https://railwail.com/api/v1/models?limit=1" \
  -H "Authorization: Bearer $RAILWAIL_API_KEY"
AnswerMeaning
200 + one modelThe key works and has the read scope (every new key does). No credits used.
401 invalid_api_keyMissing, mistyped, revoked or expired key. The message is exactly "Invalid API key".
403 ip_not_allowedThe key has an IP allowlist; the message names the address the API saw.
403 insufficient_scopeThe key was created without read. It can still work for its other scopes.

Scopes

A key only reaches the routes of its scopes. New keys get read and chat: for images, video, audio or embeddings tick those scopes (or all) when you create the key, otherwise the answer is 403 insufficient_scope with the message "This API key lacks the 'images' scope". Scopes cannot be changed on an existing key.

ScopeRoutes (under /api/v1)New key
readGET /models and /models/{slug} when you send a keyincluded
chatPOST /chat/completionsincluded
imagesPOST /images/generationstick it
videoPOST /videos/generationstick it
audioPOST /audio/speech, POST /audio/transcriptions, POST /audio/uploadstick it
embeddingsPOST /embeddingstick it
jobsGET /jobs/{id} for any job (without it: jobs of a kind the key may create)tick it
fine_tuningPOST/GET /fine_tuning/jobs, /fine_tuning/jobs/{id} and its cancel, events and checkpointstick it
allevery route abovetick it

IP allowlist

  • Optional, up to 50 entries: single IPv4 or IPv6 addresses or CIDR blocks (203.0.113.7, 198.51.100.0/24, 2001:db8::/48).
  • An empty list allows every address. A key with a list refuses everything else, and also a request whose address cannot be determined.
  • The address checked is the one your request comes from as Cloudflare sees it. If your server connects over IPv6, list its IPv6 address or range too; the 403 ip_not_allowed message tells you which address arrived.

Rate limit, credit limits and expiry

Requests per minute

60–6,000 per key, default 600. Fixed at creation. Rate limits

Daily and monthly credits

Optional per key, set and changed any time under Limits & alerts on the API keys page; counted per UTC day and calendar month. Over the limit a run answers 429 api_key_limit_exceeded with X-Key-Limit-Reset and nothing is charged. Fine-tuning jobs do not count against it (yet). Credit limits

Expiry

None, 30 days, 90 days, 1 year or a custom date. After it, the key answers 401 invalid_api_key.

Revoke

Stops the key immediately. Requests with it answer 401.

Rotate

Creates a new key with the same name, scopes, allowlist, rate limit, credit limits and expiry, and deactivates the old one at the same moment: deploy the new key right after rotating. This month's usage moves to the new key, so its credit limits keep counting.

Using the SDK

Pass the key when you create the client of the railwail npm SDK; it sends it with every request.

client.mts
import railwail from "railwail";

// Recommended: read the key from the environment (the SDK does not do it for you).
const rw = railwail(process.env.RAILWAIL_API_KEY!);
client-options.mts
import railwail from "railwail";

// With options: a longer timeout for long answers (default 120 s).
const rw = railwail(process.env.RAILWAIL_API_KEY!, { timeout: 300_000 });

Using the REST API

For direct HTTP requests, send the key as a Bearer token in the Authorization header:

Authorization: Bearer $RAILWAIL_API_KEY
const response = await fetch("https://railwail.com/api/v1/chat/completions", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.RAILWAIL_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    model: "gpt-4o-mini",
    max_tokens: 300,
    messages: [{ role: "user", content: "Hello!" }],
  }),
});

const data = await response.json();
console.log(data.choices?.[0]?.message.content ?? data.error);

With the OpenAI SDK

The chat, image and audio endpoints follow OpenAI's API, so the official SDKs work with the Railwail base URL and key. What works and what does not

import OpenAI from "openai";

const client = new OpenAI({
  apiKey: process.env.RAILWAIL_API_KEY,
  baseURL: "https://railwail.com/api/v1",
});

Without a key

Two things work without any key and cost nothing:

  • The model catalog: GET https://railwail.com/api/v1/models and /models/{slug} (with a key they need read and count toward its limit). Models API
  • The read-only tools of the MCP server at https://railwail.com/api/mcp (catalog, prices, docs; 30 requests per minute per IP). MCP server
Authentication — Railwail Docs