Quickstart

Flow AI is an OpenAI-compatible API. Point any OpenAI SDK at our base URL and use your Flow AI key — every request is routed to the cheapest model that can do the job.

python
from openai import OpenAI

client = OpenAI(
    base_url="https://api.flowaiapi.com/v1",
    api_key="fa-...",          # from your dashboard
)

r = client.chat.completions.create(
    model="flow-1",            # Flow's model — routes to the cheapest capable supply for you
    messages=[{"role": "user", "content": "Summarize this ticket: ..."}],
)
print(r.choices[0].message.content)
print(r.model_dump()["_flowaiapi"])   # which model served it, price, your cost
curl
curl https://api.flowaiapi.com/v1/chat/completions \
  -H "Authorization: Bearer fa-..." \
  -H "Content-Type: application/json" \
  -d '{"model":"auto","messages":[{"role":"user","content":"hello"}]}'

Authentication

Pass your API key as a bearer token: Authorization: Bearer fa-.... Create and manage keys (and per-key budgets) in the dashboard. A key is shown in full only once at creation — store it securely. New accounts get a 7-day free trial; after that an active membership ($4.99/mo or $45/yr) is required to make requests.

Chat completions

POST /v1/chat/completions — accepts the standard OpenAI request body (messages, max_tokens, temperature, tools, response_format, stream, …) and returns the standard OpenAI response shape plus a _flowaiapi block. Both streaming (SSE) and non-streaming are supported.

Flow AI adds two optional fields:

FieldValuesMeaning
route_strategyauto · cascade · cheapest · balanced · fastest · <model_id>Default auto runs the cascade: serve the cheapest capable model, judge the output, and escalate to a stronger model only if it fails. cheapest is single-shot. A concrete model id pins it.
task_hintfree string (e.g. code)Helps the classifier choose a capable model.
priority / X-FlowAI-Priority headerlatencyThe fast lane for synchronous, user-facing calls: routes to the fastest-decode models and never to slow/free tiers. Big structured outputs (response_format: json_object + max_tokens ≥ 1000) get the fast lane automatically.
imagesimage_url partsBase64 data URIs recommended. Remote http(s) URLs are auto-fetched by the gateway (8s timeout, 5MB cap) and inlined; a failed fetch returns a clear 400.

Routing

On each request Flow AI classifies the task with cheap, non-LLM heuristics (length, code, tool/JSON schema, multimodal parts), then picks the cheapest model whose capabilities cover the task — looked up against the live per-minute price. So a vision prompt goes to a multimodal model, a long-context prompt to a 1M-context model, and plain text to the cheapest capable one. The decision is cached by system prompt, so agent traffic pays near-zero routing overhead.

Cascade (default). Rather than guess upfront, auto serves the cheapest capable model, runs a fast judge over the output (empty / refusal / malformed-JSON checks), and escalates to a stronger model only if it fails — you pay cheap by default and only pay up when the task actually needs it. Rejected cheap attempts aren't charged. The _flowaiapi.cascade array shows each tier tried. If a provider errors, it also fails over to the next capable one (failover: true).

Response metadata

Every non-streamed response includes a _flowaiapi object so the route is fully transparent:

_flowaiapi
"_flowaiapi": {
  "seller_id": "api:deepseek-v4-flash",
  "model_used": "deepseek-v4-flash",
  "input_tokens": 412, "output_tokens": 188,
  "clearing_price_per_1k_in": 0.00014,
  "clearing_price_per_1k_out": 0.00028,
  "cost_usd": 0.0000934,
  "buyer_charge_usd": 0.0000962,
  "interval_id": 29695141,
  "routing_reason": "auto__cheapest_capable",
  "latency_ms": 885,
  "failover": false,
  "budget_alerts": []
}

You pay cost_usd × 1.025 (buyer_charge_usd) — the model's pass-through cost plus a flat 2.5% spread. The commercial price is always the ceiling.

Models

GET /v1/models lists the currently available models. You can pin one by passing its id as model or route_strategy, but auto is recommended — it tracks price changes for you. For strict, guaranteed pinning (experiments, model panels) see Panel Mode.

Panel Mode — pinned models

