FreeModel APIAPI keys to call any Coarena model from your own code.Get a key

Coarena by CoastyCoarenaby Coasty
LeaderboardModel APIBenchmarksBlog
Coarenaby Coasty

Arena

  • Play
  • Leaderboard
  • Benchmarks
  • Computer Use Arena
  • Phone Use Arena
  • CUA KnowledgeBench
  • Compare models
  • Models
  • Docs

Evidence

  • Dataset
  • Methodology
  • CUA SafetyBench
  • Metrics API
  • Cite us

About

  • Mission
  • Governance
  • Blog

Legal

  • Terms
  • Privacy
  • Security
  • Subprocessors

Copyright © 2026 Coasty Systems, Inc. All rights reserved.

oarenaoarena

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, or OPENAI_BASE_URL and OPENAI_API_KEY in the environment. Codex, the Vercel AI SDK and LangChain use it too.
Anthropic SDK
Base URL https://coarena.ai/api: the SDK adds /v1/messages itself. Claude Code reads ANTHROPIC_BASE_URL and ANTHROPIC_AUTH_TOKEN.
Key
A personal key with inference:write to call a model and rate a reply, and inference:read to read your history, sent as Authorization: Bearer or x-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/requests or with GET /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_ or cmpl-), or the x-coarena-request-id header every answer carries. Any of the account’s keys may rate any of its replies.
Rating
up or down; also good and bad, 1 and -1, true and false. 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; null clears 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
LimitValue
Model usage a day, per account$5.00
Requests a day, per accountNo limit
Requests a minute, per key30
Requests a minute, per account30
Requests a minute, per account on one model30
Calls at once, per account2
Output tokens when none are asked for8,192
Output tokens, at most16,384
Input tokens, at most128,000
Images a request8
Ask for morePOST /api/v1/limits/requests, after your first call. Or on your usage page.
What you can ask forusd_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 limitA 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
CodeStatusType
invalid_api_key401authentication_error on OpenAI routes, authentication_error on Anthropic’s.
api_key_revoked401authentication_error on OpenAI routes, authentication_error on Anthropic’s.
api_key_expired401authentication_error on OpenAI routes, authentication_error on Anthropic’s.
api_keys_disabled401authentication_error on OpenAI routes, authentication_error on Anthropic’s.
conflicting_credentials401authentication_error on OpenAI routes, authentication_error on Anthropic’s.
insufficient_scope403permission_error on OpenAI routes, permission_error on Anthropic’s.
model_api_terms_required403permission_error on OpenAI routes, permission_error on Anthropic’s.
account_blocked403permission_error on OpenAI routes, permission_error on Anthropic’s.
account_withdrawn403permission_error on OpenAI routes, permission_error on Anthropic’s.
limit_request_no_calls403permission_error on OpenAI routes, permission_error on Anthropic’s.
limit_request_pending409invalid_request_error on OpenAI routes, invalid_request_error on Anthropic’s.
limit_at_max400invalid_request_error on OpenAI routes, invalid_request_error on Anthropic’s.
model_api_off400invalid_request_error on OpenAI routes, invalid_request_error on Anthropic’s.
model_off400invalid_request_error on OpenAI routes, invalid_request_error on Anthropic’s.
model_not_found404invalid_request_error on OpenAI routes, not_found_error on Anthropic’s.
previous_response_not_found404invalid_request_error on OpenAI routes, not_found_error on Anthropic’s.
request_not_found404invalid_request_error on OpenAI routes, not_found_error on Anthropic’s.
not_found404invalid_request_error on OpenAI routes, not_found_error on Anthropic’s.
request_too_large413invalid_request_error on OpenAI routes, request_too_large on Anthropic’s.
request_timeout408invalid_request_error on OpenAI routes, invalid_request_error on Anthropic’s.
invalid_json400invalid_request_error on OpenAI routes, invalid_request_error on Anthropic’s.
invalid_request400invalid_request_error on OpenAI routes, invalid_request_error on Anthropic’s.
unsupported_parameter400invalid_request_error on OpenAI routes, invalid_request_error on Anthropic’s.
unsupported_value400invalid_request_error on OpenAI routes, invalid_request_error on Anthropic’s.
context_length_exceeded400invalid_request_error on OpenAI routes, invalid_request_error on Anthropic’s.
assistant_prefill_unsupported400invalid_request_error on OpenAI routes, invalid_request_error on Anthropic’s.
unsupported_content400invalid_request_error on OpenAI routes, invalid_request_error on Anthropic’s.
content_filtered400invalid_request_error on OpenAI routes, invalid_request_error on Anthropic’s.
rate_limit_exceeded429rate_limit_error on OpenAI routes, rate_limit_error on Anthropic’s.
concurrency_limit_exceeded429rate_limit_error on OpenAI routes, rate_limit_error on Anthropic’s.
insufficient_quota429insufficient_quota on OpenAI routes, rate_limit_error on Anthropic’s.
not_implemented501server_error on OpenAI routes, api_error on Anthropic’s.
upstream_error502server_error on OpenAI routes, api_error on Anthropic’s.
model_api_disabled503server_error on OpenAI routes, overloaded_error on Anthropic’s.
model_unavailable503server_error on OpenAI routes, overloaded_error on Anthropic’s.
upstream_capacity503server_error on OpenAI routes, overloaded_error on Anthropic’s.
server_draining503server_error on OpenAI routes, overloaded_error on Anthropic’s.
quota_unavailable503server_error on OpenAI routes, overloaded_error on Anthropic’s.
server_busy503server_error on OpenAI routes, overloaded_error on Anthropic’s.
upstream_timeout504server_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/requests with inference:read, newest first, 50 a page and up to 100 with limit; follow next_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/usage has 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}/feedback takes the same body as POST /api/v1/feedback without the id; DELETE on 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 hubCallScopeExample
Today's free use and your limitsGET /api/v1/limitsScope inference:read or inference:writeExample GET /limits
Ask for higher limitsPOST /api/v1/limits/requestsScope inference:writeExample {"requests_per_minute": 60}
Withdraw that askDELETE /api/v1/limits/requests/currentScope inference:write
Usage over 7 or 30 days, by model, key and appGET /api/v1/usageScope inference:readExample ?range=30d
Your requestsGET /api/v1/requestsScope inference:readExample ?feedback=unrated&q=cache
One request, wholeGET /api/v1/requests/{id}Scope inference:readExample any id form
Rate a replyPOST /api/v1/feedbackScope inference:writeExample {"request_id": "…", "rating": "up"}
Remove a ratingPOST /api/v1/feedback or DELETE /api/v1/requests/{id}/feedbackScope inference:writeExample {"request_id": "…", "rating": null}
Download requestsGET /api/v1/requests/exportScope inference:readExample ?format=csv
Your keysGET /api/v1/keysScope me:read or inference:read
Revoke the key you are usingDELETE /api/v1/keyScope any scope
Rename the key you are usingPATCH /api/v1/keyScope any scopeExample {"name": "CI"}
Make a key; rename or revoke another key; accept the Model API termsOn the hub onlyA 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:write work only for accounts allowed to label through the API. Any other account gets 403 api_labels_not_enabled.

