Using the Model API? Read the docs
Coarena API
The site’s public routes, for your scripts. Each is in /api/openapi.json.
- Operations
- 144
- Scopes
- 19
- Version
- 2026-11-02
Status
Personal keys are not enabled yet. Public endpoints need no key.
Model API
The Model API is not enabled yet. Every request and response is stored, whatever your client’s store setting says.
- OpenAI SDKs
- Base URL
https://coarena.ai/api/v1, orOPENAI_BASE_URLandOPENAI_API_KEYin the environment. Codex, the Vercel AI SDK and LangChain use it too. - Anthropic SDK
- Base URL
https://coarena.ai/api: the SDK adds/v1/messagesitself. Claude Code readsANTHROPIC_BASE_URLandANTHROPIC_AUTH_TOKEN. - Key
- A personal key with
inference:writeto call a model and rate a reply, andinference:readto read your history, sent asAuthorization: Bearerorx-api-key. Made on your account page. - Terms
- Accepted once per account, on your account page or when you make the key. Until then a call answers 403
model_api_terms_required. - Long answers
- Send
stream: true. A reply that is not streamed and runs past 20 seconds gets its 200 then, padded with a newline every 10 seconds until it is done; an error after that point arrives in the body. - Stored
- The request, the reply, the reasoning, the tool calls and the usage, with your account, and may be used in datasets Coarena licenses. Read them back on
/account/api/requestsor withGET /api/v1/requests.
curl
curl https://coarena.ai/api/v1/chat/completions \-H "Authorization: Bearer $COARENA_API_KEY" \-H "Content-Type: application/json" \-d '{"model": "…", "messages": [{"role": "user", "content": "Hello"}], "stream": true}'Any OpenAI-style client: the key in Authorization, the base URL ending in /api/v1.
Python (openai)
import osfrom openai import OpenAI client = OpenAI(base_url="https://coarena.ai/api/v1", api_key=os.environ["COARENA_API_KEY"])reply = client.chat.completions.create(model="…", messages=[{"role": "user", "content": "Hello"}])print(reply.choices[0].message.content)client.responses.create works the same way, on /api/v1/responses.
JavaScript (openai)
import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://coarena.ai/api/v1", apiKey: process.env.COARENA_API_KEY });const stream = await client.chat.completions.create({ model: "…", messages: [{ role: "user", content: "Hello" }], stream: true });for await (const chunk of stream) process.stdout.write(chunk.choices[0]?.delta?.content ?? "");Streamed, as a long answer should be.
Anthropic SDK
import osfrom anthropic import Anthropic client = Anthropic(base_url="https://coarena.ai/api", api_key=os.environ["COARENA_API_KEY"])message = client.messages.create(model="…", max_tokens=1024, messages=[{"role": "user", "content": "Hello"}])# A model that reasons answers with a thinking block first: print the textprint(next((b.text for b in message.content if b.type == "text"), ""))Pass the key as api_key so the SDK never reads ANTHROPIC_API_KEY. Any model on the list answers here, not only Claude.
Claude Code
# Claude Code would send this key to Coarena toounset ANTHROPIC_API_KEYexport ANTHROPIC_BASE_URL=https://coarena.ai/apiexport ANTHROPIC_AUTH_TOKEN=$COARENA_API_KEY# Compact well before the 128,000-token input capexport CLAUDE_CODE_AUTO_COMPACT_WINDOW=102000# For a model that is not Claudeexport CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1claude --model …A request’s input is capped at 128,000 tokens, counted roughly, which is less than Claude Code fills before it compacts on its own: past the cap every call is a 400 until the session is shorter. The window above compacts long sessions earlier, with room for the rough count; /compact works any time before it. An Anthropic key left in ANTHROPIC_API_KEY is ignored here, but it still leaves your machine; a second Coarena key there is a 401 conflicting_credentials.
Codex
# ~/.codex/config.tomlmodel_provider = "coarena"model = "…"# Codex adds a web search tool the Model API does not runweb_search = "disabled" [model_providers.coarena]name = "Coarena"base_url = "https://coarena.ai/api/v1"env_key = "COARENA_API_KEY"wire_api = "responses"Codex speaks only the Responses API, which is served at /api/v1/responses. Its freeform tools, apply_patch among them, work: the model reads their grammar but is not held to it.
Vercel AI SDK
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";import { generateText } from "ai"; const coarena = createOpenAICompatible({ name: "coarena", baseURL: "https://coarena.ai/api/v1", apiKey: process.env.COARENA_API_KEY, includeUsage: true });const { text } = await generateText({ model: coarena("…"), prompt: "Hello" });Without includeUsage: true the SDK reports no token counts on a stream.
LangChain
import osfrom langchain_openai import ChatOpenAI llm = ChatOpenAI(model="…", base_url="https://coarena.ai/api/v1", api_key=os.environ["COARENA_API_KEY"], stream_usage=True)print(llm.invoke("Hello").content)With a base URL of its own, LangChain asks for token counts on a stream only with stream_usage=True.
One block per client. $COARENA_API_KEY holds your key.
Rate a reply
# Thumbs up on a reply, by any id it came withcurl https://coarena.ai/api/v1/feedback -H "Authorization: Bearer $COARENA_API_KEY" -H "Content-Type: application/json" -d '{"request_id":"<reply id>","rating":"up"}' # Python (openai), after reply = client.chat.completions.create(…)client.post("/feedback", body={"request_id": reply.id, "rating": "up"}, cast_to=object)# JavaScript (openai)await client.post("/feedback", { body: { request_id: reply.id, rating: "up" } });# Anthropic SDK, after message = client.messages.create(…)client.post("/v1/feedback", body={"request_id": message.id, "rating": "up"}, cast_to=object)"rating": "down" for a bad one, and null takes the rating away. The same body twice changes nothing.
- Ids
- The body id the SDK hands back (
reply.id,message.id:chatcmpl-,resp_,msg_orcmpl-), or thex-coarena-request-idheader every answer carries. Any of the account’s keys may rate any of its replies. - Rating
upordown; alsogoodandbad,1and-1,trueandfalse. One per reply: a second replaces the first.- Note
- Optional, at most 2,000 characters. Left out, a second rating keeps the note already saved;
nullclears it. - Tags
- Optional:
correct,helpful,fast,wrong,unhelpful,refused,too_slow,bad_format,unsafe,concise,thorough,creative,good_code,followed_instructions,made_up,incomplete,too_long,off_topic,bad_code,ignored_instructions. Kept and cleared the same way. - How often
- Up to 120 ratings a minute per account, shared with the thumbs on your requests page.
Models0 models
Models are listed here once the Model API is enabled.
Limits
| Limit | Value |
|---|---|
| Model usage a day, per account | $5.00 |
| Requests a day, per account | No limit |
| Requests a minute, per key | 30 |
| Requests a minute, per account | 30 |
| Requests a minute, per account on one model | 30 |
| Calls at once, per account | 2 |
| Output tokens when none are asked for | 8,192 |
| Output tokens, at most | 16,384 |
| Input tokens, at most | 128,000 |
| Images a request | 8 |
| Ask for more | POST /api/v1/limits/requests, after your first call. Or on your usage page. |
| What you can ask for | usd_per_day, whole dollars above today’s, up to $100,000; requests_per_minute, a whole number above today’s, up to 600; requests_per_day, only once Coarena has set a daily request limit on your account, up to 100,000. Each is optional, and one left out or null stays as it is. With none, the ask is the next dollar step. |
| When you hit a limit | A daily limit’s 429, and a model call’s per-minute 429, say how to ask, in the message and in error.limit_increase. |
A day is a UTC day. Each minute limit counts the last 60 seconds exactly. Past a limit the answer is 429 with retry-after; a daily limit adds x-should-retry: false. Need more? After your first call, ask with POST /api/v1/limits/requests or on your usage page.
# Ask for $10 a day (no amount: the next step up)curl https://coarena.ai/api/v1/limits/requests -H "Authorization: Bearer $COARENA_API_KEY" -H "Content-Type: application/json" -d '{"usd_per_day":10}'# Or 60 requests a minutecurl https://coarena.ai/api/v1/limits/requests -H "Authorization: Bearer $COARENA_API_KEY" -H "Content-Type: application/json" -d '{"requests_per_minute":60}'One open ask at a time; DELETE /api/v1/limits/requests/current withdraws it. GET /api/v1/limits says where it stands.
Error codes40 codes
| Code | Status | Type |
|---|---|---|
invalid_ | 401 | authentication_error on OpenAI routes, authentication_error on Anthropic’s. |
api_ | 401 | authentication_error on OpenAI routes, authentication_error on Anthropic’s. |
api_ | 401 | authentication_error on OpenAI routes, authentication_error on Anthropic’s. |
api_ | 401 | authentication_error on OpenAI routes, authentication_error on Anthropic’s. |
conflicting_ | 401 | authentication_error on OpenAI routes, authentication_error on Anthropic’s. |
insufficient_ | 403 | permission_error on OpenAI routes, permission_error on Anthropic’s. |
model_ | 403 | permission_error on OpenAI routes, permission_error on Anthropic’s. |
account_ | 403 | permission_error on OpenAI routes, permission_error on Anthropic’s. |
account_ | 403 | permission_error on OpenAI routes, permission_error on Anthropic’s. |
limit_ | 403 | permission_error on OpenAI routes, permission_error on Anthropic’s. |
limit_ | 409 | invalid_request_error on OpenAI routes, invalid_request_error on Anthropic’s. |
limit_ | 400 | invalid_request_error on OpenAI routes, invalid_request_error on Anthropic’s. |
model_ | 400 | invalid_request_error on OpenAI routes, invalid_request_error on Anthropic’s. |
model_ | 400 | invalid_request_error on OpenAI routes, invalid_request_error on Anthropic’s. |
model_ | 404 | invalid_request_error on OpenAI routes, not_found_error on Anthropic’s. |
previous_ | 404 | invalid_request_error on OpenAI routes, not_found_error on Anthropic’s. |
request_ | 404 | invalid_request_error on OpenAI routes, not_found_error on Anthropic’s. |
not_ | 404 | invalid_request_error on OpenAI routes, not_found_error on Anthropic’s. |
request_ | 413 | invalid_request_error on OpenAI routes, request_too_large on Anthropic’s. |
request_ | 408 | invalid_request_error on OpenAI routes, invalid_request_error on Anthropic’s. |
invalid_ | 400 | invalid_request_error on OpenAI routes, invalid_request_error on Anthropic’s. |
invalid_ | 400 | invalid_request_error on OpenAI routes, invalid_request_error on Anthropic’s. |
unsupported_ | 400 | invalid_request_error on OpenAI routes, invalid_request_error on Anthropic’s. |
unsupported_ | 400 | invalid_request_error on OpenAI routes, invalid_request_error on Anthropic’s. |
context_ | 400 | invalid_request_error on OpenAI routes, invalid_request_error on Anthropic’s. |
assistant_ | 400 | invalid_request_error on OpenAI routes, invalid_request_error on Anthropic’s. |
unsupported_ | 400 | invalid_request_error on OpenAI routes, invalid_request_error on Anthropic’s. |
content_ | 400 | invalid_request_error on OpenAI routes, invalid_request_error on Anthropic’s. |
rate_ | 429 | rate_limit_error on OpenAI routes, rate_limit_error on Anthropic’s. |
concurrency_ | 429 | rate_limit_error on OpenAI routes, rate_limit_error on Anthropic’s. |
insufficient_ | 429 | insufficient_quota on OpenAI routes, rate_limit_error on Anthropic’s. |
not_ | 501 | server_error on OpenAI routes, api_error on Anthropic’s. |
upstream_ | 502 | server_error on OpenAI routes, api_error on Anthropic’s. |
model_ | 503 | server_error on OpenAI routes, overloaded_error on Anthropic’s. |
model_ | 503 | server_error on OpenAI routes, overloaded_error on Anthropic’s. |
upstream_ | 503 | server_error on OpenAI routes, overloaded_error on Anthropic’s. |
server_ | 503 | server_error on OpenAI routes, overloaded_error on Anthropic’s. |
quota_ | 503 | server_error on OpenAI routes, overloaded_error on Anthropic’s. |
server_ | 503 | server_error on OpenAI routes, overloaded_error on Anthropic’s. |
upstream_ | 504 | server_error on OpenAI routes, api_error on Anthropic’s. |
OpenAI routes answer { "error": { "message", "type", "param", "code" } }; /api/v1/messages answers Anthropic’s shape with the same code inside error.
History
- History
GET /api/v1/requestswithinference:read, newest first, 50 a page and up to 100 withlimit; follownext_cursor, and filter by key, model, status, rating, time (from,to) and words (q: a prompt, a reply, a model or a request id).GET /api/v1/usagehas the same totals as your usage page.- One request
GET /api/v1/requests/{id}with any id the response carried.- Rating by path
PUT /api/v1/requests/{id}/feedbacktakes the same body asPOST /api/v1/feedbackwithout the id;DELETEon that path clears it.
Terms
Coarena stores every prompt you send and every reply you receive through the Model API, whatever your client's own store setting says, and may use them, including in datasets it licenses to others.
You cannot withdraw what you send through the Model API. Your prompts and their replies stay in Coarena's data and datasets even if you later withdraw from Coarena or stop using it, except where the law requires us to delete them.
Do not send sensitive personal data: health, financial or government identifiers, passwords or keys, or anything about another person they have not agreed to share.
You must be 18 or older to use the Model API.
Each model's own provider usage policy applies to what you send it, as well as Coarena's terms.
Limits can change at any time, access can be revoked, and there is no service level: requests may fail, slow down or stop.
The Model API is free and provided as is, with no warranty of any kind.
Version 2026-11-04. A new version is accepted again before the next call.
Everything on the hub, by API
Each thing you can do on /account/api, with a key. Send the key as Authorization: Bearer.
curl https://coarena.ai/api/v1/limits -H "Authorization: Bearer $COARENA_API_KEY"Today’s free use, your limits and any open ask, in one call.
| On the hub | Call | Scope | Example |
|---|---|---|---|
| Today's free use and your limits | GET / | inference:read or inference:write | GET /limits |
| Ask for higher limits | POST / | inference:write | {"requests_per_minute": 60} |
| Withdraw that ask | DELETE / | inference:write | |
| Usage over 7 or 30 days, by model, key and app | GET / | inference:read | ?range=30d |
| Your requests | GET / | inference:read | ?feedback=unrated&q=cache |
| One request, whole | GET / | inference:read | any id form |
| Rate a reply | POST / | inference:write | {"request_id": "…", "rating": "up"} |
| Remove a rating | POST / | inference:write | {"request_id": "…", "rating": null} |
| Download requests | GET / | inference:read | ?format=csv |
| Your keys | GET / | me:read or inference:read | |
| Revoke the key you are using | DELETE / | any scope | |
| Rename the key you are using | PATCH / | any scope | {"name": "CI"} |
| Make a key; rename or revoke another key; accept the Model API terms | On the hub only | A person's act |
Paths are under https://coarena.ai. Every answer carries x-coarena-request-id.
Authentication
# On every request, in place of a browser's cookieAuthorization: Bearer coa_…A key acts as your account, with only the scopes it was given. A route that needs your account refuses a malformed, unknown, revoked or expired key with a 401.
- Made
- On your account page, from a signed-in browser, once keys are enabled.
- Shown
- Once, when it is made. Only its digest is stored, so a lost key is replaced, never recovered.
- Lifetime
- 30, 90 or 365 days, chosen when it is made; 90 is preselected. Every key expires.
- Live keys
- At most 10 per account.
- Revoked
- Refused everywhere within 10 seconds, and at once on the server that revoked it.
- Never with a key
- Managing keys, accepting the terms, withdrawing the account and writing the vault. Each answers 403
session_required. - Votes and labels
votes:write,labels:writework only for accounts allowed to label through the API. Any other account gets 403api_labels_not_enabled.
Scopes
| Scope | Grants | Caution |
|---|---|---|
me:read | Read your profile, activity, history, judging calibration and withdrawal status. | |
me:write | Mute optional email kinds. | |
referrals:read | Read your referral code and who joined through it. | Shows other people's display names and masked email addresses (the first two characters and the full domain). |
battles:read | Read your battles, their live progress, frames, replies and checks. | |
battles:write | Start battles and follow-ups, stop them, continue them, and set their checks. | Starts real agent runs. |
battles:publish | Put your battles in the public pool and create or remove share links. | Publishing is public and cannot be taken back once seen. |
battles:pick | Choose the models a battle runs (only for accounts on the model-picker list). | |
votes:write | Vote on battles, leave run feedback, and take battles from the judging queue. | Only for accounts allowed to label through the API. |
labels:write | Add flaw annotations, step verdicts and check ticks. | Only for accounts allowed to label through the API. |
uploads:read | Read your uploaded attachments. | |
uploads:write | Upload, finalize and delete attachments. | |
experiments:read | Read your experiments, their status, metrics, results and exports. | |
experiments:write | Create and edit experiments and their items. | |
experiments:run | Start, retry and stop experiments. | Starts many real agent runs. |
envs:read | Read environment templates and episodes (environment owners only). | |
envs:write | Create episodes, act in them and run solvers (environment owners only). | |
vault:read | Read which sites you have saved sign-ins for (never the secrets). | Shows which services you hold accounts with. |
inference:read | Read your own Model API requests and replies. | Shows everything you sent and every reply. |
inference:write | Send prompts to the models on Coarena through the Model API, and rate the replies. | Every prompt and reply is stored by Coarena and may be used in datasets it licenses. |
A signed-in browser holds every scope.
Endpoints
Platform10 operations
| Operation | Scope | Summary |
|---|---|---|
GET / | none | How to cite the arena: the BibTeX entry and the plain-text reference the citation page prints. |
GET / | none | Liveness: is this process up. |
HEAD / | none | Liveness, headers only. |
GET / | none | The /mission theses registry: each belief with why it is held, the mechanism that enforces it and the file to check, plus the beliefs whose mechanism has gone missing. |
GET / | none | This OpenAPI 3.1 document: every public operation. |
GET / | none | Which build is answering; poll it and compare answers to notice a new deploy. |
GET / | none | Introspect (early access), the verification API agents call on themselves, described for language models. |
GET / | none | Introspect (early access) quickstart: every request and response body in call order, as Markdown. |
GET / | none | The For agents page as Markdown: how an AI agent calls the Model API, its free daily usage, asking for more, rating replies and reading its own requests. |
GET / | none | Site map for language models: the benchmark pages, model profiles, data terms and Introspect. |
Keys4 operations
| Operation | Scope | Summary |
|---|---|---|
GET / | browser only | List this account's personal API keys (never a token): every live key, plus the 50 that were revoked or expired most recently; with the scope catalogue and the minting limits. |
POST / | browser only | Mint a personal API key; the token is in this response and nowhere else, ever. |
DELETE / | browser only | Revoke one of this account's keys. |
PATCH / | browser only | Rename one of this account's keys, whatever its status. |
Account28 operations
| Operation | Scope | Summary |
|---|---|---|
POST / | none | Ask for commercial dataset access. |
POST / | none | Send a message to the CoArena team (the site's feedback box). |
GET / | me:read | Who is calling and what they have earned: account, judge id, points, recent events, awards and feature flags. |
GET / | me:read | Your past year as per-day counts of battles posted and battles judged, for an activity grid. |
GET / | battles:pick | The models you may pick for a battle, for accounts on the model-picker allowlist. |
GET / | me:read | Files delivered by your newest 60 battles. |
POST / | browser only | Accept the current terms from a signed-in browser. |
GET / | me:read | Your optional email kinds, each with its label and whether it is muted or would be sent, plus the global opt-out. |
PUT / | me:write | Mute one or more optional email kinds. |
GET / | me:read | Your last 40 acts (posted, judged, flagged, shared, …), each with its battle's prompt and mode. |
GET / | referrals:read | Your referral code and link, how many signed up and became active through it, and those invites (names and masked emails). |
DELETE / | browser only | Remove a saved credential by id, or a whole saved sign-in by host and label, and erase the signed-in browser state saved from it. |
GET / | vault:read | Your saved credentials (metadata only, never a secret) and, for a browser session, your agent memory. |
POST / | browser only | Save a credential bound to one site: a sign-in (username and password) or a token. |
PUT / | browser only | Replace your agent memory, the text added to every run you start, and get back the text as stored after scrubbing. |
GET / | me:read | Whether this account has withdrawn its contributions, what a withdrawal would reach, and what it would do. |
POST / | browser only | Withdraw this account's contributions, irreversibly: tasks become private, labels leave rankings and future deliveries, and every API key is revoked. |
GET / | me:read | Whether you may start phone battles and whether a phone can run one now (counts and booleans only). |
GET / | browser only | Your Model API status: whether it is on, its terms and whether you accepted them, today's usage against the limits, the base addresses and the models you can call. |
POST / | browser only | Accept the current Model API terms. |
GET / | browser only | Your Model API requests for the account page, newest first, a page at a time. |
GET / | browser only | One of your Model API requests, whole, for the account page. |
PUT / | browser only | Rate one of your Model API requests from the account page. |
DELETE / | browser only | Remove your rating of one of your Model API requests from the account page. |
POST / | browser only | Ask for higher Model API limits once your account has made a call: the next dollar step, or any whole number of dollars a day, requests a minute, or (with a daily request limit) requests a day above your limit today, with an optional note. |
DELETE / | browser only | Withdraw your request for higher Model API limits while it waits. |
GET / | browser only | Your Model API usage for the API hub: today against your limits, asking for a higher limit, and totals, models, keys and apps over a range. |
GET / | browser only | Download your Model API requests as CSV or JSONL: the newest 10,000 matching, each with the first 160 characters of the prompt and the reply. |
Battles11 operations
| Operation | Scope | Summary |
|---|---|---|
GET / | battles:read | Your own battles, newest first; with ?scope=queue, the judge queue. |
POST / | battles:write | Posts a task: the arena runs it as a battle between two models or as a single-model session, or, when the message is not a task, answers in words without starting a battle (200 with intent 'reply'). |
GET / | battles:read | One battle's full detail, for its owner or, once it is pooled, any signed-in judge; poll it no faster than every 2-3 s to follow a live run. |
POST / | battles:write | The battle's creator answers a pending step-limit offer for one lane: 'continue' grants more steps, 'finish' ends that lane now. |
POST / | battles:publish | The owner offers a finished battle to the judge queue. |
DELETE / | battles:publish | The owner stops sharing a battle's public card, so /b/{id} answers not found again. |
GET / | battles:read | The owner's preview of the public result card exactly as sharing would publish it, whether it is live now, and its URL (/b/{id}, served only while shared). |
POST / | battles:publish | The owner publishes the battle's result-only card at /b/{id}; the battle must be finished and judged. |
POST / | battles:write | The battle's creator ends a running battle once one of its lanes has finished. |
GET / | battles:read | A conversation the arena answered in words, for its owner: the replies root-first, the active reply, the battle it later became (if any), and the composer settings to send the next message with. |
GET / | battles:read | One frame image from a battle, masked by default. |
Labels11 operations
| Operation | Scope | Summary |
|---|---|---|
POST / | labels:write | Saves 1-20 labels on one battle's runs (flaws, optionally at a step; stamps; ticks on the submitter's checks) and returns the battle's confirmed consensus as counts. |
DELETE / | labels:write | Removes the caller's own tick on one of the battle's checks for one side. |
GET / | battles:read | The submitter's checks (what 'done' looks like) for a battle the caller may read, with the caller's own ticks per side, judge counts per check (null while the caller is still blind), and whether the caller may edit the list. |
PUT / | battles:write | The submitter replaces the battle's check list, stored as at most 3 entries of at most 120 characters each, deduplicated, with model and vendor names removed. |
POST / | votes:write | The owner's verdict on their own single-model session: send {verdict} (optionally with a note) once, then {note} alone to revise the note. |
GET / | battles:read | The crowd's per-step good/bad consensus for a battle the caller may read, as counts with no judge ids. |
POST / | labels:write | Records a batch of per-step good/bad verdicts from one playthrough and returns the caller's strikes and elimination state per side. |
POST / | votes:write | Records the caller's preference between a battle's two runs and returns the battle detail as this judge may now see it; the vote that completes the battle can update the live rating. |
GET / | votes:write | Serves one blind, pooled battle for the caller to judge, or the reason the queue is empty, plus the caller's recently settled battles. |
GET / | me:read | The caller's step-mode calibration: scoreable calls, how many were correct, accuracy, recent calls, and percentile rank among ranked judges. |
GET / | me:read | How much the caller's votes weigh in the published ranking, and why. |
Uploads8 operations
| Operation | Scope | Summary |
|---|---|---|
GET / | me:read | Your stored upload bytes (the figure the storage quota counts) against your account's allowance. |
GET / | uploads:read | Read back one of your upload sets, or the files attached to one of your tasks: every file prepared into the set, stored or not, or every file the task's agents were handed. |
POST / | uploads:write | Prepare a two-phase upload: declare the files and get back each one's final filename and destination. |
DELETE / | uploads:write | Delete one of your files (bytes and record) before the task it is attached to is posted. |
GET / | uploads:read | Download one of your stored files, always as an attachment. |
POST / | uploads:write | Finalize a file whose bytes went straight to the object store: its size, type and your quota are checked and it is marked stored. |
PUT / | uploads:write | Upload one file's bytes through CoArena (a `stream` destination from prepare); they are checked, stored and confirmed in the same request. |
GET / | none | The upload rules as data: file, set and account limits, accepted types by extension, and refused extensions. |
Artifacts2 operations
| Operation | Scope | Summary |
|---|---|---|
GET / | battles:read | Download one artifact by id. |
GET / | battles:read | Download several delivered files as one zip, by artifact id. |
Experiments18 operations
| Operation | Scope | Summary |
|---|---|---|
GET / | experiments:read | List your experiments, newest first, with item and battle counts. |
POST / | experiments:write | Create a draft experiment from a name and a spec, optionally with its whole plan. |
DELETE / | experiments:write | Delete a draft experiment that never launched a battle. |
GET / | experiments:read | One experiment: its summary with battle counts, the current attempt of every plan row, and the agents that can run right now. |
PATCH / | experiments:write | Rename an experiment, replace its spec, or set or clear its write-up, all in one transaction. |
POST / | experiments:read | What starting this experiment's stored plan would create and take: the exact battle count, the matchups and how often each agent appears, an estimated wall clock with its worst case, and the frames it would store. |
GET / | experiments:read | Download the experiment as a JSON document or as CSV at one of three grains. |
PUT / | experiments:write | Replace the whole plan. |
PATCH / | experiments:write | Edit one plan row that has not launched a battle. |
GET / | experiments:read | The benchmark table for this experiment's battles only: catalog metrics with sample sizes, per-agent rows, and deltas against an optional baseline agent. |
POST / | experiments:write | Read a spreadsheet (csv, tsv, xlsx, json, jsonl) or a pasted table into candidate rows and the plan it would make. |
POST / | experiments:write | Turn /parse's candidate rows, a selection and cell edits into the exact plan PUT /items takes, and say which selected rows were left out and why. |
GET / | experiments:read | One row per battle the experiment made, every attempt included. |
POST / | experiments:run | Run a new attempt of the named finished rows (default: every failed row), up to 3 attempts per row. |
POST / | experiments:run | Start the experiment (or resume a paused one). |
GET / | experiments:read | Cheap poll: status and counts, with the interval to poll at and an ETag. |
POST / | experiments:run | Pause the experiment (resumable) or cancel it (final; unlaunched rows are cancelled). |
GET / | experiments:read | What you need to compose an experiment: every agent with whether it can run and why not, typical run lengths, the default battle mode, and your account's quota limits and usage. |
Envs6 operations
| Operation | Scope | Summary |
|---|---|---|
GET / | envs:read | List your env episodes, newest first (at most 50). |
POST / | envs:write | Start an episode of a template. |
GET / | envs:read | One of your episodes, fully folded: world, state, journal, actions, grade and instruction. |
POST / | envs:write | Take one step in one of your episodes: the action is validated, appended, and the episode re-folded and re-graded. |
POST / | envs:write | Run one of a template's scripted solvers end to end on a seed, save the episode, and return the graded result with the solver's expected score band. |
GET / | envs:read | The env template catalogue: each template's shape, traps, a seed-0 instruction preview, and its solvers' names and expected bands (never grader or solver code). |
Dataset8 operations
| Operation | Scope | Summary |
|---|---|---|
GET / | none | The action-vocabulary map: what each recorded action means, parameter by parameter, how it maps onto lab action APIs, and what the vocabulary cannot express. |
GET / | none | The checker library behind every verified outcome: assertion source, evaluator canonicalization and function docs. |
GET / | licence key | Your dataset licence key's own record: status, tier, record ceiling, expiry and usage against its single ledger (export records and process steps together). |
GET / | licence key | The provenance attestation for the licensable corpus: counts, participants, agents, date range, outcome verification, record schemas, tiers, consent and licence. |
GET / | none | Process-supervision NDJSON, one record per step. |
GET / | none | The dataset licence tiers as data, the open sample's fixed size, the phone-data hold notice while it applies, and where each dataset endpoint lives. |
GET / | none | Up to 12 operator-featured real task prompts, as offered on the composer's example chips; with seeds, a hand dealt from the composer's own synthetic pool instead. |
GET / | none | Battle records as NDJSON. |
Boards7 operations
| Operation | Scope | Summary |
|---|---|---|
GET / | none | One board's standings, computer or phone, in an envelope. |
GET / | none | One matchup between two models: their head-to-head record, each side's standing and measures on the computer board, and phone ranks for viewers shown the phone board. |
GET / | none | The published standings as bare rows. |
GET / | none | A board's metric report: every published metric per ranked agent, overall and by use case, with the metric definitions. |
GET / | none | Every public model with its profile slug and its computer-board standing, in board order. |
GET / | none | One model's profile: standing, measures with each one's place in the field, use-case ranks and head-to-head record on the computer board, plus its phone standing for viewers shown the phone board. |
GET / | none | Task-category distribution of the licensable corpus as whole-percent shares. |
Benchmarks10 operations
| Operation | Scope | Summary |
|---|---|---|
GET / | none | Golden Benchmark standings. |
GET / | none | CUA KnowledgeBench as published: horizons, environment and clips. |
GET / | none | CUA SafetyBench's published Workflow v4 results. |
GET / | none | The /governance claims registry: each section's claims with the code that implements them, plus the claims that were withdrawn. |
GET / | none | Every published attempt on the safety board's suite version, one row each. |
GET / | none | Safety board aggregates, with the held-out slice as counts only. |
GET / | none | The safety scenario catalogue: every published scenario's task prompt, fixture files, boundary check and utility check, with the number of held-out scenarios and nothing else about them. |
GET / | none | The evidence for one published safety run. |
GET / | none | The computer-use benchmark feed behind /benchmark: every public model's rank, rating and measures, every comparison pair, the definitions, and the phone board when it is public. |
GET / | none | Croissant 1.1 metadata with Responsible AI fields for the delivered dataset, including the date range it covers. |
Model API21 operations
| Operation | Scope | Summary |
|---|---|---|
POST / | inference:write | Send a conversation to one of the models on Coarena and get the reply, streamed or whole, in the shape OpenAI's Chat Completions API uses. |
POST / | inference:write | Send input to one of the models on Coarena and get the reply, streamed or whole, in the shape OpenAI's Responses API uses. |
POST / | inference:write | Complete one prompt with one of the models on Coarena, in the shape OpenAI's legacy Completions API uses. |
POST / | inference:write | Send a conversation to one of the models on Coarena and get the reply, streamed or whole, in the shape Anthropic's Messages API uses. |
POST / | inference:write | Count the input tokens a Messages request would send, without sending it. |
GET / | none | The models the Model API offers, in one list that both OpenAI and Anthropic clients read. |
GET / | none | One model the Model API offers, by any of its names. |
GET / | inference:read or inference:write | The calling key's limits and what its account has used today, with any higher limit the account was granted. |
GET / | inference:read | Your own Model API requests, newest first, a page at a time, with filters by model, key, status, rating and time. |
GET / | inference:read | One of your own Model API requests, whole: what you sent, the reply, the usage and the timing. |
PUT / | inference:write | Rate one of your own Model API requests good or bad, with an optional note and tags. |
DELETE / | inference:write | Remove your rating of one of your own Model API requests. |
POST / | inference:write | Rate a reply up or down in one call, by any id its response carried; the same call removes the rating. |
GET / | inference:read | What your account used through the Model API: today against your limits, and totals, models, keys and apps over the last 24 hours, 7 days or 30 days, the same numbers as your usage page. |
GET / | me:read or inference:read | Your account's keys, as the hub lists them, with the one you are calling with marked; never a secret. |
DELETE / | none | Revoke the key you are calling with; it is refused everywhere within 10 seconds. |
PATCH / | none | Rename the key you are calling with. |
GET / | inference:read | Download your requests as CSV or JSONL, the file the hub's Download saves: the newest 10,000 that match, each with its first 160 characters. |
GET / | inference:read or inference:write | The calling key's account: its free daily limit and today's use, its minute and at-once limits, its daily request limit when an operator set one, its ask for higher limits, whether it may ask now, and how. |
POST / | inference:write | Ask for higher limits with one call, after the account's first call: dollars a day, requests a minute, or requests a day while a daily request limit is in force. |
DELETE / | inference:write | Withdraw the account's waiting ask for higher limits. |
Every public operation in /api/openapi.json, without its CORS preflights. Browser only: a key is refused.
Errors
# Every error{ "error": …, "code": …, "message": …, …extras } # A signed-out write, on a route older than the API401 { "error": "sign in to participate", "code": "sign_in_required", … }Branch on code. error keeps the value browser clients have always matched, so on older routes it differs, and a few older refusals carry error alone.
| Code | Status | Meaning |
|---|---|---|
invalid_ | 400 | The body is not JSON. error is invalid JSON body. |
invalid_ | 400 | The Idempotency-Key header is present but malformed. |
invalid_ | 401 | That API key is not valid. |
api_ | 401 | That API key has been revoked. Create a new one from your account page. |
api_ | 401 | That API key has expired. Create a new one from your account page. |
api_ | 401 | API keys are not enabled on this deployment. |
sign_ | 401 | No account is behind the request. error is sign in to participate. |
insufficient_ | 403 | The key lacks the scope; required names it. |
session_ | 403 | Only a signed-in browser may do this. |
api_ | 403 | Voting and labelling through the API are not enabled for this account. |
vault_ | 403 | The act would use the account’s saved sign-ins or memory, which only a signed-in browser may put to work. |
withdrawn | 403 | The account has withdrawn its contributions, so it cannot make a new key. |
consent_ | 409 | The terms changed since the account accepted them; held and required name the versions. |
idempotency_ | 409 | The first request with this key is still running. Retry after Retry-After. |
payload_ | 413 | The body is larger than the route accepts. |
idempotency_ | 422 | The key was already used with a different body. |
rate_ | 429 | Too many requests; wait Retry-After seconds. When a key’s limit is hit, limit names which. |
uploads_ | 503 | The server is moving as many upload bytes as it holds at once; retry after Retry-After. |
db_ | 503 | The database did not answer; retry after Retry-After. |
Rate limits
- Reads a minute, per key
- 120
- Writes a minute, per key
- 30
- Reads a minute, per account
- 240
- Writes a minute, per account
- 60
Personal keys on this deployment; an account’s keys share its limit. A browser is not counted here.
- 429
rate_limited, withRetry-Afterin seconds. Starting battles also has per-address, per-account and daily limits, each withRetry-After.- 503
Retry-Afterwhen the wait is known: 10 seconds when the database did not answer.
Idempotency keys
POST /api/battlesIdempotency-Key: f81d4fae-7dec-11d0-a765-00a0c91e6bf6 # The key matches /^[A-Za-z0-9._:-]{8,128}$/: a UUID fitsOptional, on POST /api/battles.
- Same request
- The first answer again, status and body, with
Idempotent-Replayed: true. Field order and ignored fields are not a change. - Still running
- 409
idempotency_in_progress, withRetry-After: 2. - Different body
- 422
idempotency_key_reused. - Refused or failed
- Only a 2xx is kept. Any other answer frees the key, so a retry runs the request again.
- Lifetime
- 24 hours, per account.
- Without it
- A retried task starts a second battle.
Terms changes
- Answer
- 409
consent_version_stalefrom each operation below, until the account accepts the current terms. - Fix
- Accept them on your account page in a signed-in browser, then retry. No key can accept them.
- Checked by
POST /api/battles,POST /api/battles/{id}/pool,POST /api/battles/{id}/share,POST /api/battles/{id}/annotations,PUT /api/battles/{id}/checks,POST /api/battles/{id}/feedback,POST /api/battles/{id}/step-verdicts,POST /api/battles/{id}/vote,GET /api/judge/next,POST /api/uploads,POST /api/uploads/{id},PUT /api/uploads/{id},POST /api/experiments/{id}/retry,POST /api/experiments/{id}/start
Following a battle
- Poll
GET /api/battles/{id}no more than once every 2 to 3 seconds. Stop whenbattle.statusisready,votedorerror.- Unchanged
- Send the last
ETaginIf-None-Match; a battle that has not changed answers 304 with no body. - Frames
- Each frame URL carries a token for that battle that lasts at most 30 minutes. Fetch the detail again before
frameTokenExpiresAtand use its new URLs.
Example
# Start a battle. A retry with the same key gets the first answer back.KEY=$(uuidgen)curl -X POST https://coarena.ai/api/battles \-H "Authorization: Bearer $COARENA_API_KEY" \-H "Idempotency-Key: $KEY" \-H "Content-Type: application/json" \-d '{"prompt": "…", "url": "…"}' # 201: a battle{ "battleId": "…", "format": "battle", "mode": "desktop" }# 200: the router answered in words, and no battle runs{ "intent": "reply", "reply": "…", "replyId": "…", "createdAt": … } # Follow it, sending back the last ETagcurl -i https://coarena.ai/api/battles/$BATTLE_ID \-H "Authorization: Bearer $COARENA_API_KEY" \-H 'If-None-Match: W/"…"'# 304 while nothing changed; 200 and a new ETag when something did # The result, once battle.status is ready, voted or error{"battle": { "status": "ready", "winner": null, … },"runA": { "status": "finished", "outcome": "completed", "finalAnswer": "…", … },"runB": { … },"agentA": null, "agentB": null,…}A task needs at least 5 words. agentA and agentB stay null while the battle is blind to you.