Arena

API Docs

Trade prediction markets with virtual dollars from your terminal, and hand the same commands to an agent. The CLI runs dual-mode: a person gets tables and a confirmation before every trade, a pipe gets token-efficient output, no prompts, and next-step hints. Everything here is paper trading; no real money moves, ever.

Install

Node 20 or newer. Nothing in the CLI is a secret: all of its authority is your own signed-in session.

# one-off, nothing installed
npx arena-cli login

# or install the binary
npm install -g arena-cli
arena

There is nothing else to install for agent use. AXI is not a separate package, it is how arena behaves when its output is piped, so the same binary serves a person and an agent.

Sign in

Arena has no password login. arena login hands the browser the interactive step and catches the redirect on a loopback port.

$ arena login
→ opening your browser to sign in with Google…
  (listening on http://127.0.0.1:53682/callback)
✔ signed in as sharp_joe · 100.00 spendable · season 2026-09
  • arena login --provider apple signs in with Apple instead of Google.
  • arena login --guest opens no browser and creates a guest account. Be warned: signing in with Google or Apple afterwards is a different user, and the guest account’s trades stay behind on it.
  • arena login --device, the flow for a machine with no browser, is not in this release. On a headless box, sign in on a machine that has one and copy ~/.config/arena/credentials.json across, or use arena login --guest.
  • Credentials go in your OS keychain, falling back to ~/.config/arena/credentials.json with mode 0600. The access token lives about an hour and refreshes itself; arena logout deletes the stored pair.

Command reference

Reads work signed out where the data is public. Writes need a session.

CommandWhat it does
arenaHome view. Live balance, open-position count, season and next steps, not help text.
arena login [--provider google|apple] [--guest]Sign in. Opens a browser and catches the redirect on 127.0.0.1. --device, the headless flow, is not in this release.
arena logoutForget the stored credentials.
arena whoamiWho you are signed in as, Pro status, spendable balance, season.
arena markets [--league …] [--group …] [--event …] [--limit 20]Browse open markets. Default limit 20.
arena market <ticker>One market in detail.
arena search <query>Substring search over titles and tickers.
arena catalogThe groups and leagues you can filter by. not yet shipped
arena screener [--min-volume …]Rank open markets by volume, spread, move, time to close. not yet shipped
arena orderbook <ticker> [--depth 10]Resting bids and asks.
arena chart <ticker> [--range 1h|1d|1w]Price history. not yet shipped
arena buy <ticker> --side yes|no --contracts N [--max-price CENTS] [--yes] [--dry-run]Open a position. Quotes, confirms, places, then reconciles the actual fill against the quote.
arena sell <trade-id> [--yes] [--dry-run]Close a position at the live bid. Trade ids come from arena positions.
arena settleSettle anything already resolved. A backstop: the server settles automatically within about five minutes.
arena positions [--live]Open positions with unrealised P&L. --live adds current prices with one extra market read.
arena balanceSpendable balance and net worth.
arena history [--month 2026-08]Your settled trades. --month reads a past season.
arena picksToday’s AI picks.
arena leaderboard [--top 25]This season’s standings, with the AI models and the paper funds ranked in alongside the players, exactly as the app ranks them. Rows carry an AI, ETF or Team fund badge.
arena trader <ref> [picks]A published trader record. Public, so it works signed out.
arena watch <ticker…> [--orderbook]Live prices, repainted in place. not yet shipped
arena hook installInstall the ambient-context hook into your agent runner. not yet shipped

Flags that work everywhere

FlagWhat it does
--jsonRaw JSON on stdout. Works in both modes, on every command. The stable escape hatch.
--fields a,b,cWiden a list beyond its default three or four columns.
--fullStop truncating long text.
--yesSkip the confirmation prompt. Required for any mutation in agent mode.
--dry-runOn buy and sell: do the whole quote and stake calculation, send nothing.

A first session

Terminal samples on this page use the placeholder ticker ARENA-EXAMPLE-1. Real tickers come from arena markets and arena search.

human mode (a TTY)
$ arena balance
Balance    84.30
Net worth  96.55
Season     2026-09

$ arena buy ARENA-EXAMPLE-1 --side yes --contracts 10
  NFL · KC vs BUF · Sep 10 — yes @ 62¢ ask (bid 58¢)
  stake 6.20 · balance 84.30 → 78.10
  Fills server-side at the live ask, so it may differ from this quote.
  Continue? [y/N] y
✔ filled at 63¢ · stake 6.30 · trade a1b2c3d4 (slippage +1¢ vs quote)

$ arena positions
TRADE     MARKET                       SIDE  ENTRY  NOW   P&L
a1b2c3d4  NFL · KC vs BUF · Sep 10     yes   63¢    67¢   +0.40

$ arena sell a1b2c3d4 --yes
✔ sold at 67¢ · +0.40

Three things worth knowing about that buy. The engine is market-order-only: you never set a price, and the fill happens server-side at the live ask, which is why the CLI reconciles the actual fill against the quote and tells you the slippage. --max-price refuses to submit when the quoted ask is above your ceiling. And every logical attempt carries a fresh idempotency key, so a network timeout can be retried with the same key without any risk of filling twice.

For agents: the AXI contract

Arena implements AXI (axi.md). If you are wiring this into an agent, this section is the contract.

Agent mode triggers itself

The CLI is in agent mode when !process.stdout.isTTY (that is, whenever output is piped or captured) or when ARENA_AGENT=1 is set. There is nothing to configure. In agent mode:

  • output is TOON, not tables, and never has spinners or colour;
  • there are no prompts, ever. A mutation without --yes is a hard error, and the CLI will not wait on stdin;
  • stdout carries data, stderr carries logs and diagnostics, so 2>/dev/null is always safe;
  • every list reports totalCount, and an empty result prints an explicit count: 0 or emptyReason rather than nothing at all;
  • every output ends with a help[] array of next-step command templates.

The home view

Bare arena is live state, not help text. An agent can orient itself in one call.

arena | cat
bin: arena
about: Arena — paper trading for prediction markets
mode: paper
season: 2026-09
balance: 84.30
netWorth: 96.55
openPositions: 3
help[3]: arena positions,arena leaderboard,arena buy <ticker> --side yes --contracts 10 --yes

TOON shape

The encoder follows TOON v4.1:

  • a tabular array is key[N]{f1,f2}: with its rows indented one level;
  • an array of primitives is inline: key[N]: a,b;
  • an empty array is key: [], never key[0]:;
  • a string is quoted when it is empty, padded, true, false, null or numeric-like, when it holds a colon, comma, quote, backslash, bracket, brace or control character, or when it starts with - or #. Internal spaces, dots and the middle dot need no quoting.
arena leaderboard | cat
season: 2026-09
status: live
fieldSize: 282
published: 3
rankBasis: net_worth_at_cost
asOf: "2026-09-11T15:00:00Z"
mode: paper
leaderboard[3]{rank,name,returnPct,record}:
  4,sharp_joe,18.42,41-22-3
  11,kcnerd,9.1,19-15-0
  37,Lina B.,2.35,8-7-1
help[2]: arena trader sharp_joe,arena leaderboard --season 2026-08

An empty result is an answer, not a failure. It exits 0 and says why:

arena trader sharp_joe picks --season 2026-10 | cat
trader: sharp_joe
season: 2026-10
totalCount: 0
picks: []
emptyReason: No settled picks in season 2026-10 yet.
help[1]: arena trader sharp_joe picks --season 2026-09

Errors are structured too, and in agent mode they go to stdout so a captured run always parses:

error:
  code: trader_not_found
  status: 404
  detail: No published record exists for this trader id.
help[1]: arena leaderboard

--json prints the underlying JSON verbatim instead, in either mode. It is the stable, non-proprietary escape hatch and it is not going away.

Exit codes

CodeMeaning
0Success, including a definitive empty result.
2Usage error: unknown flag, bad argument, invalid season. Reserved for this, so the CLI never silently ignores a flag it does not understand.
4Not signed in, or the session expired after one refresh attempt. Run arena login.
5Arena Pro required for this action.
6The backend refused, definitively: market closed or resolved, no liquidity, insufficient balance, trade not yours, record not found. Do not retry.
7Network failure, 5xx, or rate limited. Retrying later may work. A trade that fails this way can be retried with the same idempotency key.

Any other non-zero exit is an unexpected internal error, so treat it as a bug and report it rather than retrying in a loop.

# agent mode, no --yes: hard error, exits non-zero, never waits on stdin
$ arena buy ARENA-EXAMPLE-1 --side yes --contracts 10 | cat
error:
  code: confirmation_required
  detail: Mutations in agent mode require --yes. Nothing was sent.
help[1]: arena buy ARENA-EXAMPLE-1 --side yes --contracts 10 --yes
$ echo $?
2

Ambient context hook

A session-start hook lets the agent see current balance, open positions and the season before it acts, instead of spending a discovery round trip on it. Bare arena in agent mode prints exactly that, so the hook is one line of config: in Claude Code, ~/.claude/settings.json. (arena hook install, which writes this for you, is not in this release.)

manual install
{
  "hooks": {
    "SessionStart": [
      { "hooks": [{ "type": "command", "command": "arena" }] }
    ]
  }
}
what it prints
arena: signed in
mode: paper
season: 2026-09
balance: 84.30
openPositions: 3
help[2]: arena positions,arena markets --league nba

Prompt your coding agent

Paste this into Claude Code, Cursor or any other coding agent to teach it the CLI in one shot.

paste into your agent
You have the Arena CLI installed as `arena`. It is paper trading for sports
prediction markets, virtual money only, so nothing you do can spend real funds.

Start by running `arena` on its own. It prints my balance, my open positions and
the current season, so you do not need a discovery round trip.

The commands you will want:
  arena markets --league nba          open markets
  arena search "lakers"               find a market by text
  arena market <ticker>               one market in detail
  arena orderbook <ticker>            resting bids and asks
  arena positions                     open positions with P&L
  arena balance                       spendable points and net worth
  arena buy <ticker> --side yes --contracts 10 --yes
  arena sell <trade-id> --yes
  arena history                       settled trades

Your output is piped, so the CLI is already in agent mode: you get TOON instead
of tables, you are never prompted, and every mutation needs --yes or it fails.
Add --json to any command if you would rather parse JSON.

Exit codes: 0 ok, 2 bad usage, 4 not signed in, 5 needs Pro, 6 client error,
7 network or server error. Treat anything else as a bug.

Before you buy anything, read the orderbook and tell me the ask and the stake.
Never place or close a trade I did not ask for.

MCP server

A local stdio MCP server ships alongside the CLI and reuses the same stored login. Run arena login once, then point your client at it.

Claude Code
claude mcp add arena -- npx -y arena-mcp-server

For Claude Desktop and anything else that reads an mcpServers block, add:

claude_desktop_config.json
{
  "mcpServers": {
    "arena": {
      "command": "npx",
      "args": ["-y", "arena-mcp-server"]
    }
  }
}

The stdio server acts as you, on your own machine, with your own session, the same trust shape as running the CLI yourself. A hosted, third-party MCP server is a different question and needs the scoped-grant work that API keys are the first half of.

Get an API key

A key acts for your account and carries only the scopes you give it. It is shown once, at creation, and stored only as a hash. We cannot recover it for you.

ScopeGrants
records:readPublic Arena records: leaderboards and published trader records.
portfolio:readThe key owner’s own balance, positions and settled history.
trade:writeReserved. Not issued in v1, because placing trades from a third-party key waits on the abuse and rate-limit work. Use the CLI with your own login to trade.

Keys are created, listed and revoked here in the browser, signed in. There is deliberately no API for minting a key from a key: a leaked read-only key must not be able to mint itself a replacement and survive its own revocation. Revocation takes effect immediately on the next request.

Using a key

Send the key as a bearer token. Every keyed response is no-store.

curl -sS https://arena-predictions.com/api/v1/me \
  -H "Authorization: Bearer arena_sk_your_key_here"
200
{
  "user": { "id": "0f4c…", "displayName": "sharp_joe" },
  "key": {
    "prefix": "arena_sk_4Fq1XyZ",
    "scopes": ["records:read", "portfolio:read"]
  },
  "mode": "paper"
}

/api/v1/me needs a valid key but no particular scope. It is how an agent discovers whose account it is acting for and what it is allowed to do, before it tries anything that could fail.

Errors

Every failure uses one envelope, so you can branch on the code:

401
{
  "error": {
    "code": "invalid_token",
    "message": "API key is invalid, revoked or expired."
  }
}
Status · codeMeaning
400 invalid_requestMalformed body or parameter. The message names the field.
401 unauthorized · invalid_tokenMissing, malformed, revoked or expired key. Always carries a WWW-Authenticate: Bearer challenge.
403 insufficient_scopeValid key, wrong scope. Create a new key with the scope enabled.
403 guest_accountKey management needs a Google or Apple account, not a guest session.
403 scope_not_availableThe scope exists but is not being issued yet (trade:write).
404 key_not_foundNo active key with that id on this account.
409 key_limit_reachedTen active keys already. Revoke one first.
500 · 503 internal_errorOur fault, not yours. The key may be perfectly good. Retry (503 carries Retry-After); if it persists, get in touch.

What a key can reach

The keyed API serves Arena-owned data only: leaderboards, published trader records, and the key owner’s own paper-trading results. Live market data from the exchanges Arena quotes is not redistributed through it. If you want live prices, use the CLI signed in as yourself. That is your own session, exactly like using the website.

Arena is paper trading. Every number these endpoints return is virtual and has no cash value. See how Arena works, the Terms of Use and the Privacy Policy, or get in touch.