API reference
Plan2API API reference
Plan2API is an OpenAI-compatible API at https://api.plan2api.com/v1 with 4 endpoints: chat completions, models, image generations and image edits. Send your p2a_ key as Authorization: Bearer and pick the subscription with the model prefix: claude/, openai/, gemini/ or kimi/.
- Base URL
- https://
api.plan2api.com/ v1 - Auth
- Authorization: Bearer p2a_
- Endpoints
- 4, all under /
v1 - Free plan
- 50 chat requests a day
Updated
Base URL, keys and a first request
The base URL is https://api.plan2api.com/v1, the same format as OpenAI's, so any OpenAI client works by changing the base URL and key. Send the key in the Authorization: Bearer header; sending x-api-key is refused with HTTP 401 invalid_api_key.
Keys look like p2a_ followed by a 16-character key id, an underscore and a 43-character secret. You get your first key when you sign up and connect a subscription, and a new key is shown only once. You can create, rename and revoke keys on the Keys page at plan2api.com/dashboard/keys (up to 2 keys on Free, 10 on Pro, and 50 on Scale). The request below reads your key from the PLAN2API_KEY environment variable and calls Claude through the Chat Completions endpoint.
curl https://api.plan2api.com/v1/chat/completions \
-H "Authorization: Bearer $PLAN2API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude/claude-sonnet-5",
"messages": [
{"role": "user", "content": "Write a haiku about base URLs."}
]
}'Endpoints
The table below lists the four endpoints served under /v1.
Anything else under /v1, including /v1/responses, /v1/messages, /v1/completions and /v1/embeddings, returns HTTP 404 not_found, so tools must use Chat Completions. All four served endpoints return Access-Control-Allow-Origin: * on both normal responses and errors like 401 and 429, answer OPTIONS preflight requests with 204 (allowing methods GET, POST, OPTIONS and headers Authorization, Content-Type, cached for 86400 seconds), and expose Retry-After, x-plan2api-requests-remaining and x-plan2api-requests-reset to browser code.
Browser apps can call the API directly, but a key in browser code is visible to anyone who loads the page, so only ship it in code you don't publish.
| Endpoint | What it does | Plans |
|---|---|---|
| POST / | Chat Completions, streaming or not | Free, Pro, Scale |
| GET / | Lists every model id Plan2API serves | Free, Pro, Scale |
| POST / | Text to image with gpt-image-2 | Pro, Scale |
| POST / | Edits up to 5 source images | Pro, Scale |
Model ids and prefixes
The table below shows how model prefixes route requests to your connected subscriptions.
For Claude, the 9 model ids are claude/claude-opus-5-5, claude/claude-fable-5-1, claude/claude-opus-5, claude/claude-sonnet-5, claude/claude-fable-5, claude/claude-opus-4-8 and claude/claude-opus-4-7 (each with a 1,000,000-token context window and up to 128,000 output tokens), plus claude/claude-sonnet-4-6 (200,000 context, 128,000 output) and claude/claude-haiku-4-5 (200,000 context, 64,000 output). The 5 Gemini ids are gemini/gemini-4-argon-high, gemini/gemini-3.8-flash-high, gemini/gemini-3-flash, gemini/gemini-3.5-flash-lite and gemini/gemini-3.1-flash-lite, each with a 1,048,576-token context and text, image, audio and video input. Codex model ids are read live from ChatGPT as openai/<model>, such as openai/gpt-6.1-sol, openai/gpt-6-astra and openai/gpt-6-luna. Kimi serves kimi/k3 (or moonshotai/kimi-k3), which starts on the 256K window and moves to the 1M window when a conversation passes about 192K tokens.
A model id without a prefix goes straight to Codex, so always prefix Claude, Gemini and Kimi ids. Sending an unknown prefix or id returns HTTP 400 model_not_found. Calling GET /v1/models lists every model Plan2API serves, including providers you haven't connected yet, along with context_length, modalities, supported_parameters and accepted reasoning efforts; calling a model whose provider isn't connected returns HTTP 403 no_credentials.
| Prefix | Subscription | Example id | Context window |
|---|---|---|---|
| claude/ | Claude Pro or Max | claude/ | 1,000,000 |
| openai/ | ChatGPT plan (Codex) | openai/ | Listed by GET / |
| gemini/ | Google AI Pro or Ultra | gemini/ | 1,048,576 |
| kimi/ | Kimi Code membership | kimi/ | 1,048,576 |
Chat completion parameters by provider
The table below covers how each provider handles standard Chat Completions parameters.
Both reasoning_effort and OpenRouter-style reasoning.effort are accepted, and reasoning.effort wins when both are sent, except on Kimi where reasoning_effort wins. Between token limit fields, max_completion_tokens wins over max_tokens, and Claude models default to 128,000 output tokens (or 64,000 on claude-haiku-4-5). System and developer messages are joined into Claude's system prompt, Codex's instructions and Gemini's system instruction, while Kimi receives the messages as sent. For image inputs, Plan2API fetches image URLs in messages for Claude, Gemini and Kimi models (up to 32 per request, up to 20 MB and 15 seconds each, public addresses only), while Codex passes image URLs to ChatGPT as sent; request bodies can be up to 64 MB, and anything larger returns HTTP 413.
| Parameter | Claude | Codex | Gemini | Kimi |
|---|---|---|---|---|
| temperature, top_ | Ignored | Ignored | Passed through | Fixed at 1 and 0.95 |
| reasoning_ | none to max | Passed on | none to max | low, high or max |
| max_ | Default 128,000 | Used | Not on gemini-* ids | Used |
| response_ | 400 unsupported_ | Supported | gemini-* ids only | Passed through |
| tools, tool_ | Function tools | Function tools | Supported | Supported |
| parallel_ | Ignored | Honoured | Ignored | Ignored |
| stop | Ignored | Ignored | Used | Ignored |
| n, seed, user | Ignored | Ignored | Ignored | Ignored |
Streaming and the OpenAI SDKs
Setting stream: true returns server-sent events ending with data: [DONE]. The usage object (prompt_tokens, completion_tokens, total_tokens, cached and reasoning token details, and a cost that is always 0) is always attached to the final chunk, so you don't need stream_options.include_usage, and model thinking streams as delta.reasoning. If an error happens after streaming starts, it arrives as a final chunk with finish_reason set to error and a top-level error object, followed by data: [DONE], and both OpenAI SDKs raise APIError for it. A non-streaming request still running after 90 seconds gets its HTTP 200 status sent early with whitespace padding, and a failure after that point comes back as an error object inside a 200 body, so check the body too.
Both openai-python 3.41.0 (using base_url) and openai-node 7.45.1 (using baseURL) work out of the box, read OPENAI_BASE_URL, and default to a 10-minute timeout with 2 automatic retries on connection errors, 408, 409, 429 and 5xx responses. Make sure to call client.chat.completions.create, because the Responses API that the SDK READMEs now open with isn't served. The Python example below streams a Claude reply and prints the token count from the final chunk.
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.plan2api.com/v1",
api_key=os.environ["PLAN2API_KEY"],
)
stream = client.chat.completions.create(
model="claude/claude-sonnet-5",
messages=[{"role": "user", "content": "Explain HPKE in two sentences."}],
reasoning_effort="low",
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(f"\n{chunk.usage.total_tokens} tokens")Image generation and edits
POST /v1/images/generations and POST /v1/images/edits run gpt-image-2 on your connected ChatGPT (Codex) subscription on Pro and Scale plans, while Free returns HTTP 403 with type plan_limit and code images before reading the request body. You can pass a prompt up to 32,000 characters, n from 1 to 4, size as auto or WIDTHxHEIGHT where only the aspect ratio counts (from 1:3 to 3:1, about 1.57 megapixels), and background as opaque, transparent or auto. Images come back as b64_json PNG only, and passing stream, mask, or another response_format or output_format returns HTTP 400; edits accept up to 5 PNG, JPEG or WebP files as multipart image or image[] fields, or data URLs in a JSON images array, never remote URLs.
A render takes about 15 to 45 seconds and up to about 110 seconds at high quality; at 90 seconds the HTTP 200 status is sent early and the connection is kept alive, so keep custom client timeouts above 2 minutes (the SDKs' 10-minute default is fine). Image calls appear in your logs and usage, valued at gpt-image-2 list prices, and you can read more in the gpt-image-2 API guide. The Node example below generates and saves a 3:2 image using the OpenAI Node SDK.
import fs from "node:fs";
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.plan2api.com/v1",
apiKey: process.env.PLAN2API_KEY,
timeout: 300_000,
});
const result = await client.images.generate({
model: "gpt-image-2",
prompt: "A lighthouse at dusk, flat vector style",
size: "1536x1024",
n: 1,
});
fs.writeFileSync("lighthouse.png", Buffer.from(result.data[0].b64_json, "base64"));Errors and rate limits
Every error response uses the shape {"error": {"message", "type", "code"}}, where type equals code except on plan limits, which set type to plan_limit.
Beyond the codes in the table, HTTP 400 context_length_exceeded means the prompt is longer than the model's window, HTTP 502 upstream_transient, upstream_unauthorized, upstream_forbidden or upstream_dead means the provider failed or a login needs reconnecting, HTTP 503 no_subscription_available or provider_unavailable means no usable subscription is available right now, and HTTP 500 internal_error marks an unexpected server failure. You can read more about how keys and connected logins are protected.
Rate limits are shared across all keys on an account: Free allows 1 request per second with bursts of 20, 4 at once and 50 chat requests a day; Pro allows 10 per second with bursts of 600 and 16 at once; Scale allows 10 per second with bursts of 600 and 32 at once (see plan limits and pricing). The daily cap on Free counts only POST /v1/chat/completions, never GET /v1/models or image calls, and gives back requests that never reached a provider. On Free, chat responses include x-plan2api-requests-remaining and x-plan2api-requests-reset (the ISO 8601 time of the next reset at 00:00 UTC), and Retry-After values are in seconds.
| Status | Code | Meaning |
|---|---|---|
| 400 | model_ | Unknown model id or prefix |
| 400 | unsupported_ | JSON response_ |
| 401 | invalid_ | Missing or wrong key, or sent as x-api-key |
| 403 | no_ | No connected subscription for that provider |
| 403 | images (plan_ | Image endpoints on the Free plan |
| 413 | invalid_ | Request body over 64 MB |
| 429 | daily_ | 51st Free chat request of the day |
| 429 | rate, inflight, rate_ | Over the per-second or at-once limit |
| 429 | subscription_ | Every connected account is out of quota |
| 429 | subscription_ | The provider is rate limiting |
Use the AI subscriptions you already pay for as one OpenAI-compatible API key.
- Free: 1 subscription, 50 requests a day, no card
- Pro: $12 a month
FAQ
What is the Plan2API base URL?
The base URL is https://api.plan2api.com/v1, and you authenticate by sending your p2a_ key in an Authorization: Bearer header. Any OpenAI-compatible client or SDK works when configured with those two values.
Does Plan2API support the Responses API or Anthropic's Messages API?
No. Plan2API serves Chat Completions, models and images, while requests to /v1/responses and /v1/messages return HTTP 404 not_found.
Can I call Plan2API from browser JavaScript?
Yes. The API allows every origin (a wildcard Access-Control-Allow-Origin header) and answers OPTIONS preflights with HTTP 204, though anyone who loads your page can read an API key shipped in browser code.
How do I see how many Free requests are left today?
Read the x-plan2api-requests-remaining header on chat completion responses. The x-plan2api-requests-reset header gives the ISO 8601 timestamp for the next reset at 00:00 UTC.
Which models support JSON mode?
Codex, Kimi and Gemini models support JSON mode (json_object and json_schema). Claude models return HTTP 400 unsupported_parameter if you pass a non-text response_format.
Does Plan2API charge per token?
No. Plan2API plans are flat-rate, the response usage object always reports a cost of 0, and your own connected subscription's usage limits apply.
Sources
- OpenAI Python SDK (GitHub)github.com
- OpenAI Node SDK (GitHub)github.com
- MDN: Using server-sent eventsdeveloper.mozilla.org
- MDN: Cross-Origin Resource Sharing (CORS)developer.mozilla.org
- MDN: Retry-After headerdeveloper.mozilla.org