Skip to main content

Quick Start

1

Get an API Key

  1. Sign up at polysimulator.com/signin.
  2. Open the developer console at polysimulator.com/console.
  3. Click Create a free API key (on a paid plan the button names your plan) and copy the ps_live_… value shown once.
Open beta. Anyone can mint a key from the dashboard or POST /v1/keys/bootstrap. A free key issued on or after 2026-09-05 can trade: it is minted with ["read", "trade"] and places orders against a $100 API wallet that cannot be reset or topped up, capped at 100 order placements per UTC day per account. A free key issued before 2026-09-05 is read-only (["read"]) — trade is stripped on every request even if the key was stored with it; create a new key to get a trade-capable one. Paid Pro / Pro+ keys trade against a $10,000 / $25,000 API wallet with no daily cap. See API Keys and Authentication.
The full key is shown only once. Save it to your password manager or a secret store immediately — only the SHA-256 hash is retained server-side, so we can’t show it again later.
The dashboard handles the one-time bootstrap with your signed-in Supabase session — you never see or paste a JWT. From here on every API call uses X-API-Key: ps_live_....
Your first API key requires one browser sign-in. Sign in from any device, create the key in the developer console, and save it in your CI secret store. The machine running your job does not need a browser after that.This API does not provide a browserless sign-in flow. The bootstrap endpoint can create a key when you already hold a valid sign-in session credential, but it does not issue that credential. See Authentication for the supported bootstrap and later key-management details.
2

Set Your Environment

3

Check Connectivity

Expected response:
4

Fetch Hot Markets

This returns actively traded markets with live prices from Polymarket.
5

Place Your First Trade

The fastest path is the Python SDK — it picks a live market and places a trade in a few lines. This snippet is complete and runnable as-is (it resolves a real market for you — no IDs to fill in):
Python SDK
A terminal: pip install polysimulator, then python first_trade.py prints 'filled FILLED @ 0.23 — order 102973'.

Install, run, filled — your first trade in seconds.

outcome takes the human-readable label ("Yes", "No", or custom labels like "Trump"), not Polymarket’s 77-digit token ID. To map a token ID to its outcome label, use GET /v1/markets-by-token/{token_id}.Market orders require price as a worst-price limit — Polymarket-faithful slippage protection. A BUY won’t fill above it; a SELL won’t fill below it. "0.99" on a YES means “accept any fill” (great for your first trade); for tighter control use the current best ask × 1.05.
Prefer raw HTTP? Resolve a live market_id from the markets endpoint first, then POST — these are runnable too:
Response (abridged; the full body also lists telemetry fields, most of them null on a default market order):
Check status. A market order defaults to time_in_force: "FAK": it fills what is available within your price cap and cancels the rest. If nothing can fill (for example the market closed), the response is still 200, with status: "CANCELLED" and a cancelled_reason. See Placing Orders.The response does not report your balance on this path: account_balance is null. Read it with GET /v1/account/balance. Orders trade against your API wallet, not the dashboard MAIN wallet. API wallets start at $100 (Free, keys issued on or after 2026-09-05 — non-renewable), $10,000 (Pro) or $25,000 (Pro+); a free key issued before 2026-09-05 is read-only and cannot place this order. The fee follows the per-category schedule in Trading Fees (this market’s category charges none).Your price cap sizes the order. Before it fills, the order reserves quantity × price from the wallet, so "0.99" on 10 shares needs $9.90 free even when the fill is at $0.22. On the $100 Free wallet, keep quantity × price under your balance.
6

Check Your Portfolio

All numeric values are strings ("10", not 10). This prevents floating-point precision loss — critical for financial applications. See String Numerics for details.

Run your first backtest

