Arena

Arena HTTP API

Base URL and versions

Every path in the reference is relative to one base URL. There is no sandbox: the money is paper either way.

REST API

base URL
https://arena-predictions.com/api/v1

HTTPS only. Requests over plain HTTP are redirected, and a key sent over HTTP should be revoked. There is one environment: every account is a paper account, so a key acts on your real Arena account and a trade placed through the CLI is a real paper trade on your record.

Versioning

The API is versioned in the path. Within v1 changes are additive: new endpoints and new optional fields, never a renamed or removed one. A breaking change ships as /api/v2, with v1 kept for at least 180 days and the retirement dated in the changelog.

Specifications

GET /status needs no key and says whether the API is up. GET /openapi.json is the OpenAPI 3.1 description of every live endpoint, and /llms.txt is the short version for a language model.

Authentication

Send your key as a bearer token. Keys are created here, signed in, and shown once.

The header

header
Authorization: Bearer arena_sk_…

A key is arena_sk_ followed by 43 base64url characters (256 bits). x-arena-key: arena_sk_… is accepted as an alternative header for clients that cannot set Authorization. A missing or bad credential answers 401 with a WWW-Authenticate: Bearer challenge; a valid key with the wrong scope answers 403 insufficient_scope.

Key lifecycle

  • Only the hash of a key is stored. The plaintext exists in one HTTP response, at creation; lose it and you revoke it and create another. The list shows the first 16 characters so you can tell your keys apart.
  • An account holds at most 10 active keys. A key may carry an expiry (1 to 365 days) and is revoked instantly; the next request that presents it gets 401 invalid_token.
  • Key management is done with your signed-in browser session, never with a key. A leaked read-only key must not be able to mint itself a replacement and outlive its own revocation, so there is deliberately no key-from-key endpoint. Creating and revoking a key accept same-origin requests only: a script that sends your session cookie must also send Origin: https://arena-predictions.com, and a create must be Content-Type: application/json.
  • There is no guest or anonymous access. Every key belongs to a Google or Apple account, and every trade and record belongs to the person who signed in.

Scopes

records:read
Public Arena records: the players' season board, trader records and settled picks. Checked by GET /leaderboard, /traders/{traderId} and /traders/{traderId}/picks.
portfolio:read
The key owner’s own paper balance, positions and settled history. Recorded on the key now; the endpoints that will check it are not shipped yet.
trade:write
Reserved. 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.

Create a key

Making your first request

Two calls: one that needs nothing, and one that tells you who you are.

Status, no key

curl
curl -sS https://arena-predictions.com/api/v1/status
200
{
  "ok": true,
  "version": "v1",
  "mode": "paper",
  "season": "2026-09",
  "time": "2026-09-22T04:10:00Z"
}

Who am I, with a key

curl
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"
}

GET /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.

The same question from the CLI

terminal
$ arena whoami
userId          4d2f8c1e-9b0a-4c6e-8f21-3a7b5d9e0c12
displayName     sharp_joe
isPro           true
balanceDollars  $1,046.00
monthKey        2026-09

Conventions

The rules every endpoint follows, so you can read one and know the rest.

Names

JSON fields are camelCase (displayName, expiresInDays). Query parameters are snake_case. Enum values are lowercase (active, revoked, paper). Resources are plural nouns; a user’s own data lives under /portfolio, and the paths under it are Kalshi’s.

Units are in the name

*Cents is an integer price in cents, 1 to 99, for the side named. *Dollars and dollars are paper dollars as JSON numbers rounded to two decimals (a trailing zero is not represented: 984.3 is $984.30). *Count is an integer. *Pct is a percentage as a number, not a fraction.

Prices

A contract pays $1 if it settles yes. Stake = contracts × cents ÷ 100. A market order fills now, buying at the live ask and selling at the live bid. A limit order fills only at its price or better, and otherwise rests. No partial fills either way: an order fills whole or not at all.

Times

Instants are RFC 3339 UTC strings at second precision ending in Z, named *At (createdAt). Seasons are calendar months in America/New_York, written YYYY-MM and named season or monthKey. The CLI’s candlestick timestamps are Unix seconds, named *Ts.

Identifiers

