API
Every alert on the live feed, as JSON. The endpoints are public and read-only, and need no key. Authentication, subscriptions and webhook delivery arrive with the notification phase.
Endpoints
Recent qualifying trades, newest first. The response meta carries total_archived, how many trades the archive behind /trades/history currently holds, so a client can tell whether older trades exist without spending a request to find out.
limitHow many rows, newest first.sinceAn ISO 8601 timestamp; only trades after it.refresh1 runs a poll cycle inline before responding.The paginated archive. Unlike /trades/latest, which serves a rolling buffer of recent qualifying trades, this reads every trade detected within the retention window (30 days by default), including the ones the public feed filters out. The response meta carries next_cursor (null when exhausted), has_more and total_archived.
limitPage size, newest first.cursorThe next_cursor from the previous page, passed back verbatim. Stable against new trades arriving mid-scroll.The currently tracked traders, in leaderboard rank order.
limitHow many traders, in rank order./api/v1/traders/:walletOne trader. stats holds settled performance counted from Polymarket’s closed-position records: realizedPnl, positionsClosed, wins, losses, breakeven, winRate, avgWin, avgLoss, profitFactor, biggestWin and biggestLoss, and a byGame breakdown. observed is a separate, smaller sample derived from trades this deployment has seen; do not conflate the two. winRate excludes breakeven positions from its denominator and is null when nothing has settled; profitFactor is null rather than infinite when there are no losses. truncated is true when the trader has more history than was fetched.
Everything a client needs to run its own feed: tracked wallets, the esports condition-ID index, URL templates and default filters.
Public service status: health, data freshness, measured alert latency per ingest source, and recent activity. Operator internals (storage backend, deploy target, poll plumbing) are omitted unless the token is supplied.
tokenThe operator’s ADMIN_TOKEN, here or in the X-Admin-Token header, adds a diagnostics object.Response envelope
{ "ok": true, "data": <payload>, "meta": { "generated_at": "..." } }Errors use the same envelope with { "ok": false, "error": { "code", "message" } }.
Event schema
One normalized shape, independent of both Polymarket’s wire format and any notification provider. It is what the webhook body will carry unchanged, and it is the row you see on the feed: the same side, size and rank, drawn here the way the tape draws them.
{
"event_id": "9f2c1ab4e7d0435c8a61f0d3c72b19ee",
"event_type": "new_trade",
"timestamp": "2026-08-22T06:32:13.000Z",
"trader": {
"username": "BOOMBOYS.Kiritych",
"wallet": "0xcd30f4698c6f5f3829893e68e183a8e5ea18f316",
"rank": 1,
"monthly_profit": 1525246.07,
"profile_url": "https://polymarket.com/profile/0xcd30…f316",
"profile_image": null
},
"market": {
"id": "0x72f93cc5347291e42699dc9db76a38e184a00a088b368fe0a71148148d0260b4",
"title": "Dota 2: Nigma Galaxy vs BoomBoys - Game 2 Winner",
"slug": "dota2-ngx-boombo-2026-08-22-game2",
"event_slug": "dota2-ngx-boombo-2026-08-22",
"category": "esports",
"game": "dota2",
"game_label": "Dota 2",
"game_short": "DOTA 2",
"game_source": "title",
"url": "https://polymarket.com/event/dota2-ngx-boombo-2026-08-22/dota2-ngx-boombo-2026-08-22-game2",
"icon": null
},
"trade": {
"side": "BUY",
"outcome": "BoomBoys",
"outcome_index": 1,
"price": 0.4767867447,
"size_usd": 1131.08548,
"shares": 2311.83,
"transaction_hash": "0x93010113387bdefb7b7c162b0c2eae2f05013b267db149af3acd95019ae83816",
"asset": "42974820668140481674546125062447708867633239207219559804172577672111834920324"
},
"timing": {
"source_timestamp": "2026-08-22T06:32:13.000Z",
"detected_timestamp": "2026-08-22T06:32:14.180Z",
"detection_latency_ms": 1180,
"ingest_source": "stream"
}
}Field notes
outcome- The real outcome label. Esports markets use team names, not YES/NO; do not assume a binary vocabulary.
price- Always the canonical probability in [0,1], whatever odds format the site is displaying. The format selector is a display preference only; it never changes a stored event or an API payload. Convert client-side: decimal is
1 / price, American is-100·p/(1-p)forp ≥ 0.5and100·(1-p)/pbelow it, fractional is(1-p)/p. Those conversions are exact: a Polymarket share settles at $1, so the price is the implied probability, unlike a sportsbook price, which carries margin. size_usd- From Polymarket’s
usdcSizewhen available; otherwiseshares × price. categoryesportsonly when the market was found in the esports index; otherwiseunknown. Never guessed.game- Derived, not supplied by Polymarket: its per-game tags are not attached to match markets, so the game is parsed from the title and slug.
game_sourcesays how.titleandslugmean Polymarket named the game outright;leaguemeans it was inferred from a tournament name (“IEM Cologne” is Counter-Strike);nonemeans unclassified, in which casegameis"other". Never treatleagueas authoritative. A market withgame: "other"that is in the esports tag index carriesgame_label: "Esports": confirmed esports, unidentified title. detection_latency_msnullwhen it cannot be measured reliably, rather than estimated.event_id- Derived from trade content, so the same trade seen via the stream and via a poll produces the same id. Safe to use as an idempotency key.
The public feed excludes markets that are not identifiably esports: a market qualifies if the classifier names a game or its condition ID is under Polymarket’s esports tag. /trades/history is unfiltered and still returns them.
Examples
curl -s https://alpha.box/api/v1/trades/latest?limit=10 | jqconst res = await fetch('https://alpha.box/api/v1/trades/latest?limit=25');
const { data } = await res.json();
for (const event of data) {
console.log(
event.trader.username,
event.trade.side,
event.trade.outcome,
`$${event.trade.size_usd.toFixed(0)}`,
event.market.title,
);
}import requests
r = requests.get("https://alpha.box/api/v1/trades/latest", params={"limit": 25})
for event in r.json()["data"]:
print(
event["trader"]["username"],
event["trade"]["side"],
event["trade"]["outcome"],
f'${event["trade"]["size_usd"]:.0f}',
event["market"]["title"],
)Rate limits
None enforced on this deployment yet. Upstream, Polymarket’s data API allows 1,000 requests per 10 seconds; this service polls far below that. Please be reasonable until keys and quotas land.
What this API does not claim
- Executed trades only
- Unfilled orders are visible only to the trader who placed them; no public API exposes them.
- BUY is not “opened a position”
- Distinguishing open from close needs position-state tracking, which is not implemented, so events are labelled by side rather than intent.
- Win rate counts positions, not dollars
- It is counted from settled position records, not estimated, but a trader can win well under half their positions and still be far ahead if the winners are larger, which is common here. It is served beside P&L, never instead of it.
- The game is parsed, not published
- Markets whose game cannot be identified are
other, never assigned to the most likely title.