Routing by default, any model on demand. Flow AI's default product is smart routing — flow-1 picks the cheapest capable model for every request. But some workloads need a specific model, or a panel of genuinely different ones: judge ensembles, consensus scoring, cross-model evals, A/B model tests, reproducible research. Panel Mode is strict, opt-in pinning for exactly those cases.

Enable it per key in the dashboard (Keys → "Panel Mode"). It's off by default — leave agent keys off, since routing is what saves you money.

The contract

GuaranteeWhat it means
Exact serveA pinned request is answered by exactly the model you pinned. No cascade, no failover.
Truthful echo_flowaiapi.served_model is the real upstream model string (e.g. claude-sonnet-4-5-20250929). Never a silent substitution.
Fail loudUnknown model → 400. Pinned model can't serve → 503 model_unavailable. Never a fallback answer from a different model.
Fresh callsPinned requests bypass all response caching — each call really hits the model, so experiments reproduce.
Real-rate billingEach pinned request bills at that model's actual rate. A 5-model panel bills 5 distinct rates.

Pin a single model

Prefix the model id with pin: — or send the header X-FlowAI-Route: pinned with a plain model id. Accepts a registry id (gpt-4o-mini) or a provider-prefixed name (openai/gpt-4o-mini).

curl — pinned request
curl https://api.flowaiapi.com/v1/chat/completions \
  -H "Authorization: Bearer fa-..." -H "Content-Type: application/json" \
  -d '{"model":"pin:gpt-4o-mini","messages":[{"role":"user","content":"..."}]}'
# -> _flowaiapi: { "served_model": "gpt-4o-mini", "pinned": true, ... }

The pinnable catalog: GET /v1/models?pinnable=true — id, provider, family, context window, per-token price, capabilities.

Fan out a panel — POST /v1/panel

One prompt across up to 10 pinned models in a single call. Legs run concurrently (a 5-model panel takes about as long as its slowest model), each leg bills at its own model's rate, and a model that can't serve returns an error entry while the rest still answer.

curl — 3-model panel
curl https://api.flowaiapi.com/v1/panel \
  -H "Authorization: Bearer fa-..." -H "Content-Type: application/json" \
  -d '{"models":["gpt-4o-mini","gemini-2.5-flash","claude-sonnet-4.6"],
       "max_tokens":256, "temperature":0,
       "messages":[{"role":"user","content":"Would you cite this source? ..."}]}'
response
{
  "object": "panel.result",
  "results": [
    { "model": "gpt-4o-mini", "status": "ok", "served_model": "gpt-4o-mini",
      "message": {"role":"assistant","content":"..."}, "usage": {...},
      "cost": {"buyer_charge_usd": 3.1e-06, "rate_per_1k_in": 0.00015, "rate_per_1k_out": 0.0006},
      "latency_ms": 812 },
    { "model": "gemini-2.5-flash", "status": "ok", ... },
    { "model": "claude-sonnet-4.6", "status": "ok", ... }
  ],
  "summary": { "requested": 3, "ok": 3, "failed": 0, "total_charge_usd": 0.00012 }
}

Shared parameters (messages, max_tokens, temperature, top_p, response_format, tools, stop, seed) apply to every leg. An unknown model anywhere in the list fails the whole call with 400 — a partial panel you didn't ask for is never run. Non-streaming only. Panel Mode errors: 403 — not enabled for this key · 400 — unknown model · 503 — pinned model unavailable (never a fallback).

Quick setup for agent frameworks

Flow AI is a drop-in backend for any agent stack — Paperclip, Hermes, OpenClaw, Claude Code, Codex, and anything OpenAI- or Anthropic-compatible. You only change the base URL and the API key; pass auto as the model and Flow AI picks the cheapest capable model per request. Pick the block that matches your framework's protocol:

Anthropic protocol — Claude Code, OpenClaw, Paperclip (claude_local)

env
export ANTHROPIC_BASE_URL="https://api.flowaiapi.com"
export ANTHROPIC_AUTH_TOKEN="fa-..."     # your Flow AI key (also accepted: ANTHROPIC_API_KEY / x-api-key)
# model: pass "auto" (any Claude model name also works and auto-routes)

