Arena API keys API

Create, list, rotate and revoke keys, at the same path Kalshi uses. A person's session only (the website, or a signed-in CLI's session token): a key can never mint, rotate or revoke a key.

Operations 4

GET /api_keys List API keys #
POST /api_keys Create API key #
DELETE /api_keys/{id} Revoke API key #
POST /api_keys/{id}/rotate Rotate API key #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/arena-predictions-com:arena-predictions-com-api-keys-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

arena-predictions-com-api-keys-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Arena API keys API
  version: 1.15.0
  summary: 'Arena with an API key: read Arena-owned data, place paper orders with trade:write, and send live orders to your own venue account with a venue''s live-order scope.'
  description: 'Arena Predictions (arena-predictions.com) is paper trading for prediction markets at live prices, in a sandbox, and live orders through the key owner''s own venue credentials. Paper is the default on every endpoint here (mode "paper"): a paper trade fills in Arena''s sandbox and is not sent to a venue. A live order is sent only when a request says execution kalshi or poly-intl with a key made with trade:write:kalshi or trade:write:poly-intl. It goes to the key owner''s own Kalshi or Polymarket International account with the venue credentials saved in Settings: real money at the venue, under the venue''s rules and terms. Arena is not a broker and never holds your funds. Polymarket International is not for US persons, and ProphetX orders are switched off (trade:write:prophetx is not issued, so execution prophetx is refused with 403 insufficient_scope; a key that carries the scope gets 503 trading_off). Keys read by default and reach Arena-owned data only: the key''s own identity and account, the leaderboards and published trader records with their resets and settled picks. Keys are managed with the signed-in browser session, never with a key. No exchange ticker, market title, entry price or venue price appears in a paper answer or a read while the venue-data switch is off; the owner''s own portfolio carries the owner''s own paper numbers (a resting order''s limitCents, a position''s costDollars and contracts). A live order''s answer names the venue''s own market and price (ticker, priceCents). Balances carry over: every account starts with 100,000 paper dollars, and a reset (at most one every 30 days, shown on a public profile) starts a new run. 1.2.0: seasons removed (no season field anywhere), leaderboard windows and kinds, trader resets, GET /account. 1.3.0: accounts are private by default. A private trader answers private: true with every P&L and rank null (resets and busts 0) and an empty pick list; a public trader whose earlier trades are still hidden has numbersShown: false and no P&L or rank. The key owner reads their own picks (each with placedPrivate and shown) and their own eval rows (GET /me/eval) with portfolio:read. No key can make an account public. 1.4.0: keys are managed by a person''s session, on the website or as a signed-in CLI''s session token (arena keys), never by a key; keys rotate with a grace period; every keyed answer carries the plan and its counts (RateLimit-*, Arena-Quota-*, Arena-Plan) and GET /usage says where the account stands; a new scope, markets:read, reads instruments, games and quotes; GET /portfolio and /portfolio/trades read the owner''s positions, orders and trades. Venue references and venue prices are served only while the venue-data switch is on (it ships off): until then the quotes routes answer 403 venue_data_off and nothing keyed names a ticker or a venue''s price. 1.5.0: GET /eval, the public eval board (records:read), each row with 95% intervals clustered by game. 1.6.0: GET /traders/{traderId}/track-record, a trader''s Ed25519-signed 90-day record exactly as Arena signed it (records:read; it names Kalshi series inside its signed bytes, so it is served only while the venue-data switch is on), and tiedWithAbove on each eval row (null while this row''s or the row above''s interval is unknown). 1.7.0: a key created with trade:write (only when asked for by name: the Settings checkbox, arena keys create --trade, or scopes in the body) places paper orders with POST /portfolio/orders and cancels them with DELETE /portfolio/orders/{orderId}, at Kalshi''s paths. Market and limit orders, buys by instrument or ticker and sells of a whole position, each with an idempotencyKey: the same key again answers the stored order with replayed: true and places nothing. Hard trade limits are always enforced: per key 10 new orders a minute and 50 a day, and 20 sells and cancels a minute; per account, across every key and connected app, 20 new orders a minute, 100 a day, and 40 sells and cancels a minute.'
  termsOfService: https://arena-predictions.com/terms
  contact:
    name: Arena
    email: support@zbgcllc.com
    url: https://arena-predictions.com/support