Keys, traders and trades are UUIDs. Market tickers are uppercase strings and are opaque: never parse a ticker to infer anything. Arena instrument ids (ins_ plus 12 characters) are permanent and opaque too; see the universal ticker.

Every keyed response

Says "mode": "paper", and answers 201 on create and 200 with the updated resource on revoke. Key management, GET /me and every error are Cache-Control: no-store; a successful records read is Cache-Control: private, max-age=30. Keyed endpoints send no CORS headers: a key belongs in a server, a CLI or an agent runner, never in browser code. There is no streaming under a key; poll.

Identify your client

Send x-arena-client: yourapp/1.2.0 (or a descriptive User-Agent). The CLI and the MCP server do. It is not enforced today; it is the hook for telling a client its version is too old instead of breaking it. Trades the CLI or the MCP server opens at once, and orders they place, keep the tag they were sent with (cli/0.3.0, mcp/0.3.0). A limit order that rests and fills later keeps the tag on the order, not on the trade the fill creates, and sells are not tagged. That tag is self-reported and not verified, and a tag that is not cli, mcp, web or ios plus a version is not kept.

Where Arena differs from Kalshi

  • Field names are camelCase here and snake_case on Kalshi. Paths and query parameters are snake_case on both.
  • Money is a JSON number in paper dollars here; Kalshi sends fixed-point dollar strings ("0.5600"). Prices are integer cents here; Kalshi’s are dollar strings between 0 and 1.
  • Instants are always *At RFC 3339 strings here; Kalshi mixes _time strings and _ts Unix seconds.
  • A key is a bearer token here; Kalshi signs each request with an RSA key. A retried trade returns the original trade here; Kalshi answers 409 on a duplicate client_order_id.

Pagination

Cursor-based, and the same on every list endpoint that has one.

How it works

GET /leaderboard and GET /traders/{traderId}/picks take limit (an integer from 1 to 100, with a documented default) and cursor, and answer with the rows and nextCursor, which is null on the last page. Pass nextCursor back as cursor and loop until it is null. A malformed cursor is 400 invalid_request. Lists do not carry a total count. Kalshi's lists work the same way, with cursor in the body instead of nextCursor.

Whole collections

GET /api_keys is not paginated: an account holds at most 10 active keys. In the CLI every list carries totalCount instead.

Errors

One envelope on every failure, so you can branch on the code and show the message.

The envelope

401
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="arena", error="invalid_token", error_description="API key is invalid, revoked or expired."
Cache-Control: no-store

{
  "error": {
    "code": "invalid_token",
    "message": "API key is invalid, revoked or expired."
  }
}

code is a stable machine token that will not be reworded. message is a human sentence and may change. Kalshi’s envelope is the same two fields at the top level; Arena nests them under error.

Codes

Status codes mean what HTTP says they mean. Each row says whether retrying can help and which exit code the CLI turns it into.

400 invalid_request
Malformed body or parameter. The message names the field. Final; fix the request. CLI exit 6.
401 unauthorized
No credential was presented. Carries WWW-Authenticate: Bearer with error="invalid_request". CLI exit 4.
401 invalid_token
A key was presented but it is malformed, unknown, revoked or expired; one answer for all four, on purpose. Carries WWW-Authenticate: Bearer with error="invalid_token". CLI exit 4.
403 insufficient_scope
Valid key, wrong scope. Create a new key with the scope enabled. CLI exit 6.
403 guest_account
Key management needs a signed-in Google or Apple account; an anonymous browser session cannot hold a key. CLI exit 6.
403 scope_not_available
The scope exists but is not being issued yet (trade:write). CLI exit 6.
404 key_not_found
No active key with that id on this account: already revoked, or never yours. CLI exit 6.
404 trader_not_found
No trader has that id or display name. Final. CLI exit 6.
404 route_not_found
No such path under /api/v1. Check the reference; a planned endpoint is not a live one. CLI exit 6.
409 key_limit_reached
Ten active keys already. Revoke one first. CLI exit 6.
429
Too many requests. Back off, honour Retry-After when it is present, and retry. CLI exit 6, marked retryable.
500 · 503 internal_error
Our fault, not yours. The key may be perfectly good. A 503 also answers when the key could not be verified or the records could not be read right now. Retry with backoff (503 carries Retry-After); if it persists, get in touch. CLI exit 7.

Rate limits