OpenAI Chat protocol — Hermes, LangChain, opencode, most SDKs

env
export OPENAI_BASE_URL="https://api.flowaiapi.com/v1"
export OPENAI_API_KEY="fa-..."
# model: "flow-1"   (Flow's model — routes to the cheapest capable supply; "auto" also works)

OpenAI Responses protocol — Codex CLI (v0.132+)

~/.codex/config.toml
model = "auto"
model_provider = "flowai"

[model_providers.flowai]
name = "Flow AI"
base_url = "https://api.flowaiapi.com/v1"
wire_api = "responses"
env_key = "FLOWAI_KEY"        # then: export FLOWAI_KEY=fa-...
FrameworkUse the blockEndpoint
Paperclip — claude_localAnthropic/v1/messages
Paperclip — opencode / OpenAI adapterOpenAI Chat/v1/chat/completions
Paperclip — codex_localResponses/v1/responses
HermesOpenAI Chat/v1/chat/completions
OpenClawAnthropic/v1/messages
Claude CodeAnthropic/v1/messages
Codex CLIResponses/v1/responses

All three protocols support full tool-calling, streaming, and multi-turn loops, so agents do real work — see below. An active membership + balance are required (top up in the dashboard).

Tool calling & agents

Flow AI speaks all three major agent protocols with full tool-calling parity — point your framework at the matching endpoint and Flow's routing, failover, ledger, and tool handling all apply. Unknown model ids auto-route to the cheapest capable model.

FrameworkEndpointAuth header
OpenAI SDK / LangChain / etc.POST /v1/chat/completionsAuthorization: Bearer fa-…
Claude Code · Claude Agent SDKPOST /v1/messagesx-api-key: fa-…
Codex CLI (v0.132+)POST /v1/responsesAuthorization: Bearer fa-…

All three accept tools, return tool calls, and accept tool results on follow-up turns, in both streaming and non-streaming modes — so multi-step agents complete real work, not just text.

Anthropic Messages

Anthropic-style clients use POST /v1/messages with the key as an x-api-key header. Full tool support: Anthropic tools (input_schema) and tool_choice are translated to the router's function-calling; responses return tool_use content blocks with stop_reason: "tool_use", and tool_result blocks on the next turn continue the loop. Streaming emits protocol-correct Anthropic SSE (incl. input_json_delta for tool input). Point Claude Code at it with ANTHROPIC_BASE_URL.

curl — /v1/messages with a tool
curl https://api.flowaiapi.com/v1/messages \
  -H "x-api-key: fa-..." -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{"model":"auto","max_tokens":256,
       "tools":[{"name":"get_weather","description":"Get weather",
         "input_schema":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"]}}],
       "messages":[{"role":"user","content":"Weather in Tokyo? Use the tool."}]}'
# -> stop_reason: "tool_use", content: [{"type":"tool_use","name":"get_weather","input":{"city":"Tokyo"}}]

OpenAI Responses

POST /v1/responses implements the OpenAI Responses API, which the current Codex CLI requires. It accepts input (a string or an array of message / function_call / function_call_output items), instructions, flattened tools, tool_choice, and max_output_tokens, and returns output items (message with output_text, and function_call). Streaming emits the Responses event set (response.createdoutput_item.addedoutput_text.delta / function_call_arguments.deltaresponse.completed).

curl — /v1/responses with a tool
curl https://api.flowaiapi.com/v1/responses \
  -H "Authorization: Bearer fa-..." -H "Content-Type: application/json" \
  -d '{"model":"auto","max_output_tokens":256,
       "tools":[{"type":"function","name":"get_weather","description":"Get weather",
         "parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"]}}],
       "input":"Weather in Tokyo? Use the tool."}'
# -> output: [{"type":"function_call","name":"get_weather","arguments":"{\"city\":\"Tokyo\"}"}]

For Codex, set the base URL to https://api.flowaiapi.com/v1 and your fa-… key.

Errors

StatusMeaning
401Missing or invalid API key.
402Membership inactive, or insufficient balance — top up to continue.
429Rate limit or per-key budget exceeded — back off and retry (honor Retry-After).
502All capable providers failed for this request.