Polymarket Market Data API & MCP Server
Four REST endpoints and a hosted MCP server over the same data set: 28 Polymarket up/down markets (7 assets × 4 timeframes), tick-level, 90 days of history. Requires a Premium plan. Base URL https://www.updowncharts.com. See what an up/down market is for the underlying market mechanics.
Authentication
Create a key at /dashboard/settings (shown once, prefix udc_). Send it as a Bearer token on every request. The key resolves to a user, the user resolves to a tier, and the tier must be Premium — keys held by Free or Pro accounts authenticate but are rejected with 403.
curl -H "Authorization: Bearer udc_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ https://www.updowncharts.com/api/v1/markets
Revoking a key at any time invalidates it immediately. Every authenticated request updates the key's last_used_at.
Rate limits & errors
Premium keys are limited to 100 requests per minute and 50,000 requests per day, counted per key. Successful responses carry X-RateLimit-Remaining-Minute and X-RateLimit-Remaining-Day.
| Status | Body | When |
|---|---|---|
| 400 | {"error":"invalid_market","code":400} | Unknown asset or timeframe. |
| 400 | {"error":"invalid_slug","code":400} | Slug does not match the canonical pattern. |
| 401 | {"error":"invalid_key","code":401} | Missing, malformed, revoked or unknown key. |
| 403 | {"error":"api_requires_premium","code":403} | Valid key on a Free or Pro account. |
| 404 | {"error":"not_found","code":404} | No data for that slug or market. |
| 429 | {"error":"rate_limited","code":429} | Minute or day quota exhausted. Retry-After holds the seconds left in the current minute window. |
GET /api/v1/markets
Returns the full catalog of 28 markets plus the tier your key resolves to. The catalog is static configuration, so this endpoint is the cheapest way to discover valid asset and timeframe values before calling anything else. The same catalog is browsable at /markets.
{
"markets": [
{
"key": "btc-5m",
"asset": "btc",
"symbol": "BTC",
"displayName": "Bitcoin",
"timeframe": "5m",
"durationSeconds": 300,
"label": "5M"
},
{
"key": "btc-15m",
"asset": "btc",
"symbol": "BTC",
"displayName": "Bitcoin",
"timeframe": "15m",
"durationSeconds": 900,
"label": "15M"
}
],
"tier": "premium"
}Takes no parameters. Errors: 401, 403, 429.
GET /api/v1/epochs
Paginated epoch history for one market, newest first. Each row is one settled or open window with its price to beat, close, outcome and final volume. Pagination is keyset based on the epoch number: pass the returned nextCursor back as cursor to get the next page. nextCursor is null on the last page.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| asset | string | no | btc | btc, eth, sol, xrp, doge, bnb, hype. An unrecognised value returns 400 invalid_market. |
| timeframe | string | no | 15m | 5m, 15m, 1h, 4h. An unrecognised value returns 400 invalid_market. |
| date | string (YYYY-MM-DD) | no | all dates | Restrict to one calendar date. |
| limit | integer | no | 100 | Rows per page. Clamped to 1000; non-numeric or non-positive values fall back to 100. |
| cursor | string (epoch) | no | newest | Return only epochs strictly older than this epoch number. |
curl -H "Authorization: Bearer udc_..." \
"https://www.updowncharts.com/api/v1/epochs?asset=btc&timeframe=15m&limit=50"
{
"epochs": [
{
"slug": "btc-updown-15m-1751000400",
"date": "2026-06-27",
"outcome": "UP",
"ptb": 107412.5,
"close": 107480.1,
"volume": 18422.36,
"hasTickPrices": true,
"polymarketSlug": "btc-updown-15m-1751000400"
},
{
"slug": "btc-updown-15m-1750999500",
"date": "2026-06-27",
"outcome": "DOWN",
"ptb": 107455.0,
"close": 107412.5,
"volume": 20117.02,
"hasTickPrices": true,
"polymarketSlug": null
}
],
"nextCursor": "1750999500"
}Errors: 400 invalid_market, 401, 403, 429.
GET /api/v1/ticks/{slug}
Full tick series for one epoch, returned column-wise: every array is the same length and index i across all arrays describes the same tick. Timestamps in ts are offsets in milliseconds from baseTs. Missing values are null rather than interpolated. If the epoch is the current live one and no recorded rows exist yet, the live buffer is returned instead.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| slug | string (path) | yes | — | Canonical slug {asset}-updown-{timeframe}-{epoch}, epoch exactly 10 digits. |
curl -H "Authorization: Bearer udc_..." \
https://www.updowncharts.com/api/v1/ticks/btc-updown-15m-1751000400
{
"slug": "btc-updown-15m-1751000400",
"date": "2026-06-27",
"outcome": "UP",
"ptb": 107412.5,
"close": 107480.1,
"baseTs": 1751000400000,
"ticks": {
"ts": [0, 412, 907],
"upBid": [0.49, 0.51, 0.53],
"upAsk": [0.51, 0.53, 0.55],
"downBid": [0.47, 0.45, 0.43],
"downAsk": [0.51, 0.49, 0.47],
"binance": [107412.5, 107419.8, 107433.1],
"chainlink": [107410.0, null, 107431.7],
"pyth": [107413.2, 107420.4, 107434.0],
"ptbCaptured": [107412.5, 107412.5, 107412.5],
"ptbApi": [null, 107412.5, 107412.5]
}
}Errors: 400 invalid_slug, 404 not_found (no data, including epochs older than the 90-day retention window), 401, 403, 429.
GET /api/v1/live/{asset}/{timeframe}
Latest single snapshot for the market's current epoch: most recent underlying prices, most recent top-of-book quotes, and the price to beat. ts is the newer of the two source timestamps in Unix milliseconds. The slug is derived server-side from the current wall clock, so no epoch argument is needed.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| asset | string (path) | yes | — | btc, eth, sol, xrp, doge, bnb, hype. |
| timeframe | string (path) | yes | — | 5m, 15m, 1h, 4h. |
curl -H "Authorization: Bearer udc_..." \
https://www.updowncharts.com/api/v1/live/btc/15m
{
"slug": "btc-updown-15m-1751000400",
"ts": 1751000812345,
"binance": 107419.8,
"chainlink": 107418.0,
"pyth": 107420.4,
"upBid": 0.51,
"upAsk": 0.53,
"downBid": 0.45,
"downAsk": 0.49,
"ptb": 107412.5
}Errors: 400 invalid_market, 404 not_found (no tick or quote rows yet for the current epoch), 401, 403, 429.
MCP server
A hosted Model Context Protocol server at https://www.updowncharts.com/api/mcp. Transport is Streamable HTTP and the server is stateless — every request carries the same udc_ Bearer key and is authorized before it reaches the tool layer, so the 401 / 403 / 429 responses above apply identically. GET, POST and DELETE are handled.
claude mcp add --transport http updown https://www.updowncharts.com/api/mcp --header "Authorization: Bearer udc_..."
| Tool | Arguments | Returns |
|---|---|---|
| list_markets | none | Returns { markets: [...] } — the same catalog as GET /api/v1/markets. |
| get_epochs | asset (string), timeframe (string), date (string, optional), limit (number, optional, default 100, max 1000) | Epochs newest-first for one market. No cursor argument — the tool always starts from the newest epoch. |
| get_ticks | slug (string) | Full tick arrays for one epoch slug. |
| get_live_price | asset (string), timeframe (string) | Latest snapshot for the current epoch of that market. |
| get_epoch_summary | slug (string) | Outcome, ptb, close and volume for one epoch slug. |
Tool results are JSON encoded as text content, matching the REST response shapes above. An unknown market or missing slug comes back as an error result (isError), not an HTTP error.
Field glossary
| Field | Meaning |
|---|---|
| slug | Canonical market identifier: {asset}-updown-{timeframe}-{epoch}, e.g. btc-updown-15m-1751000000. Assets and timeframes come from the catalog; epoch is exactly 10 digits. |
| epoch | Unix second at which the window opened, floored to the timeframe duration (300 / 900 / 3600 / 14400 s). The window ends at epoch + durationSeconds. |
| ptb | Price to beat — the open price of the window. Close above it resolves UP; a tie resolves DOWN. ptbCaptured is our own capture at window open; ptbApi is Polymarket’s (delayed) reported value. |
| close | Final crypto price for the window, used together with ptb to derive outcome. |
| outcome | "UP", "DOWN", or null while the epoch is still open or unresolved. |
| upBid / upAsk / downBid / downAsk | Top-of-book Polymarket CLOB quotes for the UP and DOWN outcome tokens, in probability terms (0–1). null when no quote was recorded. |
| binance / chainlink / pyth | Underlying spot price from each source at that tick. null where a source does not cover the asset (HYPE has no Binance or Chainlink series). |
| volume | Final market volume in USD, read from Polymarket at resolution. null until resolved. |
| hasTickPrices | true when tick-level rows exist for the epoch, so /api/v1/ticks/{slug} will return series data. |
| polymarketSlug | Polymarket’s own slug for the market. Identical to the canonical slug for 5m, 15m and 4h; 1h markets use an ET date slug instead (e.g. bitcoin-up-or-down-july-9-2026-10am-et). null when not yet recorded. |
Retention & data access by tier
Tick and quote tables are monthly partitions with a 90-day retention window; partitions older than 90 days are dropped daily. Epoch summaries and tick series outside that window return 404. Export what you need to keep.
| Tier | Markets | History | API / MCP |
|---|---|---|---|
| Free | BTC 15m | Live epoch only | No |
| Pro | All 28 | Full 90 days + CSV + WebSocket | No |
| Premium | All 28 | Full 90 days + CSV + WebSocket | REST + MCP, 100/min, 50,000/day |