No per-key limit is enforced yet. Please keep each key under 60 requests a minute.

Today

The keyed endpoints enforce no per-key limit yet. Please keep each key under 60 requests a minute, and use nextCursor rather than re-reading a whole list. Treat any 429 as retryable: honour Retry-After when it is present, otherwise back off exponentially from one second.

Before a limit applies

Any per-key limit will be announced in the changelog before it applies, with the headers that report it documented here first.

Client-side caps

The MCP server caps itself, as runaway protection for an agent in a loop: by default 60 reads and 10 trades a minute, and 50 trades a day. That cap is local to the server and separate from anything the API does. The CLI has no cap of its own.

Idempotency and retries

Which calls are safe to repeat, and which failures are worth retrying.

Safe to repeat

Every GET. DELETE /api_keys/{id}: the second call answers 404 key_not_found, which means the key is already gone. POST /api_keys is not idempotent: every call mints a new key.

Reads and key management

Retry a network failure, a timeout, a 429 or a 5xx, with backoff. A 4xx other than 429 is final; fix the request.

Trades

Through the CLI today: every logical attempt carries a fresh UUID idempotencyKey. The same key is reused only when the attempt got no answer at all (a transport failure), and the server then returns the original trade rather than filling twice. An answered failure, 4xx or 5xx, is never retried with the same key. A sell is never retried.

Status

Is the API up. Paths are relative to the base URL.

Get status

GET /status

Whether the API is up, which version this is, that it is paper trading, and the running season (an America/New_York calendar month). Reads nothing, so it reports the API rather than the database.

Authorization: None.

Kalshi equivalent: GET /exchange/status

curl
curl -sS https://arena-predictions.com/api/v1/status
200
{
  "ok": true,
  "version": "v1",
  "mode": "paper",
  "season": "2026-09",
  "time": "2026-09-22T04:10:00Z"
}

Errors

5xx
The API is not up. Retry with backoff.

Identity

Whose account a key acts for, and what it may do.

Get me

GET /me

Whose account the presented key acts for and which scopes it carries. Needs a valid key and no particular scope, so an agent can discover its own permissions before trying anything that could fail. displayName is null until the account has chosen one.

Authorization: API key, any scope.

Kalshi equivalent: none; Arena only.

curl
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"
}

Errors

401 unauthorized
No key was sent.
401 invalid_token
The key is malformed, unknown, revoked or expired.
503 internal_error
The key could not be verified right now. Retry after the Retry-After seconds; the key may be perfectly good.

API keys

Create, list and revoke keys, at the same paths Kalshi uses. Browser session only: a key presented here is refused with 401.

In practice you use these through the panel in the Authentication section; they are documented so what the panel does is not a mystery.

List API keys

GET /api_keys

Every key on the account, newest first, including revoked and expired ones. status is computed server-side from revokedAt and expiresAt. Not paginated: an account holds at most 10 active keys.

Authorization: Browser session (Google or Apple).

Kalshi equivalent: GET /api_keys

curl
curl -sS https://arena-predictions.com/api/v1/api_keys \
  -H "Cookie: <your signed-in browser session>"
200
{
  "keys": [
    {
      "id": "8c0b6f1e-2a3d-4e5f-9a7b-1c2d3e4f5a6b",
      "label": "Claude Code on the laptop",
      "tokenPrefix": "arena_sk_4Fq1XyZ",
      "scopes": ["records:read", "portfolio:read"],
      "status": "active",
      "createdAt": "2026-09-15T18:02:11Z",
      "lastUsedAt": "2026-09-22T03:58:40Z",
      "expiresAt": null,
      "revokedAt": null
    }
  ],
  "count": 1
}

Errors

401 unauthorized
Not signed in.
403 guest_account
The session is anonymous. Sign in with Google or Apple.
500 internal_error
Could not load the keys. Retry.

Create API key

POST /api_keys

Mint a key. The plaintext token is in this response and nowhere else, ever. Not idempotent: every call mints a new key.

Authorization: Browser session (Google or Apple).

Kalshi equivalent: POST /api_keys

Request body