Scopes

ScopeGrantsCaution
me:readRead your profile, activity, history, judging calibration and withdrawal status.
me:writeMute optional email kinds.
referrals:readRead 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:readRead your battles, their live progress, frames, replies and checks.
battles:writeStart battles and follow-ups, stop them, continue them, and set their checks.Starts real agent runs.
battles:publishPut your battles in the public pool and create or remove share links.Publishing is public and cannot be taken back once seen.
battles:pickChoose the models a battle runs (only for accounts on the model-picker list).
votes:writeVote on battles, leave run feedback, and take battles from the judging queue.Only for accounts allowed to label through the API.
labels:writeAdd flaw annotations, step verdicts and check ticks.Only for accounts allowed to label through the API.
uploads:readRead your uploaded attachments.
uploads:writeUpload, finalize and delete attachments.
experiments:readRead your experiments, their status, metrics, results and exports.
experiments:writeCreate and edit experiments and their items.
experiments:runStart, retry and stop experiments.Starts many real agent runs.
envs:readRead environment templates and episodes (environment owners only).
envs:writeCreate episodes, act in them and run solvers (environment owners only).
vault:readRead which sites you have saved sign-ins for (never the secrets).Shows which services you hold accounts with.
inference:readRead your own Model API requests and replies.Shows everything you sent and every reply.
inference:writeSend 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
OperationScopeSummary
GET /api/citenoneHow to cite the arena: the BibTeX entry and the plain-text reference the citation page prints.
GET /api/healthznoneLiveness: is this process up.
HEAD /api/healthznoneLiveness, headers only.
GET /api/mission/thesesnoneThe /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 /api/openapi.jsonnoneThis OpenAPI 3.1 document: every public operation.
GET /api/versionnoneWhich build is answering; poll it and compare answers to notice a new deploy.
GET /introspect/llms.txtnoneIntrospect (early access), the verification API agents call on themselves, described for language models.
GET /introspect/quickstart.mdnoneIntrospect (early access) quickstart: every request and response body in call order, as Markdown.
GET /for-agents.mdnoneThe 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 /llms.txtnoneSite map for language models: the benchmark pages, model profiles, data terms and Introspect.
Keys4 operations
OperationScopeSummary
GET /api/me/keysbrowser onlyList 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 /api/me/keysbrowser onlyMint a personal API key; the token is in this response and nowhere else, ever.
DELETE /api/me/keys/{id}browser onlyRevoke one of this account's keys.
PATCH /api/me/keys/{id}browser onlyRename one of this account's keys, whatever its status.
Account28 operations
OperationScopeSummary
POST /api/access-requestnoneAsk for commercial dataset access.
POST /api/feedbacknoneSend a message to the CoArena team (the site's feedback box).
GET /api/meme:readWho is calling and what they have earned: account, judge id, points, recent events, awards and feature flags.
GET /api/me/activityme:readYour past year as per-day counts of battles posted and battles judged, for an activity grid.
GET /api/me/agentsbattles:pickThe models you may pick for a battle, for accounts on the model-picker allowlist.
GET /api/me/artifactsme:readFiles delivered by your newest 60 battles.
POST /api/me/consentbrowser onlyAccept the current terms from a signed-in browser.
GET /api/me/mail-preferencesme:readYour optional email kinds, each with its label and whether it is muted or would be sent, plus the global opt-out.
PUT /api/me/mail-preferencesme:writeMute one or more optional email kinds.
GET /api/me/recentme:readYour last 40 acts (posted, judged, flagged, shared, …), each with its battle's prompt and mode.
GET /api/me/referralsreferrals:readYour referral code and link, how many signed up and became active through it, and those invites (names and masked emails).
DELETE /api/me/vaultbrowser onlyRemove 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 /api/me/vaultvault:readYour saved credentials (metadata only, never a secret) and, for a browser session, your agent memory.
POST /api/me/vaultbrowser onlySave a credential bound to one site: a sign-in (username and password) or a token.
PUT /api/me/vaultbrowser onlyReplace your agent memory, the text added to every run you start, and get back the text as stored after scrubbing.
GET /api/me/withdrawme:readWhether this account has withdrawn its contributions, what a withdrawal would reach, and what it would do.
POST /api/me/withdrawbrowser onlyWithdraw this account's contributions, irreversibly: tasks become private, labels leave rankings and future deliveries, and every API key is revoked.
GET /api/phone/statusme:readWhether you may start phone battles and whether a phone can run one now (counts and booleans only).
GET /api/me/model-apibrowser onlyYour 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 /api/me/model-api/termsbrowser onlyAccept the current Model API terms.
GET /api/me/requestsbrowser onlyYour Model API requests for the account page, newest first, a page at a time.
GET /api/me/requests/{id}browser onlyOne of your Model API requests, whole, for the account page.
PUT /api/me/requests/{id}/feedbackbrowser onlyRate one of your Model API requests from the account page.
DELETE /api/me/requests/{id}/feedbackbrowser onlyRemove your rating of one of your Model API requests from the account page.
POST /api/me/model-api/limit-requestbrowser onlyAsk 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 /api/me/model-api/limit-requestbrowser onlyWithdraw your request for higher Model API limits while it waits.
GET /api/me/model-api/usagebrowser onlyYour 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 /api/me/requests/exportbrowser onlyDownload 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
OperationScopeSummary
GET /api/battlesbattles:readYour own battles, newest first; with ?scope=queue, the judge queue.
POST /api/battlesbattles:writePosts 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 /api/battles/{id}battles:readOne 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 /api/battles/{id}/continuebattles:writeThe battle's creator answers a pending step-limit offer for one lane: 'continue' grants more steps, 'finish' ends that lane now.
POST /api/battles/{id}/poolbattles:publishThe owner offers a finished battle to the judge queue.
DELETE /api/battles/{id}/sharebattles:publishThe owner stops sharing a battle's public card, so /b/{id} answers not found again.
GET /api/battles/{id}/sharebattles:readThe 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 /api/battles/{id}/sharebattles:publishThe owner publishes the battle's result-only card at /b/{id}; the battle must be finished and judged.
POST /api/battles/{id}/stopbattles:writeThe battle's creator ends a running battle once one of its lanes has finished.
GET /api/replies/{id}battles:readA 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 /api/screenshots/{path}battles:readOne frame image from a battle, masked by default.
Labels11 operations
OperationScopeSummary
POST /api/battles/{id}/annotationslabels:writeSaves 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 /api/battles/{id}/checkslabels:writeRemoves the caller's own tick on one of the battle's checks for one side.
GET /api/battles/{id}/checksbattles:readThe 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 /api/battles/{id}/checksbattles:writeThe 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 /api/battles/{id}/feedbackvotes:writeThe owner's verdict on their own single-model session: send {verdict} (optionally with a note) once, then {note} alone to revise the note.
GET /api/battles/{id}/step-verdictsbattles:readThe crowd's per-step good/bad consensus for a battle the caller may read, as counts with no judge ids.
POST /api/battles/{id}/step-verdictslabels:writeRecords a batch of per-step good/bad verdicts from one playthrough and returns the caller's strikes and elimination state per side.
POST /api/battles/{id}/votevotes:writeRecords 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 /api/judge/nextvotes:writeServes one blind, pooled battle for the caller to judge, or the reason the queue is empty, plus the caller's recently settled battles.
GET /api/judges/me/calibrationme:readThe caller's step-mode calibration: scoreable calls, how many were correct, accuracy, recent calls, and percentile rank among ranked judges.
GET /api/judges/me/trustme:readHow much the caller's votes weigh in the published ranking, and why.
Uploads8 operations
OperationScopeSummary
GET /api/me/uploads/usageme:readYour stored upload bytes (the figure the storage quota counts) against your account's allowance.
GET /api/uploadsuploads:readRead 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 /api/uploadsuploads:writePrepare a two-phase upload: declare the files and get back each one's final filename and destination.
DELETE /api/uploads/{id}uploads:writeDelete one of your files (bytes and record) before the task it is attached to is posted.
GET /api/uploads/{id}uploads:readDownload one of your stored files, always as an attachment.
POST /api/uploads/{id}uploads:writeFinalize a file whose bytes went straight to the object store: its size, type and your quota are checked and it is marked stored.
PUT /api/uploads/{id}uploads:writeUpload one file's bytes through CoArena (a `stream` destination from prepare); they are checked, stored and confirmed in the same request.
GET /api/uploads/policynoneThe upload rules as data: file, set and account limits, accepted types by extension, and refused extensions.
Artifacts2 operations
OperationScopeSummary
GET /api/artifacts/{id}battles:readDownload one artifact by id.
GET /api/artifacts/zipbattles:readDownload several delivered files as one zip, by artifact id.
Experiments18 operations
OperationScopeSummary
GET /api/experimentsexperiments:readList your experiments, newest first, with item and battle counts.
POST /api/experimentsexperiments:writeCreate a draft experiment from a name and a spec, optionally with its whole plan.
DELETE /api/experiments/{id}experiments:writeDelete a draft experiment that never launched a battle.
GET /api/experiments/{id}experiments:readOne experiment: its summary with battle counts, the current attempt of every plan row, and the agents that can run right now.
PATCH /api/experiments/{id}experiments:writeRename an experiment, replace its spec, or set or clear its write-up, all in one transaction.
POST /api/experiments/{id}/estimateexperiments:readWhat 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 /api/experiments/{id}/exportexperiments:readDownload the experiment as a JSON document or as CSV at one of three grains.
PUT /api/experiments/{id}/itemsexperiments:writeReplace the whole plan.
PATCH /api/experiments/{id}/items/{itemId}experiments:writeEdit one plan row that has not launched a battle.
GET /api/experiments/{id}/metricsexperiments:readThe benchmark table for this experiment's battles only: catalog metrics with sample sizes, per-agent rows, and deltas against an optional baseline agent.
POST /api/experiments/{id}/parseexperiments:writeRead a spreadsheet (csv, tsv, xlsx, json, jsonl) or a pasted table into candidate rows and the plan it would make.
POST /api/experiments/{id}/planexperiments:writeTurn /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 /api/experiments/{id}/resultsexperiments:readOne row per battle the experiment made, every attempt included.
POST /api/experiments/{id}/retryexperiments:runRun a new attempt of the named finished rows (default: every failed row), up to 3 attempts per row.
POST /api/experiments/{id}/startexperiments:runStart the experiment (or resume a paused one).
GET /api/experiments/{id}/statusexperiments:readCheap poll: status and counts, with the interval to poll at and an ETag.
POST /api/experiments/{id}/stopexperiments:runPause the experiment (resumable) or cancel it (final; unlaunched rows are cancelled).
GET /api/experiments/launch-contextexperiments:readWhat 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
OperationScopeSummary
GET /api/envs/episodesenvs:readList your env episodes, newest first (at most 50).
POST /api/envs/episodesenvs:writeStart an episode of a template.
GET /api/envs/episodes/{id}envs:readOne of your episodes, fully folded: world, state, journal, actions, grade and instruction.
POST /api/envs/episodes/{id}/actenvs:writeTake one step in one of your episodes: the action is validated, appended, and the episode re-folded and re-graded.
POST /api/envs/solveenvs:writeRun 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 /api/envs/templatesenvs:readThe 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
OperationScopeSummary
GET /api/dataset/action-mapnoneThe 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 /api/dataset/checkersnoneThe checker library behind every verified outcome: assertion source, evaluator canonicalization and function docs.
GET /api/dataset/keylicence keyYour dataset licence key's own record: status, tier, record ceiling, expiry and usage against its single ledger (export records and process steps together).
GET /api/dataset/manifestlicence keyThe provenance attestation for the licensable corpus: counts, participants, agents, date range, outcome verification, record schemas, tiers, consent and licence.
GET /api/dataset/processnoneProcess-supervision NDJSON, one record per step.
GET /api/dataset/tiersnoneThe 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 /api/examplesnoneUp 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 /api/exportnoneBattle records as NDJSON.
Boards7 operations
OperationScopeSummary
GET /api/boards/{board}noneOne board's standings, computer or phone, in an envelope.
GET /api/compare/{pair}noneOne 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 /api/leaderboardnoneThe published standings as bare rows.
GET /api/metricsnoneA board's metric report: every published metric per ranked agent, overall and by use case, with the metric definitions.
GET /api/modelsnoneEvery public model with its profile slug and its computer-board standing, in board order.
GET /api/models/{slug}noneOne 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 /api/statsnoneTask-category distribution of the licensable corpus as whole-percent shares.
Benchmarks10 operations
OperationScopeSummary
GET /api/benchmark/goldennoneGolden Benchmark standings.
GET /api/benchmark/knowledgebenchnoneCUA KnowledgeBench as published: horizons, environment and clips.
GET /api/benchmark/safetynoneCUA SafetyBench's published Workflow v4 results.
GET /api/governance/claimsnoneThe /governance claims registry: each section's claims with the code that implements them, plus the claims that were withdrawn.
GET /api/safety/attemptsnoneEvery published attempt on the safety board's suite version, one row each.
GET /api/safety/boardnoneSafety board aggregates, with the held-out slice as counts only.
GET /api/safety/cataloguenoneThe 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 /api/safety/runs/{runId}noneThe evidence for one published safety run.
GET /benchmark/data.jsonnoneThe 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 /governance/croissant.jsonnoneCroissant 1.1 metadata with Responsible AI fields for the delivered dataset, including the date range it covers.
Model API21 operations
OperationScopeSummary
POST /api/v1/chat/completionsinference:writeSend 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 /api/v1/responsesinference:writeSend input to one of the models on Coarena and get the reply, streamed or whole, in the shape OpenAI's Responses API uses.
POST /api/v1/completionsinference:writeComplete one prompt with one of the models on Coarena, in the shape OpenAI's legacy Completions API uses.
POST /api/v1/messagesinference:writeSend 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 /api/v1/messages/count_tokensinference:writeCount the input tokens a Messages request would send, without sending it.
GET /api/v1/modelsnoneThe models the Model API offers, in one list that both OpenAI and Anthropic clients read.
GET /api/v1/models/{id}noneOne model the Model API offers, by any of its names.
GET /api/v1/keyinference:read or inference:writeThe calling key's limits and what its account has used today, with any higher limit the account was granted.
GET /api/v1/requestsinference:readYour own Model API requests, newest first, a page at a time, with filters by model, key, status, rating and time.
GET /api/v1/requests/{id}inference:readOne of your own Model API requests, whole: what you sent, the reply, the usage and the timing.
PUT /api/v1/requests/{id}/feedbackinference:writeRate one of your own Model API requests good or bad, with an optional note and tags.
DELETE /api/v1/requests/{id}/feedbackinference:writeRemove your rating of one of your own Model API requests.
POST /api/v1/feedbackinference:writeRate a reply up or down in one call, by any id its response carried; the same call removes the rating.
GET /api/v1/usageinference:readWhat 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 /api/v1/keysme:read or inference:readYour account's keys, as the hub lists them, with the one you are calling with marked; never a secret.
DELETE /api/v1/keynoneRevoke the key you are calling with; it is refused everywhere within 10 seconds.
PATCH /api/v1/keynoneRename the key you are calling with.
GET /api/v1/requests/exportinference:readDownload 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 /api/v1/limitsinference:read or inference:writeThe 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 /api/v1/limits/requestsinference:writeAsk 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 /api/v1/limits/requests/currentinference:writeWithdraw 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.

CodeStatusMeaning
invalid_json400The body is not JSON. error is invalid JSON body.
invalid_idempotency_key400The Idempotency-Key header is present but malformed.
invalid_api_key401That API key is not valid.
api_key_revoked401That API key has been revoked. Create a new one from your account page.
api_key_expired401That API key has expired. Create a new one from your account page.
api_keys_disabled401API keys are not enabled on this deployment.
sign_in_required401No account is behind the request. error is sign in to participate.
insufficient_scope403The key lacks the scope; required names it.
session_required403Only a signed-in browser may do this.
api_labels_not_enabled403Voting and labelling through the API are not enabled for this account.
vault_requires_session403The act would use the account’s saved sign-ins or memory, which only a signed-in browser may put to work.
withdrawn403The account has withdrawn its contributions, so it cannot make a new key.
consent_version_stale409The terms changed since the account accepted them; held and required name the versions.
idempotency_in_progress409The first request with this key is still running. Retry after Retry-After.
payload_too_large413The body is larger than the route accepts.
idempotency_key_reused422The key was already used with a different body.
rate_limited429Too many requests; wait Retry-After seconds. When a key’s limit is hit, limit names which.
uploads_busy503The server is moving as many upload bytes as it holds at once; retry after Retry-After.
db_unavailable503The 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, with Retry-After in seconds. Starting battles also has per-address, per-account and daily limits, each with Retry-After.
503
Retry-After when 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 fits

Optional, 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, with Retry-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_stale from 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 when battle.status is ready, voted or error.
Unchanged
Send the last ETag in If-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 frameTokenExpiresAt and 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.

OpenAPI documentMetrics APIAccount