servers:
- url: https://arena-predictions.com/api/v1
  description: The Arena API. Paper trading, Arena's sandbox, is the default on every account. An order is sent to a venue only when the request says execution and the key carries that venue's scope.
tags:
- name: API Keys
  description: 'Create, list, rotate and revoke keys, at the same path Kalshi uses. A person''s session only (the website, or a signed-in CLI''s session token): a key can never mint, rotate or revoke a key.'
paths:
  /api_keys:
    get:
      tags:
      - API Keys
      operationId: listApiKeys
      summary: List API keys
      description: 'Every key on the signed-in account, newest first, including revoked, expired and rotating ones, each with this month''s requests, and the account''s plan. status=active keeps the keys that still work (active and rotating). Not paginated: an account holds at most 10 active keys.'
      security:
      - session: []
      - sessionBearer: []
      parameters:
      - name: status
        in: query
        required: false
        schema:
          type: string
          enum:
          - all
          - active
          default: all
      responses:
        '200':
          description: The account's keys.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/KeyListResponse'
        '400':
          description: 'invalid_request: status is not all or active.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: 'unauthorized (not signed in) or session_required (an API key was presented: a key never manages keys).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: 'guest_account: the session is anonymous. Sign in with Google or Apple.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Could not load the keys.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    post:
      tags:
      - API Keys
      operationId: createApiKey
      summary: Create API key
      description: 'Mint a key. The plaintext token is returned once, in this response, and never again. Not idempotent: every call mints a new key. At most 20 keys (created or rotated) per account in any 24 hours.'
      security:
      - session: []
      - sessionBearer: []
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/KeyCreateRequest'
      responses:
        '201':
          description: Created.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/KeyCreateResponse'
        '400':
          description: 'invalid_request: a cookie-session request that is not same-origin (Sec-Fetch-Site must be same-origin, or Origin must equal this site), Content-Type is not application/json, the body is not an object, label is not a string, scopes is malformed or names an unknown scope, or expiresInDays is out of 1..365.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: unauthorized (not signed in) or session_required (an API key was presented).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: guest_account (anonymous session), or scope_not_available (trade:write requested while trading with keys is switched off).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: 'key_limit_reached: ten active keys already. Revoke one first.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: 'key_creation_limited: 20 keys made in the last 24 hours. Retry-After says when the oldest turns a day old.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers:
            Retry-After:
              $ref: '#/components/headers/Retry-After'
        '500':
          description: Could not create the key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /api_keys/{id}:
    delete:
      tags:
      - API Keys
      operationId: revokeApiKey
      summary: Revoke API key
      description: 'Revoke one key (a rotating key too, which ends its grace). Takes effect on the next request that presents it. Safe to repeat: a second call answers 404 key_not_found, which means it is already gone.'
      security:
      - session: []
      - sessionBearer: []
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
        description: The key's id from the list.
      responses:
        '200':
          description: Revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/KeyRevokeResponse'
        '400':
          description: 'invalid_request: a cookie-session request that is not same-origin (Sec-Fetch-Site must be same-origin, or Origin must equal this site), or the id is not a uuid.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: unauthorized (not signed in) or session_required (an API key was presented).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: 'guest_account: the session is anonymous.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: 'key_not_found: no active key with that id on this account (already revoked, or never yours).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Could not revoke the key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /api_keys/{id}/rotate:
    post:
      tags:
      - API Keys
      operationId: rotateApiKey
      summary: Rotate API key
      description: 'Replace a key: a new key with the same label and scopes (its token shown once in this response), and the old key kept working for graceSeconds (0 to 86400; 0 revokes it at once). One transaction. A rotating key does not count toward the 10-key cap; a rotation counts toward the 20 keys a day.'
      security:
      - session: []
      - sessionBearer: []
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/KeyRotateRequest'
      responses:
        '201':
          description: Rotated.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/KeyRotateResponse'
        '400':
          description: 'invalid_request: not same-origin (cookie session), not JSON, the id is not a uuid, graceSeconds is out of 0..86400, or expiresInDays is out of 1..365.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: unauthorized (not signed in) or session_required (an API key was presented).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: guest_account, or scope_not_available (the key carries trade:write while trading with keys is switched off).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: 'key_not_found: not an active key of this account (revoked, expired, already rotated, or not yours).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: 'key_creation_limited: 20 keys made in the last 24 hours.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers:
            Retry-After:
              $ref: '#/components/headers/Retry-After'
        '500':
          description: Could not rotate the key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