label
string, optional. Trimmed and truncated to 64 characters; empty becomes “Untitled key”.
scopes
string[], optional. Defaults to records:read and portfolio:read. Unknown scopes are refused; trade:write is reserved.
expiresInDays
integer 1..365, optional. Omit for a key that never expires.
curl
curl -sS -X POST https://arena-predictions.com/api/v1/api_keys \
  -H "Cookie: <your signed-in browser session>" \
  -H "Origin: https://arena-predictions.com" \
  -H "Content-Type: application/json" \
  -d '{ "label": "Claude Code on the laptop", "scopes": ["records:read", "portfolio:read"], "expiresInDays": 90 }'
201
{
  "key": {
    "id": "8c0b6f1e-2a3d-4e5f-9a7b-1c2d3e4f5a6b",
    "label": "Claude Code on the laptop",
    "tokenPrefix": "arena_sk_4Fq1XyZ",
    "scopes": ["records:read", "portfolio:read"],
    "status": "active",
    "createdAt": "2026-09-22T04:10:00Z",
    "lastUsedAt": null,
    "expiresAt": "2026-12-21T04:10:00Z",
    "revokedAt": null
  },
  "token": "arena_sk_4Fq1XyZ…(43 characters after the prefix)",
  "warning": "Copy this key now. It is shown once and cannot be recovered, so if you lose it, revoke it and create another."
}

Errors

400 invalid_request
The body is not an object, label is not a string, scopes is malformed or names an unknown scope, or expiresInDays is outside 1..365.
401 unauthorized
Not signed in.
403 guest_account
The session is anonymous.
403 scope_not_available
trade:write was requested. It is not issued yet.
409 key_limit_reached
Ten active keys already. Revoke one first.
500 internal_error
Could not create the key. Retry.

Revoke API key

DELETE /api_keys/{id}

Revoke one key. It takes effect on the next request that presents the key. The row stays in the list as revoked. Safe to repeat: a second call answers 404, which means it is already gone.

Authorization: Browser session (Google or Apple).

Kalshi equivalent: DELETE /api_keys/{api_key}

Path parameters

id
uuid, required. The key’s id from the list.
curl
curl -sS -X DELETE https://arena-predictions.com/api/v1/api_keys/8c0b6f1e-2a3d-4e5f-9a7b-1c2d3e4f5a6b \
  -H "Cookie: <your signed-in browser session>" \
  -H "Origin: https://arena-predictions.com"
200
{
  "key": {
    "id": "8c0b6f1e-2a3d-4e5f-9a7b-1c2d3e4f5a6b",
    "label": "Claude Code on the laptop",
    "tokenPrefix": "arena_sk_4Fq1XyZ",
    "scopes": ["records:read", "portfolio:read"],
    "status": "revoked",
    "createdAt": "2026-09-22T04:10:00Z",
    "lastUsedAt": null,
    "expiresAt": "2026-12-21T04:10:00Z",
    "revokedAt": "2026-09-22T04:12:30Z"
  },
  "revoked": true
}

Errors

400 invalid_request
The id is not a uuid.
401 unauthorized
Not signed in.
403 guest_account
The session is anonymous.
404 key_not_found
No active key with that id on this account: already revoked, or never yours.
500 internal_error
Could not revoke the key. Retry.

Records

The players’ season board, trader records and settled picks. Scope records:read. Arena-owned data only: no exchange tickers, titles or prices.

Every successful response says "mode": "paper". Money is paper dollars; a season is an America/New_York calendar month. Sample ids and names are placeholders.

Get the leaderboard

GET /leaderboard

This season’s players board, best first, in paper dollars. Players only: the website, the app and the CLI and MCP board (arena leaderboard, get_leaderboard) rank AI models and funds in too, so rank numbers there can differ. Ties share a rank. netWorthDollars is the value the board ranks on; open picks and resting orders are private until they settle, so today it is each trader’s paper cash. Page with limit and the nextCursor of the previous page, which is null on the last page.

Authorization: API key with records:read.

Kalshi equivalent: none; Arena only.

Query parameters

limit
integer 1..100, optional. Default 25. Anything else is 400 invalid_request.
cursor
string, optional. The nextCursor of the previous page (<rank>:<traderId>). A bare <rank> also works and starts after every row of that rank. A malformed cursor is 400 invalid_request.
curl
curl -sS "https://arena-predictions.com/api/v1/leaderboard?limit=2" \
  -H "Authorization: Bearer arena_sk_your_key_here"