Backtesting is the reason most people come to the API, and it was reachable from here only as a link. This is the whole path, in four calls. Needs your plan’s analytics.backtesting entitlement. Every call takes X-API-Key. Production (POLYSIM_BASE_URL=https://api.polysimulator.com) and staging serve the same curated archive. A host with the simulation switch off answers 404 NOT_FOUND to every call below; see Simulation for how to tell a switched-off host from a missing key (401). Use an archived market, not the one you just traded. The archive is a curated sample: hourly order-book snapshots of about 960 markets between 2026-09-05 and 2026-09-08. The live market from the trade above is not in it, and a backtest on it is refused with 422 UNIVERSE_EXCLUDED (not charged). This walkthrough uses a soccer market with eight consecutive archived hours; Simulation lists more.

1. Check what you have left

Backtests are metered in market-hours — window length x number of markets. A 10-market, 3-day run costs 10 x 72 = 720 market-hours, not “one backtest”. Check here before a large run rather than discovering the cap mid-request.

2. Check the market has the data

Do this first. A backtest is refused outright when book coverage for the window is below the minimum (80% by default), and the refusal names the percentage. Checking coverage turns a confusing rejection into a window you can adjust. confidence and missing_periods in the response tell you which hours are thin.

3. Submit the run

Save the request body first — the next step reuses it:
Submit once. Each successful POST charges the window’s market-hours and one job against your monthly count. Re-running this block starts a second backtest; it does not re-read the first. Worth knowing before you size a run: both endpoints count. The 14:00→21:00 window above is 8 market-hours, not 7: 14:00, 15:00 … 21:00 is eight hour marks. Multiply by the number of markets. Buy 10 shares of Yes whenever it trades at or below 50c, sell at or above 60c. depth_walk walks the real order book rather than assuming you fill at the top — see Fill models for what each one assumes. outcome must be one of that market’s own outcome labels (case-insensitive). "Yes" fits a Yes/No market; a crypto up/down market’s outcomes are "Up" and "Down", and a sports market names its teams. A label the market does not have is refused with 400 INVALID_REQUEST listing the labels it does have, before anything is charged. You get back a job_id and status: "pending". The run is charged at submission; a run that fails is refunded.

4. Poll for the result

For this example the job completes with num_trades: 2. Measured on staging on 2026-10-22: a BUY of 10 Yes at 0.45 on the 15:00 book, a SELL at 0.579 on the 21:00 book, total_pnl about +1.04 after fees, 8 market-hours charged. Why the fills land one hour after the signal. Every run models the time an order takes to arrive: latency_ms, default 104 (our measured production API p50, in milliseconds). An order decided at a tick is priced on the book as it stood latency_ms later. The archive holds one book snapshot per hour, so that instant is never in it, and the order fills on the next archived book in the window, one hour later. The entry signal at 14:00 (Yes about 0.45) fills on the 15:00 book; the exit signal at 20:00 (Yes about 0.63) fills on the 21:00 book at 0.579. An order with no later book in the window does not fill, and execution.orders_refused counts it as NO_BOOK_AFTER_LATENCY. A window of a single archived hour therefore never fills at the default. "latency_ms": 0 prices each order on the book its own signal saw. The same run then fills at 14:00 and 20:00 (0.45 and 0.63, about +1.56), and the summary reports execution.signals_used_pre_decision_data_only: false: the fill used a price no real order could have reached in time. Keep the default unless you are deliberately measuring that gap.
The latency_ms model ships with the 2.0 engine, which staging (https://staging-api.polysimulator.com) runs today. Until 2.0 reaches https://api.polysimulator.com, production prices each order on its signal’s own book, so there this example gives the latency_ms: 0 result (fills at 14:00 and 20:00).
When status is completed, the summary carries total_pnl, total_return_pct, and — read these — tick_mode, ticks_evaluated, tick_degraded and tick_notice. tick_degraded: true means the run evaluated at a coarser resolution than requested, and tick_notice says why. They do not, on their own, tell you the data was complete. Those two describe the evaluation GRID; coverage describes the DATA. A window can clear the 80% coverage gate with an hour missing, and if that hour held the only entry your rules would have matched, the run completes with zero trades, tick_degraded: false, and nothing obviously wrong. Zero trades is a result to investigate, not a finding: check execution.orders_refused in the summary (why orders did not fill) and missing_periods from step 2 before concluding a strategy does not trade. Then:
settlement_scored: false in the summary means the window ended before the market resolved: any position still open was marked to its last observed price rather than to a payout. That is the normal case for a sub-interval backtest and not an error.

What’s Next?