components:
  schemas:
    KeyRevokeResponse:
      type: object
      required:
      - key
      - revoked
      properties:
        key:
          $ref: '#/components/schemas/KeyView'
        revoked:
          type: boolean
          const: true
    KeyCreateResponse:
      type: object
      required:
      - key
      - token
      - warning
      properties:
        key:
          $ref: '#/components/schemas/KeyView'
        token:
          type: string
          description: The plaintext key. Shown once; only its hash is stored.
        warning:
          type: string
    KeyRotateRequest:
      type: object
      properties:
        graceSeconds:
          type: integer
          minimum: 0
          maximum: 86400
          default: 0
          description: How long the old key keeps working. 0 revokes it at once.
        expiresInDays:
          type: integer
          minimum: 1
          maximum: 365
          description: 'The new key''s life. Omitted: the same life as the old key (at most 365 days), or none when the old key had none.'
    Error:
      type: object
      required:
      - error
      properties:
        error:
          type: object
          required:
          - code
          - message
          properties:
            code:
              type: string
              description: Stable machine token. Branch on this, never on message.
              enum:
              - invalid_request
              - unauthorized
              - invalid_token
              - insufficient_scope
              - guest_account
              - scope_not_available
              - key_limit_reached
              - key_not_found
              - trader_not_found
              - account_not_found
              - route_not_found
              - method_not_allowed
              - internal_error
              - session_required
              - key_creation_limited
              - rate_limited
              - quota_exceeded
              - venue_data_off
              - consolidated_book_off
              - instrument_not_found
              - game_not_found
              - membership_required
              - trading_off
              - kalshi_key_missing
              - kalshi_rejected
              - poly_intl_key_missing
              - poly_intl_rejected
              - us_not_eligible
              - prophetx_key_missing
              - prophetx_rejected
              - not_configured
              - live_trading_unavailable
              - idempotency_key_reused
              - insufficient_balance
              - market_not_found
              - no_tradable_listing
              - market_not_open
              - market_resolved
              - no_liquidity
              - price_moved
              - trade_not_found
              - already_closed
              - order_not_found
              - organization_not_found
              - organization_forbidden
            message:
              type: string
              description: Human sentence. May change.
            retryAfterSeconds:
              type: integer
              minimum: 1
              description: 'On every 429: the same number as the Retry-After header.'
    KeyCreateRequest:
      type: object
      properties:
        label:
          type: string
          maxLength: 64
          description: Trimmed; an empty label becomes "Untitled key".
        scopes:
          type: array
          minItems: 1
          items:
            type: string
            enum:
            - records:read
            - portfolio:read
            - markets:read
            - trade:write
            - trade:write:kalshi
            - trade:write:poly-intl
            - trade:write:prophetx
          description: 'Defaults to records:read, portfolio:read and markets:read. trade:write is never a default: name it to place and cancel paper orders. While trading with keys is switched off it is refused (403 scope_not_available). trade:write:kalshi and trade:write:poly-intl are never defaults either: name one to send orders to your own venue account with the venue credentials saved in Settings (each is refused with 403 scope_not_available while its venue''s orders are switched off). trade:write:prophetx is not issued: ProphetX orders are switched off.'
        expiresInDays:
          type: integer
          minimum: 1
          maximum: 365
          description: Omit for a key that never expires.
    KeyRotateResponse:
      type: object
      required:
      - key
      - token
      - warning
      - replaced
      properties:
        key:
          $ref: '#/components/schemas/KeyView'
        token:
          type: string
          description: The new plaintext key. Shown once; only its hash is stored.
        warning:
          type: string
        replaced:
          $ref: '#/components/schemas/KeyView'
    KeyListResponse:
      type: object
      required:
      - keys
      - count
      - usage
      properties:
        keys:
          type: array
          items:
            $ref: '#/components/schemas/KeyView'
        count:
          type: integer
          minimum: 0
        usage:
          type:
          - object
          - 'null'
          description: The account's plan and this month's requests, or null when they could not be read.
          properties:
            tier:
              type: string
              enum:
              - none
              - basic
              - builder
              - desk
              - enterprise
            month:
              type: object
              properties:
                limit:
                  type: integer
                used:
                  type: integer
                resetsAt:
                  type:
                  - string
                  - 'null'
                  format: date-time
            minuteLimit:
              type: integer
            enforced:
              type: boolean
    KeyView:
      type: object
      required:
      - id
      - label
      - tokenPrefix
      - scopes
      - status
      - createdAt
      - lastUsedAt
      - expiresAt
      - revokedAt
      - createdVia
      - rotatedFrom
      - rotatedAt
      - requestsThisMonth
      properties:
        id:
          type: string
          format: uuid
        label:
          type: string
          maxLength: 64
        tokenPrefix:
          type: string
          description: First 16 characters of the token, for matching a row to a key. Never the token.
        scopes:
          type: array
          items:
            type: string
            enum:
            - records:read
            - portfolio:read
            - markets:read
            - trade:write
            - trade:write:kalshi
            - trade:write:poly-intl
            - trade:write:prophetx
        status:
          type: string
          enum:
          - active
          - rotating
          - revoked
          - expired
          description: 'rotating: replaced by a rotation and still inside its grace period (it works until expiresAt).'
        createdAt:
          type: string
          format: date-time
        lastUsedAt:
          type:
          - string
          - 'null'
          format: date-time
          description: Updated at most once an hour.
        expiresAt:
          type:
          - string
          - 'null'
          format: date-time
        revokedAt:
          type:
          - string
          - 'null'
          format: date-time
        createdVia:
          type: string
          enum:
          - web
          - cli
          description: 'web: Settings. cli: arena keys.'
        rotatedFrom:
          type:
          - string
          - 'null'
          format: uuid
          description: The key this one replaced by rotation.
        rotatedAt:
          type:
          - string
          - 'null'
          format: date-time
          description: When this key was rotated.
        requestsThisMonth:
          type: integer
          minimum: 0
          description: Keyed requests counted against this key this month (UTC).
  headers:
    Retry-After:
      description: Seconds to wait before trying again.
      schema:
        type: integer
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      bearerFormat: arena_sk_ + 43 base64url characters
      description: 'Authorization: Bearer arena_sk_... (the x-arena-key header is accepted too). Keys are made in Settings (https://arena-predictions.com/settings#api-keys) or with `arena keys create`, shown once, stored hashed.'
    session:
      type: apiKey
      in: cookie
      name: sb-access-token
      description: The signed-in browser session (Google or Apple). Used only for key management, same-origin JSON requests only. An API key presented here is refused (401 session_required).
    sessionBearer:
      type: http
      scheme: bearer
      bearerFormat: Supabase access token (JWT) of a signed-in person
      description: 'A signed-in person''s session token, as the CLI sends it after arena login. Key management checks it with the auth server (JSON bodies required; an arena_sk_ key there is 401 session_required). POST /backtest, POST /evals, GET /runs and GET /runs/{run_id} take it in place of a key and forward it to the backtest function, which checks it with the auth server. A bearer that starts with arena_sk_ is always read as a key. Never a guest session (403 guest_account) or a connected app''s OAuth token (401 session_required). Every other keyed route answers it 401 unauthorized: that route takes an API key.'
externalDocs:
  description: Arena API docs
  url: https://docs.arena-predictions.com