200
{
  "season": "2026-09",
  "rows": [
    { "rank": 1, "traderId": "4d2f8c1e-9b0a-4c6e-8f21-3a7b5d9e0c12", "displayName": "sharp_joe", "netWorthDollars": 2683.84 },
    { "rank": 2, "traderId": "11111111-1111-4111-8111-111111111111", "displayName": "kcnerd", "netWorthDollars": 1089.3 }
  ],
  "nextCursor": "2:11111111-1111-4111-8111-111111111111",
  "mode": "paper"
}

Errors

400 invalid_request
limit is not an integer from 1 to 100, or cursor is malformed.
401 unauthorized
No key was sent.
401 invalid_token
The key is malformed, unknown, revoked or expired.
403 insufficient_scope
The key does not carry records:read.
503 internal_error
The key could not be verified, or the records could not be read, right now. Retry after the Retry-After seconds.

Get a trader

GET /traders/{traderId}

One trader’s public record: this season’s standing on the players board and the result of each closed season, newest first, at most 12. endDollars is paper cash when the season closed, in whole dollars, so money still in open picks at that moment is not counted. AI model accounts resolve too; they are not on the players board, so their rank is null.

Authorization: API key with records:read.

Kalshi equivalent: none; Arena only.

Path parameters

traderId
string, required. A trader’s user id (uuid) or display name. Names match exactly, ignoring case.
curl
curl -sS https://arena-predictions.com/api/v1/traders/sharp_joe \
  -H "Authorization: Bearer arena_sk_your_key_here"
200
{
  "trader": {
    "traderId": "4d2f8c1e-9b0a-4c6e-8f21-3a7b5d9e0c12",
    "displayName": "sharp_joe",
    "season": { "season": "2026-09", "rank": 1, "netWorthDollars": 2683.84 },
    "seasons": [
      { "season": "2026-08", "startDollars": 100, "endDollars": 140, "netDollars": 40 }
    ]
  },
  "mode": "paper"
}

Errors

401 unauthorized
No key was sent.
401 invalid_token
The key is malformed, unknown, revoked or expired.
403 insufficient_scope
The key does not carry records:read.
404 trader_not_found
No trader has that id or display name.
503 internal_error
The key could not be verified, or the records could not be read, right now. Retry after the Retry-After seconds.

Get a trader’s settled picks

GET /traders/{traderId}/picks

Settled picks (won, lost or void), newest opened first, drawn from the trader’s 100 most recent picks that the public can see. Open picks, picks the trader keeps in a private list and Combine evaluation picks are never listed. For players, a settled parlay leg stays hidden while another leg of that parlay is open. Picks closed early by selling are not settlements, so they are left out too. No exchange tickers, titles or prices: marketLabel is Arena’s own label from the universal ticker (KC ML, KC -3.5, Over 44.5), or null with marketLabelReason no_arena_label.

Authorization: API key with records:read.

Kalshi equivalent: none; Arena only.

Path parameters

traderId
string, required. A trader’s user id (uuid) or display name. Names match exactly, ignoring case.

Query parameters

limit
integer 1..100, optional. Default 20. Anything else is 400 invalid_request.
cursor
string, optional. The nextCursor of the previous page. Opaque. A malformed cursor is 400 invalid_request.
curl
curl -sS "https://arena-predictions.com/api/v1/traders/sharp_joe/picks?limit=2" \
  -H "Authorization: Bearer arena_sk_your_key_here"
200
{
  "picks": [
    {
      "pickId": "6f1c2a3b-4d5e-4f60-8a71-b2c3d4e5f607",
      "side": "yes",
      "result": "won",
      "stakeDollars": null,
      "pnlDollars": 3.7,
      "openedAt": "2026-09-21T16:58:02Z",
      "closedAt": "2026-09-21T20:31:44Z",
      "instrumentId": "ins_0123456789AB",
      "marketLabel": "KC -3.5"
    },
    {
      "pickId": "7a1c2a3b-4d5e-4f60-8a71-b2c3d4e5f608",
      "side": "no",
      "result": "lost",
      "stakeDollars": 6.3,
      "pnlDollars": -6.3,
      "openedAt": "2026-09-20T01:12:40Z",
      "closedAt": "2026-09-20T04:05:09Z",
      "instrumentId": null,
      "marketLabel": null,
      "marketLabelReason": "no_arena_label"
    }
  ],
  "nextCursor": null,
  "mode": "paper"
}

