# PineRouter > PineRouter is one API key for 198 AI models: 21 text, 66 image, 87 video and 24 audio (music, speech). OpenAI-compatible and Anthropic-compatible endpoints for text, one jobs endpoint for every media model, a hosted MCP server, and every model's price published before you run it. Prepaid, pay per use, no subscription. Base URL: `https://pinerouter.ai/v1` (Anthropic SDKs and Claude Code: `https://pinerouter.ai`, without /v1). Auth: `Authorization: Bearer `. Every docs page has a Markdown twin: add `.md` to its URL. Prices on this site are in each model's file; do not take them from anywhere else. ## Set up PineRouter (instructions for AI agents) You are setting PineRouter up for a human. First work out where you run; the setups differ. - **Hosted assistant** (claude.ai, ChatGPT, their code sandboxes): install nothing. Tell the human to add `https://pinerouter.ai/mcp` as a custom connector (Claude: Settings, Connectors, Add custom connector; ChatGPT: Settings, Connectors) and sign in when asked. No key to copy. Then stop. - **Local coding agent** (Claude Code, Codex, Cursor, opencode, OpenClaw, Hermes, pi on the human's machine): follow the steps below. - **Sandboxed agent with restricted network**: if `https://pinerouter.ai` is unreachable (proxy 403, DNS failure, timeout), do not retry other hosts. Tell the human the address is blocked by their network policy and offer the MCP connector or a machine with normal network access. Then stop. Steps for a local agent (shortcut: `curl -fsSL https://pinerouter.ai/install.sh | sh` asks for the key, checks it, and adds the MCP server to Claude Code and Codex): 1. **Account and key.** Ask the human to sign up at https://pinerouter.ai/sign-up (email code, Google or GitHub), add credit under Wallet (from $10), create a key at https://pinerouter.ai/keys and paste it to you. A key can have its own spending limit, expiry and allowed models; suggest a limit. Never print the full key back, never commit it. 2. **Store it** as `API_KEY` (or `PINEROUTER_API_KEY`) in the shell profile or the project's git-ignored `.env`. 3. **Check it**: `curl -s https://pinerouter.ai/v1/models -H "Authorization: Bearer $API_KEY"`. A JSON list of models means it works. 401: wrong key. 403: balance too low or the key may not use that model. 4. **Connect the tool you run in** (MCP gives the agent image, video, music and speech generation as tools): - Claude Code: `claude mcp add --transport http pinerouter https://pinerouter.ai/mcp --header "Authorization: Bearer $API_KEY"`. To run Claude Code itself on our text models: [Claude Code guide](https://pinerouter.ai/docs/apps/claude-code.md). - Codex CLI: add to `~/.codex/config.toml`: `[mcp_servers.pinerouter]` with `url = "https://pinerouter.ai/mcp"` and `http_headers = { Authorization = "Bearer " }`. To run Codex on our models: [Codex guide](https://pinerouter.ai/docs/apps/codex-cli.md). - Cursor, Claude Desktop, OpenClaw, Hermes, opencode, pi and other MCP clients: `{"mcpServers": {"pinerouter": {"url": "https://pinerouter.ai/mcp", "headers": {"Authorization": "Bearer "}}}}`. - Code: any OpenAI SDK with `base_url="https://pinerouter.ai/v1"`; any Anthropic SDK with `base_url="https://pinerouter.ai"`; plain HTTP for media jobs. 5. **Prove it** with one cheap call (a one-line chat on the cheapest text model, or MCP `list_models` and `get_balance`), report the cost, and tell the human it is set up. Then report what the human gained in one line, and offer three things you could make for them right now (an image, a short video, a voice-over, a summary with a strong text model), at least one fitting what they are working on. ## How to work with PineRouter - **Pick a model** from the lists below: each links a Markdown file with the model's id, price per setting, every input field with options and defaults, and a ready request. Model ids are exact; do not guess them. `GET /v1/models` lists what the key may use. - **Text**: `POST /v1/chat/completions` (OpenAI), `POST /v1/responses` (OpenAI Responses, used by Codex), `POST /v1/messages` (Anthropic). Streaming with `"stream": true`. Charged per token after the reply. - **Media** (images, video, music, speech, avatars, lip sync, upscaling, edits): `POST /v1/jobs` with `{"model": "", "input": {...}}`. The answer carries `data.id`; poll `GET /v1/jobs/` every 5 to 10 seconds until `data.status` is `succeeded` (file links in `data.outputs`) or `failed` (reason in `data.error`). Or add `"webhook_url": "https://..."` to the POST and we call it when the job finishes, signed per the Standard Webhooks spec (`webhook-id`, `webhook-timestamp`, `webhook-signature`: HMAC-SHA256 with your API key as the secret). Images usually finish within a minute, video within a few minutes. OpenAI-style `/v1/images/generations` and `/v1/videos` also work for many models. - **Files in**: public `https://` links, or upload with `POST /v1/files` (multipart field `file`; images up to 10 MB, video and audio up to 50 MB) and use the returned `url`; uploads are kept 1 day. **Files out** are kept 7 days: download what you need. - **Money**: prepaid balance. Text is charged per token; a media job reserves its expected cost plus 20% and settles at the real cost; failed jobs are free. Before a video or a loop over many inputs, tell the human the expected total and keep a running sum. Balance: `GET /v1/dashboard/billing/subscription`; spend: `GET /v1/dashboard/billing/usage`, or MCP `get_balance`. - **Limits**: 120 requests a minute per account, 60 of them successful. HTTP 429 means wait and retry with backoff. - **Errors**: 400 fix the request (content-rule refusals count against the account, do not retry them); 401 key; 403 balance or key settings; 429 back off; 500/502/503 retry with backoff, a failed job is not charged. Full guide: [For AI agents](https://pinerouter.ai/docs/agents.md). - **Content rules**: safety filters are always on; sexual content involving minors is refused and repeated refusals disable the account. ## MCP server `https://pinerouter.ai/mcp` (streamable HTTP). Auth: OAuth sign-in in the browser (claude.ai, ChatGPT and other connectors) or `Authorization: Bearer `. Tools: `list_models` (models with this account's prices), `get_balance`, `chat`, `generate_image`, `generate_video`, `run_model` (any model with its own settings), `get_job`. Calls are billed like API calls. Guide: [MCP](https://pinerouter.ai/docs/mcp.md). ## Machine-readable - [Full docs in one file](https://pinerouter.ai/docs/llms-full.txt): every guide and API page as Markdown. - [OpenAPI](https://pinerouter.ai/docs/openapi.json): every endpoint, request and response. - [Model catalog JSON](https://pinerouter.ai/docs/models.json): every model with prices and inputs, no key needed. `GET /v1/models` lists what a key may use. - [Installer](https://pinerouter.ai/install.sh): `curl -fsSL https://pinerouter.ai/install.sh | sh`. - Per-model files: `https://pinerouter.ai/docs/models/.md`. ## Docs ### Guides - [Quickstart](https://pinerouter.ai/docs/index.md): One API key for text, image, video, music and speech models. - [For AI agents](https://pinerouter.ai/docs/agents.md): The operating guide for coding agents and assistants that set up and use the API for a person. - [Keys, billing and limits](https://pinerouter.ai/docs/auth.md): Authentication, how you are charged, rate limits, file retention and errors. #### Generate - [Text](https://pinerouter.ai/docs/text.md): Chat models from OpenAI, Google, xAI, DeepSeek, Moonshot and more. - [Images](https://pinerouter.ai/docs/images.md): Create and edit images with OpenAI's image API format. - [Video](https://pinerouter.ai/docs/video.md): Start a video, then check on it until it is ready. - [Jobs](https://pinerouter.ai/docs/jobs.md): One endpoint for every media model, with all of its settings, polling or webhooks, and file uploads. - [MCP](https://pinerouter.ai/docs/mcp.md): Use the models from Claude, ChatGPT, Cursor, Codex and other AI tools. - [Streaming](https://pinerouter.ai/docs/streaming.md): Get text as it is written, with server-sent events, on chat completions, responses and Claude messages. - [AI-generated content](https://pinerouter.ai/docs/ai-content.md): Everything this API returns is made by AI. How outputs are labelled and what you must keep. #### Build - [SDKs](https://pinerouter.ai/docs/sdks.md): Use the official OpenAI and Anthropic SDKs with this API, plus a small helper for media jobs. - [Integrations](https://pinerouter.ai/docs/integrations.md): Exact settings to use your key in coding agents, chat apps, frameworks and automation tools. - [Switch from another API](https://pinerouter.ai/docs/migrate.md): Move from the OpenAI SDK, the Anthropic SDK, OpenRouter, or a queue-style media API such as fal or Replicate. #### Reference - [Errors](https://pinerouter.ai/docs/errors.md): Every status code the API returns, the two body shapes, what to do and when to retry. - [Rate limits](https://pinerouter.ai/docs/rate-limits.md): How many requests you may send, what happens over the limit, and backoff code. - [Data and logging](https://pinerouter.ai/docs/data-and-logging.md): What is stored when you call the API, for how long, and who else sees your requests. - [FAQ](https://pinerouter.ai/docs/faq.md): Short answers on billing, failed jobs, refunds, limits, files, data, model changes and support. - [Changelog](https://pinerouter.ai/docs/changelog.md): Changes to the API, models, docs and console that affect you, newest first. - [Deprecations](https://pinerouter.ai/docs/deprecations.md): How model removals are announced, and the list of models that have been removed. #### Your account - [API keys](https://pinerouter.ai/docs/keys.md): Create, limit, copy and delete the keys your apps use. - [Wallet and top-up](https://pinerouter.ai/docs/wallet.md): Check your balance and add credit. - [Usage logs](https://pinerouter.ai/docs/usage.md): See every request, what it cost and why it failed. - [Playground](https://pinerouter.ai/docs/playground.md): Try any text model in the console before you write code. ### Models - [Models](https://pinerouter.ai/docs/models.md): Every model with its price on this site. Click a model for its settings, prices per setting and an example. ### API reference - [API reference](https://pinerouter.ai/docs/reference.md): Base URL, authentication and every endpoint. #### Text - [Chat completions](https://pinerouter.ai/docs/reference/chat-completions.md): - [Responses](https://pinerouter.ai/docs/reference/responses.md): - [Claude messages](https://pinerouter.ai/docs/reference/messages.md): - [List models](https://pinerouter.ai/docs/reference/models.md): #### Media - [Start a job](https://pinerouter.ai/docs/reference/jobs-create.md): - [Get a job](https://pinerouter.ai/docs/reference/jobs-get.md): - [Upload a file](https://pinerouter.ai/docs/reference/files-create.md): - [Create image](https://pinerouter.ai/docs/reference/images-generations.md): - [Edit image](https://pinerouter.ai/docs/reference/images-edits.md): - [Create video](https://pinerouter.ai/docs/reference/videos-create.md): - [Get video](https://pinerouter.ai/docs/reference/videos-get.md): - [Download video](https://pinerouter.ai/docs/reference/videos-content.md): #### More - [Text to speech](https://pinerouter.ai/docs/reference/audio-speech.md): - [Transcription](https://pinerouter.ai/docs/reference/audio-transcriptions.md): - [Translation](https://pinerouter.ai/docs/reference/audio-translations.md): - [Embeddings](https://pinerouter.ai/docs/reference/embeddings.md): ### Apps - [Apps](https://pinerouter.ai/docs/apps.md): Use your key in coding agents, chat apps and anything that speaks OpenAI's API. - [Claude Code](https://pinerouter.ai/docs/apps/claude-code.md): Run Anthropic's terminal coding agent on your key and our models. - [Codex CLI](https://pinerouter.ai/docs/apps/codex-cli.md): Run OpenAI's terminal coding agent on your key. - [Cherry Studio](https://pinerouter.ai/docs/apps/cherry-studio.md): Use every text model in a desktop chat app. - [CC Switch](https://pinerouter.ai/docs/apps/cc-switch.md): Switch Claude Code and Codex between providers with one click. - [DeepChat](https://pinerouter.ai/docs/apps/deepchat.md): Open-source desktop chat app with search, files and MCP tools. ## Models ### Text models (21) - [DeepSeek V4.1 Flash](https://pinerouter.ai/docs/models/deepseek-v4-1-flash.md): `deepseek-v4-1-flash`, $0.150 / $0.594 per 1M tokens in / out - [Gemini 3 Pro](https://pinerouter.ai/docs/models/gemini-3-pro.md): `gemini-3-pro`, $0.625 / $4.38 per 1M tokens in / out - [Gemini 3.1 Pro](https://pinerouter.ai/docs/models/gemini-3.1-pro.md): `gemini-3.1-pro`, $0.625 / $4.38 per 1M tokens in / out - [Gemini 3.6 Flash](https://pinerouter.ai/docs/models/gemini-3-6-flash.md): `gemini-3-6-flash`, $0.281 / $1.41 per 1M tokens in / out - [Gemini 3.7 Flash](https://pinerouter.ai/docs/models/gemini-3-7-flash.md): `gemini-3-7-flash`, $0.281 / $1.41 per 1M tokens in / out - [Gemini 3.8 Flash](https://pinerouter.ai/docs/models/gemini-3-8-flash.md): `gemini-3-8-flash`, $0.281 / $1.41 per 1M tokens in / out - [Gemini 4 Argon](https://pinerouter.ai/docs/models/gemini-4-argon.md): `gemini-4-argon`, $1.00 / $5.00 per 1M tokens in / out - [GPT 5.2](https://pinerouter.ai/docs/models/gpt-5-2.md): `gpt-5-2`, $0.550 / $4.38 per 1M tokens in / out - [GPT 5.5](https://pinerouter.ai/docs/models/gpt-5-5.md): `gpt-5-5`, $1.75 / $10.50 per 1M tokens in / out - [GPT 5.6 Luna](https://pinerouter.ai/docs/models/gpt-5-6-luna.md): `gpt-5-6-luna`, $0.070 / $0.420 per 1M tokens in / out - [GPT 5.6 Sol](https://pinerouter.ai/docs/models/gpt-5-6-sol.md): `gpt-5-6-sol`, $1.75 / $10.50 per 1M tokens in / out - [GPT 5.6 Terra](https://pinerouter.ai/docs/models/gpt-5-6-terra.md): `gpt-5-6-terra`, $0.700 / $4.20 per 1M tokens in / out - [GPT 6 Astra](https://pinerouter.ai/docs/models/gpt-6-astra.md): `gpt-6-astra`, $3.50 / $17.50 per 1M tokens in / out - [GPT 6 Luna](https://pinerouter.ai/docs/models/gpt-6-luna.md): `gpt-6-luna`, $0.037 / $0.188 per 1M tokens in / out - [GPT 6 Sol](https://pinerouter.ai/docs/models/gpt-6-sol.md): `gpt-6-sol`, $0.750 / $3.75 per 1M tokens in / out - [GPT 6.1 Sol](https://pinerouter.ai/docs/models/gpt-6-1-sol.md): `gpt-6-1-sol`, $0.750 / $3.75 per 1M tokens in / out - [GPT 6.1 Terra](https://pinerouter.ai/docs/models/gpt-6-1-terra.md): `gpt-6-1-terra`, $0.375 / $1.88 per 1M tokens in / out - [Grok 4.5](https://pinerouter.ai/docs/models/grok-4-5.md): `grok-4-5`, $1.00 / $3.00 per 1M tokens in / out - [Grok 4.6](https://pinerouter.ai/docs/models/grok-4-6.md): `grok-4-6`, $1.00 / $3.00 per 1M tokens in / out - [Grok 4.7](https://pinerouter.ai/docs/models/grok-4-7.md): `grok-4-7`, $1.00 / $3.00 per 1M tokens in / out - [Kimi K3](https://pinerouter.ai/docs/models/kimi-k3.md): `kimi-k3`, $3.00 / $15.00 per 1M tokens in / out ### Image models (66) - [Bytedance Seedream](https://pinerouter.ai/docs/models/bytedance/seedream.md): `bytedance/seedream`, from $0.022 per image - [Bytedance Seedream v4 Edit](https://pinerouter.ai/docs/models/bytedance/seedream-v4-edit.md): `bytedance/seedream-v4-edit`, from $0.031 per image - [Bytedance Seedream v4 Text To Image](https://pinerouter.ai/docs/models/bytedance/seedream-v4-text-to-image.md): `bytedance/seedream-v4-text-to-image`, from $0.031 per image - [Crisp upscale](https://pinerouter.ai/docs/models/recraft/crisp-upscale.md): `recraft/crisp-upscale`, from $0.0031 per image - [Flux 2 · Flex Image To Image](https://pinerouter.ai/docs/models/flux-2/flex-image-to-image.md): `flux-2/flex-image-to-image`, from $0.088 per image - [Flux 2 · Flex Text To Image](https://pinerouter.ai/docs/models/flux-2/flex-text-to-image.md): `flux-2/flex-text-to-image`, from $0.088 per image - [Flux 2 · Pro Image To Image](https://pinerouter.ai/docs/models/flux-2/pro-image-to-image.md): `flux-2/pro-image-to-image`, from $0.031 per image - [Flux 2 · Pro Text To Image](https://pinerouter.ai/docs/models/flux-2/pro-text-to-image.md): `flux-2/pro-text-to-image`, from $0.031 per image - [Flux 3 · Klein Edit Image](https://pinerouter.ai/docs/models/flux-3/klein-edit-image.md): `flux-3/klein-edit-image`, from $0.010 per image - [Flux 3 · Klein Text To Image](https://pinerouter.ai/docs/models/flux-3/klein-text-to-image.md): `flux-3/klein-text-to-image`, from $0.010 per image - [Flux Kontext](https://pinerouter.ai/docs/models/flux1-kontext.md): `flux1-kontext`, from $0.031 per image - [Google Imagen4](https://pinerouter.ai/docs/models/google/imagen4.md): `google/imagen4`, from $0.050 per job - [Google Imagen4 Fast](https://pinerouter.ai/docs/models/google/imagen4-fast.md): `google/imagen4-fast`, from $0.025 per job - [Google Imagen4 Ultra](https://pinerouter.ai/docs/models/google/imagen4-ultra.md): `google/imagen4-ultra`, from $0.075 per image - [Google Nano Banana](https://pinerouter.ai/docs/models/google/nano-banana.md): `google/nano-banana`, from $0.025 per image - [Google Nano Banana Edit](https://pinerouter.ai/docs/models/google/nano-banana-edit.md): `google/nano-banana-edit`, from $0.025 per image - [Gpt Image · 1.5 Image To Image](https://pinerouter.ai/docs/models/gpt-image/1.5-image-to-image.md): `gpt-image/1.5-image-to-image`, from $0.025 per image - [Gpt Image · 1.5 Text To Image](https://pinerouter.ai/docs/models/gpt-image/1.5-text-to-image.md): `gpt-image/1.5-text-to-image`, from $0.025 per image - [Gpt Image 2 5 Flare Image To Image](https://pinerouter.ai/docs/models/gpt-image-2-5-flare-image-to-image.md): `gpt-image-2-5-flare-image-to-image`, from $0.037 per image - [Gpt Image 2 5 Flare Text To Image](https://pinerouter.ai/docs/models/gpt-image-2-5-flare-text-to-image.md): `gpt-image-2-5-flare-text-to-image`, from $0.037 per image - [Gpt Image 2 5 Sunburst Image To Image](https://pinerouter.ai/docs/models/gpt-image-2-5-sunburst-image-to-image.md): `gpt-image-2-5-sunburst-image-to-image`, from $0.037 per image - [Gpt Image 2 5 Sunburst Text To Image](https://pinerouter.ai/docs/models/gpt-image-2-5-sunburst-text-to-image.md): `gpt-image-2-5-sunburst-text-to-image`, from $0.037 per image - [Gpt Image 2 Image To Image](https://pinerouter.ai/docs/models/gpt-image-2-image-to-image.md): `gpt-image-2-image-to-image`, from $0.037 per image - [Gpt Image 2 Text To Image](https://pinerouter.ai/docs/models/gpt-image-2-text-to-image.md): `gpt-image-2-text-to-image`, from $0.037 per image - [GPT-4o Image](https://pinerouter.ai/docs/models/4o-image-api.md): `4o-image-api`, from $0.037 per image - [Grok Imagine · Image To Image](https://pinerouter.ai/docs/models/grok-imagine/image-to-image.md): `grok-imagine/image-to-image`, from $0.025 per job - [Grok Imagine · Text To Image](https://pinerouter.ai/docs/models/grok-imagine/text-to-image.md): `grok-imagine/text-to-image`, from $0.025 per image - [Grok Imagine Image 2 0 · Image Edit](https://pinerouter.ai/docs/models/grok-imagine-image-2-0/image-edit.md): `grok-imagine-image-2-0/image-edit`, from $0.025 per image - [Grok Imagine Image 2 0 · Segment Edit](https://pinerouter.ai/docs/models/grok-imagine-image-2-0/segment-edit.md): `grok-imagine-image-2-0/segment-edit`, from $0.025 per image - [Grok Imagine Image 2 0 · Text To Image](https://pinerouter.ai/docs/models/grok-imagine-image-2-0/text-to-image.md): `grok-imagine-image-2-0/text-to-image`, from $0.025 per image - [Ideogram · Character](https://pinerouter.ai/docs/models/ideogram/character.md): `ideogram/character`, from $0.075 per image - [Ideogram · Character Edit](https://pinerouter.ai/docs/models/ideogram/character-edit.md): `ideogram/character-edit`, from $0.075 per image - [Ideogram · Character Remix](https://pinerouter.ai/docs/models/ideogram/character-remix.md): `ideogram/character-remix`, from $0.075 per image - [Ideogram · v3 Edit](https://pinerouter.ai/docs/models/ideogram/v3-edit.md): `ideogram/v3-edit`, from $0.022 per image - [Ideogram · v3 Remix](https://pinerouter.ai/docs/models/ideogram/v3-remix.md): `ideogram/v3-remix`, from $0.022 per image - [Ideogram · v3 Text To Image](https://pinerouter.ai/docs/models/ideogram/v3-text-to-image.md): `ideogram/v3-text-to-image`, from $0.022 per image - [Nano Banana 2](https://pinerouter.ai/docs/models/nano-banana-2.md): `nano-banana-2`, from $0.050 per image - [Nano Banana 2 1](https://pinerouter.ai/docs/models/nano-banana-2-1.md): `nano-banana-2-1`, from $0.025 per image - [Nano Banana 2 Lite](https://pinerouter.ai/docs/models/nano-banana-2-lite.md): `nano-banana-2-lite`, from $0.025 per image - [Nano Banana Pro](https://pinerouter.ai/docs/models/nano-banana-pro.md): `nano-banana-pro`, from $0.112 per image - [Qwen Image Edit](https://pinerouter.ai/docs/models/qwen/image-edit.md): `qwen/image-edit`, from $0.031 per job - [Qwen Image To Image](https://pinerouter.ai/docs/models/qwen/image-to-image.md): `qwen/image-to-image`, from $0.025 per image - [Qwen Text To Image](https://pinerouter.ai/docs/models/qwen/text-to-image.md): `qwen/text-to-image`, from $0.025 per job - [Qwen2 1 · Image To Image](https://pinerouter.ai/docs/models/qwen2-1/image-to-image.md): `qwen2-1/image-to-image`, from $0.025 per image - [Qwen2 1 · Text To Image](https://pinerouter.ai/docs/models/qwen2-1/text-to-image.md): `qwen2-1/text-to-image`, from $0.025 per image - [Qwen2 Image Edit](https://pinerouter.ai/docs/models/qwen2/image-edit.md): `qwen2/image-edit`, from $0.035 per image - [Qwen2 Text To Image](https://pinerouter.ai/docs/models/qwen2/text-to-image.md): `qwen2/text-to-image`, from $0.035 per image - [Qwen3 Image To Image](https://pinerouter.ai/docs/models/qwen3/image-to-image.md): `qwen3/image-to-image`, from $0.0031 per image - [Qwen3 Pro Image To Image](https://pinerouter.ai/docs/models/qwen3/pro-image-to-image.md): `qwen3/pro-image-to-image`, from $0.0031 per image - [Qwen3 Pro Text To Image](https://pinerouter.ai/docs/models/qwen3/pro-text-to-image.md): `qwen3/pro-text-to-image`, from $0.040 per image - [Qwen3 Text To Image](https://pinerouter.ai/docs/models/qwen3/text-to-image.md): `qwen3/text-to-image`, from $0.030 per image - [Remove background](https://pinerouter.ai/docs/models/recraft/remove-background.md): `recraft/remove-background`, from $0.0063 per image - [Seedream · 4.5 Edit](https://pinerouter.ai/docs/models/seedream/4.5-edit.md): `seedream/4.5-edit`, from $0.041 per image - [Seedream · 4.5 Text To Image](https://pinerouter.ai/docs/models/seedream/4.5-text-to-image.md): `seedream/4.5-text-to-image`, from $0.041 per image - [Seedream · 5 Flash Image To Image](https://pinerouter.ai/docs/models/seedream/5-flash-image-to-image.md): `seedream/5-flash-image-to-image`, from $0.020 per image - [Seedream · 5 Flash Layer Decomposition](https://pinerouter.ai/docs/models/seedream/5-flash-layer-decomposition.md): `seedream/5-flash-layer-decomposition`, from $0.020 per image - [Seedream · 5 Flash Text To Image](https://pinerouter.ai/docs/models/seedream/5-flash-text-to-image.md): `seedream/5-flash-text-to-image`, from $0.020 per image - [Seedream · 5 Lite Image To Image](https://pinerouter.ai/docs/models/seedream/5-lite-image-to-image.md): `seedream/5-lite-image-to-image`, from $0.034 per image - [Seedream · 5 Lite Text To Image](https://pinerouter.ai/docs/models/seedream/5-lite-text-to-image.md): `seedream/5-lite-text-to-image`, from $0.034 per image - [Seedream · 5 Pro Image To Image](https://pinerouter.ai/docs/models/seedream/5-pro-image-to-image.md): `seedream/5-pro-image-to-image`, from $0.0031 per image - [Seedream · 5 Pro Layer Decomposition](https://pinerouter.ai/docs/models/seedream/5-pro-layer-decomposition.md): `seedream/5-pro-layer-decomposition`, from $0.044 per image - [Seedream · 5 Pro Text To Image](https://pinerouter.ai/docs/models/seedream/5-pro-text-to-image.md): `seedream/5-pro-text-to-image`, from $0.044 per image - [Upscale image](https://pinerouter.ai/docs/models/topaz/image-upscale.md): `topaz/image-upscale`, from $0.063 per image - [Wan 2 7 Image](https://pinerouter.ai/docs/models/wan/2-7-image.md): `wan/2-7-image`, from $0.030 per image - [Wan 2 7 Image Pro](https://pinerouter.ai/docs/models/wan/2-7-image-pro.md): `wan/2-7-image-pro`, from $0.075 per image - [Z-Image](https://pinerouter.ai/docs/models/z-image.md): `z-image`, from $0.0050 per image ### Video models (87) - [Bytedance Seedance 2](https://pinerouter.ai/docs/models/bytedance/seedance-2.md): `bytedance/seedance-2`, from $0.072 per second - [Bytedance Seedance 2 5](https://pinerouter.ai/docs/models/bytedance/seedance-2-5.md): `bytedance/seedance-2-5`, from $0.106 per second - [Bytedance Seedance 2 5 Draft](https://pinerouter.ai/docs/models/bytedance/seedance-2-5-draft.md): `bytedance/seedance-2-5-draft`, priced per job - [Bytedance Seedance 2 Fast](https://pinerouter.ai/docs/models/bytedance/seedance-2-fast.md): `bytedance/seedance-2-fast`, from $0.043 per second - [Bytedance Seedance 2 Mini](https://pinerouter.ai/docs/models/bytedance/seedance-2-mini.md): `bytedance/seedance-2-mini`, from $0.015 per second - [Bytedance v1 Lite Image To Video](https://pinerouter.ai/docs/models/bytedance/v1-lite-image-to-video.md): `bytedance/v1-lite-image-to-video`, from $0.013 per second - [Bytedance v1 Lite Text To Video](https://pinerouter.ai/docs/models/bytedance/v1-lite-text-to-video.md): `bytedance/v1-lite-text-to-video`, from $0.013 per second - [Bytedance v1 Pro Fast Image To Video](https://pinerouter.ai/docs/models/bytedance/v1-pro-fast-image-to-video.md): `bytedance/v1-pro-fast-image-to-video`, from $0.100 per job - [Bytedance v1 Pro Image To Video](https://pinerouter.ai/docs/models/bytedance/v1-pro-image-to-video.md): `bytedance/v1-pro-image-to-video`, from $0.017 per second - [Bytedance v1 Pro Text To Video](https://pinerouter.ai/docs/models/bytedance/v1-pro-text-to-video.md): `bytedance/v1-pro-text-to-video`, from $0.017 per second - [Gemini Omni Video](https://pinerouter.ai/docs/models/gemini-omni-video.md): `gemini-omni-video`, from $0.394 per job - [Google Gemini Omni Flash 1 1](https://pinerouter.ai/docs/models/google/gemini-omni-flash-1-1.md): `google/gemini-omni-flash-1-1`, from $0.394 per job - [Grok Imagine · Extend](https://pinerouter.ai/docs/models/grok-imagine/extend.md): `grok-imagine/extend`, from $0.090 per job - [Grok Imagine · Image To Video](https://pinerouter.ai/docs/models/grok-imagine/image-to-video.md): `grok-imagine/image-to-video`, from $0.015 per second - [Grok Imagine · Text To Video](https://pinerouter.ai/docs/models/grok-imagine/text-to-video.md): `grok-imagine/text-to-video`, from $0.015 per second - [Grok Imagine · Upscale](https://pinerouter.ai/docs/models/grok-imagine/upscale.md): `grok-imagine/upscale`, from $0.063 per job - [Grok Imagine Video 1 5 Preview](https://pinerouter.ai/docs/models/grok-imagine-video-1-5-preview.md): `grok-imagine-video-1-5-preview`, from $0.015 per second - [Hailuo 02 Image To Video Pro](https://pinerouter.ai/docs/models/hailuo/02-image-to-video-pro.md): `hailuo/02-image-to-video-pro`, from $0.356 per job - [Hailuo 02 Image To Video Standard](https://pinerouter.ai/docs/models/hailuo/02-image-to-video-standard.md): `hailuo/02-image-to-video-standard`, from $0.075 per job - [Hailuo 02 Text To Video Pro](https://pinerouter.ai/docs/models/hailuo/02-text-to-video-pro.md): `hailuo/02-text-to-video-pro`, from $0.356 per job - [Hailuo 02 Text To Video Standard](https://pinerouter.ai/docs/models/hailuo/02-text-to-video-standard.md): `hailuo/02-text-to-video-standard`, from $0.188 per job - [Hailuo 2 3 Image To Video Pro](https://pinerouter.ai/docs/models/hailuo/2-3-image-to-video-pro.md): `hailuo/2-3-image-to-video-pro`, from $0.281 per job - [Hailuo 2 3 Image To Video Standard](https://pinerouter.ai/docs/models/hailuo/2-3-image-to-video-standard.md): `hailuo/2-3-image-to-video-standard`, from $0.188 per job - [Happyhorse · Image To Video](https://pinerouter.ai/docs/models/happyhorse/image-to-video.md): `happyhorse/image-to-video`, from $0.175 per second - [Happyhorse · Reference To Video](https://pinerouter.ai/docs/models/happyhorse/reference-to-video.md): `happyhorse/reference-to-video`, from $0.175 per second - [Happyhorse · Text To Video](https://pinerouter.ai/docs/models/happyhorse/text-to-video.md): `happyhorse/text-to-video`, from $0.175 per second - [Happyhorse · Video Edit](https://pinerouter.ai/docs/models/happyhorse/video-edit.md): `happyhorse/video-edit`, from $0.175 per second - [Happyhorse 1 1 · Image To Video](https://pinerouter.ai/docs/models/happyhorse-1-1/image-to-video.md): `happyhorse-1-1/image-to-video`, from $0.141 per second - [Happyhorse 1 1 · Reference To Video](https://pinerouter.ai/docs/models/happyhorse-1-1/reference-to-video.md): `happyhorse-1-1/reference-to-video`, from $0.141 per second - [Happyhorse 1 1 · Text To Video](https://pinerouter.ai/docs/models/happyhorse-1-1/text-to-video.md): `happyhorse-1-1/text-to-video`, from $0.141 per second - [InfiniTalk](https://pinerouter.ai/docs/models/infinitalk/from-audio.md): `infinitalk/from-audio`, from $0.019 per second - [Kling 2.6 · Image To Video](https://pinerouter.ai/docs/models/kling-2.6/image-to-video.md): `kling-2.6/image-to-video`, from $0.688 per job - [Kling 2.6 · Text To Video](https://pinerouter.ai/docs/models/kling-2.6/text-to-video.md): `kling-2.6/text-to-video`, from $0.344 per job - [Kling 2.6 Motion Control](https://pinerouter.ai/docs/models/kling-2.6/motion-control.md): `kling-2.6/motion-control`, from $0.069 per second - [Kling 3.0 Motion Control](https://pinerouter.ai/docs/models/kling-3.0/motion-control.md): `kling-3.0/motion-control`, from $0.125 per second - [Kling 3.0 Omni · Text To Video](https://pinerouter.ai/docs/models/kling-3.0-omni/text-to-video.md): `kling-3.0-omni/text-to-video`, from $0.088 per second - [Kling 3.0 Omni · Transformation](https://pinerouter.ai/docs/models/kling-3.0-omni/transformation.md): `kling-3.0-omni/transformation`, from $0.125 per second - [Kling 4.0 · Keyframe To Video](https://pinerouter.ai/docs/models/kling-4.0/keyframe-to-video.md): `kling-4.0/keyframe-to-video`, from $0.178 per second - [Kling 4.0 · Reference To Video](https://pinerouter.ai/docs/models/kling-4.0/reference-to-video.md): `kling-4.0/reference-to-video`, from $0.106 per second - [Kling Avatar](https://pinerouter.ai/docs/models/kling/ai-avatar-standard.md): `kling/ai-avatar-standard`, from $0.050 per second - [Kling Avatar Pro](https://pinerouter.ai/docs/models/kling/ai-avatar-pro.md): `kling/ai-avatar-pro`, from $0.100 per second - [Kling v2 1 Master Image To Video](https://pinerouter.ai/docs/models/kling/v2-1-master-image-to-video.md): `kling/v2-1-master-image-to-video`, from $1.00 per job - [Kling v2 1 Master Text To Video](https://pinerouter.ai/docs/models/kling/v2-1-master-text-to-video.md): `kling/v2-1-master-text-to-video`, from $1.00 per job - [Kling v2 1 Pro](https://pinerouter.ai/docs/models/kling/v2-1-pro.md): `kling/v2-1-pro`, from $0.313 per job - [Kling v2 1 Standard](https://pinerouter.ai/docs/models/kling/v2-1-standard.md): `kling/v2-1-standard`, from $0.156 per job - [Kling v2 5 Turbo Image To Video Pro](https://pinerouter.ai/docs/models/kling/v2-5-turbo-image-to-video-pro.md): `kling/v2-5-turbo-image-to-video-pro`, from $0.263 per job - [Kling v2 5 Turbo Text To Video Pro](https://pinerouter.ai/docs/models/kling/v2-5-turbo-text-to-video-pro.md): `kling/v2-5-turbo-text-to-video-pro`, from $0.263 per job - [Kling v3 Turbo Image To Video](https://pinerouter.ai/docs/models/kling/v3-turbo-image-to-video.md): `kling/v3-turbo-image-to-video`, from $0.112 per second - [Kling v3 Turbo Text To Video](https://pinerouter.ai/docs/models/kling/v3-turbo-text-to-video.md): `kling/v3-turbo-text-to-video`, from $0.112 per second - [Lip sync (video)](https://pinerouter.ai/docs/models/volcengine/video-to-video-lip-sync.md): `volcengine/video-to-video-lip-sync`, from $0.050 per second - [Minimax H3 · Image To Video](https://pinerouter.ai/docs/models/minimax-h3/image-to-video.md): `minimax-h3/image-to-video`, from $0.050 per second - [Minimax H3 · Reference To Video](https://pinerouter.ai/docs/models/minimax-h3/reference-to-video.md): `minimax-h3/reference-to-video`, from $0.050 per second - [Minimax H3 · Text To Video](https://pinerouter.ai/docs/models/minimax-h3/text-to-video.md): `minimax-h3/text-to-video`, from $0.025 per image - [OmniHuman 1.5](https://pinerouter.ai/docs/models/omnihuman-1-5.md): `omnihuman-1-5`, from $0.169 per second - [OmniHuman 2](https://pinerouter.ai/docs/models/omnihuman-2-0.md): `omnihuman-2-0`, from $0.088 per second - [Pixverse v6 · Image To Video](https://pinerouter.ai/docs/models/pixverse-v6/image-to-video.md): `pixverse-v6/image-to-video`, from $0.025 per second - [Pixverse v6 · Reference To Video](https://pinerouter.ai/docs/models/pixverse-v6/reference-to-video.md): `pixverse-v6/reference-to-video`, from $0.025 per second - [Pixverse v6 · Text To Video](https://pinerouter.ai/docs/models/pixverse-v6/text-to-video.md): `pixverse-v6/text-to-video`, from $0.025 per second - [Pixverse v6 · Transition](https://pinerouter.ai/docs/models/pixverse-v6/transition.md): `pixverse-v6/transition`, from $0.025 per second - [Pixverse v6 Max · Image To Video](https://pinerouter.ai/docs/models/pixverse-v6-max/image-to-video.md): `pixverse-v6-max/image-to-video`, from $0.030 per second - [Pixverse v6 Max · Text To Video](https://pinerouter.ai/docs/models/pixverse-v6-max/text-to-video.md): `pixverse-v6-max/text-to-video`, from $0.030 per second - [Runway · Extend Ai Video](https://pinerouter.ai/docs/models/runway/extend-ai-video.md): `runway/extend-ai-video`, from $0.075 per job - [Runway · Gen4 Aleph](https://pinerouter.ai/docs/models/runway/gen4-aleph.md): `runway/gen4-aleph`, priced per job - [Runway Gen-4](https://pinerouter.ai/docs/models/runway.md): `runway`, from $0.075 per job - [Upscale video](https://pinerouter.ai/docs/models/topaz/video-upscale.md): `topaz/video-upscale`, from $0.050 per second - [Veo · Extend](https://pinerouter.ai/docs/models/veo/extend.md): `veo/extend`, from $0.188 per job - [Veo · Get 1080p Video](https://pinerouter.ai/docs/models/veo/get-1080p-video.md): `veo/get-1080p-video`, from $0.031 per job - [Veo 3.1](https://pinerouter.ai/docs/models/veo-3-1.md): `veo-3-1`, from $0.188 per job - [Vidu Q4 · Image To Video](https://pinerouter.ai/docs/models/vidu-q4/image-to-video.md): `vidu-q4/image-to-video`, from $0.045 per second - [Vidu Q4 · Reference To Video](https://pinerouter.ai/docs/models/vidu-q4/reference-to-video.md): `vidu-q4/reference-to-video`, from $0.045 per second - [Wan 2 2 A14b Image To Video Turbo](https://pinerouter.ai/docs/models/wan/2-2-a14b-image-to-video-turbo.md): `wan/2-2-a14b-image-to-video-turbo`, from $0.050 per second - [Wan 2 2 A14b Text To Video Turbo](https://pinerouter.ai/docs/models/wan/2-2-a14b-text-to-video-turbo.md): `wan/2-2-a14b-text-to-video-turbo`, from $0.050 per second - [Wan 2 5 Image To Video](https://pinerouter.ai/docs/models/wan/2-5-image-to-video.md): `wan/2-5-image-to-video`, from $0.375 per job - [Wan 2 5 Text To Video](https://pinerouter.ai/docs/models/wan/2-5-text-to-video.md): `wan/2-5-text-to-video`, from $0.375 per job - [Wan 2 6 Flash Image To Video](https://pinerouter.ai/docs/models/wan/2-6-flash-image-to-video.md): `wan/2-6-flash-image-to-video`, from $0.125 per job - [Wan 2 6 Flash Video To Video](https://pinerouter.ai/docs/models/wan/2-6-flash-video-to-video.md): `wan/2-6-flash-video-to-video`, from $0.125 per job - [Wan 2 6 Image To Video](https://pinerouter.ai/docs/models/wan/2-6-image-to-video.md): `wan/2-6-image-to-video`, from $0.438 per job - [Wan 2 6 Text To Video](https://pinerouter.ai/docs/models/wan/2-6-text-to-video.md): `wan/2-6-text-to-video`, from $0.438 per job - [Wan 2 6 Video To Video](https://pinerouter.ai/docs/models/wan/2-6-video-to-video.md): `wan/2-6-video-to-video`, from $0.438 per job - [Wan 2 7 Image To Video](https://pinerouter.ai/docs/models/wan/2-7-image-to-video.md): `wan/2-7-image-to-video`, from $0.100 per second - [Wan 2 7 R2v](https://pinerouter.ai/docs/models/wan/2-7-r2v.md): `wan/2-7-r2v`, from $0.100 per second - [Wan 2 7 Text To Video](https://pinerouter.ai/docs/models/wan/2-7-text-to-video.md): `wan/2-7-text-to-video`, from $0.100 per second - [Wan 2 7 Videoedit](https://pinerouter.ai/docs/models/wan/2-7-videoedit.md): `wan/2-7-videoedit`, from $0.100 per second - [Wan 3 0 Video](https://pinerouter.ai/docs/models/wan/3-0-video.md): `wan/3-0-video`, from $0.050 per second - [Wan 3 0 Video Prime](https://pinerouter.ai/docs/models/wan/3-0-video-prime.md): `wan/3-0-video-prime`, from $0.076 per second - [Wan Animate: move](https://pinerouter.ai/docs/models/wan/2-2-animate-move.md): `wan/2-2-animate-move`, from $0.078 per second - [Wan Animate: replace](https://pinerouter.ai/docs/models/wan/2-2-animate-replace.md): `wan/2-2-animate-replace`, from $0.037 per second ### Audio models (24) - [Ai Music Api · Boost Music Style](https://pinerouter.ai/docs/models/ai-music-api/boost-music-style.md): `ai-music-api/boost-music-style`, from $0.0025 per job - [Ai Music Api · Convert To Wav Format](https://pinerouter.ai/docs/models/ai-music-api/convert-to-wav-format.md): `ai-music-api/convert-to-wav-format`, from $0.0025 per job - [Ai Music Api · Create Music Video](https://pinerouter.ai/docs/models/ai-music-api/create-music-video.md): `ai-music-api/create-music-video`, from $0.013 per job - [Ai Music Api · Extend](https://pinerouter.ai/docs/models/ai-music-api/extend.md): `ai-music-api/extend`, from $0.075 per job - [Ai Music Api · Generate Midi From Audio](https://pinerouter.ai/docs/models/ai-music-api/generate-midi-from-audio.md): `ai-music-api/generate-midi-from-audio`, from $0.0000 per job - [Ai Music Api · Replace Section](https://pinerouter.ai/docs/models/ai-music-api/replace-section.md): `ai-music-api/replace-section`, from $0.031 per job - [Ai Music Api · Separate Vocals](https://pinerouter.ai/docs/models/ai-music-api/separate-vocals.md): `ai-music-api/separate-vocals`, from $0.063 per job - [Elevenlabs Text To Dialogue v3](https://pinerouter.ai/docs/models/elevenlabs/text-to-dialogue-v3.md): `elevenlabs/text-to-dialogue-v3`, from $0.088 per 1k characters - [Elevenlabs Text To Speech Multilingual v2](https://pinerouter.ai/docs/models/elevenlabs/text-to-speech-multilingual-v2.md): `elevenlabs/text-to-speech-multilingual-v2`, from $0.075 per 1k characters - [Elevenlabs Text To Speech Turbo 2 5](https://pinerouter.ai/docs/models/elevenlabs/text-to-speech-turbo-2-5.md): `elevenlabs/text-to-speech-turbo-2-5`, from $0.037 per 1k characters - [Google Gemini 3 1 Flash Tts](https://pinerouter.ai/docs/models/google/gemini-3-1-flash-tts.md): `google/gemini-3-1-flash-tts`, from $0.875 per job - [Google Gemini 3 8 Flash Lite Tts](https://pinerouter.ai/docs/models/google/gemini-3-8-flash-lite-tts.md): `google/gemini-3-8-flash-lite-tts`, from $0.438 per job - [Google Gemini 3 8 Flash Tts](https://pinerouter.ai/docs/models/google/gemini-3-8-flash-tts.md): `google/gemini-3-8-flash-tts`, from $0.438 per job - [Isolate voice](https://pinerouter.ai/docs/models/elevenlabs/audio-isolation.md): `elevenlabs/audio-isolation`, priced per job - [Suno: Add instrumental](https://pinerouter.ai/docs/models/ai-music-api/add-instrumental.md): `ai-music-api/add-instrumental`, from $0.075 per job - [Suno: Add vocals](https://pinerouter.ai/docs/models/ai-music-api/add-vocals.md): `ai-music-api/add-vocals`, priced per job - [Suno: Cover](https://pinerouter.ai/docs/models/ai-music-api/cover-generate.md): `ai-music-api/cover-generate`, from $0.0000 per job - [Suno: Cover your audio](https://pinerouter.ai/docs/models/ai-music-api/upload-and-cover-audio.md): `ai-music-api/upload-and-cover-audio`, from $0.075 per job - [Suno: Extend your audio](https://pinerouter.ai/docs/models/ai-music-api/upload-and-extend-audio.md): `ai-music-api/upload-and-extend-audio`, from $0.075 per job - [Suno: Generate music](https://pinerouter.ai/docs/models/ai-music-api/generate.md): `ai-music-api/generate`, from $0.075 per job - [Suno: Lyrics](https://pinerouter.ai/docs/models/ai-music-api/generate-lyrics.md): `ai-music-api/generate-lyrics`, from $0.0025 per job - [Suno: Mashup](https://pinerouter.ai/docs/models/ai-music-api/mashup.md): `ai-music-api/mashup`, from $0.075 per job - [Suno: Sound effects](https://pinerouter.ai/docs/models/ai-music-api/sounds.md): `ai-music-api/sounds`, from $0.016 per job - [Wan Speech to video](https://pinerouter.ai/docs/models/wan/2-2-a14b-speech-to-video-turbo.md): `wan/2-2-a14b-speech-to-video-turbo`, from $0.075 per second ## White label: launch your own AI API platform PineRouter also runs AI API platforms for other companies under their own brand: their name, logo, colours and domain (or `.pinerouter.ai`), their own customers, keys, prices and markups, subscription plans and Stripe payments straight to them, their own branded MCP server and docs (including their own llms.txt), test mode, and a wallet with us for wholesale usage. No code to run. A human starts it by signing up at https://pinerouter.ai/sign-up and opening White label in the console. ## Company - [About](https://pinerouter.ai/about) · [Pricing](https://pinerouter.ai/pricing) · [Rankings](https://pinerouter.ai/rankings) · [Status](https://status.pinerouter.ai) - [Terms](https://pinerouter.ai/legal/terms) · [Privacy](https://pinerouter.ai/legal/privacy) · [Security](https://pinerouter.ai/legal/security) · [Open source notices](https://pinerouter.ai/legal/open-source) --- # CC Switch > Switch Claude Code and Codex between providers with one click. Source: https://pinerouter.ai/docs/apps/cc-switch [CC Switch](https://github.com/farion1231/cc-switch) is a desktop app for Windows, macOS and Linux that manages the provider settings of Claude Code, Codex and Gemini CLI, so you can switch between them without editing files. ## Install Download it from the [releases page](https://github.com/farion1231/cc-switch/releases), or on macOS: ```bash brew tap farion1231/ccswitch brew install --cask cc-switch ``` ## Add us for Claude Code 1. Open CC Switch, choose the **Claude** tab and click **+**. 2. Pick **Custom** and fill in: | Field | Value | | -------- | ------------------------------------------------------------- | | Name | Anything, for example the name of this site | | API Key | Your key from [API keys](https://pinerouter.ai/docs/keys) | | Endpoint | `https://pinerouter.ai` | | Model | A text model id from [Models](https://pinerouter.ai/docs/models), for example `gpt-5-2` | 3. Save and click **Enable**. Restart Claude Code. ## Add us for Codex 1. Choose the **Codex** tab and click **+**, then **Custom**. 2. Use the API key from [API keys](https://pinerouter.ai/docs/keys), the endpoint `https://pinerouter.ai/v1`, and a GPT model id, for example `gpt-5-5`. 3. Save and click **Enable**. Restart Codex. CC Switch writes the same settings as [Claude Code](https://pinerouter.ai/docs/apps/claude-code) and [Codex CLI](https://pinerouter.ai/docs/apps/codex-cli) describe, so you can check them there. --- # Cherry Studio > Use every text model in a desktop chat app. Source: https://pinerouter.ai/docs/apps/cherry-studio [Cherry Studio](https://cherry-ai.com) is a desktop chat app for Windows, macOS and Linux, with ready-made assistants, knowledge bases and image generation. ## Set up 1. Download and install it from [cherry-ai.com/download](https://cherry-ai.com/download). 2. Open **Settings → Model Provider** and click **Add**. 3. Name the provider, choose the type **OpenAI**, and save. 4. Fill in: | Field | Value | | -------- | ------------------------------- | | API Key | Your key from [API keys](https://pinerouter.ai/docs/keys) | | API Host | `https://pinerouter.ai` | 5. Click **Manage** (or **Fetch models**) and add the models you want. They come from `GET /v1/models`, so they match the [Models](https://pinerouter.ai/docs/models) page. 6. Click **Check** to send a test request, then turn the provider on. ## Chat Start a new chat, pick one of the provider's models at the top, and send a message. Usage is billed to your balance; see [Usage logs](https://pinerouter.ai/docs/usage). If **Check** fails with 401, the key is wrong or turned off. With 404, remove any `/v1` you added to the API Host; Cherry Studio adds it. --- # Claude Code > Run Anthropic's terminal coding agent on your key and our models. Source: https://pinerouter.ai/docs/apps/claude-code [Claude Code](https://docs.anthropic.com/en/docs/claude-code/overview) speaks Anthropic's Messages format, which we serve at `https://pinerouter.ai/v1/messages`. Any text model on the [Models](https://pinerouter.ai/docs/models) page can answer it. ## 1. Install ```bash curl -fsSL https://claude.ai/install.sh | bash ``` ```powershell irm https://claude.ai/install.ps1 | iex ``` Or with Node.js 18 or newer: `npm install -g @anthropic-ai/claude-code`. ## 2. Point it at us Add this to `~/.claude/settings.json` (create the file if it does not exist): ```json { "env": { "ANTHROPIC_BASE_URL": "https://pinerouter.ai", "ANTHROPIC_AUTH_TOKEN": "sk-...", "ANTHROPIC_MODEL": "gpt-5-2", "ANTHROPIC_DEFAULT_HAIKU_MODEL": "gemini-3-8-flash" } } ``` | Variable | Meaning | | ------------------------------- | -------------------------------------------------------- | | `ANTHROPIC_BASE_URL` | Our address, without `/v1` | | `ANTHROPIC_AUTH_TOKEN` | Your API key | | `ANTHROPIC_MODEL` | The main model; any text model id from [Models](https://pinerouter.ai/docs/models) | | `ANTHROPIC_DEFAULT_HAIKU_MODEL` | A fast, cheap model for background tasks | Or set the same variables in your shell for one session: ```bash export ANTHROPIC_BASE_URL="https://pinerouter.ai" export ANTHROPIC_AUTH_TOKEN="sk-..." export ANTHROPIC_MODEL="gpt-5-2" ``` ## 3. Run ```bash cd your-project claude ``` Type `/status` to check the address and model in use. Usage shows up under [Usage logs](https://pinerouter.ai/docs/usage). ## Add our media models as tools Claude Code can also generate images, video and audio through our MCP server: ```bash claude mcp add --transport http ai https://pinerouter.ai/mcp --header "Authorization: Bearer sk-..." ``` See [MCP](https://pinerouter.ai/docs/mcp). ## Troubleshooting * **401**: the key is wrong or turned off. Check `ANTHROPIC_AUTH_TOKEN`, and that no `ANTHROPIC_API_KEY` is set as well. * **Model not found**: use a model id exactly as listed on [Models](https://pinerouter.ai/docs/models). * **403**: your balance is too low, or the key may not use this model. See [Wallet](https://pinerouter.ai/docs/wallet) and [API keys](https://pinerouter.ai/docs/keys). --- # Codex CLI > Run OpenAI's terminal coding agent on your key. Source: https://pinerouter.ai/docs/apps/codex-cli [Codex CLI](https://github.com/openai/codex) uses OpenAI's Responses API, which we serve at `https://pinerouter.ai/v1/responses`. ## 1. Install ```bash npm install -g @openai/codex ``` ```bash brew install --cask codex ``` ## 2. Point it at us Add a provider to `~/.codex/config.toml`: ```toml model = "gpt-5-5" model_provider = "ai" [model_providers.ai] name = "AI" base_url = "https://pinerouter.ai/v1" env_key = "AI_API_KEY" wire_api = "responses" ``` Then put your key in the environment named by `env_key`: ```bash echo 'export AI_API_KEY="sk-..."' >> ~/.zshrc # or ~/.bashrc source ~/.zshrc ``` ```powershell setx AI_API_KEY "sk-..." ``` Open a new terminal afterwards. ## 3. Run ```bash cd your-project codex ``` Switch models inside Codex with `/model`, using any GPT model id from [Models](https://pinerouter.ai/docs/models). ## Add our media models as tools ```toml [mcp_servers.ai] url = "https://pinerouter.ai/mcp" http_headers = { Authorization = "Bearer sk-..." } ``` See [MCP](https://pinerouter.ai/docs/mcp). ## Troubleshooting * **401**: check that `AI_API_KEY` is set in the terminal you run `codex` from. * **Model not found**: use a model id exactly as listed on [Models](https://pinerouter.ai/docs/models). * **Stream errors**: make sure `wire_api = "responses"` and `base_url` ends in `/v1`. --- # DeepChat > Open-source desktop chat app with search, files and MCP tools. Source: https://pinerouter.ai/docs/apps/deepchat [DeepChat](https://deepchat.thinkinlab.io) is a free desktop chat app for Windows, macOS and Linux. ## Set up 1. Download it from [deepchat.thinkinlab.io](https://deepchat.thinkinlab.io) and open it. 2. Go to **Settings → Model providers** and add an **OpenAI compatible** provider. 3. Fill in: | Field | Value | | ------- | ------------------------------- | | API URL | `https://pinerouter.ai/v1` | | API Key | Your key from [API keys](https://pinerouter.ai/docs/keys) | 4. Click **Verify**, then turn on the models you want. They match the [Models](https://pinerouter.ai/docs/models) page. ## Chat Start a new conversation and pick one of the models. Usage is billed to your balance; see [Usage logs](https://pinerouter.ai/docs/usage). DeepChat also supports MCP servers: add `https://pinerouter.ai/mcp` with the header `Authorization: Bearer sk-...` to generate images, video and audio from a chat. See [MCP](https://pinerouter.ai/docs/mcp). --- # Apps > Use your key in coding agents, chat apps and anything that speaks OpenAI's API. Source: https://pinerouter.ai/docs/apps Most AI apps let you change the API address. Point them at us and they run on your balance, with every model on the [Models](https://pinerouter.ai/docs/models) page. | Setting | Value | | ------------------------ | ---------------------------------------------------------- | | Base URL (OpenAI format) | `https://pinerouter.ai/v1` | | Base URL (Claude format) | `https://pinerouter.ai` | | API key | A key from [API keys](https://pinerouter.ai/docs/keys), starting with `sk-` | | Model | Any model id from [Models](https://pinerouter.ai/docs/models), for example `gpt-5-2` | Want the models inside Claude, ChatGPT or Cursor as tools instead? Use [MCP](https://pinerouter.ai/docs/mcp). Each key is billed to your account. Use a separate key per app, so you can limit or delete one without touching the others. --- # For AI agents > The operating guide for coding agents and assistants that set up and use the API for a person. Source: https://pinerouter.ai/docs/agents This page is written for AI agents (Claude Code, Codex, Cursor, opencode, OpenClaw, Hermes, pi, ChatGPT, Claude). People can read it too. The short version is the site's [llms.txt](https://pinerouter.ai/llms.txt); this page is the full set of rules. Every docs page has a Markdown copy at its URL plus `.md`, every model has one at `https://pinerouter.ai/docs/models/.md`, and the whole docs are in one file at [llms-full.txt](https://pinerouter.ai/docs/llms-full.txt). ## 1. Set up Work out where you run first; the setups differ. | Where you run | What to do | | ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Hosted assistant (claude.ai, ChatGPT) | Install nothing. Ask the person to add `https://pinerouter.ai/mcp` as a custom connector and sign in. No key needed. Stop there. | | Coding agent on the person's machine | Get a key, store it, check it, connect your tool (below). | | Sandbox with restricted network | If `https://pinerouter.ai` does not answer (proxy 403, DNS failure, timeout), say the address is blocked by the network policy and offer the connector or a normal machine. Do not try other hosts. | For a coding agent: 1. Ask the person to sign up at [https://pinerouter.ai/sign-up](https://pinerouter.ai/sign-up), add credit in [Wallet](https://pinerouter.ai/wallet), create a key in [API keys](https://pinerouter.ai/keys) and paste it to you. Suggest a spending limit on the key. 2. Store it as `API_KEY` in the shell profile or a git-ignored `.env`. Never print it in full, never commit it, never send it anywhere except `https://pinerouter.ai`. 3. Check it: `curl -s https://pinerouter.ai/v1/models -H "Authorization: Bearer $API_KEY"` returns the models the key may use. 4. Connect your tool to the [MCP server](https://pinerouter.ai/docs/mcp), or point your SDK at `https://pinerouter.ai/v1` (OpenAI) or `https://pinerouter.ai` (Anthropic). App guides: [Claude Code](https://pinerouter.ai/docs/apps/claude-code), [Codex CLI](https://pinerouter.ai/docs/apps/codex-cli), [Cherry Studio](https://pinerouter.ai/docs/apps/cherry-studio). 5. Make one cheap call to prove it works, report its cost, and tell the person what they can now do. ## 2. Choose a model * Use the exact model id from [Models](https://pinerouter.ai/docs/models) or `GET /v1/models`. Do not use an id remembered from elsewhere; ids here can differ from the maker's. * Read the model's `.md` file before a media call: it lists every input field, its options and default, and the price for each setting. * For text, pick by job: a fast cheap model for drafts, classification and background tasks; a strong model for reasoning and code. * For media, the price depends on the settings (resolution, duration, count). Choose the cheapest settings that meet the request, and say which you chose. ## 3. Call it | Job | Endpoint | | ----------------------------------------------- | ----------------------------------------------------------- | | Chat, OpenAI format | `POST /v1/chat/completions` | | OpenAI Responses (Codex) | `POST /v1/responses` | | Anthropic Messages (Claude Code, Anthropic SDK) | `POST /v1/messages` | | Any media model with all its settings | `POST /v1/jobs`, then `GET /v1/jobs/` or a webhook | | Upload an input file | `POST /v1/files` (multipart `file`), use the returned `url` | | Images, OpenAI format | `POST /v1/images/generations`, `POST /v1/images/edits` | | Video, OpenAI format | `POST /v1/videos`, `GET /v1/videos/` | | Speech and transcription | `POST /v1/audio/speech`, `POST /v1/audio/transcriptions` | | Embeddings | `POST /v1/embeddings` | Full request and response shapes: [API reference](https://pinerouter.ai/docs/reference) or [openapi.json](https://pinerouter.ai/docs/openapi.json). **Jobs are asynchronous.** Start one and keep `data.id`. If you can receive HTTPS calls, pass `webhook_url`; otherwise poll `GET /v1/jobs/` every 5 to 10 seconds (not faster). Stop on `succeeded` (links in `data.outputs`) or `failed` (reason in `data.error`). Images usually take under a minute, video a few minutes. If a job is still running after 15 minutes, tell the person and keep the `id`; do not start a second copy. **Files** go in as `https://` links: a public link, or upload with `POST /v1/files` (kept 1 day). Results are kept 7 days: download what the person wants to keep and save it where they expect it. ## 4. Money * The account is prepaid. Text is charged per token after the reply. A media job reserves its expected cost plus 20% when it starts and is charged the real cost when it finishes. A failed job costs nothing. * Before anything that costs more than a few cents (a video, a batch, a loop over many inputs), tell the person the expected total from the model's price table and wait for their yes. Keep a running total and stop before it passes what they agreed. * Check the balance with `GET /v1/dashboard/billing/subscription` or MCP `get_balance`; spending with `GET /v1/dashboard/billing/usage`. * After work that cost money, report what it cost. * A 403 for balance means stop and ask the person to top up. Do not retry. ## 5. Errors and retries | Status | Meaning | What you do | | ------------- | -------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | | 400 | Invalid request, or the content rules refused it | Fix the request using the model's fields. A content refusal is final: do not retry or reword around it. | | 401 | Missing or wrong key | Check the header. Ask the person for a new key if needed. | | 403 | Balance too low, or the key may not use this model | Ask the person to top up or change the key's settings. | | 429 | Rate limit: 120 requests a minute, 60 successful | Wait, then retry with exponential backoff (2, 4, 8 seconds). | | 500, 502, 503 | The model failed or we are briefly busy | Retry up to 3 times with backoff. A failed job is not charged. | Never retry a job that is still running: poll it. Retrying a started job runs and charges it twice. ## 6. Rules * Safety filters are always on. Sexual content involving minors is refused, and repeated refusals disable the account. * Keep keys out of code, logs, screenshots and chat replies. * Use the person's money as you would your own: the cheapest model and settings that do the job. * If something is unclear, wrong or slow, tell the person and point them to [support](https://pinerouter.ai/about). ## Building it into a product Call the API from your backend, never from a browser or mobile app (the key would leak). Give each of your customers their own key with a spending limit and allowed models, so one customer cannot spend another's balance. --- # AI-generated content > Everything this API returns is made by AI. How outputs are labelled and what you must keep. Source: https://pinerouter.ai/docs/ai-content Every image, video, audio file and text you get from this API is generated by an AI model. ## Labels in your files Many models label what they make: a [C2PA Content Credentials](https://c2pa.org) manifest that records the file was created by AI, other metadata, or a watermark in the pixels or sound. We hand you each file exactly as the model made it. The stored copies behind your result links are the same file, byte for byte, so these labels stay intact. To look at a file's C2PA manifest, you can use a viewer such as [Content Credentials Verify](https://contentcredentials.org/verify). Not every model adds labels; where a model does, we never remove them. Some models add no C2PA Content Credentials manifest to their outputs. PineRouter passes labels through unchanged and does not add any. If you publish such output, you may need to label it yourself (Article 50(4) for deepfakes). ## What you must do * Keep the labels. Do not remove or alter watermarks, content credentials or other AI labels on outputs you publish or pass on. * The EU AI Act ([Article 50](https://ai-act-service-desk.ec.europa.eu/en/ai-act/article-50), [Commission FAQ](https://digital-strategy.ec.europa.eu/en/faqs/transparency-obligations-under-article-50-ai-act)) adds duties for those who publish AI content: * Deepfakes: if you publish AI-generated or edited images, audio or video that resemble real people, objects, places or events and would falsely appear authentic or truthful, you must disclose that the content was artificially generated or manipulated. For clearly artistic, creative, satirical or fictional work, the disclosure can be light, so long as it does not spoil the work. * Text: if you publish AI-generated text to inform the public on matters of public interest, you must disclose that it is AI-generated, unless a person has substantively reviewed it or exercised editorial control over it, and someone holds editorial responsibility for it. Spell checks, grammar fixes and other light edits do not count. * These duties come on top of the rule above: keep the labels in every case. * If you build an app or service on this API, your users get AI-generated content too: tell them, and keep the labels on what you deliver. Our [Terms](https://pinerouter.ai/user-agreement) apply. --- # Keys, billing and limits > Authentication, how you are charged, rate limits, file retention and errors. Source: https://pinerouter.ai/docs/auth ## Authentication Send your key in the `Authorization` header on every request: ```http Authorization: Bearer sk-... ``` Keys are created and deleted in the console under [API keys](https://pinerouter.ai/docs/keys). You can give a key its own spending limit, an expiry date and a list of allowed models. Delete a key straight away if it leaks. ## Billing * **Prepaid.** Each request is charged from your balance at the price on the [Models](https://pinerouter.ai/docs/models) page. * **Text** is charged per token, after the reply. * **Media jobs** reserve the expected cost plus 20% when they start, then settle at the real cost when they finish; the difference comes back to your balance. A failed job costs nothing. * If a job turns out to cost more than it reserved, your balance can go below zero. New requests then wait until you top up. * See your balance with `GET /v1/dashboard/billing/subscription` and spending with `GET /v1/dashboard/billing/usage`, or in the console. ## Rate limits Each account may send **120 requests a minute, 60 of them successful**. Over the limit you get HTTP `429`; wait and retry. Need more? Contact us. Details and backoff code: [Rate limits](https://pinerouter.ai/docs/rate-limits). ## Files and results * Pass input files (images, audio, video) as public `https://` links. Files you upload in the Studio are kept for **1 day**. * Result links stay available for **7 days**, then the files are deleted. Download anything you want to keep. ## Errors Errors come back with an HTTP status and a JSON body: `{"error": {"message": "...", "type": "...", "code": "..."}}` (jobs: `{"code": 400, "msg": "..."}`). | Status | Meaning | What to do | | ------------- | -------------------------------------------------- | ------------------------------------------------------------------ | | 400 | The request is invalid or breaks the content rules | Fix the request; content-rules refusals count against your account | | 401 | Missing or wrong key | Check the `Authorization` header | | 403 | Balance too low, or the key may not use this model | Top up, or check the key's settings | | 429 | Rate limit | Wait and retry with backoff | | 500, 502, 503 | The model failed, or we are briefly overloaded | Retry; a failed job is not charged | Every status, both body shapes and retry rules: [Errors](https://pinerouter.ai/docs/errors). ## Content rules Our [Terms](https://pinerouter.ai/user-agreement) apply. Safety filters are always on. Sexual content involving minors is refused, and repeated refusals disable an account automatically. --- # Changelog > Changes to the API, models, docs and console that affect you, newest first. Source: https://pinerouter.ai/docs/changelog Model removals are also listed on [Deprecations](https://pinerouter.ai/docs/deprecations). ## 2 November 2026 * **Docs for AI agents.** New [llms.txt](https://pinerouter.ai/llms.txt) and [llms-full.txt](https://pinerouter.ai/docs/llms-full.txt), a Markdown copy of every docs page (add `.md` to its URL), one Markdown file per model (`/docs/models/.md`) and one merged [OpenAPI file](https://pinerouter.ai/docs/openapi.json). New guide: [For AI agents](https://pinerouter.ai/docs/agents). * **New guide: [AI-generated content](https://pinerouter.ai/docs/ai-content).** Outputs keep the labels the model adds (C2PA metadata, watermarks), byte for byte. * **Jobs: webhooks, uploads and clearer fields.** Pass `webhook_url` on `POST /v1/jobs` to be called when a job finishes (Standard Webhooks signatures). Upload input files with `POST /v1/files`. Jobs now return `id`, `status`, `outputs` and `error`; the older fields still work. [Jobs](https://pinerouter.ai/docs/jobs). * **Jobs.** `GET /v1/jobs/{id}` now returns only the fields you need. A finished job shows `generating` until its files are copied to our storage (up to 10 minutes), so result links always point to lasting copies. Error and fail messages no longer contain third-party links. * **Docs pages** have canonical links, and the site has a `robots.txt` and `sitemap.xml`. ## 1 November 2026 * **Text holds.** A text request now reserves its longest possible answer before it runs: `max_tokens` if set, otherwise 8192 tokens, at the output price. You still pay only the real tokens; the rest is released when the reply ends. * **Model removed:** `google/gemini-2-5-pro-tts`. See [Deprecations](https://pinerouter.ai/docs/deprecations). * **Console** cleaned up: clearer type, colour and wording throughout. ## 31 October 2026 * **Legal pages** published under `/legal`: terms, privacy, acceptable use, refunds, cookies, model provider terms. * **Console** redesign. The app sidebar slides out on narrow screens. ## 30 October 2026 * **Docs site** with a searchable [Models](https://pinerouter.ai/docs/models) index and a page per model (price per setting, every input field, an example), the [API reference](https://pinerouter.ai/docs/reference) with a playground, and [Apps](https://pinerouter.ai/docs/apps) guides for Claude Code, Codex CLI, Cherry Studio, CC Switch and DeepChat. ## 29 October 2026 * **Media models.** Image, video, music and speech models through one [Jobs](https://pinerouter.ai/docs/jobs) endpoint, `POST /v1/jobs` and `GET /v1/jobs/{taskId}`, and through OpenAI-style `/v1/images/generations` and `/v1/videos`. Each job reserves its expected cost plus 20% and settles at the real cost; failed jobs cost nothing. * **Text models** on `/v1/chat/completions`, `/v1/responses` and `/v1/messages`. * **[MCP server](https://pinerouter.ai/docs/mcp)** at `/mcp`, with sign-in for Claude.ai and ChatGPT connectors, or your key. * **Studio** in the console: Image, Edit image, Video, Music, Speech, Motion and avatars, Tools (upscale, remove background, isolate voice) and a Gallery, with follow-up actions on finished results. * **Rate limits:** 120 requests a minute per account, 60 of them successful. See [Rate limits](https://pinerouter.ai/docs/rate-limits). * **Retention:** uploaded files are kept 1 day, generated files 7 days. See [Data and logging](https://pinerouter.ai/docs/data-and-logging). * **Content rules:** safety filters always on; sexual content involving minors is refused. * **Sign-in** with a 6-digit email code; sign-up needs a verified email. --- # Data and logging > What is stored when you call the API, for how long, and who else sees your requests. Source: https://pinerouter.ai/docs/data-and-logging The [Privacy Policy](https://pinerouter.ai/legal/privacy) is the binding text. This page is the short version for developers. ## What is kept, and for how long | Data | Kept | | ----------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- | | Text of chat prompts and replies | Not kept | | Usage records: time, model, key name, token or unit counts, price, duration, status, request id | While your account is open, so you can see them in [Usage logs](https://pinerouter.ai/docs/usage) | | Media job records: model, status, result links, and the prompt and settings you sent | With your job history while your account is open | | Files you upload in the Studio | 1 day, then deleted | | Generated files (images, video, audio) behind result links | 7 days, then deleted | | Server logs | 14 days | | IP address of API requests | Only if you turn on "record IP" in your settings | | Encrypted backups | Deleted data leaves them within 30 days | Download any result you want to keep within 7 days. After that the link stops working and the file cannot be recovered. ## Who else sees your requests Your prompts, input files and outputs pass through our servers to the company that runs the model. The model makers handle that data under their own terms; we do not control whether they log or keep it. Their terms are listed on the [Model Provider Terms](https://pinerouter.ai/legal/model-provider-terms) page. There is no zero-retention option today, and no way to choose or exclude a model maker's servers for a request. ## What you should do * Do not send secrets, passwords or data you are not allowed to share with the model maker. * Keep your own copy of outputs; result links are not storage. * Turn on "record IP" only if you need it for your own auditing. * To see, export or delete your data, use the contact in the [Privacy Policy](https://pinerouter.ai/legal/privacy). --- # Deprecations > How model removals are announced, and the list of models that have been removed. Source: https://pinerouter.ai/docs/deprecations ## Policy * We serve models that their makers run. When a maker stops offering a model, we remove it too, often at short notice. * A removed model disappears from `GET /v1/models` and from [Models](https://pinerouter.ai/docs/models). Calls to its id then fail with an error (see [Errors](https://pinerouter.ai/docs/errors)). * Every removal is listed below and in the [Changelog](https://pinerouter.ai/docs/changelog). * Model settings can change too, for example a new resolution or duration option, or one that is no longer offered. The model's page always shows the current fields and prices. To stay safe: * Read model ids from `GET /v1/models` instead of hard-coding a long list, or check it on start-up. * Keep a second model in mind for each job, and switch in your own code if its first choice stops working. * Check this page or the [Changelog](https://pinerouter.ai/docs/changelog) when a model stops working. ## Removed models | Model id | Removed | | --------------------------- | --------------- | | `google/gemini-2-5-pro-tts` | 1 November 2026 | --- # Errors > Every status code the API returns, the two body shapes, what to do and when to retry. Source: https://pinerouter.ai/docs/errors Every error has an HTTP status and a JSON body. The message ends with a request id, for example `(request id: 202611020537...)`. Keep it: support can find your request with it. ## Body shapes `/v1/chat/completions`, `/v1/responses`, `/v1/models`, `/v1/images/*`, `/v1/videos/*`, `/v1/audio/*` and `/v1/embeddings` answer in OpenAI's shape. This is a real answer to a request with a wrong key: ```json {"error": {"code": "", "message": "Invalid token (request id: 202611020537046636626168268d9d6vfJBhWIJ)", "type": "api_error"}} ``` | Field | Meaning | | --------------- | -------------------------------------------------------------------------------- | | `error.message` | What went wrong, with the request id | | `error.type` | For example `api_error` or `invalid_request_error` | | `error.code` | A short code such as `insufficient_user_quota` or `model_not_found`; often empty | | `error.param` | Present on some errors; usually empty | Errors from the model on `/v1/messages` use Anthropic's shape: ```json {"type": "error", "error": {"type": "api_error", "message": "... (request id: ...)"}} ``` Errors found before the request reaches the model (wrong key, rate limit, a model the key may not use) come back in the OpenAI shape shown in the first tab. Read both. `POST /v1/jobs` and `GET /v1/jobs/{id}` answer with `{code, msg, data}`. `code` repeats the HTTP status: ```json {"code": 404, "msg": "Task not found", "data": null} ``` Two cases come back in the OpenAI shape instead, because they are answered before the job is read: a missing or wrong key (`401`) and the rate limit (`429`). A job that started and then failed is not an HTTP error. `GET /v1/jobs/{id}` answers `200` with `data.status` set to `failed` and the reason in `data.error`. A failed job costs nothing. ## Status codes | Status | Meaning | What to do | Retry? | | ------ | -------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------- | | `400` | The request is invalid: a missing field, a wrong value, a field the model does not take, or a refusal by the content rules | Fix the request using the model's fields on [Models](https://pinerouter.ai/docs/models) | No. A content refusal is final; do not reword around it | | `401` | No key, a wrong key, or a key that is turned off or expired | Check the `Authorization: Bearer sk-...` header and the key in [API keys](https://pinerouter.ai/docs/keys) | No | | `403` | Balance too low (`insufficient_user_quota`), the key may not use this model, or your IP is not on the key's allowlist | Top up in [Wallet](https://pinerouter.ai/docs/wallet), or change the key's model list or IP allowlist | No, not until you fix the cause | | `404` | Wrong path, or no job or video with this id on your account | Check the URL and the id | No | | `413` | The request body is too large (over 128 MB) | Send files as `https://` links instead of inline data | No | | `429` | Rate limit: 120 requests a minute, 60 of them successful. On jobs it can also mean the model is at capacity | Wait for the `Retry-After` header (seconds), then retry. See [Rate limits](https://pinerouter.ai/docs/rate-limits) | Yes, after the wait | | `500` | An error on our side or the model's | Retry with backoff | Yes, up to 3 times | | `502` | The model's server sent a bad or empty answer | Retry with backoff | Yes, up to 3 times | | `503` | We are briefly overloaded, or no server can take this model right now (`model_not_found`) | Check the model id on [Models](https://pinerouter.ai/docs/models); then retry with backoff | Yes, up to 3 times | | `504` | The model's server or a proxy on the way timed out | Retry with backoff | Yes, up to 3 times | The model's own servers can return other statuses; they are passed to you as they come. ## Timeouts * A text call with `"stream": true` is closed if no data arrives for 5 minutes. * Other calls wait until the model answers. `POST /v1/images/generations` waits for the image, which can take a minute or more, so give your HTTP client a long timeout (several minutes). * For anything long, prefer [Jobs](https://pinerouter.ai/docs/jobs): the call returns a job `id` at once; you poll or get a webhook. If your client times out on a `POST /v1/jobs`, the job may still have started. Check **Task logs** in [Usage logs](https://pinerouter.ai/docs/usage) before you send it again. There are no idempotency keys yet, so a second request is a second job, charged again. ## Retry rules * Retry only `429`, `500`, `502`, `503` and `504`. * Wait `Retry-After` seconds when the header is present. Otherwise back off: 2, 4, then 8 seconds, plus a little random jitter. Stop after 3 tries. * Never resend a job that is still running: poll `GET /v1/jobs/{id}`. * Do not retry `400`, `401`, `403` or `413`. They fail the same way until you change the request, the key or the balance. Repeated content refusals can disable the account. Code for backoff: [Rate limits](https://pinerouter.ai/docs/rate-limits#back-off-and-retry). ## Errors during a stream Once a stream has started, the status is already `200`. An error then arrives as a last event in the stream. See [Streaming](https://pinerouter.ai/docs/streaming#errors-during-a-stream). --- # FAQ > Short answers on billing, failed jobs, refunds, limits, files, data, model changes and support. Source: https://pinerouter.ai/docs/faq ## Billing ### How am I charged? The account is prepaid. Each request is charged from your balance at the price on [Models](https://pinerouter.ai/docs/models). Text is charged per token after the reply. A media job reserves its expected cost plus 20% when it starts and is charged the real cost when it finishes; the rest goes back to your balance. Details: [Keys, billing and limits](https://pinerouter.ai/docs/auth). ### Why did a text request hold more than it cost? Before a text request runs, it reserves enough for its longest possible answer: `max_tokens` if you set it, otherwise 8192 tokens, at the model's output price. When the reply ends you pay the real tokens and the rest of the hold is released. Set `max_tokens` to keep holds small. ### Can my balance go below zero? Yes, if a job costs more than it reserved. New requests then wait until you top up in [Wallet](https://pinerouter.ai/docs/wallet). ### Where do I see what I spent? In [Usage logs](https://pinerouter.ai/docs/usage), or from the API: `GET /v1/dashboard/billing/usage` (spending) and `GET /v1/dashboard/billing/subscription` (balance). ### Is there a subscription or a free tier? No subscription. You pay for what you use from a prepaid balance. ## Failed jobs and refunds ### Am I charged for a failed job? No. A job that ends in `fail` costs nothing; its hold comes back and a refund row shows in [Usage logs](https://pinerouter.ai/docs/usage). The reason is in `data.failMsg`. ### I was charged for something that failed. What now? Send the request id (it is in every error message and in Usage logs) through the contact on the [About](https://pinerouter.ai/about) page. ### Can I get my money back? See the [Terms](https://pinerouter.ai/legal/terms) and the billing pages on this site. ### Can I cancel a running job? No, not yet. A started job runs to the end. Do not start a second copy; poll the first. ## Limits ### What are the rate limits? 120 requests a minute per account, 60 of them successful. Over that you get `429` with `Retry-After`. See [Rate limits](https://pinerouter.ai/docs/rate-limits). ### How long can a job take? Images usually finish within a minute, video within a few minutes. If a job is still running after 15 minutes, keep its `id` and keep polling now and then. ### Is there a maximum request size? Request bodies over 128 MB are refused with `413`. Send files as `https://` links instead. ## Files and data ### How long are my results kept? Generated files are kept 7 days, then deleted. Files you upload in the Studio are kept 1 day. Download what you want to keep. ### How do I send an input file? As an `https://` link in the model's field (for example `image_url`). Use any public link, or upload the file with `POST /v1/files` and use the `url` it returns (kept 1 day). See [Jobs](https://pinerouter.ai/docs/jobs#inputs). ### Do you keep my prompts? The text of chat prompts and replies is not kept. Usage records (model, tokens, price, time) are. Media job records keep the prompt and settings you sent. The companies that run the models handle requests under their own terms. See [Data and logging](https://pinerouter.ai/docs/data-and-logging). ### Is there a zero data retention option? No, not today. ## Models ### Which model id do I use? The exact id from [Models](https://pinerouter.ai/docs/models) or `GET /v1/models`. Ids here can differ from the maker's own. ### Can models or prices change? Yes. Models can be added or removed, and prices can change. The [Models](https://pinerouter.ai/docs/models) page and `GET /v1/models` always show what is live now. Removals are listed on [Deprecations](https://pinerouter.ai/docs/deprecations) and in the [Changelog](https://pinerouter.ai/docs/changelog). ### Can I use my own provider key, or set fallback models? No. Every request is billed from your balance here, and each request goes to the one model you name. Handle fallbacks in your own code. ### Are there webhooks for finished jobs? Yes. Add `webhook_url` to `POST /v1/jobs` and we call it when the job finishes, signed per the Standard Webhooks spec. Or poll `GET /v1/jobs/{id}` every 5 to 10 seconds. See [Jobs](https://pinerouter.ai/docs/jobs#webhook). ## Support ### How do I get help? Use the contact on this site's [About](https://pinerouter.ai/about) page. Include the request id from the error message, the model id and the time of the request. --- # Images > Create and edit images with OpenAI's image API format. Source: https://pinerouter.ai/docs/images `POST /v1/images/generations` in OpenAI's format, for every image model on the [Models](https://pinerouter.ai/docs/models) page. ```bash curl https://pinerouter.ai/v1/images/generations \ -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \ -d '{"model": "nano-banana-pro", "prompt": "A matte ceramic mug on a beige backdrop, studio light", "size": "1024x1024"}' ``` The answer has `data[].url`. Image models can take a while; the call waits for the result. | Field | Meaning | | ------------------- | ---------------------------------------------------------------------------------- | | `model` | Model id from the Models page | | `prompt` | What to create | | `size` | `WIDTHxHEIGHT`; mapped to the nearest aspect ratio and resolution the model offers | | `quality` | Passed on where the model has it | | `n` | Number of images, where the model supports it | | `image` or `images` | Links to reference images, for edit and image-to-image models | Need a model's own settings (resolution, style, seed, output format)? Use [Jobs](https://pinerouter.ai/docs/jobs), which takes every field listed on the model's page. Outputs are AI-generated and keep the labels the model adds (C2PA metadata, watermarks). See [AI-generated content](https://pinerouter.ai/docs/ai-content). --- # Quickstart > One API key for text, image, video, music and speech models. Source: https://pinerouter.ai/docs PineRouter gives you one API key for text, image, video, music and speech models. Requests are billed from your balance at the prices on the [Models](https://pinerouter.ai/docs/models) page. ### Get a key Sign in to the console and open [API keys](https://pinerouter.ai/keys) to create a key. It starts with `sk-`. Add credit in [Wallet](https://pinerouter.ai/wallet) first; a request only starts when your balance covers it. ### Make a call The API follows OpenAI's format, so OpenAI's SDKs work with a different base URL: `https://pinerouter.ai/v1`. ```bash curl https://pinerouter.ai/v1/chat/completions \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{"model": "gemini-3-8-flash", "messages": [{"role": "user", "content": "Say hello"}]}' ``` ```python from openai import OpenAI client = OpenAI(base_url="https://pinerouter.ai/v1", api_key="sk-...") r = client.chat.completions.create( model="gemini-3-8-flash", messages=[{"role": "user", "content": "Say hello"}], ) print(r.choices[0].message.content) ``` ```js import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://pinerouter.ai/v1", apiKey: "sk-..." }); const r = await client.chat.completions.create({ model: "gemini-3-8-flash", messages: [{ role: "user", content: "Say hello" }], }); console.log(r.choices[0].message.content); ``` ### Pick what you need | You want | Use | | -------------------------------------------------------------------------- | ------------------------------------------------------------------ | | Text, chat, code | [Text](https://pinerouter.ai/docs/text): `POST /v1/chat/completions` or `POST /v1/responses` | | Images | [Images](https://pinerouter.ai/docs/images): `POST /v1/images/generations` | | Video | [Video](https://pinerouter.ai/docs/video): `POST /v1/videos`, then poll | | Any model with all its settings (music, speech, avatars, upscaling, edits) | [Jobs](https://pinerouter.ai/docs/jobs): `POST /v1/jobs`, then poll | | The models inside Claude, ChatGPT, Cursor or Codex | [MCP](https://pinerouter.ai/docs/mcp) | | A coding agent or chat app on your key | [Apps](https://pinerouter.ai/docs/apps) | The **Studio** in the console runs the same models from a form, and the **Playground** lets you chat with any text model. --- # Integrations > Exact settings to use your key in coding agents, chat apps, frameworks and automation tools. Source: https://pinerouter.ai/docs/integrations Almost every tool that speaks OpenAI's API can use this one. You need three values: | Setting | Value | | --------------------------- | --------------------------------------------------- | | Base URL (OpenAI format) | `https://pinerouter.ai/v1` | | Base URL (Anthropic format) | `https://pinerouter.ai` | | API key | A key from [API keys](https://pinerouter.ai/docs/keys), starting with `sk-` | | Model | An id from [Models](https://pinerouter.ai/docs/models), for example `gpt-5-2` | Use one key per tool, so you can limit or delete one without touching the others. Setting labels below are taken from each tool's own docs; they can move between versions. ## Coding agents ### Claude Code ```bash export ANTHROPIC_BASE_URL="https://pinerouter.ai" export ANTHROPIC_AUTH_TOKEN="sk-..." export ANTHROPIC_MODEL="gpt-5-2" ``` Full guide: [Claude Code](https://pinerouter.ai/docs/apps/claude-code). ### Codex CLI ```toml # ~/.codex/config.toml model = "gpt-5-5" model_provider = "ai" [model_providers.ai] name = "AI" base_url = "https://pinerouter.ai/v1" env_key = "AI_API_KEY" wire_api = "responses" ``` Full guide: [Codex CLI](https://pinerouter.ai/docs/apps/codex-cli). ### Cursor In **Cursor Settings > Models**, paste your key in the **OpenAI API Key** field, turn on the base URL override and set it to `https://pinerouter.ai/v1`, then add a model id such as `gpt-5-2` by hand. Cursor uses your own key for chat models only; Tab completion stays on Cursor's models. Cursor sends these requests from its own servers. ### Cline, Roo Code **API Provider**: `OpenAI Compatible`, then: | Field | Value | | -------- | ---------------------------- | | Base URL | `https://pinerouter.ai/v1` | | API Key | `sk-...` | | Model | `gpt-5-2` | ### Kilo Code Add a custom provider: **Provider API** `OpenAI Compatible`, **Base URL** `https://pinerouter.ai/v1`, **API key** `sk-...`, then add model ids under **Models** (or pick from the fetched list). ### opencode ```json { "$schema": "https://opencode.ai/config.json", "provider": { "ai": { "npm": "@ai-sdk/openai-compatible", "name": "AI", "options": { "baseURL": "https://pinerouter.ai/v1", "apiKey": "{env:API_KEY}" }, "models": { "gpt-5-2": { "name": "GPT 5.2" } } } } } ``` Save it as `opencode.json` in your project. ## Chat apps ### Cherry Studio, CC Switch, DeepChat Step-by-step guides: [Cherry Studio](https://pinerouter.ai/docs/apps/cherry-studio), [CC Switch](https://pinerouter.ai/docs/apps/cc-switch), [DeepChat](https://pinerouter.ai/docs/apps/deepchat). ### LobeChat (self-hosted) ```bash OPENAI_API_KEY=sk-... OPENAI_PROXY_URL=https://pinerouter.ai/v1 ``` ### Open WebUI ```bash OPENAI_API_BASE_URL=https://pinerouter.ai/v1 OPENAI_API_KEY=sk-... ``` Or add the same URL and key as an OpenAI API connection in the admin settings under **Connections**. ### Chatbox **Settings > Model Provider > Add**, type **OpenAI API compatible**: | Field | Value | | -------- | ------------------------------------------------ | | API Host | `https://pinerouter.ai` | | API Path | Leave empty (defaults to `/v1/chat/completions`) | | API Key | `sk-...` | Add at least one model id, save and click **Check**. ## Frameworks ### Vercel AI SDK ```ts const ai = createOpenAICompatible({ name: "ai", baseURL: "https://pinerouter.ai/v1", apiKey: process.env.API_KEY }); const { text } = await generateText({ model: ai("gpt-5-2"), prompt: "Hello" }); ``` ### LangChain ```python from langchain_openai import ChatOpenAI llm = ChatOpenAI(model="gpt-5-2", base_url="https://pinerouter.ai/v1", api_key="sk-...") print(llm.invoke("Hello").content) ``` `ChatOpenAI` reads only fields in OpenAI's standard format, so model-specific extras in answers are dropped. ### LlamaIndex ```python # pip install llama-index-llms-openai-like from llama_index.llms.openai_like import OpenAILike llm = OpenAILike(model="gpt-5-2", api_base="https://pinerouter.ai/v1", api_key="sk-...", is_chat_model=True, context_window=128000) ``` Set `context_window` to the model's real context length. ### LiteLLM ```python import litellm r = litellm.completion(model="openai/gpt-5-2", api_base="https://pinerouter.ai/v1", api_key="sk-...", messages=[{"role": "user", "content": "Hello"}]) ``` The `openai/` prefix tells LiteLLM to use the OpenAI format; the part after it is our model id. ## Automation ### n8n Create an **OpenAI** credential: **API Key** `sk-...`, **Base URL** `https://pinerouter.ai/v1`. Use it in the OpenAI and Chat OpenAI nodes and type the model id. ### Dify Install the **OpenAI-API-compatible** model provider, then add a model: **Model Name** `gpt-5-2`, **API Key** `sk-...`, **API endpoint URL** `https://pinerouter.ai/v1`. ### Flowise Use the **ChatOpenAI** node with an OpenAI credential holding your key, set **Model Name** to an id such as `gpt-5-2`, and under **Additional Parameters** set **Base Path** to `https://pinerouter.ai/v1`. ## MCP clients Claude.ai, ChatGPT, Claude Code, Codex, Cursor and other MCP clients can call the media and text models as tools through `https://pinerouter.ai/mcp`, with sign-in or your key. See [MCP](https://pinerouter.ai/docs/mcp). ## Media models in these tools These tools speak the text formats. For images, video, music and speech with every setting, call [Jobs](https://pinerouter.ai/docs/jobs) from your code, or use the MCP tools above. --- # Jobs > One endpoint for every media model, with all of its settings, polling or webhooks, and file uploads. Source: https://pinerouter.ai/docs/jobs Images, video, music, speech, avatars, lip sync, upscaling and edits all run through `/v1/jobs`. You send the model's own fields, as listed on each model's page under [Models](https://pinerouter.ai/docs/models). ## Start a job ```bash curl https://pinerouter.ai/v1/jobs \ -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \ -d '{"model": "z-image", "input": {"prompt": "A red paper boat on a calm lake", "aspect_ratio": "1:1"}}' ``` ```json {"code": 200, "msg": "success", "data": {"id": "task_abc123", "status": "queued"}} ``` ## Get the result Either poll the job, or give a `webhook_url` and we call you when it finishes. ### Poll ```bash curl https://pinerouter.ai/v1/jobs/task_abc123 -H "Authorization: Bearer $API_KEY" ``` ```json {"code": 200, "msg": "success", "data": {"id": "task_abc123", "model": "z-image", "status": "succeeded", "outputs": ["https://files.example.com/outputs/abc.png"]}} ``` `data.status` is `queued`, `running`, `succeeded` or `failed`. On `succeeded`, `data.outputs` lists the links to your files. On `failed`, `data.error` says why; a failed job costs nothing. Poll every 5 to 10 seconds; most images finish in under a minute, videos in a few minutes. ```python import time, requests BASE = "https://pinerouter.ai" H = {"Authorization": "Bearer sk-..."} job = requests.post(f"{BASE}/v1/jobs", headers=H, json={"model": "z-image", "input": {"prompt": "A red paper boat"}}).json()["data"] while job["status"] not in ("succeeded", "failed"): time.sleep(5) job = requests.get(f"{BASE}/v1/jobs/{job['id']}", headers=H).json()["data"] print(job["outputs"] if job["status"] == "succeeded" else job["error"]) ``` Older field names still work: `taskId` (same as `id`), `state` (`waiting`, `queuing`, `generating`, `success`, `fail`), `resultJson` (a JSON string with `resultUrls`) and `failMsg`. ### Webhook Add `webhook_url` (a public `https://` address) to the request. When the job succeeds or fails we `POST` the same job object to it: ```bash curl https://pinerouter.ai/v1/jobs \ -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \ -d '{"model": "z-image", "input": {"prompt": "A red paper boat"}, "webhook_url": "https://your.app/hooks/jobs"}' ``` ```json {"type": "job.succeeded", "timestamp": "2026-11-02T10:00:00Z", "data": {"id": "task_abc123", "status": "succeeded", "outputs": ["https://..."]}} ``` * Signed per the [Standard Webhooks](https://www.standardwebhooks.com) spec: headers `webhook-id`, `webhook-timestamp` and `webhook-signature` (`v1,`). The signature is HMAC-SHA256 of `..` with **your API key** as the secret. * Answer with any `2xx` within 15 seconds. Otherwise we retry after 1, 5, 15 and 60 minutes, then stop. * Delivery is at least once: use `webhook-id` to ignore repeats. * Private and local addresses are refused. ```python import base64, hashlib, hmac def verify(body: bytes, headers, api_key: str) -> bool: msg = f"{headers['webhook-id']}.{headers['webhook-timestamp']}.".encode() + body sig = base64.b64encode(hmac.new(api_key.encode(), msg, hashlib.sha256).digest()).decode() return any(hmac.compare_digest(sig, s.split(",", 1)[1]) for s in headers["webhook-signature"].split()) ``` ## Inputs * `input` takes the fields on the model's page. Leave a field out to use its default. * Files go in as `https://` links (for example `image_url`, `image_urls`, `video_url`, `audio_url`). Use any public link, or upload the file first: ```bash curl https://pinerouter.ai/v1/files -H "Authorization: Bearer $API_KEY" -F "file=@photo.png" ``` ```json {"id": "6f1c...", "object": "file", "url": "https://files.example.com/uploads/6f1c....png", "bytes": 48213, "content_type": "image/png", "expires_at": 1793680000} ``` Images up to 10 MB, video and audio up to 50 MB. Uploads are deleted after 1 day. * Safety filters are always on. ## Cost The model's page shows its prices by setting. A job reserves its expected cost plus 20% when it starts and is charged the real cost when it finishes; a failed job costs nothing. Result links last 7 days. --- # API keys > Create, limit, copy and delete the keys your apps use. Source: https://pinerouter.ai/docs/keys Open **API keys** in the console menu, or go to [/keys](https://pinerouter.ai/keys). The list shows each key's name, status, how much of its limit is used, and when it was last used. ## Create a key 1. Click **Create API key**. 2. Name it after where it will be used, for example "Production" or "Laptop". 3. Optionally set a spending limit, an expiry date, the models it may use, and the IP addresses it may be used from. 4. Save, then copy the key from the list. It starts with `sk-`. | Setting | What it does | | ------------ | --------------------------------------------------------------------------- | | Name | Your label for the key | | Expiry | The key stops working after this date | | Quota | The most this key may spend; or turn on unlimited to use your whole balance | | Model limits | Only these models may be called with the key; empty means all | | IP allowlist | Only these IPs or CIDR ranges may use the key; empty means any | A key works like a password. Keep it out of code repositories, screenshots, logs and chats. If you think it leaked, delete it and create a new one. ## Edit, turn off or delete Each row has **Edit**, an on/off switch and **Delete**. Turning a key off keeps its settings and blocks calls; deleting it is permanent and takes effect at once. ## Use a key in an app **Copy connection info** in a key's menu copies the key and the base URL together. For coding agents and chat apps, see [Apps](https://pinerouter.ai/docs/apps). --- # MCP > Use the models from Claude, ChatGPT, Cursor, Codex and other AI tools. Source: https://pinerouter.ai/docs/mcp Our MCP server lets AI tools call the models on your account. It is at: ```text https://pinerouter.ai/mcp ``` ## Claude.ai and ChatGPT (connectors) Add a custom connector with the URL above. You sign in with your account; no key to copy. ## Claude Code ```bash claude mcp add --transport http ai https://pinerouter.ai/mcp --header "Authorization: Bearer YOUR_API_KEY" ``` ## Cursor, Claude Desktop and others ```json {"mcpServers": {"ai": {"url": "https://pinerouter.ai/mcp", "headers": {"Authorization": "Bearer YOUR_API_KEY"}}}} ``` ## Codex ```toml # ~/.codex/config.toml [mcp_servers.ai] url = "https://pinerouter.ai/mcp" http_headers = { Authorization = "Bearer YOUR_API_KEY" } ``` ## Tools | Tool | Does | | ---------------- | --------------------------------------------------- | | `list_models` | Models with your prices | | `get_balance` | Your balance | | `chat` | Text models | | `generate_image` | Images | | `generate_video` | Starts a video | | `run_model` | Any model with its own settings (see [Jobs](https://pinerouter.ai/docs/jobs)) | | `get_job` | Status and result of a video or job | Calls are billed like API calls. --- # Switch from another API > Move from the OpenAI SDK, the Anthropic SDK, OpenRouter, or a queue-style media API such as fal or Replicate. Source: https://pinerouter.ai/docs/migrate Most moves are a new base URL, a new key and new model ids. Model ids here are our own: take them from [Models](https://pinerouter.ai/docs/models) or `GET /v1/models`, never from another provider's list. ## From the OpenAI SDK Change two lines. Everything else (messages, tools, streaming, the Responses API) stays. ```python from openai import OpenAI client = OpenAI( base_url="https://pinerouter.ai/v1", # was https://api.openai.com/v1 api_key="sk-...", # a key from this site ) r = client.chat.completions.create(model="gpt-5-5", messages=[{"role": "user", "content": "Hi"}]) ``` ```js import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://pinerouter.ai/v1", // was https://api.openai.com/v1 apiKey: "sk-...", // a key from this site }); ``` Or set the environment variables the SDK already reads: `OPENAI_BASE_URL=https://pinerouter.ai/v1` and `OPENAI_API_KEY=sk-...`. | You used | Use here | | --------------------------------------- | ------------------------------------------------------------------------------------- | | OpenAI's model ids | Our ids, such as `gpt-5-5`, `gpt-6-luna` or `gpt-6-1-sol`, from [Models](https://pinerouter.ai/docs/models) | | `/v1/chat/completions`, `/v1/responses` | The same paths | | `/v1/images/generations`, `/v1/videos` | The same paths, for image and video models; see [Images](https://pinerouter.ai/docs/images) and [Video](https://pinerouter.ai/docs/video) | | Files, Assistants, Batch, fine-tuning | Not available. Only the endpoints in the [API reference](https://pinerouter.ai/docs/reference) are served | ## From the Anthropic SDK Point the SDK at our address without `/v1`. It calls `POST /v1/messages`, which any text model here can answer. ```python import anthropic client = anthropic.Anthropic(base_url="https://pinerouter.ai", api_key="sk-...") msg = client.messages.create( model="gpt-5-2", max_tokens=500, messages=[{"role": "user", "content": "Hi"}], ) print(msg.content[0].text) ``` ```js import Anthropic from "@anthropic-ai/sdk"; const client = new Anthropic({ baseURL: "https://pinerouter.ai", apiKey: "sk-..." }); const msg = await client.messages.create({ model: "gpt-5-2", max_tokens: 500, messages: [{ role: "user", content: "Hi" }], }); ``` Environment variables work too: `ANTHROPIC_BASE_URL=https://pinerouter.ai` and `ANTHROPIC_API_KEY=sk-...` (Claude Code reads `ANTHROPIC_AUTH_TOKEN`; see [Claude Code](https://pinerouter.ai/docs/apps/claude-code)). * Claude models are not on the [Models](https://pinerouter.ai/docs/models) list today. Pick another text model, such as `gpt-5-2`, `gemini-3-8-flash` or `kimi-k3`. * Tools, system prompts and streaming are passed to the model in Messages format. Whether a feature such as extended thinking works depends on the model you pick. * The Message Batches, Files and token counting endpoints are not available. ## From OpenRouter | OpenRouter | Here | | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | | `https://openrouter.ai/api/v1` | `https://pinerouter.ai/v1` | | `maker/model` ids | Our ids, such as `gpt-5-5` or `gemini-3-8-flash`, from [Models](https://pinerouter.ai/docs/models). Text model ids have no maker prefix | | `models: [...]` fallback list, `provider` routing options | Not available. Each request goes to the one model you name; handle fallbacks in your code | | Bring your own provider key | Not available. Every request is billed from your balance here | | Credits | Prepaid balance in [Wallet](https://pinerouter.ai/docs/wallet) | The OpenAI and Anthropic SDK setups above apply unchanged. Streaming works the same way, and usage comes in the last chunk; see [Streaming](https://pinerouter.ai/docs/streaming). ## From fal, Replicate and other queue-style media APIs The flow is the same idea: submit, then poll for the result. | Queue-style API | Here | | --------------------------- | -------------------------------------------------------------------------------------------- | | Submit to the model's queue | `POST /v1/jobs` with `{"model": "", "input": {...}}` | | Status and result | `GET /v1/jobs/{id}`: `data.status` is `queued`, `running`, `succeeded` or `failed` | | Output files | `data.outputs`, a list of links | | Model inputs | The model's own fields, listed with options and defaults on its page under [Models](https://pinerouter.ai/docs/models) | ```bash curl https://pinerouter.ai/v1/jobs \ -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \ -d '{"model": "nano-banana-pro", "input": {"prompt": "A ceramic mug on a beige backdrop"}}' ``` What is different: * **Webhooks or polling.** Pass `webhook_url` to be called when the job finishes (Standard Webhooks signatures), or poll every 5 to 10 seconds; see [Jobs](https://pinerouter.ai/docs/jobs). A copy-paste helper is on [SDKs](https://pinerouter.ai/docs/sdks#jobs-helper). * **Files go in as `https://` links.** Pass any public link, or upload with `POST /v1/files` and use the returned `url` (kept 1 day), in the model's field such as `image_url` or `image_urls`. * **Results last 7 days.** Download what you want to keep. See [Data and logging](https://pinerouter.ai/docs/data-and-logging). * **No cancel.** A started job runs to the end. A failed job costs nothing. * **Cost is known up front.** Each model's page lists its price per setting. A job reserves its expected cost plus 20% and settles at the real cost. * **Field names differ by model.** Do not copy inputs from another provider's schema; read the model's page. You can also use OpenAI-style `POST /v1/images/generations` and `POST /v1/videos` for image and video models if your code already speaks that format. --- # Playground > Try any text model in the console before you write code. Source: https://pinerouter.ai/docs/playground Open **Playground** in the console menu. Pick a model, type a message and send. You can set the system prompt, temperature and maximum length, and stream the answer. Playground calls are charged like API calls, at the prices on the [Models](https://pinerouter.ai/docs/models) page. For images, video, music and speech, use the **Studio** pages in the console: they run every media model from a form, with the same settings as [Jobs](https://pinerouter.ai/docs/jobs). ## Good to know * The Playground uses the same models and ids as the API, so what works here works with `POST /v1/chat/completions`. * When you are ready to write code, see [SDKs](https://pinerouter.ai/docs/sdks) or [Text](https://pinerouter.ai/docs/text). --- # Rate limits > How many requests you may send, what happens over the limit, and backoff code. Source: https://pinerouter.ai/docs/rate-limits ## The limits | Limit | Value | | ---------------------------------- | ------------ | | All requests, failed ones included | 120 a minute | | Successful requests | 60 a minute | * The limits are per account. All your keys share them. * Every call to `/v1/...` counts, including `GET /v1/models` and each poll of `GET /v1/jobs/{id}` or `GET /v1/videos/{id}`. Poll every 5 to 10 seconds, not faster. * A request that fails (any status of `400` or higher) counts toward the 120, not toward the 60. Need more? Ask through the contact on the [About](https://pinerouter.ai/about) page. ## Over the limit You get HTTP `429` with a `Retry-After` header in seconds (60 today) and an OpenAI-style body: ```json {"error": {"code": "", "message": "You have reached the request limit: maximum 60 requests in 1 minutes (request id: ...)", "type": "api_error"}} ``` When the 120 limit is the one you hit, the message says "total request limit" and reminds you that failed attempts count. There are no `x-ratelimit-*` headers. Responses do not tell you how many requests you have left, so count them yourself if you run near the limit. A `429` on `/v1/jobs` can also mean the model itself is at capacity. The same backoff works. ## Back off and retry Retry `429` and `5xx` answers only. Wait `Retry-After` seconds when it is set, otherwise 2, 4, then 8 seconds with some jitter, and give up after 3 retries. See [Errors](https://pinerouter.ai/docs/errors) for what not to retry. ```python import random, time, requests RETRY = {429, 500, 502, 503, 504} def call(method, url, tries=4, **kw): for i in range(tries): r = requests.request(method, url, timeout=300, **kw) if r.status_code not in RETRY or i == tries - 1: return r wait = r.headers.get("Retry-After") time.sleep(float(wait) if wait else 2 ** (i + 1) + random.random()) return r r = call("POST", "https://pinerouter.ai/v1/chat/completions", headers={"Authorization": "Bearer sk-..."}, json={"model": "gpt-6-luna", "messages": [{"role": "user", "content": "Hi"}]}) print(r.status_code, r.json()) ``` ```js const RETRY = new Set([429, 500, 502, 503, 504]); const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); async function call(url, init, tries = 4) { for (let i = 0; ; i++) { const r = await fetch(url, init); if (!RETRY.has(r.status) || i === tries - 1) return r; const wait = r.headers.get("retry-after"); await sleep(wait ? Number(wait) * 1000 : 2 ** (i + 1) * 1000 + Math.random() * 1000); } } const r = await call("https://pinerouter.ai/v1/chat/completions", { method: "POST", headers: { Authorization: "Bearer sk-...", "Content-Type": "application/json" }, body: JSON.stringify({ model: "gpt-6-luna", messages: [{ role: "user", content: "Hi" }] }), }); console.log(r.status, await r.json()); ``` The official OpenAI and Anthropic SDKs already retry `429` and `5xx` with backoff (2 retries by default, `max_retries` to change it). Do not wrap them in a second retry loop. Do not use this loop to resend `POST /v1/jobs` after a timeout or a `5xx`: the job may have started. Check **Task logs** first. See [Errors](https://pinerouter.ai/docs/errors#timeouts). --- # SDKs > Use the official OpenAI and Anthropic SDKs with this API, plus a small helper for media jobs. Source: https://pinerouter.ai/docs/sdks There is no SDK package of our own. Use the official OpenAI or Anthropic SDK for text, and plain HTTP for media jobs (a copy-paste helper is below). | SDK | Base URL | Install | | -------------------------------------- | ------------------------------------ | --------------------------------------------------------- | | OpenAI (Python, Node.js and others) | `https://pinerouter.ai/v1` | `pip install openai` / `npm install openai` | | Anthropic (Python, Node.js and others) | `https://pinerouter.ai` (no `/v1`) | `pip install anthropic` / `npm install @anthropic-ai/sdk` | The key is a key from [API keys](https://pinerouter.ai/docs/keys). Keep it in an environment variable, not in code. ## OpenAI SDK ```python import os from openai import OpenAI client = OpenAI(base_url="https://pinerouter.ai/v1", api_key=os.environ["API_KEY"]) r = client.chat.completions.create( model="gpt-6-luna", messages=[{"role": "user", "content": "Name three rivers in Europe."}], ) print(r.choices[0].message.content) img = client.images.generate(model="z-image", prompt="A red paper boat on a calm lake", size="1024x1024") print(img.data[0].url) ``` ```js import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://pinerouter.ai/v1", apiKey: process.env.API_KEY }); const r = await client.chat.completions.create({ model: "gpt-6-luna", messages: [{ role: "user", content: "Name three rivers in Europe." }], }); console.log(r.choices[0].message.content); const img = await client.images.generate({ model: "z-image", prompt: "A red paper boat on a calm lake", size: "1024x1024" }); console.log(img.data[0].url); ``` `client.responses.create(...)` works too. The SDK retries `429` and `5xx` on its own (2 retries by default; set `max_retries` / `maxRetries`). Raise its timeout for image calls, which wait for the image: `OpenAI(..., timeout=300)`. ## Anthropic SDK ```python import os import anthropic client = anthropic.Anthropic(base_url="https://pinerouter.ai", api_key=os.environ["API_KEY"]) msg = client.messages.create( model="gpt-5-2", max_tokens=500, messages=[{"role": "user", "content": "Name three rivers in Europe."}], ) print(msg.content[0].text) ``` ```js import Anthropic from "@anthropic-ai/sdk"; const client = new Anthropic({ baseURL: "https://pinerouter.ai", apiKey: process.env.API_KEY }); const msg = await client.messages.create({ model: "gpt-5-2", max_tokens: 500, messages: [{ role: "user", content: "Name three rivers in Europe." }], }); console.log(msg.content[0].text); ``` Any text model on [Models](https://pinerouter.ai/docs/models) answers in Messages format. Claude models are not on the list today. ## Jobs helper For media models with all their settings, use [Jobs](https://pinerouter.ai/docs/jobs). This helper starts a job, polls it every 5 seconds, and downloads the files. Copy it into your project. ```python # pip install requests import json, os, time, requests BASE = "https://pinerouter.ai" H = {"Authorization": f"Bearer {os.environ['API_KEY']}"} def run_job(model, input, every=5, max_wait=1800): r = requests.post(f"{BASE}/v1/jobs", headers=H, json={"model": model, "input": input}, timeout=60) body = r.json() if r.status_code != 200 or body.get("code") != 200: raise RuntimeError(f"start failed ({r.status_code}): {body}") task = body["data"]["id"] waited = 0 while waited < max_wait: time.sleep(every) waited += every r = requests.get(f"{BASE}/v1/jobs/{task}", headers=H, timeout=60) if r.status_code == 429 or r.status_code >= 500: continue # poll again; never start the job a second time d = r.json()["data"] if d["status"] == "succeeded": return task, d["outputs"] if d["status"] == "failed": raise RuntimeError(f"job {task} failed: {d.get('error')}") raise TimeoutError(f"job {task} still running; poll it later") def download(urls, folder="."): paths = [] for i, url in enumerate(urls): name = os.path.join(folder, f"{i}_{url.split('/')[-1].split('?')[0]}") with requests.get(url, stream=True, timeout=300) as r, open(name, "wb") as f: r.raise_for_status() for chunk in r.iter_content(1 << 20): f.write(chunk) paths.append(name) return paths task, urls = run_job("z-image", {"prompt": "A red paper boat on a calm lake", "aspect_ratio": "1:1"}) print(download(urls)) ``` ```js // Node.js 18 or newer (built-in fetch) import { writeFile } from "node:fs/promises"; const BASE = "https://pinerouter.ai"; const H = { Authorization: `Bearer ${process.env.API_KEY}`, "Content-Type": "application/json" }; const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); export async function runJob(model, input, { every = 5000, maxWait = 1_800_000 } = {}) { const r = await fetch(`${BASE}/v1/jobs`, { method: "POST", headers: H, body: JSON.stringify({ model, input }) }); const body = await r.json(); if (r.status !== 200 || body.code !== 200) throw new Error(`start failed (${r.status}): ${JSON.stringify(body)}`); const task = body.data.id; for (let waited = 0; waited < maxWait; waited += every) { await sleep(every); const p = await fetch(`${BASE}/v1/jobs/${task}`, { headers: H }); if (p.status === 429 || p.status >= 500) continue; // poll again; never start the job a second time const d = (await p.json()).data; if (d.status === "succeeded") return { task, urls: d.outputs }; if (d.status === "failed") throw new Error(`job ${task} failed: ${d.error}`); } throw new Error(`job ${task} still running; poll it later`); } export async function download(urls) { const paths = []; for (const [i, url] of urls.entries()) { const name = `${i}_${new URL(url).pathname.split("/").pop()}`; await writeFile(name, Buffer.from(await (await fetch(url)).arrayBuffer())); paths.push(name); } return paths; } const { urls } = await runJob("z-image", { prompt: "A red paper boat on a calm lake", aspect_ratio: "1:1" }); console.log(await download(urls)); ``` * Result links last 7 days, so the helper downloads them straight away. * A job that fails costs nothing. A job still running after the wait is not lost: keep the `id` and poll it later. * Each model's fields are on its page under [Models](https://pinerouter.ai/docs/models). --- # Streaming > Get text as it is written, with server-sent events, on chat completions, responses and Claude messages. Source: https://pinerouter.ai/docs/streaming Add `"stream": true` to a text request. The answer comes as server-sent events (`Content-Type: text/event-stream`) in the same format as the original API: OpenAI's for `/v1/chat/completions` and `/v1/responses`, Anthropic's for `/v1/messages`. Any client that streams from those APIs works unchanged. ## Chat completions Each event is a line `data: {...}` with a chunk whose `choices[0].delta.content` holds the next piece of text. The stream ends with `data: [DONE]`. ```bash curl -N https://pinerouter.ai/v1/chat/completions \ -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \ -d '{"model": "gpt-6-luna", "stream": true, "messages": [{"role": "user", "content": "Write a haiku about rivers."}]}' ``` ```python from openai import OpenAI client = OpenAI(base_url="https://pinerouter.ai/v1", api_key="sk-...") stream = client.chat.completions.create( model="gpt-6-luna", messages=[{"role": "user", "content": "Write a haiku about rivers."}], stream=True, ) for chunk in stream: if chunk.choices and chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True) if chunk.usage: print("\n", chunk.usage) ``` ```js import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://pinerouter.ai/v1", apiKey: "sk-..." }); const stream = await client.chat.completions.create({ model: "gpt-6-luna", messages: [{ role: "user", content: "Write a haiku about rivers." }], stream: true, }); for await (const chunk of stream) { process.stdout.write(chunk.choices[0]?.delta?.content ?? ""); if (chunk.usage) console.log("\n", chunk.usage); } ``` ### Usage in the last chunk The last chunk before `data: [DONE]` carries `usage` (prompt, completion and total tokens) and an empty `choices` list. You get it by default. To leave it out, send `"stream_options": {"include_usage": false}`. Check `choices` is not empty before you read `choices[0]`, as in the examples above. ## Responses `POST /v1/responses` with `"stream": true` sends typed events (`event: response.output_text.delta`, then `response.completed` with the full response and its `usage`), as OpenAI's Responses API does. ```python stream = client.responses.create(model="gpt-5-5", input="Write a haiku about rivers.", stream=True) for event in stream: if event.type == "response.output_text.delta": print(event.delta, end="", flush=True) ``` ## Claude messages `POST /v1/messages` with `"stream": true` sends Anthropic's events: `message_start`, `content_block_delta` with the text, `message_delta` with the final usage, and `message_stop`. ```python import anthropic client = anthropic.Anthropic(base_url="https://pinerouter.ai", api_key="sk-...") with client.messages.stream( model="gpt-5-2", max_tokens=300, messages=[{"role": "user", "content": "Write a haiku about rivers."}], ) as stream: for text in stream.text_stream: print(text, end="", flush=True) ``` ## Errors during a stream Errors before the first byte come back as a normal HTTP error with a JSON body (see [Errors](https://pinerouter.ai/docs/errors)). Once the stream has started, the status is already `200`, so an error arrives inside the stream: * Chat completions and responses: a last event `data: {"error": {"message": "...", "type": "...", "code": "..."}}`, then `data: [DONE]`. * Claude messages: `event: error` with `{"type": "error", "error": {"type": "...", "message": "..."}}`. Check every event for an `error` field, and treat a stream that ends without its normal final event (`[DONE]` after a chunk with a `finish_reason`, `response.completed`, or `message_stop`) as incomplete. A stream that sends nothing for 5 minutes is closed. ## Stop a stream Close the connection: `break` out of the loop and close the stream (`stream.close()` in Python, `controller.abort()` with an `AbortController` in JavaScript, Ctrl+C for curl). Generation stops, and you pay only for the tokens produced up to that point. ```js const controller = new AbortController(); const stream = await client.chat.completions.create( { model: "gpt-6-luna", messages: [{ role: "user", content: "Count to 1000." }], stream: true }, { signal: controller.signal }, ); setTimeout(() => controller.abort(), 2000); ``` Jobs do not stream: start them and poll. See [Jobs](https://pinerouter.ai/docs/jobs). --- # Text > Chat models from OpenAI, Google, xAI, DeepSeek, Moonshot and more. Source: https://pinerouter.ai/docs/text See the full list and prices on the [Models](https://pinerouter.ai/docs/models) page. Text is charged per token after the reply. ## Chat completions ```bash curl https://pinerouter.ai/v1/chat/completions \ -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \ -d '{ "model": "gpt-5-2", "messages": [ {"role": "system", "content": "You are concise."}, {"role": "user", "content": "Explain DNS in one sentence."} ], "max_tokens": 200 }' ``` The answer is in `choices[0].message.content`, and `usage` has the token counts you are charged for. Full field list: [Chat completions](https://pinerouter.ai/docs/reference/chat-completions). Set `max_tokens`: before a request runs it reserves its longest possible answer (`max_tokens`, or 8192 tokens if unset) at the output price, then you pay the real tokens. ## Streaming Add `"stream": true` to get the text as it is written, as server-sent events. Usage comes in the last chunk. See [Streaming](https://pinerouter.ai/docs/streaming). ## Model features The fields below follow OpenAI's format and are passed to the model. Whether they work depends on the model; if a model does not support one, it may ignore it or return a `400`. Test with the model you plan to use. ### Tools (function calling) ```json { "model": "gpt-5-5", "messages": [{"role": "user", "content": "What is the weather in Paris?"}], "tools": [{ "type": "function", "function": { "name": "get_weather", "description": "Current weather for a city", "parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]} } }] } ``` If the model calls a tool, the answer has `choices[0].message.tool_calls`. Run the function yourself, then send its result back as a message with `"role": "tool"` and the matching `tool_call_id`. `tool_choice` works as in OpenAI's API. ### JSON mode and structured output `"response_format": {"type": "json_object"}` asks for valid JSON. `"response_format": {"type": "json_schema", "json_schema": {...}}` asks for JSON that matches your schema. Still validate the answer in your code. ### Images in messages (vision) Give the image as a public `https://` link in a content part: ```json { "model": "gemini-3-8-flash", "messages": [{"role": "user", "content": [ {"type": "text", "text": "What is in this picture?"}, {"type": "image_url", "image_url": {"url": "https://example.com/photo.jpg"}} ]}] } ``` ### Reasoning models Models that think before they answer use more output tokens, and those tokens are charged. `reasoning_effort` (chat completions) and `reasoning` (responses) are passed to the model where it has them. Leave room in `max_tokens` for the thinking. ## Responses API OpenAI's newer format also works: `POST /v1/responses` with `{"model": "...", "input": "..."}`. This is what Codex CLI uses. See [Responses](https://pinerouter.ai/docs/reference/responses). ## Claude format Tools built for Anthropic's Messages API can call `POST /v1/messages` with any text model (base URL without `/v1`). See [Claude messages](https://pinerouter.ai/docs/reference/messages). Claude models themselves are not on the list today. ## List models `GET /v1/models` returns every model your key may use. ## More * SDK setup for Python and Node.js: [SDKs](https://pinerouter.ai/docs/sdks) * Coding agents, chat apps and frameworks: [Integrations](https://pinerouter.ai/docs/integrations) * Status codes and retries: [Errors](https://pinerouter.ai/docs/errors) --- # Usage logs > See every request, what it cost and why it failed. Source: https://pinerouter.ai/docs/usage Open **Usage logs** in the console menu. Each request has a row with its time, key, model, tokens or job, cost and how long it took. ## Filter Filter by time range, model, key name, log type (consumption, top-up, refund, error) or request id. The totals above the list follow your filters. ## Jobs Image, video, music and other media jobs also appear under **Task logs**, with their status and progress. A job that fails is refunded and shows a refund row. ## From the API ```bash curl https://pinerouter.ai/v1/dashboard/billing/usage -H "Authorization: Bearer $API_KEY" ``` returns what the key has spent. `GET /v1/dashboard/billing/subscription` returns the balance. ## Find a request Every error message ends with a request id. Paste it into the request id filter to find the row, with its model, key, cost and status. Send that id when you ask for help. ## Good to know * Failed requests and jobs are not charged. * The text of prompts and replies is not stored, so rows show counts and costs, not content. See [Data and logging](https://pinerouter.ai/docs/data-and-logging). --- # Video > Start a video, then check on it until it is ready. Source: https://pinerouter.ai/docs/video Video takes from seconds to minutes, so you start a job and check on it. ## Start ```bash curl https://pinerouter.ai/v1/videos \ -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \ -d '{"model": "grok-imagine/text-to-video", "prompt": "A paper boat drifting on a calm lake at sunrise", "seconds": 6, "size": "1280x720"}' ``` The answer has an `id` and `status`. | Field | Meaning | | ----------------- | ----------------------------------------------------------------- | | `model` | Model id from the [Models](https://pinerouter.ai/docs/models) page | | `prompt` | What happens in the video | | `seconds` | Length; rounded to the nearest length the model offers | | `size` | `WIDTHxHEIGHT`; mapped to the model's aspect ratio and resolution | | `input_reference` | Link to a start image, for image-to-video models | ## Check ```bash curl https://pinerouter.ai/v1/videos/$VIDEO_ID -H "Authorization: Bearer $API_KEY" ``` Poll every 5 to 10 seconds until `status` is `completed` (with the video link) or `failed`. A failed video is not charged. Every setting of a model (keyframes, camera, audio, quality tiers) is available through [Jobs](https://pinerouter.ai/docs/jobs). Outputs are AI-generated and keep the labels the model adds (C2PA metadata, watermarks). See [AI-generated content](https://pinerouter.ai/docs/ai-content). --- # Wallet and top-up > Check your balance and add credit. Source: https://pinerouter.ai/docs/wallet Open **Wallet** in the console menu, or go to [/wallet](https://pinerouter.ai/wallet). ## Your balance The wallet shows your **current balance**, what you have **spent** in total, and how many **requests** you have made. Every request is charged from the balance at the prices on the [Models](https://pinerouter.ai/docs/models) page; see [Keys, billing and limits](https://pinerouter.ai/docs/auth) for how holds and refunds work. ## Add credit 1. In **Top up**, pick an amount or type your own. 2. Check the amount you will pay and choose a payment method. 3. Pay on the payment page, then come back to the wallet. Your balance updates as soon as the payment is confirmed, usually within seconds. If you paid and the balance has not changed after a few minutes, keep the receipt and contact us; do not pay twice. ## Redemption codes If you have a code, enter it under **Redeem** and click **Redeem**. Each code works once. ## Low balance A request only starts when your balance covers it, so top up before it runs out to keep your apps running. --- # Models > Every model with its price on this site. Click a model for its settings, prices per setting and an example. Source: https://pinerouter.ai/docs/models Prices are what your balance is charged. Media jobs reserve the expected cost plus 20% and settle at the real cost; see [Keys, billing and limits](https://pinerouter.ai/docs/auth). ## Picking a model * **Use the exact id.** Copy it from this page or `GET /v1/models`. Ids here can differ from the maker's own. * **Text:** a fast, cheap model for drafts, classification and background work; a stronger one for reasoning and code. Text prices are per million input and output tokens. * **Images, video and audio:** the price depends on the settings (resolution, duration, count). Open the model's page for its price per setting, every input field with its default, and a ready request. Choose the cheapest settings that do the job. * **Try before you build:** the Playground (text) and the Studio (media) in the console run the same models. * Models can be added or removed. See [Deprecations](https://pinerouter.ai/docs/deprecations). ### Text models (21) - [DeepSeek V4.1 Flash](https://pinerouter.ai/docs/models/deepseek-v4-1-flash.md): `deepseek-v4-1-flash`, $0.150 / $0.594 per 1M tokens in / out - [Gemini 3 Pro](https://pinerouter.ai/docs/models/gemini-3-pro.md): `gemini-3-pro`, $0.625 / $4.38 per 1M tokens in / out - [Gemini 3.1 Pro](https://pinerouter.ai/docs/models/gemini-3.1-pro.md): `gemini-3.1-pro`, $0.625 / $4.38 per 1M tokens in / out - [Gemini 3.6 Flash](https://pinerouter.ai/docs/models/gemini-3-6-flash.md): `gemini-3-6-flash`, $0.281 / $1.41 per 1M tokens in / out - [Gemini 3.7 Flash](https://pinerouter.ai/docs/models/gemini-3-7-flash.md): `gemini-3-7-flash`, $0.281 / $1.41 per 1M tokens in / out - [Gemini 3.8 Flash](https://pinerouter.ai/docs/models/gemini-3-8-flash.md): `gemini-3-8-flash`, $0.281 / $1.41 per 1M tokens in / out - [Gemini 4 Argon](https://pinerouter.ai/docs/models/gemini-4-argon.md): `gemini-4-argon`, $1.00 / $5.00 per 1M tokens in / out - [GPT 5.2](https://pinerouter.ai/docs/models/gpt-5-2.md): `gpt-5-2`, $0.550 / $4.38 per 1M tokens in / out - [GPT 5.5](https://pinerouter.ai/docs/models/gpt-5-5.md): `gpt-5-5`, $1.75 / $10.50 per 1M tokens in / out - [GPT 5.6 Luna](https://pinerouter.ai/docs/models/gpt-5-6-luna.md): `gpt-5-6-luna`, $0.070 / $0.420 per 1M tokens in / out - [GPT 5.6 Sol](https://pinerouter.ai/docs/models/gpt-5-6-sol.md): `gpt-5-6-sol`, $1.75 / $10.50 per 1M tokens in / out - [GPT 5.6 Terra](https://pinerouter.ai/docs/models/gpt-5-6-terra.md): `gpt-5-6-terra`, $0.700 / $4.20 per 1M tokens in / out - [GPT 6 Astra](https://pinerouter.ai/docs/models/gpt-6-astra.md): `gpt-6-astra`, $3.50 / $17.50 per 1M tokens in / out - [GPT 6 Luna](https://pinerouter.ai/docs/models/gpt-6-luna.md): `gpt-6-luna`, $0.037 / $0.188 per 1M tokens in / out - [GPT 6 Sol](https://pinerouter.ai/docs/models/gpt-6-sol.md): `gpt-6-sol`, $0.750 / $3.75 per 1M tokens in / out - [GPT 6.1 Sol](https://pinerouter.ai/docs/models/gpt-6-1-sol.md): `gpt-6-1-sol`, $0.750 / $3.75 per 1M tokens in / out - [GPT 6.1 Terra](https://pinerouter.ai/docs/models/gpt-6-1-terra.md): `gpt-6-1-terra`, $0.375 / $1.88 per 1M tokens in / out - [Grok 4.5](https://pinerouter.ai/docs/models/grok-4-5.md): `grok-4-5`, $1.00 / $3.00 per 1M tokens in / out - [Grok 4.6](https://pinerouter.ai/docs/models/grok-4-6.md): `grok-4-6`, $1.00 / $3.00 per 1M tokens in / out - [Grok 4.7](https://pinerouter.ai/docs/models/grok-4-7.md): `grok-4-7`, $1.00 / $3.00 per 1M tokens in / out - [Kimi K3](https://pinerouter.ai/docs/models/kimi-k3.md): `kimi-k3`, $3.00 / $15.00 per 1M tokens in / out ### Image models (66) - [Bytedance Seedream](https://pinerouter.ai/docs/models/bytedance/seedream.md): `bytedance/seedream`, from $0.022 per image - [Bytedance Seedream v4 Edit](https://pinerouter.ai/docs/models/bytedance/seedream-v4-edit.md): `bytedance/seedream-v4-edit`, from $0.031 per image - [Bytedance Seedream v4 Text To Image](https://pinerouter.ai/docs/models/bytedance/seedream-v4-text-to-image.md): `bytedance/seedream-v4-text-to-image`, from $0.031 per image - [Crisp upscale](https://pinerouter.ai/docs/models/recraft/crisp-upscale.md): `recraft/crisp-upscale`, from $0.0031 per image - [Flux 2 · Flex Image To Image](https://pinerouter.ai/docs/models/flux-2/flex-image-to-image.md): `flux-2/flex-image-to-image`, from $0.088 per image - [Flux 2 · Flex Text To Image](https://pinerouter.ai/docs/models/flux-2/flex-text-to-image.md): `flux-2/flex-text-to-image`, from $0.088 per image - [Flux 2 · Pro Image To Image](https://pinerouter.ai/docs/models/flux-2/pro-image-to-image.md): `flux-2/pro-image-to-image`, from $0.031 per image - [Flux 2 · Pro Text To Image](https://pinerouter.ai/docs/models/flux-2/pro-text-to-image.md): `flux-2/pro-text-to-image`, from $0.031 per image - [Flux 3 · Klein Edit Image](https://pinerouter.ai/docs/models/flux-3/klein-edit-image.md): `flux-3/klein-edit-image`, from $0.010 per image - [Flux 3 · Klein Text To Image](https://pinerouter.ai/docs/models/flux-3/klein-text-to-image.md): `flux-3/klein-text-to-image`, from $0.010 per image - [Flux Kontext](https://pinerouter.ai/docs/models/flux1-kontext.md): `flux1-kontext`, from $0.031 per image - [Google Imagen4](https://pinerouter.ai/docs/models/google/imagen4.md): `google/imagen4`, from $0.050 per job - [Google Imagen4 Fast](https://pinerouter.ai/docs/models/google/imagen4-fast.md): `google/imagen4-fast`, from $0.025 per job - [Google Imagen4 Ultra](https://pinerouter.ai/docs/models/google/imagen4-ultra.md): `google/imagen4-ultra`, from $0.075 per image - [Google Nano Banana](https://pinerouter.ai/docs/models/google/nano-banana.md): `google/nano-banana`, from $0.025 per image - [Google Nano Banana Edit](https://pinerouter.ai/docs/models/google/nano-banana-edit.md): `google/nano-banana-edit`, from $0.025 per image - [Gpt Image · 1.5 Image To Image](https://pinerouter.ai/docs/models/gpt-image/1.5-image-to-image.md): `gpt-image/1.5-image-to-image`, from $0.025 per image - [Gpt Image · 1.5 Text To Image](https://pinerouter.ai/docs/models/gpt-image/1.5-text-to-image.md): `gpt-image/1.5-text-to-image`, from $0.025 per image - [Gpt Image 2 5 Flare Image To Image](https://pinerouter.ai/docs/models/gpt-image-2-5-flare-image-to-image.md): `gpt-image-2-5-flare-image-to-image`, from $0.037 per image - [Gpt Image 2 5 Flare Text To Image](https://pinerouter.ai/docs/models/gpt-image-2-5-flare-text-to-image.md): `gpt-image-2-5-flare-text-to-image`, from $0.037 per image - [Gpt Image 2 5 Sunburst Image To Image](https://pinerouter.ai/docs/models/gpt-image-2-5-sunburst-image-to-image.md): `gpt-image-2-5-sunburst-image-to-image`, from $0.037 per image - [Gpt Image 2 5 Sunburst Text To Image](https://pinerouter.ai/docs/models/gpt-image-2-5-sunburst-text-to-image.md): `gpt-image-2-5-sunburst-text-to-image`, from $0.037 per image - [Gpt Image 2 Image To Image](https://pinerouter.ai/docs/models/gpt-image-2-image-to-image.md): `gpt-image-2-image-to-image`, from $0.037 per image - [Gpt Image 2 Text To Image](https://pinerouter.ai/docs/models/gpt-image-2-text-to-image.md): `gpt-image-2-text-to-image`, from $0.037 per image - [GPT-4o Image](https://pinerouter.ai/docs/models/4o-image-api.md): `4o-image-api`, from $0.037 per image - [Grok Imagine · Image To Image](https://pinerouter.ai/docs/models/grok-imagine/image-to-image.md): `grok-imagine/image-to-image`, from $0.025 per job - [Grok Imagine · Text To Image](https://pinerouter.ai/docs/models/grok-imagine/text-to-image.md): `grok-imagine/text-to-image`, from $0.025 per image - [Grok Imagine Image 2 0 · Image Edit](https://pinerouter.ai/docs/models/grok-imagine-image-2-0/image-edit.md): `grok-imagine-image-2-0/image-edit`, from $0.025 per image - [Grok Imagine Image 2 0 · Segment Edit](https://pinerouter.ai/docs/models/grok-imagine-image-2-0/segment-edit.md): `grok-imagine-image-2-0/segment-edit`, from $0.025 per image - [Grok Imagine Image 2 0 · Text To Image](https://pinerouter.ai/docs/models/grok-imagine-image-2-0/text-to-image.md): `grok-imagine-image-2-0/text-to-image`, from $0.025 per image - [Ideogram · Character](https://pinerouter.ai/docs/models/ideogram/character.md): `ideogram/character`, from $0.075 per image - [Ideogram · Character Edit](https://pinerouter.ai/docs/models/ideogram/character-edit.md): `ideogram/character-edit`, from $0.075 per image - [Ideogram · Character Remix](https://pinerouter.ai/docs/models/ideogram/character-remix.md): `ideogram/character-remix`, from $0.075 per image - [Ideogram · v3 Edit](https://pinerouter.ai/docs/models/ideogram/v3-edit.md): `ideogram/v3-edit`, from $0.022 per image - [Ideogram · v3 Remix](https://pinerouter.ai/docs/models/ideogram/v3-remix.md): `ideogram/v3-remix`, from $0.022 per image - [Ideogram · v3 Text To Image](https://pinerouter.ai/docs/models/ideogram/v3-text-to-image.md): `ideogram/v3-text-to-image`, from $0.022 per image - [Nano Banana 2](https://pinerouter.ai/docs/models/nano-banana-2.md): `nano-banana-2`, from $0.050 per image - [Nano Banana 2 1](https://pinerouter.ai/docs/models/nano-banana-2-1.md): `nano-banana-2-1`, from $0.025 per image - [Nano Banana 2 Lite](https://pinerouter.ai/docs/models/nano-banana-2-lite.md): `nano-banana-2-lite`, from $0.025 per image - [Nano Banana Pro](https://pinerouter.ai/docs/models/nano-banana-pro.md): `nano-banana-pro`, from $0.112 per image - [Qwen Image Edit](https://pinerouter.ai/docs/models/qwen/image-edit.md): `qwen/image-edit`, from $0.031 per job - [Qwen Image To Image](https://pinerouter.ai/docs/models/qwen/image-to-image.md): `qwen/image-to-image`, from $0.025 per image - [Qwen Text To Image](https://pinerouter.ai/docs/models/qwen/text-to-image.md): `qwen/text-to-image`, from $0.025 per job - [Qwen2 1 · Image To Image](https://pinerouter.ai/docs/models/qwen2-1/image-to-image.md): `qwen2-1/image-to-image`, from $0.025 per image - [Qwen2 1 · Text To Image](https://pinerouter.ai/docs/models/qwen2-1/text-to-image.md): `qwen2-1/text-to-image`, from $0.025 per image - [Qwen2 Image Edit](https://pinerouter.ai/docs/models/qwen2/image-edit.md): `qwen2/image-edit`, from $0.035 per image - [Qwen2 Text To Image](https://pinerouter.ai/docs/models/qwen2/text-to-image.md): `qwen2/text-to-image`, from $0.035 per image - [Qwen3 Image To Image](https://pinerouter.ai/docs/models/qwen3/image-to-image.md): `qwen3/image-to-image`, from $0.0031 per image - [Qwen3 Pro Image To Image](https://pinerouter.ai/docs/models/qwen3/pro-image-to-image.md): `qwen3/pro-image-to-image`, from $0.0031 per image - [Qwen3 Pro Text To Image](https://pinerouter.ai/docs/models/qwen3/pro-text-to-image.md): `qwen3/pro-text-to-image`, from $0.040 per image - [Qwen3 Text To Image](https://pinerouter.ai/docs/models/qwen3/text-to-image.md): `qwen3/text-to-image`, from $0.030 per image - [Remove background](https://pinerouter.ai/docs/models/recraft/remove-background.md): `recraft/remove-background`, from $0.0063 per image - [Seedream · 4.5 Edit](https://pinerouter.ai/docs/models/seedream/4.5-edit.md): `seedream/4.5-edit`, from $0.041 per image - [Seedream · 4.5 Text To Image](https://pinerouter.ai/docs/models/seedream/4.5-text-to-image.md): `seedream/4.5-text-to-image`, from $0.041 per image - [Seedream · 5 Flash Image To Image](https://pinerouter.ai/docs/models/seedream/5-flash-image-to-image.md): `seedream/5-flash-image-to-image`, from $0.020 per image - [Seedream · 5 Flash Layer Decomposition](https://pinerouter.ai/docs/models/seedream/5-flash-layer-decomposition.md): `seedream/5-flash-layer-decomposition`, from $0.020 per image - [Seedream · 5 Flash Text To Image](https://pinerouter.ai/docs/models/seedream/5-flash-text-to-image.md): `seedream/5-flash-text-to-image`, from $0.020 per image - [Seedream · 5 Lite Image To Image](https://pinerouter.ai/docs/models/seedream/5-lite-image-to-image.md): `seedream/5-lite-image-to-image`, from $0.034 per image - [Seedream · 5 Lite Text To Image](https://pinerouter.ai/docs/models/seedream/5-lite-text-to-image.md): `seedream/5-lite-text-to-image`, from $0.034 per image - [Seedream · 5 Pro Image To Image](https://pinerouter.ai/docs/models/seedream/5-pro-image-to-image.md): `seedream/5-pro-image-to-image`, from $0.0031 per image - [Seedream · 5 Pro Layer Decomposition](https://pinerouter.ai/docs/models/seedream/5-pro-layer-decomposition.md): `seedream/5-pro-layer-decomposition`, from $0.044 per image - [Seedream · 5 Pro Text To Image](https://pinerouter.ai/docs/models/seedream/5-pro-text-to-image.md): `seedream/5-pro-text-to-image`, from $0.044 per image - [Upscale image](https://pinerouter.ai/docs/models/topaz/image-upscale.md): `topaz/image-upscale`, from $0.063 per image - [Wan 2 7 Image](https://pinerouter.ai/docs/models/wan/2-7-image.md): `wan/2-7-image`, from $0.030 per image - [Wan 2 7 Image Pro](https://pinerouter.ai/docs/models/wan/2-7-image-pro.md): `wan/2-7-image-pro`, from $0.075 per image - [Z-Image](https://pinerouter.ai/docs/models/z-image.md): `z-image`, from $0.0050 per image ### Video models (87) - [Bytedance Seedance 2](https://pinerouter.ai/docs/models/bytedance/seedance-2.md): `bytedance/seedance-2`, from $0.072 per second - [Bytedance Seedance 2 5](https://pinerouter.ai/docs/models/bytedance/seedance-2-5.md): `bytedance/seedance-2-5`, from $0.106 per second - [Bytedance Seedance 2 5 Draft](https://pinerouter.ai/docs/models/bytedance/seedance-2-5-draft.md): `bytedance/seedance-2-5-draft`, priced per job - [Bytedance Seedance 2 Fast](https://pinerouter.ai/docs/models/bytedance/seedance-2-fast.md): `bytedance/seedance-2-fast`, from $0.043 per second - [Bytedance Seedance 2 Mini](https://pinerouter.ai/docs/models/bytedance/seedance-2-mini.md): `bytedance/seedance-2-mini`, from $0.015 per second - [Bytedance v1 Lite Image To Video](https://pinerouter.ai/docs/models/bytedance/v1-lite-image-to-video.md): `bytedance/v1-lite-image-to-video`, from $0.013 per second - [Bytedance v1 Lite Text To Video](https://pinerouter.ai/docs/models/bytedance/v1-lite-text-to-video.md): `bytedance/v1-lite-text-to-video`, from $0.013 per second - [Bytedance v1 Pro Fast Image To Video](https://pinerouter.ai/docs/models/bytedance/v1-pro-fast-image-to-video.md): `bytedance/v1-pro-fast-image-to-video`, from $0.100 per job - [Bytedance v1 Pro Image To Video](https://pinerouter.ai/docs/models/bytedance/v1-pro-image-to-video.md): `bytedance/v1-pro-image-to-video`, from $0.017 per second - [Bytedance v1 Pro Text To Video](https://pinerouter.ai/docs/models/bytedance/v1-pro-text-to-video.md): `bytedance/v1-pro-text-to-video`, from $0.017 per second - [Gemini Omni Video](https://pinerouter.ai/docs/models/gemini-omni-video.md): `gemini-omni-video`, from $0.394 per job - [Google Gemini Omni Flash 1 1](https://pinerouter.ai/docs/models/google/gemini-omni-flash-1-1.md): `google/gemini-omni-flash-1-1`, from $0.394 per job - [Grok Imagine · Extend](https://pinerouter.ai/docs/models/grok-imagine/extend.md): `grok-imagine/extend`, from $0.090 per job - [Grok Imagine · Image To Video](https://pinerouter.ai/docs/models/grok-imagine/image-to-video.md): `grok-imagine/image-to-video`, from $0.015 per second - [Grok Imagine · Text To Video](https://pinerouter.ai/docs/models/grok-imagine/text-to-video.md): `grok-imagine/text-to-video`, from $0.015 per second - [Grok Imagine · Upscale](https://pinerouter.ai/docs/models/grok-imagine/upscale.md): `grok-imagine/upscale`, from $0.063 per job - [Grok Imagine Video 1 5 Preview](https://pinerouter.ai/docs/models/grok-imagine-video-1-5-preview.md): `grok-imagine-video-1-5-preview`, from $0.015 per second - [Hailuo 02 Image To Video Pro](https://pinerouter.ai/docs/models/hailuo/02-image-to-video-pro.md): `hailuo/02-image-to-video-pro`, from $0.356 per job - [Hailuo 02 Image To Video Standard](https://pinerouter.ai/docs/models/hailuo/02-image-to-video-standard.md): `hailuo/02-image-to-video-standard`, from $0.075 per job - [Hailuo 02 Text To Video Pro](https://pinerouter.ai/docs/models/hailuo/02-text-to-video-pro.md): `hailuo/02-text-to-video-pro`, from $0.356 per job - [Hailuo 02 Text To Video Standard](https://pinerouter.ai/docs/models/hailuo/02-text-to-video-standard.md): `hailuo/02-text-to-video-standard`, from $0.188 per job - [Hailuo 2 3 Image To Video Pro](https://pinerouter.ai/docs/models/hailuo/2-3-image-to-video-pro.md): `hailuo/2-3-image-to-video-pro`, from $0.281 per job - [Hailuo 2 3 Image To Video Standard](https://pinerouter.ai/docs/models/hailuo/2-3-image-to-video-standard.md): `hailuo/2-3-image-to-video-standard`, from $0.188 per job - [Happyhorse · Image To Video](https://pinerouter.ai/docs/models/happyhorse/image-to-video.md): `happyhorse/image-to-video`, from $0.175 per second - [Happyhorse · Reference To Video](https://pinerouter.ai/docs/models/happyhorse/reference-to-video.md): `happyhorse/reference-to-video`, from $0.175 per second - [Happyhorse · Text To Video](https://pinerouter.ai/docs/models/happyhorse/text-to-video.md): `happyhorse/text-to-video`, from $0.175 per second - [Happyhorse · Video Edit](https://pinerouter.ai/docs/models/happyhorse/video-edit.md): `happyhorse/video-edit`, from $0.175 per second - [Happyhorse 1 1 · Image To Video](https://pinerouter.ai/docs/models/happyhorse-1-1/image-to-video.md): `happyhorse-1-1/image-to-video`, from $0.141 per second - [Happyhorse 1 1 · Reference To Video](https://pinerouter.ai/docs/models/happyhorse-1-1/reference-to-video.md): `happyhorse-1-1/reference-to-video`, from $0.141 per second - [Happyhorse 1 1 · Text To Video](https://pinerouter.ai/docs/models/happyhorse-1-1/text-to-video.md): `happyhorse-1-1/text-to-video`, from $0.141 per second - [InfiniTalk](https://pinerouter.ai/docs/models/infinitalk/from-audio.md): `infinitalk/from-audio`, from $0.019 per second - [Kling 2.6 · Image To Video](https://pinerouter.ai/docs/models/kling-2.6/image-to-video.md): `kling-2.6/image-to-video`, from $0.688 per job - [Kling 2.6 · Text To Video](https://pinerouter.ai/docs/models/kling-2.6/text-to-video.md): `kling-2.6/text-to-video`, from $0.344 per job - [Kling 2.6 Motion Control](https://pinerouter.ai/docs/models/kling-2.6/motion-control.md): `kling-2.6/motion-control`, from $0.069 per second - [Kling 3.0 Motion Control](https://pinerouter.ai/docs/models/kling-3.0/motion-control.md): `kling-3.0/motion-control`, from $0.125 per second - [Kling 3.0 Omni · Text To Video](https://pinerouter.ai/docs/models/kling-3.0-omni/text-to-video.md): `kling-3.0-omni/text-to-video`, from $0.088 per second - [Kling 3.0 Omni · Transformation](https://pinerouter.ai/docs/models/kling-3.0-omni/transformation.md): `kling-3.0-omni/transformation`, from $0.125 per second - [Kling 4.0 · Keyframe To Video](https://pinerouter.ai/docs/models/kling-4.0/keyframe-to-video.md): `kling-4.0/keyframe-to-video`, from $0.178 per second - [Kling 4.0 · Reference To Video](https://pinerouter.ai/docs/models/kling-4.0/reference-to-video.md): `kling-4.0/reference-to-video`, from $0.106 per second - [Kling Avatar](https://pinerouter.ai/docs/models/kling/ai-avatar-standard.md): `kling/ai-avatar-standard`, from $0.050 per second - [Kling Avatar Pro](https://pinerouter.ai/docs/models/kling/ai-avatar-pro.md): `kling/ai-avatar-pro`, from $0.100 per second - [Kling v2 1 Master Image To Video](https://pinerouter.ai/docs/models/kling/v2-1-master-image-to-video.md): `kling/v2-1-master-image-to-video`, from $1.00 per job - [Kling v2 1 Master Text To Video](https://pinerouter.ai/docs/models/kling/v2-1-master-text-to-video.md): `kling/v2-1-master-text-to-video`, from $1.00 per job - [Kling v2 1 Pro](https://pinerouter.ai/docs/models/kling/v2-1-pro.md): `kling/v2-1-pro`, from $0.313 per job - [Kling v2 1 Standard](https://pinerouter.ai/docs/models/kling/v2-1-standard.md): `kling/v2-1-standard`, from $0.156 per job - [Kling v2 5 Turbo Image To Video Pro](https://pinerouter.ai/docs/models/kling/v2-5-turbo-image-to-video-pro.md): `kling/v2-5-turbo-image-to-video-pro`, from $0.263 per job - [Kling v2 5 Turbo Text To Video Pro](https://pinerouter.ai/docs/models/kling/v2-5-turbo-text-to-video-pro.md): `kling/v2-5-turbo-text-to-video-pro`, from $0.263 per job - [Kling v3 Turbo Image To Video](https://pinerouter.ai/docs/models/kling/v3-turbo-image-to-video.md): `kling/v3-turbo-image-to-video`, from $0.112 per second - [Kling v3 Turbo Text To Video](https://pinerouter.ai/docs/models/kling/v3-turbo-text-to-video.md): `kling/v3-turbo-text-to-video`, from $0.112 per second - [Lip sync (video)](https://pinerouter.ai/docs/models/volcengine/video-to-video-lip-sync.md): `volcengine/video-to-video-lip-sync`, from $0.050 per second - [Minimax H3 · Image To Video](https://pinerouter.ai/docs/models/minimax-h3/image-to-video.md): `minimax-h3/image-to-video`, from $0.050 per second - [Minimax H3 · Reference To Video](https://pinerouter.ai/docs/models/minimax-h3/reference-to-video.md): `minimax-h3/reference-to-video`, from $0.050 per second - [Minimax H3 · Text To Video](https://pinerouter.ai/docs/models/minimax-h3/text-to-video.md): `minimax-h3/text-to-video`, from $0.025 per image - [OmniHuman 1.5](https://pinerouter.ai/docs/models/omnihuman-1-5.md): `omnihuman-1-5`, from $0.169 per second - [OmniHuman 2](https://pinerouter.ai/docs/models/omnihuman-2-0.md): `omnihuman-2-0`, from $0.088 per second - [Pixverse v6 · Image To Video](https://pinerouter.ai/docs/models/pixverse-v6/image-to-video.md): `pixverse-v6/image-to-video`, from $0.025 per second - [Pixverse v6 · Reference To Video](https://pinerouter.ai/docs/models/pixverse-v6/reference-to-video.md): `pixverse-v6/reference-to-video`, from $0.025 per second - [Pixverse v6 · Text To Video](https://pinerouter.ai/docs/models/pixverse-v6/text-to-video.md): `pixverse-v6/text-to-video`, from $0.025 per second - [Pixverse v6 · Transition](https://pinerouter.ai/docs/models/pixverse-v6/transition.md): `pixverse-v6/transition`, from $0.025 per second - [Pixverse v6 Max · Image To Video](https://pinerouter.ai/docs/models/pixverse-v6-max/image-to-video.md): `pixverse-v6-max/image-to-video`, from $0.030 per second - [Pixverse v6 Max · Text To Video](https://pinerouter.ai/docs/models/pixverse-v6-max/text-to-video.md): `pixverse-v6-max/text-to-video`, from $0.030 per second - [Runway · Extend Ai Video](https://pinerouter.ai/docs/models/runway/extend-ai-video.md): `runway/extend-ai-video`, from $0.075 per job - [Runway · Gen4 Aleph](https://pinerouter.ai/docs/models/runway/gen4-aleph.md): `runway/gen4-aleph`, priced per job - [Runway Gen-4](https://pinerouter.ai/docs/models/runway.md): `runway`, from $0.075 per job - [Upscale video](https://pinerouter.ai/docs/models/topaz/video-upscale.md): `topaz/video-upscale`, from $0.050 per second - [Veo · Extend](https://pinerouter.ai/docs/models/veo/extend.md): `veo/extend`, from $0.188 per job - [Veo · Get 1080p Video](https://pinerouter.ai/docs/models/veo/get-1080p-video.md): `veo/get-1080p-video`, from $0.031 per job - [Veo 3.1](https://pinerouter.ai/docs/models/veo-3-1.md): `veo-3-1`, from $0.188 per job - [Vidu Q4 · Image To Video](https://pinerouter.ai/docs/models/vidu-q4/image-to-video.md): `vidu-q4/image-to-video`, from $0.045 per second - [Vidu Q4 · Reference To Video](https://pinerouter.ai/docs/models/vidu-q4/reference-to-video.md): `vidu-q4/reference-to-video`, from $0.045 per second - [Wan 2 2 A14b Image To Video Turbo](https://pinerouter.ai/docs/models/wan/2-2-a14b-image-to-video-turbo.md): `wan/2-2-a14b-image-to-video-turbo`, from $0.050 per second - [Wan 2 2 A14b Text To Video Turbo](https://pinerouter.ai/docs/models/wan/2-2-a14b-text-to-video-turbo.md): `wan/2-2-a14b-text-to-video-turbo`, from $0.050 per second - [Wan 2 5 Image To Video](https://pinerouter.ai/docs/models/wan/2-5-image-to-video.md): `wan/2-5-image-to-video`, from $0.375 per job - [Wan 2 5 Text To Video](https://pinerouter.ai/docs/models/wan/2-5-text-to-video.md): `wan/2-5-text-to-video`, from $0.375 per job - [Wan 2 6 Flash Image To Video](https://pinerouter.ai/docs/models/wan/2-6-flash-image-to-video.md): `wan/2-6-flash-image-to-video`, from $0.125 per job - [Wan 2 6 Flash Video To Video](https://pinerouter.ai/docs/models/wan/2-6-flash-video-to-video.md): `wan/2-6-flash-video-to-video`, from $0.125 per job - [Wan 2 6 Image To Video](https://pinerouter.ai/docs/models/wan/2-6-image-to-video.md): `wan/2-6-image-to-video`, from $0.438 per job - [Wan 2 6 Text To Video](https://pinerouter.ai/docs/models/wan/2-6-text-to-video.md): `wan/2-6-text-to-video`, from $0.438 per job - [Wan 2 6 Video To Video](https://pinerouter.ai/docs/models/wan/2-6-video-to-video.md): `wan/2-6-video-to-video`, from $0.438 per job - [Wan 2 7 Image To Video](https://pinerouter.ai/docs/models/wan/2-7-image-to-video.md): `wan/2-7-image-to-video`, from $0.100 per second - [Wan 2 7 R2v](https://pinerouter.ai/docs/models/wan/2-7-r2v.md): `wan/2-7-r2v`, from $0.100 per second - [Wan 2 7 Text To Video](https://pinerouter.ai/docs/models/wan/2-7-text-to-video.md): `wan/2-7-text-to-video`, from $0.100 per second - [Wan 2 7 Videoedit](https://pinerouter.ai/docs/models/wan/2-7-videoedit.md): `wan/2-7-videoedit`, from $0.100 per second - [Wan 3 0 Video](https://pinerouter.ai/docs/models/wan/3-0-video.md): `wan/3-0-video`, from $0.050 per second - [Wan 3 0 Video Prime](https://pinerouter.ai/docs/models/wan/3-0-video-prime.md): `wan/3-0-video-prime`, from $0.076 per second - [Wan Animate: move](https://pinerouter.ai/docs/models/wan/2-2-animate-move.md): `wan/2-2-animate-move`, from $0.078 per second - [Wan Animate: replace](https://pinerouter.ai/docs/models/wan/2-2-animate-replace.md): `wan/2-2-animate-replace`, from $0.037 per second ### Audio models (24) - [Ai Music Api · Boost Music Style](https://pinerouter.ai/docs/models/ai-music-api/boost-music-style.md): `ai-music-api/boost-music-style`, from $0.0025 per job - [Ai Music Api · Convert To Wav Format](https://pinerouter.ai/docs/models/ai-music-api/convert-to-wav-format.md): `ai-music-api/convert-to-wav-format`, from $0.0025 per job - [Ai Music Api · Create Music Video](https://pinerouter.ai/docs/models/ai-music-api/create-music-video.md): `ai-music-api/create-music-video`, from $0.013 per job - [Ai Music Api · Extend](https://pinerouter.ai/docs/models/ai-music-api/extend.md): `ai-music-api/extend`, from $0.075 per job - [Ai Music Api · Generate Midi From Audio](https://pinerouter.ai/docs/models/ai-music-api/generate-midi-from-audio.md): `ai-music-api/generate-midi-from-audio`, from $0.0000 per job - [Ai Music Api · Replace Section](https://pinerouter.ai/docs/models/ai-music-api/replace-section.md): `ai-music-api/replace-section`, from $0.031 per job - [Ai Music Api · Separate Vocals](https://pinerouter.ai/docs/models/ai-music-api/separate-vocals.md): `ai-music-api/separate-vocals`, from $0.063 per job - [Elevenlabs Text To Dialogue v3](https://pinerouter.ai/docs/models/elevenlabs/text-to-dialogue-v3.md): `elevenlabs/text-to-dialogue-v3`, from $0.088 per 1k characters - [Elevenlabs Text To Speech Multilingual v2](https://pinerouter.ai/docs/models/elevenlabs/text-to-speech-multilingual-v2.md): `elevenlabs/text-to-speech-multilingual-v2`, from $0.075 per 1k characters - [Elevenlabs Text To Speech Turbo 2 5](https://pinerouter.ai/docs/models/elevenlabs/text-to-speech-turbo-2-5.md): `elevenlabs/text-to-speech-turbo-2-5`, from $0.037 per 1k characters - [Google Gemini 3 1 Flash Tts](https://pinerouter.ai/docs/models/google/gemini-3-1-flash-tts.md): `google/gemini-3-1-flash-tts`, from $0.875 per job - [Google Gemini 3 8 Flash Lite Tts](https://pinerouter.ai/docs/models/google/gemini-3-8-flash-lite-tts.md): `google/gemini-3-8-flash-lite-tts`, from $0.438 per job - [Google Gemini 3 8 Flash Tts](https://pinerouter.ai/docs/models/google/gemini-3-8-flash-tts.md): `google/gemini-3-8-flash-tts`, from $0.438 per job - [Isolate voice](https://pinerouter.ai/docs/models/elevenlabs/audio-isolation.md): `elevenlabs/audio-isolation`, priced per job - [Suno: Add instrumental](https://pinerouter.ai/docs/models/ai-music-api/add-instrumental.md): `ai-music-api/add-instrumental`, from $0.075 per job - [Suno: Add vocals](https://pinerouter.ai/docs/models/ai-music-api/add-vocals.md): `ai-music-api/add-vocals`, priced per job - [Suno: Cover](https://pinerouter.ai/docs/models/ai-music-api/cover-generate.md): `ai-music-api/cover-generate`, from $0.0000 per job - [Suno: Cover your audio](https://pinerouter.ai/docs/models/ai-music-api/upload-and-cover-audio.md): `ai-music-api/upload-and-cover-audio`, from $0.075 per job - [Suno: Extend your audio](https://pinerouter.ai/docs/models/ai-music-api/upload-and-extend-audio.md): `ai-music-api/upload-and-extend-audio`, from $0.075 per job - [Suno: Generate music](https://pinerouter.ai/docs/models/ai-music-api/generate.md): `ai-music-api/generate`, from $0.075 per job - [Suno: Lyrics](https://pinerouter.ai/docs/models/ai-music-api/generate-lyrics.md): `ai-music-api/generate-lyrics`, from $0.0025 per job - [Suno: Mashup](https://pinerouter.ai/docs/models/ai-music-api/mashup.md): `ai-music-api/mashup`, from $0.075 per job - [Suno: Sound effects](https://pinerouter.ai/docs/models/ai-music-api/sounds.md): `ai-music-api/sounds`, from $0.016 per job - [Wan Speech to video](https://pinerouter.ai/docs/models/wan/2-2-a14b-speech-to-video-turbo.md): `wan/2-2-a14b-speech-to-video-turbo`, from $0.075 per second --- # Text to speech Source: https://pinerouter.ai/docs/reference/audio-speech OpenAPI for `POST /v1/audio/speech`: ```json { "POST": "/v1/audio/speech", "tags": [ "Audio" ], "summary": "Text to speech", "description": "Turns text into audio", "operationId": "createspeech", "parameters": [], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": [ "model", "input", "voice" ], "properties": { "model": { "type": "string", "examples": [ "tts-1" ] }, "input": { "type": "string", "description": "Text to convert", "maxLength": 4096 }, "voice": { "type": "string", "enum": [ "alloy", "echo", "fable", "onyx", "nova", "shimmer" ] }, "response_format": { "type": "string", "enum": [ "mp3", "opus", "aac", "flac", "wav", "pcm" ], "default": "mp3" }, "speed": { "type": "number", "minimum": 0.25, "maximum": 4, "default": 1 } } } } } }, "security": [ { "BearerAuth": [] } ], "responses": { "200": { "description": "Audio generated", "content": { "audio/mpeg": { "schema": { "type": "string", "format": "binary" } } } }, "401": { "description": "Missing, wrong, disabled or expired key", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "", "message": "Invalid token (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } } }, "403": { "description": "Balance too low, or the key may not use this model or IP", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "", "message": "This token has no access to model gpt-5-2 (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } } }, "429": { "description": "Rate limit: 120 requests a minute per account, 60 of them successful", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "", "message": "You have reached the request limit: maximum 60 requests in 1 minutes (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } }, "headers": { "Retry-After": { "description": "Seconds to wait before retrying.", "schema": { "type": "integer", "example": 60 } } } }, "500": { "description": "Error on our side or the model's. Safe to retry with backoff.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "bad_response_status_code", "message": "bad response status code 500 (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error", "param": "" } } } } } }, "components": { "securitySchemes": { "BearerAuth": { "type": "http", "scheme": "bearer", "description": "Bearer token authentication.\nFormat: `Authorization: Bearer sk-xxxxxx`\n" } } } } ``` --- # Transcription Source: https://pinerouter.ai/docs/reference/audio-transcriptions OpenAPI for `POST /v1/audio/transcriptions`: ```json { "POST": "/v1/audio/transcriptions", "tags": [ "Audio" ], "summary": "Audio transcription", "description": "Turns audio into text", "operationId": "createtranscription", "parameters": [], "requestBody": { "required": true, "content": { "multipart/form-data": { "schema": { "type": "object", "properties": { "file": { "type": "string", "format": "binary", "description": "Audio file" }, "model": { "type": "string", "examples": [ "whisper-1" ] }, "language": { "type": "string", "description": "ISO-639-1 language code" }, "prompt": { "type": "string" }, "response_format": { "type": "string", "enum": [ "json", "text", "srt", "verbose_json", "vtt" ], "default": "json" }, "temperature": { "type": "number" }, "timestamp_granularities": { "type": "array", "items": { "type": "string", "enum": [ "word", "segment" ] } } }, "required": [ "file", "model" ] } } } }, "security": [ { "BearerAuth": [] } ], "responses": { "200": { "description": "Transcribed", "content": { "application/json": { "schema": { "type": "object", "properties": { "text": { "type": "string" } } } } } }, "401": { "description": "Missing, wrong, disabled or expired key", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "", "message": "Invalid token (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } } }, "403": { "description": "Balance too low, or the key may not use this model or IP", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "", "message": "This token has no access to model gpt-5-2 (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } } }, "429": { "description": "Rate limit: 120 requests a minute per account, 60 of them successful", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "", "message": "You have reached the request limit: maximum 60 requests in 1 minutes (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } }, "headers": { "Retry-After": { "description": "Seconds to wait before retrying.", "schema": { "type": "integer", "example": 60 } } } }, "500": { "description": "Error on our side or the model's. Safe to retry with backoff.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "bad_response_status_code", "message": "bad response status code 500 (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error", "param": "" } } } } } }, "components": { "securitySchemes": { "BearerAuth": { "type": "http", "scheme": "bearer", "description": "Bearer token authentication.\nFormat: `Authorization: Bearer sk-xxxxxx`\n" } } } } ``` --- # Translation Source: https://pinerouter.ai/docs/reference/audio-translations OpenAPI for `POST /v1/audio/translations`: ```json { "POST": "/v1/audio/translations", "tags": [ "Audio" ], "summary": "Audio translation", "description": "Translates audio into English text", "operationId": "createtranslation", "parameters": [], "requestBody": { "required": true, "content": { "multipart/form-data": { "schema": { "type": "object", "properties": { "file": { "type": "string", "format": "binary" }, "model": { "type": "string" }, "prompt": { "type": "string" }, "response_format": { "type": "string" }, "temperature": { "type": "number" } }, "required": [ "file", "model" ] } } } }, "security": [ { "BearerAuth": [] } ], "responses": { "200": { "description": "Translated", "content": { "application/json": { "schema": { "type": "object", "properties": { "text": { "type": "string" } } } } } }, "401": { "description": "Missing, wrong, disabled or expired key", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "", "message": "Invalid token (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } } }, "403": { "description": "Balance too low, or the key may not use this model or IP", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "", "message": "This token has no access to model gpt-5-2 (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } } }, "429": { "description": "Rate limit: 120 requests a minute per account, 60 of them successful", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "", "message": "You have reached the request limit: maximum 60 requests in 1 minutes (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } }, "headers": { "Retry-After": { "description": "Seconds to wait before retrying.", "schema": { "type": "integer", "example": 60 } } } }, "500": { "description": "Error on our side or the model's. Safe to retry with backoff.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "bad_response_status_code", "message": "bad response status code 500 (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error", "param": "" } } } } } }, "components": { "securitySchemes": { "BearerAuth": { "type": "http", "scheme": "bearer", "description": "Bearer token authentication.\nFormat: `Authorization: Bearer sk-xxxxxx`\n" } } } } ``` --- # Chat completions Source: https://pinerouter.ai/docs/reference/chat-completions OpenAPI for `POST /v1/chat/completions`: ```json { "POST": "/v1/chat/completions", "tags": [ "Chat" ], "summary": "Chat Completions", "description": "Creates a model response from a conversation. Streaming and non-streaming.\n\nCompatible with OpenAI's Chat Completions API.\n", "operationId": "createchatcompletion", "parameters": [], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": [ "model", "messages" ], "properties": { "model": { "type": "string", "description": "Model id", "examples": [ "gpt-5-2" ] }, "messages": { "type": "array", "items": { "type": "object", "required": [ "role", "content" ], "properties": { "role": { "type": "string", "enum": [ "system", "user", "assistant", "tool", "developer" ], "description": "Message role" }, "content": { "oneOf": [ { "type": "string" }, { "type": "array", "items": { "type": "object", "properties": { "type": { "type": "string", "enum": [ "text", "image_url", "input_audio", "file", "video_url" ] }, "text": { "type": "string" }, "image_url": { "type": "object", "properties": { "url": { "type": "string", "description": "Image URL or base64" }, "detail": { "type": "string", "enum": [ "low", "high", "auto" ] } } }, "input_audio": { "type": "object", "properties": { "data": { "type": "string", "description": "Base64-encoded audio data" }, "format": { "type": "string", "enum": [ "wav", "mp3" ] } } }, "file": { "type": "object", "properties": { "filename": { "type": "string" }, "file_data": { "type": "string" }, "file_id": { "type": "string" } } }, "video_url": { "type": "object", "properties": { "url": { "type": "string" } } } } } } ], "description": "Message content" }, "name": { "type": "string", "description": "Sender name" }, "tool_calls": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "type": { "type": "string", "examples": [ "function" ] }, "function": { "type": "object", "properties": { "name": { "type": "string" }, "arguments": { "type": "string" } } } } } }, "tool_call_id": { "type": "string", "description": "Tool call id (for tool role messages)" }, "reasoning_content": { "type": "string", "description": "Reasoning content" } } }, "description": "Conversation messages" }, "temperature": { "type": "number", "minimum": 0, "maximum": 2, "default": 1, "description": "Sampling temperature" }, "top_p": { "type": "number", "minimum": 0, "maximum": 1, "default": 1, "description": "Nucleus sampling (top_p)" }, "n": { "type": "integer", "minimum": 1, "default": 1, "description": "Number of results" }, "stream": { "type": "boolean", "default": false, "description": "Stream the response" }, "stream_options": { "type": "object", "properties": { "include_usage": { "type": "boolean" } } }, "stop": { "oneOf": [ { "type": "string" }, { "type": "array", "items": { "type": "string" } } ], "description": "Stop sequences" }, "max_tokens": { "type": "integer", "description": "Maximum tokens to generate" }, "max_completion_tokens": { "type": "integer", "description": "Maximum completion tokens" }, "presence_penalty": { "type": "number", "minimum": -2, "maximum": 2, "default": 0 }, "frequency_penalty": { "type": "number", "minimum": -2, "maximum": 2, "default": 0 }, "logit_bias": { "type": "object", "additionalProperties": { "type": "number" }, "properties": {} }, "user": { "type": "string" }, "tools": { "type": "array", "items": { "type": "object", "properties": { "type": { "type": "string", "examples": [ "function" ] }, "function": { "type": "object", "properties": { "name": { "type": "string" }, "description": { "type": "string" }, "parameters": { "type": "object", "description": "Parameters as a JSON Schema", "properties": {} } } } } } }, "tool_choice": { "oneOf": [ { "type": "string", "enum": [ "none", "auto", "required" ] }, { "type": "object", "properties": { "type": { "type": "string" }, "function": { "type": "object", "properties": { "name": { "type": "string" } } } } } ] }, "response_format": { "type": "object", "properties": { "type": { "type": "string", "enum": [ "text", "json_object", "json_schema" ] }, "json_schema": { "type": "object", "description": "JSON Schema definition", "properties": {} } } }, "seed": { "type": "integer" }, "reasoning_effort": { "type": "string", "enum": [ "low", "medium", "high" ], "description": "Reasoning effort (for reasoning models)" }, "modalities": { "type": "array", "items": { "type": "string", "enum": [ "text", "audio" ] } }, "audio": { "type": "object", "properties": { "voice": { "type": "string" }, "format": { "type": "string" } } } } }, "example": { "model": "gpt-5-2", "messages": [ { "role": "user", "content": "Explain DNS in one sentence." } ], "max_tokens": 200 } } } }, "security": [ { "BearerAuth": [] } ], "responses": { "200": { "description": "Response created", "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "string" }, "object": { "type": "string", "examples": [ "chat.completion" ] }, "created": { "type": "integer" }, "model": { "type": "string" }, "choices": { "type": "array", "items": { "type": "object", "properties": { "index": { "type": "integer" }, "message": { "type": "object", "required": [ "role", "content" ], "properties": { "role": { "type": "string", "enum": [ "system", "user", "assistant", "tool", "developer" ], "description": "Message role" }, "content": { "oneOf": [ { "type": "string" }, { "type": "array", "items": { "type": "object", "properties": { "type": { "type": "string", "enum": [ "text", "image_url", "input_audio", "file", "video_url" ] }, "text": { "type": "string" }, "image_url": { "type": "object", "properties": { "url": { "type": "string", "description": "Image URL or base64" }, "detail": { "type": "string", "enum": [ "low", "high", "auto" ] } } }, "input_audio": { "type": "object", "properties": { "data": { "type": "string", "description": "Base64-encoded audio data" }, "format": { "type": "string", "enum": [ "wav", "mp3" ] } } }, "file": { "type": "object", "properties": { "filename": { "type": "string" }, "file_data": { "type": "string" }, "file_id": { "type": "string" } } }, "video_url": { "type": "object", "properties": { "url": { "type": "string" } } } } } } ], "description": "Message content" }, "name": { "type": "string", "description": "Sender name" }, "tool_calls": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "type": { "type": "string", "examples": [ "function" ] }, "function": { "type": "object", "properties": { "name": { "type": "string" }, "arguments": { "type": "string" } } } } } }, "tool_call_id": { "type": "string", "description": "Tool call id (for tool role messages)" }, "reasoning_content": { "type": "string", "description": "Reasoning content" } } }, "finish_reason": { "type": "string", "enum": [ "stop", "length", "tool_calls", "content_filter" ] } } } }, "usage": { "type": "object", "properties": { "prompt_tokens": { "type": "integer", "description": "Prompt tokens" }, "completion_tokens": { "type": "integer", "description": "Completion tokens" }, "total_tokens": { "type": "integer", "description": "Total tokens" }, "prompt_tokens_details": { "type": "object", "properties": { "cached_tokens": { "type": "integer" }, "text_tokens": { "type": "integer" }, "audio_tokens": { "type": "integer" }, "image_tokens": { "type": "integer" } } }, "completion_tokens_details": { "type": "object", "properties": { "text_tokens": { "type": "integer" }, "audio_tokens": { "type": "integer" }, "reasoning_tokens": { "type": "integer" } } } } }, "system_fingerprint": { "type": "string" } } } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "Error message" }, "type": { "type": "string", "description": "Error type" }, "param": { "type": [ "string", "null" ], "description": "Related parameters" }, "code": { "type": [ "string", "null" ], "description": "Error code" } } } } } } } }, "401": { "description": "Missing, wrong, disabled or expired key", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "", "message": "Invalid token (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } } }, "403": { "description": "Balance too low, or the key may not use this model or IP", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "", "message": "This token has no access to model gpt-5-2 (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } } }, "429": { "description": "Rate limited", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "Error message" }, "type": { "type": "string", "description": "Error type" }, "param": { "type": [ "string", "null" ], "description": "Related parameters" }, "code": { "type": [ "string", "null" ], "description": "Error code" } } } } }, "example": { "error": { "code": "", "message": "You have reached the request limit: maximum 60 requests in 1 minutes (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } }, "headers": { "Retry-After": { "description": "Seconds to wait before retrying.", "schema": { "type": "integer", "example": 60 } } } }, "500": { "description": "Error on our side or the model's. Safe to retry with backoff.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "bad_response_status_code", "message": "bad response status code 500 (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error", "param": "" } } } } } }, "components": { "securitySchemes": { "BearerAuth": { "type": "http", "scheme": "bearer", "description": "Bearer token authentication.\nFormat: `Authorization: Bearer sk-xxxxxx`\n" } } } } ``` --- # Embeddings Source: https://pinerouter.ai/docs/reference/embeddings OpenAPI for `POST /v1/embeddings`: ```json { "POST": "/v1/embeddings", "tags": [ "Embeddings" ], "summary": "Create embeddings", "description": "Turns text into embedding vectors", "operationId": "createembedding", "parameters": [], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": [ "model", "input" ], "properties": { "model": { "type": "string", "examples": [ "text-embedding-ada-002" ] }, "input": { "oneOf": [ { "type": "string" }, { "type": "array", "items": { "type": "string" } } ], "description": "Text to embed" }, "encoding_format": { "type": "string", "enum": [ "float", "base64" ], "default": "float" }, "dimensions": { "type": "integer", "description": "Output vector dimensions" } } } } } }, "security": [ { "BearerAuth": [] } ], "responses": { "200": { "description": "Embeddings created", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "examples": [ "list" ] }, "data": { "type": "array", "items": { "type": "object", "properties": { "object": { "type": "string", "examples": [ "embedding" ] }, "index": { "type": "integer" }, "embedding": { "type": "array", "items": { "type": "number" } } } } }, "model": { "type": "string" }, "usage": { "type": "object", "properties": { "prompt_tokens": { "type": "integer" }, "total_tokens": { "type": "integer" } } } } } } } }, "401": { "description": "Missing, wrong, disabled or expired key", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "", "message": "Invalid token (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } } }, "403": { "description": "Balance too low, or the key may not use this model or IP", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "", "message": "This token has no access to model gpt-5-2 (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } } }, "429": { "description": "Rate limit: 120 requests a minute per account, 60 of them successful", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "", "message": "You have reached the request limit: maximum 60 requests in 1 minutes (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } }, "headers": { "Retry-After": { "description": "Seconds to wait before retrying.", "schema": { "type": "integer", "example": 60 } } } }, "500": { "description": "Error on our side or the model's. Safe to retry with backoff.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "bad_response_status_code", "message": "bad response status code 500 (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error", "param": "" } } } } } }, "components": { "securitySchemes": { "BearerAuth": { "type": "http", "scheme": "bearer", "description": "Bearer token authentication.\nFormat: `Authorization: Bearer sk-xxxxxx`\n" } } } } ``` --- # Upload a file Source: https://pinerouter.ai/docs/reference/files-create OpenAPI for `POST /v1/files`: ```json { "POST": "/v1/files", "summary": "Upload a file", "operationId": "uploadFile", "description": "Upload an image, video or audio file to use as a job input. Returns a public https link, kept 1 day. Images up to 10 MB, video and audio up to 50 MB.", "security": [ { "bearer": [] } ], "requestBody": { "required": true, "content": { "multipart/form-data": { "schema": { "type": "object", "required": [ "file" ], "properties": { "file": { "type": "string", "format": "binary" } } } } } }, "responses": { "200": { "description": "Uploaded", "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "string" }, "object": { "type": "string", "example": "file" }, "url": { "type": "string", "format": "uri", "example": "https://files.example.com/uploads/6f1c.png" }, "bytes": { "type": "integer", "example": 48213 }, "content_type": { "type": "string", "example": "image/png" }, "expires_at": { "type": "integer", "description": "Unix time when the file is deleted." } } } } } }, "400": { "description": "No file, or not an image, video or audio file" }, "401": { "description": "Missing or wrong API key", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string" }, "type": { "type": "string", "example": "invalid_api_key" }, "code": { "type": "string", "example": "invalid_api_key" } } } } } } } }, "413": { "description": "File too large" } }, "components": { "securitySchemes": { "bearer": { "type": "http", "scheme": "bearer" } } } } ``` --- # Edit image Source: https://pinerouter.ai/docs/reference/images-edits OpenAPI for `POST /v1/images/edits`: ```json { "POST": "/v1/images/edits", "tags": [ "Images" ], "summary": "Edit image", "description": "Creates an edited or extended image from an original image and a prompt.\n\n", "operationId": "post-v1-images-edits", "parameters": [], "requestBody": { "required": false, "content": { "multipart/form-data": { "schema": { "type": "object", "properties": { "image": { "description": "The image to edit, as a file or link.", "type": "string", "format": "binary", "examples": [ "cmMtdXBsb2FkLTE2ODc4MzMzNDc3NTEtMjA=/31225951_59371037e9_small.png" ] }, "mask": { "description": "An extra image whose fully transparent areas mark where to edit. Same size as the image.", "type": "string", "format": "binary", "examples": [ [] ] }, "prompt": { "description": "Text description of the image you want.", "type": "string", "examples": [ "A cute baby sea otter wearing a beret." ] }, "n": { "description": "Number of images, where the model supports it.", "type": "string", "examples": [ "1" ] }, "size": { "description": "Size of the image, `WIDTHxHEIGHT`; mapped to the nearest size the model offers.", "type": "string", "examples": [ "1024x1024" ] }, "response_format": { "description": "Format of the returned images: `url` or `b64_json`.", "type": "string", "examples": [ "url" ] }, "user": { "description": "A unique id for your end user; helps detect abuse.", "type": "string", "examples": [ "" ] }, "model": { "type": "string", "examples": [ "google/nano-banana-edit" ] } }, "required": [ "image", "prompt" ] } } } }, "security": [ { "Bearer": [] } ], "responses": { "200": { "description": "Response", "content": { "application/json": { "schema": { "type": "object", "properties": {} } } } }, "401": { "description": "Missing, wrong, disabled or expired key", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "", "message": "Invalid token (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } } }, "403": { "description": "Balance too low, or the key may not use this model or IP", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "", "message": "This token has no access to model gpt-5-2 (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } } }, "429": { "description": "Rate limit: 120 requests a minute per account, 60 of them successful", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "", "message": "You have reached the request limit: maximum 60 requests in 1 minutes (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } }, "headers": { "Retry-After": { "description": "Seconds to wait before retrying.", "schema": { "type": "integer", "example": 60 } } } }, "500": { "description": "Error on our side or the model's. Safe to retry with backoff." } }, "components": { "securitySchemes": { "Bearer": { "type": "http", "scheme": "bearer" } } } } ``` --- # Create image Source: https://pinerouter.ai/docs/reference/images-generations OpenAPI for `POST /v1/images/generations`: ```json { "POST": "/v1/images/generations", "tags": [ "Images" ], "summary": "Create image", "description": "Creates an image from a prompt.", "operationId": "post-v1-images-generations", "parameters": [], "requestBody": { "required": false, "content": { "application/json": { "schema": { "type": "object", "properties": { "model": { "type": "string", "description": "The model id, from the Models page." }, "prompt": { "type": "string", "description": "Text description of the image you want." }, "n": { "type": "integer", "description": "Number of images, where the model supports it." }, "size": { "type": "string", "description": "Size of the image, `WIDTHxHEIGHT`; mapped to the nearest aspect ratio and resolution the model offers." }, "background": { "type": "string", "description": "Sets transparency for the image background, where the model supports it: `transparent`, `opaque` or `auto` (default). With `auto` the model picks the best background.\n\nFor `transparent`, use an output format that supports transparency: `png` (default) or `webp`." }, "quality": { "type": "string", "description": "Image quality, where the model supports it." }, "stream": { "type": "string" }, "style": { "type": "string" }, "user": { "type": "string" } }, "required": [ "prompt" ] }, "example": { "model": "nano-banana-pro", "prompt": "A matte ceramic mug on a beige backdrop, studio light", "size": "1024x1024" } } } }, "security": [ { "Bearer": [] } ], "responses": { "200": { "description": "Response", "content": { "application/json": { "schema": { "type": "object", "properties": { "created": { "type": "integer" }, "data": { "type": "array", "items": { "type": "object", "properties": { "b64_json": { "type": "string" }, "url": { "type": "string" } }, "required": [ "url" ] } }, "usage": { "type": "object", "properties": { "total_tokens": { "type": "integer" }, "input_tokens": { "type": "integer" }, "output_tokens": { "type": "integer" }, "input_tokens_details": { "type": "object", "properties": { "text_tokens": { "type": "integer" }, "image_tokens": { "type": "integer" } }, "required": [ "text_tokens", "image_tokens" ] } }, "required": [ "total_tokens", "input_tokens", "output_tokens", "input_tokens_details" ] } }, "required": [ "created", "data", "usage" ] }, "example": { "data": [ { "url": "https://files.example.com/abc.png" } ] } } } }, "401": { "description": "Missing, wrong, disabled or expired key", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "", "message": "Invalid token (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } } }, "403": { "description": "Balance too low, or the key may not use this model or IP", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "", "message": "This token has no access to model gpt-5-2 (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } } }, "429": { "description": "Rate limit: 120 requests a minute per account, 60 of them successful", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "", "message": "You have reached the request limit: maximum 60 requests in 1 minutes (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } }, "headers": { "Retry-After": { "description": "Seconds to wait before retrying.", "schema": { "type": "integer", "example": 60 } } } }, "500": { "description": "Error on our side or the model's. Safe to retry with backoff." } }, "components": { "securitySchemes": { "Bearer": { "type": "http", "scheme": "bearer" } } } } ``` --- # API reference > Base URL, authentication and every endpoint. Source: https://pinerouter.ai/docs/reference ## Base URL ```text https://pinerouter.ai/v1 ``` The API follows OpenAI's format, so OpenAI's SDKs and most OpenAI-compatible tools work by changing the base URL. Anthropic's Messages format works too. ## Authentication ```http Authorization: Bearer sk-... ``` Create keys in the console under [API keys](https://pinerouter.ai/docs/keys). Every endpoint page has a playground: paste your key under **Authorization** and send a real request. ## Endpoints Limits, billing and error codes: [Keys, billing and limits](https://pinerouter.ai/docs/auth). --- # Start a job Source: https://pinerouter.ai/docs/reference/jobs-create OpenAPI for `POST /v1/jobs`: ```json { "POST": "/v1/jobs", "summary": "Start a job", "description": "Starts a job on any image, video, audio or tool model. `input` takes the fields listed on the model's page; fields you leave out use their defaults. Files go in as public https links.", "operationId": "createJob", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": [ "model", "input" ], "properties": { "model": { "type": "string", "description": "Model id from the Models page.", "example": "z-image" }, "input": { "type": "object", "description": "The model's fields, as listed on its page.", "additionalProperties": true, "example": { "prompt": "A red paper boat on a calm lake", "aspect_ratio": "1:1" } }, "webhook_url": { "type": "string", "format": "uri", "description": "Optional public https URL. When the job succeeds or fails we POST the job to it, signed per the Standard Webhooks spec (webhook-id, webhook-timestamp, webhook-signature: HMAC-SHA256 with your API key as the secret). Retried after 1, 5, 15 and 60 minutes.", "example": "https://your.app/hooks/jobs" } } }, "example": { "model": "z-image", "input": { "prompt": "A red paper boat on a calm lake", "aspect_ratio": "1:1" } } } } }, "responses": { "200": { "description": "Job started", "content": { "application/json": { "schema": { "type": "object", "properties": { "code": { "type": "integer", "example": 200 }, "msg": { "type": "string", "example": "success" }, "data": { "type": "object", "properties": { "id": { "type": "string", "example": "task_abc123" }, "status": { "type": "string", "enum": [ "queued", "running", "succeeded", "failed" ], "example": "queued" }, "taskId": { "type": "string", "description": "Same as id (older name)." }, "state": { "type": "string", "description": "Older status name: waiting, queuing, generating, success, fail." } } } } } } } }, "400": { "description": "Invalid request or refused by the content rules", "content": { "application/json": { "schema": { "type": "object", "properties": { "code": { "type": "integer", "example": 400 }, "msg": { "type": "string" } } } } } }, "401": { "description": "Missing, wrong, disabled or expired key", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "", "message": "Invalid token (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } } }, "403": { "description": "Balance too low, or the key may not use this model", "content": { "application/json": { "schema": { "type": "object", "properties": { "code": { "type": "integer", "description": "The HTTP status again." }, "msg": { "type": "string" }, "data": { "type": "null" } }, "required": [ "code", "msg" ] }, "example": { "code": 403, "msg": "This token has no access to model z-image", "data": null } } } }, "429": { "description": "Rate limit: 120 requests a minute per account, 60 of them successful", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "", "message": "You have reached the request limit: maximum 60 requests in 1 minutes (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } }, "headers": { "Retry-After": { "description": "Seconds to wait before retrying.", "schema": { "type": "integer", "example": 60 } } } }, "500": { "description": "Error on our side or the model's. Safe to retry with backoff.", "content": { "application/json": { "schema": { "type": "object", "properties": { "code": { "type": "integer", "description": "The HTTP status again." }, "msg": { "type": "string" }, "data": { "type": "null" } }, "required": [ "code", "msg" ] }, "example": { "code": 500, "msg": "Task request failed", "data": null } } } } }, "components": { "securitySchemes": { "BearerAuth": { "type": "http", "scheme": "bearer", "description": "`Authorization: Bearer sk-...`" } } } } ``` --- # Get a job Source: https://pinerouter.ai/docs/reference/jobs-get OpenAPI for `GET /v1/jobs/{taskId}`: ```json { "GET": "/v1/jobs/{taskId}", "summary": "Get a job", "description": "Status and result of a job. Poll every 3 to 10 seconds until `data.state` is `success` or `fail`. On success, `data.resultJson` is a JSON string with `resultUrls`, the links to your files (kept for 7 days).", "operationId": "getJob", "parameters": [ { "name": "taskId", "in": "path", "required": true, "schema": { "type": "string" }, "description": "The `taskId` from Start a job.", "example": "task_abc123" } ], "responses": { "200": { "description": "Job status", "content": { "application/json": { "schema": { "type": "object", "properties": { "code": { "type": "integer", "example": 200 }, "msg": { "type": "string", "example": "success" }, "data": { "type": "object", "properties": { "id": { "type": "string", "example": "task_abc123" }, "model": { "type": "string", "example": "z-image" }, "status": { "type": "string", "enum": [ "queued", "running", "succeeded", "failed" ], "example": "succeeded" }, "outputs": { "type": "array", "items": { "type": "string", "format": "uri" }, "description": "File links when status is succeeded. Kept 7 days.", "example": [ "https://files.example.com/outputs/abc.png" ] }, "error": { "type": "string", "description": "Why the job failed, when status is failed. A failed job costs nothing." }, "taskId": { "type": "string", "description": "Same as id (older name)." }, "state": { "type": "string", "description": "Older status name." }, "resultJson": { "type": "string", "description": "Older field: JSON string with resultUrls." }, "failMsg": { "type": "string", "description": "Older field: same as error." } } } } } } } }, "401": { "description": "Missing, wrong, disabled or expired key", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "", "message": "Invalid token (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } } }, "403": { "description": "Balance too low, or the key may not use this model", "content": { "application/json": { "schema": { "type": "object", "properties": { "code": { "type": "integer", "description": "The HTTP status again." }, "msg": { "type": "string" }, "data": { "type": "null" } }, "required": [ "code", "msg" ] }, "example": { "code": 403, "msg": "This token has no access to model z-image", "data": null } } } }, "404": { "description": "No job with this id on your account", "content": { "application/json": { "schema": { "type": "object", "properties": { "code": { "type": "integer", "description": "The HTTP status again." }, "msg": { "type": "string" }, "data": { "type": "null" } }, "required": [ "code", "msg" ] }, "example": { "code": 404, "msg": "Task not found", "data": null } } } }, "429": { "description": "Rate limit: 120 requests a minute per account, 60 of them successful", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "", "message": "You have reached the request limit: maximum 60 requests in 1 minutes (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } }, "headers": { "Retry-After": { "description": "Seconds to wait before retrying.", "schema": { "type": "integer", "example": 60 } } } }, "500": { "description": "Error on our side or the model's. Safe to retry with backoff.", "content": { "application/json": { "schema": { "type": "object", "properties": { "code": { "type": "integer", "description": "The HTTP status again." }, "msg": { "type": "string" }, "data": { "type": "null" } }, "required": [ "code", "msg" ] }, "example": { "code": 500, "msg": "Task request failed", "data": null } } } } }, "components": { "securitySchemes": { "BearerAuth": { "type": "http", "scheme": "bearer", "description": "`Authorization: Bearer sk-...`" } } } } ``` --- # Claude messages Source: https://pinerouter.ai/docs/reference/messages OpenAPI for `POST /v1/messages`: ```json { "POST": "/v1/messages", "tags": [ "Chat" ], "summary": "Claude Messages", "description": "A request in Anthropic's Claude Messages format.\nSend the `anthropic-version` header.\n", "operationId": "createmessage", "parameters": [ { "name": "anthropic-version", "in": "header", "required": true, "description": "Anthropic API version", "schema": { "type": "string", "examples": [ "2023-06-01" ] } }, { "name": "x-api-key", "in": "header", "required": false, "description": "Anthropic API key (optional; a Bearer token also works)", "schema": { "type": "string" } } ], "requestBody": { "required": false, "content": { "application/json": { "schema": { "type": "object", "required": [ "model", "messages", "max_tokens" ], "properties": { "model": { "type": "string", "examples": [ "gpt-5-2" ] }, "messages": { "type": "array", "items": { "type": "object", "required": [ "role", "content" ], "properties": { "role": { "type": "string", "enum": [ "user", "assistant" ] }, "content": { "oneOf": [ { "type": "string" }, { "type": "array", "items": { "type": "object", "properties": { "type": { "type": "string", "enum": [ "text", "image", "tool_use", "tool_result" ] }, "text": { "type": "string" }, "source": { "type": "object", "properties": { "type": { "type": "string", "enum": [ "base64", "url" ] }, "media_type": { "type": "string" }, "data": { "type": "string" }, "url": { "type": "string" } } }, "id": { "type": "string" }, "name": { "type": "string" }, "input": { "type": "object", "properties": {} }, "tool_use_id": { "type": "string" }, "content": { "type": "string" } } } } ] } } } }, "system": { "oneOf": [ { "type": "string" }, { "type": "array", "items": { "type": "object", "properties": {} } } ] }, "max_tokens": { "type": "integer", "minimum": 1 }, "temperature": { "type": "number", "minimum": 0, "maximum": 1 }, "top_p": { "type": "number" }, "top_k": { "type": "integer" }, "stream": { "type": "boolean" }, "stop_sequences": { "type": "array", "items": { "type": "string" } }, "tools": { "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string" }, "description": { "type": "string" }, "input_schema": { "type": "object", "properties": {} } } } }, "tool_choice": { "oneOf": [ { "type": "object", "properties": { "type": { "type": "string", "enum": [ "auto", "any", "tool" ] }, "name": { "type": "string" } } } ] }, "thinking": { "type": "object", "properties": { "type": { "type": "string", "enum": [ "enabled", "disabled" ] }, "budget_tokens": { "type": "integer" } } }, "metadata": { "type": "object", "properties": { "user_id": { "type": "string" } } } } }, "example": { "model": "gpt-5-2", "max_tokens": 200, "messages": [ { "role": "user", "content": "Explain DNS in one sentence." } ] } } } }, "security": [ { "BearerAuth": [] } ], "responses": { "200": { "description": "Response created", "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "string" }, "type": { "type": "string", "examples": [ "message" ] }, "role": { "type": "string", "examples": [ "assistant" ] }, "content": { "type": "array", "items": { "type": "object", "properties": { "type": { "type": "string" }, "text": { "type": "string" } } } }, "model": { "type": "string" }, "stop_reason": { "type": "string", "enum": [ "end_turn", "max_tokens", "stop_sequence", "tool_use" ] }, "usage": { "type": "object", "properties": { "input_tokens": { "type": "integer" }, "output_tokens": { "type": "integer" }, "cache_creation_input_tokens": { "type": "integer" }, "cache_read_input_tokens": { "type": "integer" } } } } } } } }, "401": { "description": "Missing, wrong, disabled or expired key", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "", "message": "Invalid token (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } } }, "403": { "description": "Balance too low, or the key may not use this model or IP", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "", "message": "This token has no access to model gpt-5-2 (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } } }, "429": { "description": "Rate limit: 120 requests a minute per account, 60 of them successful", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "", "message": "You have reached the request limit: maximum 60 requests in 1 minutes (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } }, "headers": { "Retry-After": { "description": "Seconds to wait before retrying.", "schema": { "type": "integer", "example": 60 } } } }, "500": { "description": "Error on our side or the model's. Safe to retry with backoff.", "content": { "application/json": { "schema": { "type": "object", "properties": { "type": { "type": "string", "example": "error" }, "error": { "type": "object", "properties": { "type": { "type": "string" }, "message": { "type": "string" } }, "required": [ "type", "message" ] } }, "required": [ "type", "error" ] }, "example": { "type": "error", "error": { "type": "api_error", "message": "bad response status code 500 (request id: 20261102053704107200635868d9d6m7EOL5JK)" } } } } } }, "components": { "securitySchemes": { "BearerAuth": { "type": "http", "scheme": "bearer", "description": "Bearer token authentication.\nFormat: `Authorization: Bearer sk-xxxxxx`\n" } } } } ``` --- # List models Source: https://pinerouter.ai/docs/reference/models OpenAPI for `GET /v1/models`: ```json { "GET": "/v1/models", "tags": [ "Models" ], "summary": "List models", "description": "Lists the models your key may use.\n\nThe format follows the request headers:\n- `x-api-key` and `anthropic-version` headers: Anthropic format\n- `x-goog-api-key` header or `key` query parameter: Gemini format\n- otherwise: OpenAI format\n", "operationId": "listmodels", "parameters": [ { "name": "key", "in": "query", "required": false, "description": "API key, Google style (for the Gemini format)", "schema": { "type": "string" } }, { "name": "x-api-key", "in": "header", "required": false, "description": "API key, Anthropic style (for the Claude format)", "schema": { "type": "string" } }, { "name": "anthropic-version", "in": "header", "required": false, "description": "Anthropic API version", "schema": { "type": "string", "examples": [ "2023-06-01" ] } }, { "name": "x-goog-api-key", "in": "header", "required": false, "description": "API key, Google style (for the Gemini format)", "schema": { "type": "string" } } ], "security": [ { "BearerAuth": [] } ], "responses": { "200": { "description": "Model list", "content": { "application/json": { "schema": { "type": "object", "properties": { "object": { "type": "string", "examples": [ "list" ] }, "data": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "description": "Model id", "examples": [ "gpt-5-2" ] }, "object": { "type": "string", "description": "Object type", "examples": [ "model" ] }, "created": { "type": "integer", "description": "Created at (Unix seconds)" }, "owned_by": { "type": "string", "description": "Model owner", "examples": [ "openai" ] } } } } } } } } }, "401": { "description": "Authentication failed", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "Error message" }, "type": { "type": "string", "description": "Error type" }, "param": { "type": [ "string", "null" ], "description": "Related parameters" }, "code": { "type": [ "string", "null" ], "description": "Error code" } } } } }, "example": { "error": { "code": "", "message": "Invalid token (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } } }, "403": { "description": "Balance too low, or the key may not use this model or IP", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "", "message": "This token has no access to model gpt-5-2 (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } } }, "429": { "description": "Rate limit: 120 requests a minute per account, 60 of them successful", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "", "message": "You have reached the request limit: maximum 60 requests in 1 minutes (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } }, "headers": { "Retry-After": { "description": "Seconds to wait before retrying.", "schema": { "type": "integer", "example": 60 } } } }, "500": { "description": "Error on our side or the model's. Safe to retry with backoff.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "bad_response_status_code", "message": "bad response status code 500 (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error", "param": "" } } } } } }, "components": { "securitySchemes": { "BearerAuth": { "type": "http", "scheme": "bearer", "description": "Bearer token authentication.\nFormat: `Authorization: Bearer sk-xxxxxx`\n" } } } } ``` --- # Responses Source: https://pinerouter.ai/docs/reference/responses OpenAPI for `POST /v1/responses`: ```json { "POST": "/v1/responses", "tags": [ "Chat" ], "summary": "Responses", "description": "OpenAI Responses API: creates a model response.\nSupports multi-turn conversations, tool calls and reasoning.\n", "operationId": "createresponse", "parameters": [], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": [ "model" ], "properties": { "model": { "type": "string" }, "input": { "description": "Input: a string or an array of messages", "oneOf": [ { "type": "string" }, { "type": "array", "items": { "type": "object", "properties": {} } } ] }, "instructions": { "type": "string" }, "max_output_tokens": { "type": "integer" }, "temperature": { "type": "number" }, "top_p": { "type": "number" }, "stream": { "type": "boolean" }, "tools": { "type": "array", "items": { "type": "object", "properties": {} } }, "tool_choice": { "oneOf": [ { "type": "string" }, { "type": "object", "properties": {} } ] }, "reasoning": { "type": "object", "properties": { "effort": { "type": "string", "enum": [ "low", "medium", "high" ] }, "summary": { "type": "string" } } }, "previous_response_id": { "type": "string" }, "truncation": { "type": "string", "enum": [ "auto", "disabled" ] } } }, "example": { "model": "gpt-5-5", "input": "Explain DNS in one sentence." } } } }, "security": [ { "BearerAuth": [] } ], "responses": { "200": { "description": "Response created", "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "string" }, "object": { "type": "string", "examples": [ "response" ] }, "created_at": { "type": "integer" }, "status": { "type": "string", "enum": [ "completed", "failed", "in_progress", "incomplete" ] }, "model": { "type": "string" }, "output": { "type": "array", "items": { "type": "object", "properties": { "type": { "type": "string" }, "id": { "type": "string" }, "status": { "type": "string" }, "role": { "type": "string" }, "content": { "type": "array", "items": { "type": "object", "properties": { "type": { "type": "string" }, "text": { "type": "string" } } } } } } }, "usage": { "type": "object", "properties": { "prompt_tokens": { "type": "integer", "description": "Prompt tokens" }, "completion_tokens": { "type": "integer", "description": "Completion tokens" }, "total_tokens": { "type": "integer", "description": "Total tokens" }, "prompt_tokens_details": { "type": "object", "properties": { "cached_tokens": { "type": "integer" }, "text_tokens": { "type": "integer" }, "audio_tokens": { "type": "integer" }, "image_tokens": { "type": "integer" } } }, "completion_tokens_details": { "type": "object", "properties": { "text_tokens": { "type": "integer" }, "audio_tokens": { "type": "integer" }, "reasoning_tokens": { "type": "integer" } } } } } } } } } }, "401": { "description": "Missing, wrong, disabled or expired key", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "", "message": "Invalid token (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } } }, "403": { "description": "Balance too low, or the key may not use this model or IP", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "", "message": "This token has no access to model gpt-5-2 (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } } }, "429": { "description": "Rate limit: 120 requests a minute per account, 60 of them successful", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "", "message": "You have reached the request limit: maximum 60 requests in 1 minutes (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } }, "headers": { "Retry-After": { "description": "Seconds to wait before retrying.", "schema": { "type": "integer", "example": 60 } } } }, "500": { "description": "Error on our side or the model's. Safe to retry with backoff.", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "bad_response_status_code", "message": "bad response status code 500 (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error", "param": "" } } } } } }, "components": { "securitySchemes": { "BearerAuth": { "type": "http", "scheme": "bearer", "description": "Bearer token authentication.\nFormat: `Authorization: Bearer sk-xxxxxx`\n" } } } } ``` --- # Download video Source: https://pinerouter.ai/docs/reference/videos-content OpenAPI for `GET /v1/videos/{task_id}/content`: ```json { "GET": "/v1/videos/{task_id}/content", "tags": [ "Videos" ], "summary": "Get video content", "description": "Returns the video file of a finished video job.\n\nStreams the file.\n", "operationId": "getvideocontent", "parameters": [ { "name": "task_id", "in": "path", "required": true, "description": "Video job id", "schema": { "type": "string" } } ], "security": [ { "BearerAuth": [] } ], "responses": { "200": { "description": "Video content", "content": { "video/mp4": { "schema": { "type": "string", "format": "binary" } } } }, "401": { "description": "Missing, wrong, disabled or expired key", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "", "message": "Invalid token (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } } }, "403": { "description": "Balance too low, or the key may not use this model or IP", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "", "message": "This token has no access to model grok-imagine/text-to-video (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } } }, "404": { "description": "Video not found or not finished", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "Error message" }, "type": { "type": "string", "description": "Error type" }, "param": { "type": [ "string", "null" ], "description": "Related parameters" }, "code": { "type": [ "string", "null" ], "description": "Error code" } } } } } } } }, "429": { "description": "Rate limit: 120 requests a minute per account, 60 of them successful", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "", "message": "You have reached the request limit: maximum 60 requests in 1 minutes (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } }, "headers": { "Retry-After": { "description": "Seconds to wait before retrying.", "schema": { "type": "integer", "example": 60 } } } }, "500": { "description": "Error on our side or the model's. Safe to retry with backoff." } }, "components": { "securitySchemes": { "BearerAuth": { "type": "http", "scheme": "bearer", "description": "Bearer token authentication.\nFormat: `Authorization: Bearer sk-xxxxxx`\n" } } } } ``` --- # Create video Source: https://pinerouter.ai/docs/reference/videos-create OpenAPI for `POST /v1/videos`: ```json { "POST": "/v1/videos", "tags": [ "Videos" ], "summary": "Create video", "description": "OpenAI-compatible video generation.\n\nStarts a video job; poll it with the video id.\n", "operationId": "createvideo", "parameters": [], "requestBody": { "required": false, "content": { "multipart/form-data": { "schema": { "type": "object", "description": "Video generation request", "properties": { "model": { "type": "string", "description": "Model or style id", "examples": [ "grok-imagine/text-to-video" ] }, "prompt": { "type": "string", "description": "Text prompt", "examples": [ "The astronaut stands up and walks away" ] }, "image": { "type": "string", "description": "Image input (URL or base64)", "examples": [ "https://example.com/image.jpg" ] }, "duration": { "type": "number", "description": "Video length (seconds)", "examples": [ 5 ] }, "width": { "type": "integer", "description": "Video width", "examples": [ 1280 ] }, "height": { "type": "integer", "description": "Video height", "examples": [ 720 ] }, "fps": { "type": "integer", "description": "Video frame rate", "examples": [ 30 ] }, "seed": { "type": "integer", "description": "Random seed", "examples": [ 20231234 ] }, "n": { "type": "integer", "description": "Number of videos", "examples": [ 1 ] }, "response_format": { "type": "string", "description": "Response format", "examples": [ "url" ] }, "user": { "type": "string", "description": "User id", "examples": [ "user-1234" ] }, "metadata": { "type": "object", "description": "Extra parameters (for example negative_prompt, style, quality_level)", "additionalProperties": true, "properties": {} } } } } } }, "security": [ { "BearerAuth": [] } ], "responses": { "200": { "description": "Video job created", "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "string", "description": "Video id" }, "object": { "type": "string", "description": "Object type" }, "model": { "type": "string", "description": "Model used" }, "status": { "type": "string", "description": "Job status" }, "progress": { "type": "integer", "description": "Progress (percent)" }, "created_at": { "type": "integer", "description": "Created at (Unix seconds)" }, "seconds": { "type": "string", "description": "Video length" }, "completed_at": { "type": "integer", "description": "Completed at (Unix seconds)" }, "expires_at": { "type": "integer", "description": "Expires at (Unix seconds)" }, "size": { "type": "string", "description": "Video size" }, "error": { "type": "object", "description": "Video error", "properties": { "message": { "type": "string", "description": "Error message" }, "code": { "type": "string", "description": "Error code" } } }, "metadata": { "type": "object", "description": "Extra metadata", "additionalProperties": true, "properties": {} } }, "required": [ "id", "object", "model", "status", "progress", "created_at", "seconds" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "Error message" }, "type": { "type": "string", "description": "Error type" }, "param": { "type": [ "string", "null" ], "description": "Related parameters" }, "code": { "type": [ "string", "null" ], "description": "Error code" } } } } } } } }, "401": { "description": "Missing, wrong, disabled or expired key", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "", "message": "Invalid token (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } } }, "403": { "description": "Balance too low, or the key may not use this model or IP", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "", "message": "This token has no access to model grok-imagine/text-to-video (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } } }, "429": { "description": "Rate limit: 120 requests a minute per account, 60 of them successful", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "", "message": "You have reached the request limit: maximum 60 requests in 1 minutes (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } }, "headers": { "Retry-After": { "description": "Seconds to wait before retrying.", "schema": { "type": "integer", "example": 60 } } } }, "500": { "description": "Error on our side or the model's. Safe to retry with backoff." } }, "components": { "securitySchemes": { "BearerAuth": { "type": "http", "scheme": "bearer", "description": "Bearer token authentication.\nFormat: `Authorization: Bearer sk-xxxxxx`\n" } } } } ``` --- # Get video Source: https://pinerouter.ai/docs/reference/videos-get OpenAPI for `GET /v1/videos/{task_id}`: ```json { "GET": "/v1/videos/{task_id}", "tags": [ "Videos" ], "summary": "Get video job status", "description": "OpenAI-compatible video job status.\n\nReturns the job's status in detail.\n", "operationId": "getvideo", "parameters": [ { "name": "task_id", "in": "path", "required": true, "description": "Video job id", "schema": { "type": "string" } } ], "security": [ { "BearerAuth": [] } ], "responses": { "200": { "description": "Video job status", "content": { "application/json": { "schema": { "type": "object", "properties": { "id": { "type": "string" }, "object": { "type": "string" }, "model": { "type": "string" }, "status": { "type": "string" }, "progress": { "type": "integer" }, "created_at": { "type": "integer" }, "seconds": { "type": "string" } }, "required": [ "id", "object", "model", "status", "progress", "created_at", "seconds" ] }, "example": { "id": "task_abc123", "object": "video", "model": "grok-imagine/text-to-video", "status": "in_progress", "progress": 40, "created_at": 1793600000, "seconds": "6" } } } }, "401": { "description": "Missing, wrong, disabled or expired key", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "", "message": "Invalid token (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } } }, "403": { "description": "Balance too low, or the key may not use this model or IP", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "", "message": "This token has no access to model grok-imagine/text-to-video (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } } }, "404": { "description": "Job not found", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "Error message" }, "type": { "type": "string", "description": "Error type" }, "param": { "type": [ "string", "null" ], "description": "Related parameters" }, "code": { "type": [ "string", "null" ], "description": "Error code" } } } } } } } }, "429": { "description": "Rate limit: 120 requests a minute per account, 60 of them successful", "content": { "application/json": { "schema": { "type": "object", "properties": { "error": { "type": "object", "properties": { "message": { "type": "string", "description": "What went wrong. Ends with the request id." }, "type": { "type": "string", "description": "Error type, for example `api_error` or `invalid_request_error`." }, "param": { "type": "string" }, "code": { "type": "string", "description": "Error code, often empty." } }, "required": [ "message", "type" ] } }, "required": [ "error" ] }, "example": { "error": { "code": "", "message": "You have reached the request limit: maximum 60 requests in 1 minutes (request id: 20261102053704107200635868d9d6m7EOL5JK)", "type": "api_error" } } } }, "headers": { "Retry-After": { "description": "Seconds to wait before retrying.", "schema": { "type": "integer", "example": 60 } } } }, "500": { "description": "Error on our side or the model's. Safe to retry with backoff." } }, "components": { "securitySchemes": { "BearerAuth": { "type": "http", "scheme": "bearer", "description": "Bearer token authentication.\nFormat: `Authorization: Bearer sk-xxxxxx`\n" } } } } ```