Everything the app shows is available as JSON, Polymarket-style. Read endpoints are open (CORS *, no key). Trading and account endpoints take an API key as Authorization: Bearer hm_live_… (or X-API-Key) — or, from the app itself, your login session with credentials: "include". Start at /api/v1.
Log in to create API keys for scripts and bots.
Like Polymarket's CLOB, you pick an outcome by its token id (or market id + outcome), a side, and a size. Unlike a CLOB there is no resting order book: the house is your counterparty at Polymarket's live price ± 1 ¢, so every order is fill-or-kill — it fills in full at once or is rejected. A price works as a limit: buys fill only at or below it, sells only at or above it (409 if the market has moved).
| POST /api/v1/orders | Place an order — fills in full immediately at the mirrored price, or is rejected (fill-or-kill). See the order body below. |
| GET /api/v1/orders | Your fills, newest first · ?limit=50 ?offset=0 ?marketId= |
| GET /api/v1/orders/{id} | One fill |
| GET /api/v1/me/positions | Your positions, open and closed |
| GET /api/v1/me/keys · POST · DELETE | List / mint ({ "name" }) / revoke ({ "id" }) API keys — the secret is returned once on POST |
POST /api/v1/orders
{
"tokenId": "1234…", // or "marketId": "2252244" + "outcomeIdx": 0 | "outcome": "Yes"
"side": "BUY", // BUY | SELL (any case)
"size": 10, // shares ("shares" also accepted) …
"amount": 500, // … or coins to spend (buys only; floors to whole shares)
"price": 0.42, // optional limit: 0..1 probability or 1..99 cents
"competitionId": "…" // optional: bet with a competition pot instead of your wallet
}
→ 201 { "data": { "id": 17, "status": "filled", "marketId", "tokenId", "outcomeIdx", "outcome": "Yes",
"side": "BUY", "shares": 10, "priceCents": 43, "price": 0.43, "total": 430, "createdAt" } }
→ 409 { "error": "Price 46 ¢ is above your limit of 42 ¢ — order not filled", "status": "rejected", "priceCents": 46 }
→ 400 { "error": "Insufficient funds: have 120, need 430", "status": "rejected" }| GET /api/v1 | Index of every endpoint (machine-readable) |
| GET /api/v1/markets | List markets · ?status=open|resolving|closed|resolved|voided|all ?category= ?q= ?source=feed|search|quick ?limit=50 ?offset=0 |
| GET /api/v1/markets/soon | Open markets ending within ?minutes=180 |
| GET /api/v1/markets/live | In-play markets — open, past their scheduled start |
| GET /api/v1/markets/{id} | One market: outcomes, live bid/ask/mid, what the crowd holds, Polymarket link |
| GET /api/v1/markets/{id}/history | Price history from Polymarket · ?interval=1h|6h|1d|1w|1m|max (default max) · ?outcome=0 |
| GET /api/v1/leaderboard | Rankings · ?period=today|week (realized profit) or all (net worth) ?limit=50 |
| GET /api/v1/users/{id} | Public profile: stats, 30-day daily P&L, followers, following |
| GET /api/v1/users/{id}/positions | A player's open bets |
| GET /api/v1/users/{id}/collection | A player's cards |
| GET /api/v1/cards | Card catalog · ?league=isr|eng|esp|ita|ger|fra ?tier=normal|rare|epic|legendary ?position=GK|DF|MF|FW ?club= ?q= ?limit=100 ?offset=0 |
| GET /api/v1/cards/{id} | One card version plus its sibling versions |
| GET /api/v1/packs | Pack types with costs, per-card odds and pool sizes |
| GET /api/v1/tags | Live tag numbers · ?slugs=soccer,champions-league (open markets + bets per tag) or, without slugs, the top tags per category |
| GET /api/v1/arena/ladder | Arena ladder — Elo, W–D–L, bot record, net coins · ?limit=20 |
| GET /api/v1/me/battles | Your arena record and recent matches (Bearer key or session) |
| GET /api/v1/markets/{id}/quote | What an order would fill at right now · ?side=buy|sell ?shares=10 &outcomeIdx=0 | &tokenId= ({id} may itself be a token id) |
| GET /api/v1/me | Your wallet, stats, daily-gift and free-pack cooldown status |
| GET /api/v1/me/competitions | Your friend competitions: status, rules, your pot and rank in each |
| GET /api/v1/competitions/{id} | A competition you belong to: settings, rules, standings, everyone's open bets |
| POST /api/v1/competitions | { "name", "startingFunds", "startsAt", "endsAt", "categories"?: ["Sports"], "maxBuyPriceCents"?: 50, "maxStakeCents"? } |
| POST /api/v1/competitions/join | { "code": "ABCD2345" } — join with an invite code |
| POST /api/v1/competitions/{id}/topup | { "amount": 500 } — host only: add the same amount to every member's pot |
| GET /api/v1/me/views · POST · DELETE | Saved board filters / searches: { "name", "path": "/"|"/search", "query": "c=Sports&tags=soccer" } |
| POST /api/v1/daily | Claim today's gift |
| POST /api/v1/packs/{id}/open | Open a pack (ids from /api/v1/packs; the free one is "daily") |
Calls per minute, refilling continuously — a spent budget comes back a little at a time rather than all at once on the turn of the minute, so a burst is fine and a hot loop is not. A key counts as one caller wherever it calls from; without one you are counted by IP.
| Caller | Reads | Price history | Leaderboard | Writes |
|---|---|---|---|---|
| With an API key | 300 | 120 | 30 | 60 |
| Logged in (session) | 600 | 120 | 60 | 60 |
| Anonymous (by IP) | 120 | 60 | 20 | 20 |
Every response tells you where you stand — RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset (seconds until the budget is whole) — so a well-behaved client never has to find the wall. Over it you get a 429 with Retry-After in seconds. A CORS preflight is never counted. Price history (/markets/{id}/history) has a budget of its own because it is a live proxy to Polymarket rather than a read of our own database, and so does leaderboard, which reads every player and every open position however small a?limit you ask for. Each budget also has a burst — roughly a twentieth of it — so a client that fires its whole minute at once is throttled even though its rate is fine.
# Ending-soon markets
curl -s "http://localhost:3000/api/v1/markets/soon?minutes=60" | jq '.data[] | {question, endDate, prices}'
# Search the board
curl -s "http://localhost:3000/api/v1/markets?q=bitcoin&limit=5" | jq '.data[].question'
# Ligat Ha'Al legendaries
curl -s "http://localhost:3000/api/v1/cards?league=isr&tier=legendary" | jq '.data[] | {name, club, ovr}'
# Quote before you trade (token id or market id)
curl -s "http://localhost:3000/api/v1/markets/2252244/quote?side=buy&shares=10&outcomeIdx=0"
# Buy 10 Yes shares with an API key, but only at 45 ¢ or better
curl -s -X POST "http://localhost:3000/api/v1/orders" -H "Authorization: Bearer hm_live_…" -H "content-type: application/json" -d '{ "marketId": "2252244", "outcome": "Yes", "side": "BUY", "size": 10, "price": 0.45 }'
# Spend 500 ¢ on the No token, then sell 5 of them back
curl -s -X POST "http://localhost:3000/api/v1/orders" -H "Authorization: Bearer hm_live_…" -H "content-type: application/json" -d '{ "tokenId": "8123…", "side": "BUY", "amount": 500 }'
curl -s -X POST "http://localhost:3000/api/v1/orders" -H "Authorization: Bearer hm_live_…" -H "content-type: application/json" -d '{ "tokenId": "8123…", "side": "SELL", "size": 5 }'
# Your fills and positions
curl -s "http://localhost:3000/api/v1/orders?limit=20" -H "Authorization: Bearer hm_live_…"
curl -s "http://localhost:3000/api/v1/me/positions" -H "Authorization: Bearer hm_live_…"
# From the app (logged in), the session cookie works too
await fetch("/api/v1/orders", {
method: "POST",
credentials: "include",
headers: { "content-type": "application/json" },
body: JSON.stringify({ marketId: "2252244", outcomeIdx: 0, side: "buy", shares: 10 }),
}).then((r) => r.json());{ data, ... }; errors are { error } with a 4xx status.bid, ask, mid); order fills are reported in whole cents.wp:Erling Haaland or wp:Erling Haaland#legendary (URL-encode them).