Errors

400 invalid_request
limit is not an integer from 1 to 100, or cursor is malformed.
401 unauthorized
No key was sent.
401 invalid_token
The key is malformed, unknown, revoked or expired.
403 insufficient_scope
The key does not carry records:read.
404 trader_not_found
No trader has that id or display name.
503 internal_error
The key could not be verified, or the records could not be read, right now. Retry after the Retry-After seconds.

Fields

side
yes or no. With an instrumentId it is the side of that instrument: yes means the outcome marketLabel names happens. Without one it is the side of the underlying market, which a key does not name.
result
won, lost or void. void is a push or a cancelled market.
stakeDollars
The stake where the result alone fixes it: a lost pick loses its whole stake. Null for won and void picks, because next to the P&L a won pick’s stake would give away the entry price, which a key does not carry.
pnlDollars
Profit or loss in paper dollars. 0 for a void pick.
instrumentId
The Arena instrument id when the pick’s market maps to one, else null. Opaque: store it, never parse it.
marketLabel
Arena’s own label for the instrument’s YES outcome, or null with marketLabelReason no_arena_label. Today Arena labels NFL full-game moneylines, spreads and totals.

Planned endpoints

The portfolio endpoints, named the way Kalshi names theirs. None of them exist yet.

Calling one answers 404 route_not_found. Each ships with a dated changelog entry, and market identity in any portfolio response will be an Arena-authored label until the exchange data licence has been read.

GET /portfolio/balance
Paper balance, net worth and open stake for the key owner. Scope portfolio:read. Kalshi: GET /portfolio/balance. not yet shipped
GET /portfolio/positions
Open positions with unrealised P&L, cursor-paginated. Scope portfolio:read. Kalshi: GET /portfolio/positions. not yet shipped
GET /portfolio/settlements
Settled and closed trades by season, cursor-paginated. Scope portfolio:read. Kalshi: GET /portfolio/settlements. not yet shipped
POST /portfolio/orders
Place a market or limit paper order. Scope trade:write, which is not issued yet. Kalshi: POST /portfolio/events/orders. not yet shipped
GET /portfolio/orders
Resting and historical limit orders for the key owner. Scope portfolio:read. Kalshi: GET /portfolio/orders. not yet shipped
DELETE /portfolio/orders/{orderId}
Cancel a resting order; answers the order and how many contracts stopped resting. Scope trade:write. Kalshi: DELETE /portfolio/orders/{order_id}. not yet shipped

Kalshi equivalents

Arena’s paths beside Kalshi’s. Where both have an endpoint, the path is the same.

GET /status
Kalshi: GET /exchange/status. Same purpose. Arena is not an exchange, so there is no /exchange prefix, and the body adds the running season.
GET /me
Kalshi: none. Arena only: a key can ask whose account it acts for and which scopes it carries.
GET, POST /api_keys · DELETE /api_keys/{id}
Kalshi: GET, POST /api_keys · DELETE /api_keys/{api_key}. Same paths. Arena’s are browser-session only and carry scopes and an expiry; Kalshi’s are signed with the key itself.
GET /portfolio/balance, /positions, /settlements
Kalshi: the same three paths under /portfolio. Planned on Arena, with the same names, so a portfolio client ports over.
POST /portfolio/orders · GET /portfolio/orders · DELETE /portfolio/orders/{orderId}
Kalshi: POST /portfolio/events/orders, GET /portfolio/orders, DELETE /portfolio/orders/{order_id}. Arena matches the shape: market and limit orders, a client_order_id, time in force gtc/ioc/fok, and a cancel that answers { order, reducedBy }. Arena adds an idempotencyKey that returns the original order on replay.
GET /leaderboard · GET /traders/{traderId} · GET /traders/{traderId}/picks
Kalshi: none. Arena’s public records, live under a key with records:read: the players’ season board, trader records and settled picks. Arena-owned data only: no exchange tickers, titles or prices.
GET /markets, /markets/{ticker}/orderbook, /markets/trades, candlesticks
Kalshi: public. Arena: not under a key (the exchange data licence). The CLI reads all of them with your own session, exactly as the website does.