polymarket 数据聚合 API · 接口文档
基线:https://api.wanminguo.top/quant/polymarket/v1/数据:Polymarket 5 分钟涨跌盘的结算输入——官方 Chainlink TWAP60 + 多家现货 + 盘口,2 秒一条。 覆盖 7 个市场,见下节。 ⚠️ 端点路径都带.php(如/v1/window.php)。本站没有配置 URL rewrite, 写成/v1/window会直接 404。
市场(★ 2026-09-26 起从「只有 BTC」扩到 7 个) #
?market= 短名 | slug 前缀 | 现货源数 | 官方 Chainlink TWAP60 |
|---|---|---|---|
btc(默认) | btc-updown-5m | 6 | ✅ |
eth | eth-updown-5m | 6 | ✅ |
sol | sol-updown-5m | 6 | ✅ |
xrp | xrp-updown-5m | 6 | ✅ |
doge | doge-updown-5m | 5 | ✅ |
bnb | bnb-updown-5m | 5 | ✅ |
hype | hype-updown-5m | 4(无 coinbase / kraken) | ✅ |
market 参数认四种写法,都归一化到同一个前缀:
?market=eth ?market=ETH ?market=ETH-5m ?market=eth-updown-5m
非法值 → 400 bad_market,响应体里带 markets 数组列出全部可用短名。
现货源数不是恒定的 6。 真实家数看响应里的spot_n_sources;venue_bp里出现的键就是这一拍真正取到的源。HYPE 只有 4 家, 做「多源中位」类信号时请按实际家数判断,不要写死 6。 官方读数 7 个市场都有,所以price_to_beat的beat_source正常都是official;少数窗口官方流没进来时会退化成computed,用beat_trusted判断。
鉴权 #
每个 /v1/* 请求都要带 key:
X-Api-Key: pm_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
也支持 Authorization: Bearer <key>;?api_key= 能用但不推荐(会进 access log)。
没有 key → 401 missing_api_key。
注意例外:/v1/下只有/v1/index.php(端点清单)是免鉴权的 —— 它就是给客户端自举用的,自己会在data.note里说明这一点。 其余端点一律要 key。 但free档(0 元)就能取当前窗口的完整数据(含 2 秒序列)—— 这是设计上的漏斗口,不受history_days/max_samples_per_call限制。 受限的是历史:history_days = 0的套餐只能看当前窗口。
响应信封 #
成功:
{
"ok": true,
"data": { },
"meta": {
"endpoint": "window",
"plan": "pro",
"key_prefix": "pm_live_ab12",
"quota": { "mode": "subscription", "daily_limit": null,
"used_today": 0, "remaining": null, "reset_at": null },
"credits": { "balance": 10000, "total_in": 10000, "total_used": 0 },
"limits": { "qps": 60, "daily_quota": 0, "history_days": 90,
"max_windows_per_call": 2000, "max_samples_per_call": 2000,
"features": { "samples_raw": 1, "stream": 1, "bulk": 0 } },
"feed": { "ok": true, "latest_slug": "btc-updown-5m-1790389200",
"latest_age_sec": 43 },
"server_ts": 1790389243
}
}
失败:
{ "ok": false, "error": "rate_limited",
"message": "超过每秒请求上限 20(当前第 21 次)",
"retry_after_sec": 1 }
meta.credits.balance 每次都会返回——账号额度余额(股),成交 1 股扣 1 额度。 信号订阅制下没有请求配额(不再按请求数计费),meta.quota.mode = "subscription"。
错误码
| HTTP | error | 含义 |
|---|---|---|
| 400 | missing_param / bad_slug / bad_param | 参数缺失或非法 |
| 400 | bad_market | ?market= 认不出来(响应带 markets 数组给出可用值) |
| 401 | missing_api_key | 没带 key |
| 401 | invalid_api_key | key 不存在 |
| 403 | key_revoked / key_expired / customer_inactive / plan_retired | key 或套餐已停用 |
| 403 | history_not_in_plan | 套餐不含历史(history_days = 0,当前窗口仍可访问) |
| 403 | history_depth_exceeded | 请求的窗口超出套餐可回溯天数 |
| 403 | samples_not_in_plan | 套餐不含原始样本(max_samples_per_call = 0;历史窗口的 /v1/window.php?series=1 同门槛) |
| 403 | plan_feature_denied | 套餐不含该功能开关(如 samples_raw) |
| 404 | window_not_found / settle_not_found | 没有该窗口 |
| 429 | rate_limited | 同一秒请求过多(秒级防护,20 qps 上限;稍等 1 秒重试即可) |
| 503 | feed_root_missing / no_window / billing_db_unavailable | 服务端数据/计费库不可用 |
限速相关响应头:Retry-After / X-RateLimit-Limit / X-RateLimit-Remaining。
GET /v1/window.php ★ 主力 #
当前(或指定)窗口的快照。
| 参数 | 默认 | 说明 |
|---|---|---|
market | 配置的默认市场(btc) | btc / eth / sol / xrp / doge / hype / bnb(也接受 ETH-5m 这种写法)。只在没传 slug 时起作用 —— 传了 slug 就按那个 slug 取 |
slug | 最新窗口 | 如 btc-updown-5m-1790389200 |
series | 0 | 1 = 附带时间序列。当前窗口任何套餐都能用(漏斗口);历史窗口需要样本权限(套餐 max_samples_per_call > 0) |
n | 240 | 序列最多返回点数,1~2000(超了等距抽样,末点必留) |
raw | 0 | 1 = 序列里含原始盘口字段(需要 samples_raw 功能,体积大) |
curl -H "X-Api-Key: $KEY" \
'https://api.wanminguo.top/quant/polymarket/v1/window.php'
{
"ok": true,
"data": {
"slug": "btc-updown-5m-1790389200",
"market": "btc-updown-5m",
"window_start": 1790389200, "window_end": 1790389500,
"remaining": 62.4, "last_ts": 1790389437.6, "sample_age_sec": 1.9,
"n_samples": 149,
"price_to_beat": 85397.81,
"beat_source": "official",
"beat_trusted": true,
"official": 85740.74,
"official_source": "official",
"official_ts": 1790389436.0,
"official_bp": 40.19,
"spot_composite": 85750.61,
"spot_bp": 41.34,
"momentum_bp": -19.72,
"leading_side": "UP",
"venue_bp": { "binance": 41.31, "coinbase": 40.16, "kraken": 38.05,
"okx": 41.58, "bybit": 42.02, "bitget": 41.14 },
"spot_n_sources": 6,
"computed_twap60": 85760.41,
"computed_at_start": 85929.06,
"computed_bp": 42.49,
"spot_at_start": 85900.0,
"implied_up": 0.001, "yes_bid": null, "yes_ask": 0.01,
"no_bid": 0.99, "no_ask": null, "liquidity": 14667.98,
"settled": { "outcome": "DOWN", "rules": { "A": "DOWN", "...": "..." } }
}
}
★ 关于 price_to_beat —— 请务必读这一段
市场规则写明是:
the TWAP of Bitcoin, generated by Chainlink, of the time range specified in the title … greater than or equal to the price at the beginning of that range … not according to any other sources or spot markets
所以锚点必须是官方读数。数据里有两个"起点值",语义完全不同:
| 字段 | 语义 | 能当 beat 吗 |
|---|---|---|
twap60_off_start | 开盘那一刻的官方 Chainlink 流读数 | ✅ 就是它 |
twap60_at_start | 六个平台中位价自算的 TWAP60 起点值 | ❌ |
自算值是系统性偏差,不是噪声。 本机实测(60 个窗口):
- 官方 − 自算 = 中位 −2.72 bp(区间 −3.37 ~ −1.26)
- 而 60 秒位移的中位数只有 1.83 bp
- 有 3% 的窗口,用官方 beat 与用自算 beat 得出的方向是相反的
本接口优先用官方值,并把来源如实返回:
| 字段 | 含义 |
|---|---|
price_to_beat | 锚点值 |
beat_source | official / computed / none |
beat_trusted | false = 该窗口官方读数没进来,price_to_beat 退化成自算值(偏 ~3bp,判方向不可信) |
客户侧建议:beat_trusted == false 时不要用它判方向。 (线上实测官方值覆盖率 98%,退化是低频事件。)
三个核心数
| 字段 | 含义 |
|---|---|
price_to_beat | 开盘那一刻的官方读数 |
official_bp | 官方 TWAP60 现值距 beat 的 bp —— 这就是结算方向的直接输入 |
spot_bp | 六家现货中位距 beat 的 bp —— 用来判断"官方会不会跟过来" |
momentum_bp | TWAP60 的 60 秒动量距 beat 的 bp |
leading_side = 官方现值相对 beat 偏向 UP / DOWN / FLAT。
想把"官方 vs 自算"的差异拿来做信号,就用computed_*那几个字段 (computed_twap60/computed_bp)—— 它们与官方量分开存放,不会混淆。spot_at_start是"边界点假设"的锚点,不是 price to beat,别混用。
GET /v1/index.php —— 端点清单 / 自描述 #
免鉴权 —— 这是 /v1/ 下唯一的公开端点(客户端自举用,不必先有 key)。 返回本站当前支持的端点、参数、市场清单与你的套餐限额, 适合客户端启动时自举(不用把路径硬编码在客户端里)。 带上 key 也不会错,只是没有必要。
⚠️ 本端点是全站唯一不走标准信封的:它没有data/meta两层, 所有字段都在顶层。其余/v1/*一律是{ok, data, meta}。 客户端解析时请对本页特例处理,或两种形状都兼容。
{
"ok": true,
"service": "polymarket 数据聚合 API",
"note": "除本页(端点清单)外,其余 /v1/* 端点都需要请求头 X-Api-Key。",
"auth": { "header": "X-Api-Key: pm_live_…",
"alt": "Authorization: Bearer <key>",
"query": "?api_key=<key> (不推荐,会进 access log)" },
"endpoints": [ { "path": "/v1/window.php", "desc": "…", "params": "market, slug, series, n, raw" }, … ],
"markets": [ "btc-updown-5m", "eth-updown-5m", … ],
"market_codes": [ "btc", "eth", "sol", "xrp", "doge", "hype", "bnb" ],
"response_envelope": { … },
"error_codes": { … },
"plans": [ { "code": "free", "qps": 1, "daily_quota": 500, "history_days": 1,
"max_windows_per_call": 20, "max_samples_per_call": 0 }, … ],
"health": { … },
"server_ts": 1790409000
}
字段都在顶层:service / note / auth / endpoints / markets / market_codes / response_envelope / error_codes / plans / health / server_ts。
GET /v1/settle.php —— 结算核对 #
| 参数 | 默认 | 说明 |
|---|---|---|
market | all | 只看某个市场(eth / ETH-5m / …);all 或不传 = 全部市场 |
slug | — | 单个窗口的结算记录 |
last | 20 | 最近 N 个已结算窗口 |
响应没有单独的 market 字段 —— 市场信息在每行的 slug 前缀里 (如 eth-updown-5m-1790401800 一眼可辨)。 (会在顶层返回 market 的是 /v1/history.php,别混。)
curl -H "X-Api-Key: $KEY" \
'https://api.wanminguo.top/quant/polymarket/v1/settle.php?last=5'
{ "ok": true, "data": { "count": 5, "windows": [
{ "slug": "btc-updown-5m-1790389200", "ver": "1.2.15",
"window_start": 1790389200, "outcome": "DOWN", "n_samples": 149,
"last_remaining": 0.62, "open_offset": 1.38,
"orders": 312, "used": 252, "skip": 0, "unfillable": 14,
"fam": { "boundary": {"n":14,"win":6,...}, "...": {} },
"rules": { "A":"DOWN","B":"DOWN","C":"DOWN","D":"DOWN","agree":true,
"n_eval":4, "gap_bp":0.0, "move_bp":6.307, "anchor":"spot",
"so":85594.295,"sc":85644.75,"to":85594.295,"tc":85634.44 } }
] } }
outcome= 官方结算结果。rules.so/sc/to/tc= 开盘/收盘的现货与 TWAP,你可以自己复算,不用信我。- 顶部
rounds.jsonl有重复 slug(采集器重启会重新结算同一窗口),本接口已按 slug 去重,保留最后一条。
GET /v1/history.php —— 历史窗口列表 #
| 参数 | 默认 | 说明 |
|---|---|---|
market | all | 只看某个市场(eth / ETH-5m / …);all 或不传 = 全部市场 |
from / to | 不限 | unix 秒,或 2026-09-22 这种日期串 |
limit | min(200, 套餐 max_windows_per_call) | 上限 = 套餐 max_windows_per_call(free 档默认 20) |
offset | 0 | 分页 |
compact | 0 | 1 = 只返回 slug/window_start/outcome/n_samples |
新的在前。total 是符合条件的总数。响应里带 older_available 便于翻页。
范围超限会整体 403(history_depth_exceeded),不做静默截断—— 少给数据不报错比报错更糟。
GET /v1/samples.php —— 原始 2 秒样本 #
套餐 max_samples_per_call > 0 才能用(免费档是 0)。
| 参数 | 默认 | 说明 |
|---|---|---|
slug | 必填 | |
limit | min(200, 套餐 max_samples_per_call) | 上限 = 套餐 max_samples_per_call |
offset | 0 | |
tail | 0 | 1 = 取最后 N 条(做实时最常用) |
raw | 0 | 1 = 返回原始 JSONL 字段;否则返回加工后的字段 |
GET /v1/stats.php —— 预聚合统计(免费档可用) #
返回 stats/summary.json(定长)+ 完整健康度。用来快速判断数据质量与规则表现。
套餐 #
| code | U/月 | QPS | 日配额 | 历史 | 样本/次 | 说明 |
|---|---|---|---|---|---|---|
free | 0 | 1 | 500 | 1 天 | —(0) | 当前窗口 + 1 天历史 + 汇总统计 |
basic | 29 | 5 | 20,000 | 7 天 | 200 | 加原始样本 |
pro | 129 | 60 | 500,000 | 90 天 | 2,000 | 全量 |
internal | — | 500 | 100,000,000(实际等于无限) | 3650 天 | 100,000 | 自用 |
配额按 UTC 天计,UTC 零点重置。
字段语义(和01_billing.sql的列注释一致,别猜): -daily_quota = 0⇒ 不限量(不是禁用) -history_days = 0⇒ 只能访问当前窗口,历史端点 403 -max_samples_per_call = 0⇒ 禁用原始样本(/v1/samples.php与/v1/window.php?series=1都拒)
客户端建议 #
- 每次读
meta.quota.remaining,低于 10% 就降频。 - 轮询
/v1/window.php的合理频率是 1~2 秒(数据本身就是 2 秒一条),更密没有意义。 - ★ 但先看你的日配额:
free档是 500 次/天,
2 秒轮询约 17 分钟就会打光(1 秒轮询还会撞 qps = 1)。 免费档适合"看一眼当前窗口",要持续轮询请上 basic 以上。
- 要做实时推送(WebSocket)请单独联系——目前是轮询制。
- 要的是实时信号而不是原始数据? 那是同站的另一条线,见下一节
(按成交次数计费,有现成客户端,不用自己写)。
信号订阅(另一条产品线,按成交次数计费) #
★ 2026-09-28 新增。与上面的数据 API 互不影响:鉴权用同一个X-Api-Key, 但计费单位是成交次数,且这三个端点不消耗数据套餐的daily_quota。
| 端点 | 参数 | 说明 |
|---|---|---|
GET /v1/signals.php | since, market, wait(0~15,默认 10), limit(≤50) | 长轮询取新信号。当前只开放 BTC-5m |
POST /v1/receipt.php | signal_id(必填), filled_shares(必填), requested_shares, avg_price, status, order_id, raw | 下单回执 —— 唯一扣次的地方 |
GET /v1/credits.php | ledger(≤200) | 次数余额 / 流水 / 订阅市场 / 价目表 / 当前计次口径 |
信号长什么样
{
"signal_id": "btc-updown-5m-1790526000:93",
"slug": "btc-updown-5m-1790526000", "market": "BTC-5m",
"window_start": 1790526000, "ts": 1790526549.0,
"side": "DOWN",
"entry_price": 0.68,
"limit_price": 0.73, "limit_capped": 0.73, "over_cap": false,
"price_cap": 0.85, "exec_delta": 0.05,
"base_shares": 10, "max_multiplier": 5,
"condition_id": "0x…",
"token_up": "877…", "token_down": "864…", "token_id": "864…",
"ask_sz": 40.0, "src_fill_ok": true,
"first_delivery": true
}
side是方向;entry_price是信号那一刻的 ask,limit_capped是按硬上限压过的挂单价;token_id是下单必需的(condition_id对应的 Up/Down token,服务端已解析并缓存);ask_sz是那一刻最优档的挂单量 —— 小于你要买的份数时只可能部分成交;signal_id稳定不变,客户端按它去重(重复投递是正常的,长轮询游标含边界)。
计费与执行口径(写死,别猜)
- 1 次 = 10 份成交(部分成交按
ceil(成交量/10)折算);没成交不扣;
status=paper / dry 之类的纸面回执不计次。
- 同一
(signal_id, key_id)只扣一次:重发、并发、分笔成交都幂等。 - 一把 key 只绑一个市场(当前只开放
BTC-5m),绑定后不可改。 - 余额 ≤ 0 时
/v1/signals.php返回 402no_credits(客户端会每 60 秒探一次,
充值到账后自动继续,不用重启)。
- 价目:注册送 10 次、9.9U / 300 次、19.9U / 800 次。
★ --multiplier 5 一次下单是 50 份 = 5 次,倍数越高次数烧得越快。
用现成客户端(推荐)
不用自己写:客户端在本站下载页 <https://api.wanminguo.top/download/>, 装好后 python -m finhub --key … --paper 就能跑,本地面板看信号与扣次。 国内网络下单需要出网隧道(客户端内置,按域名白名单转发、不解密你的流量; 私钥只在你本机,平台不代持、不代下单)。
⚠️ 不承诺收益。 同一套规则在真实历史(93 单 / 1.36 天)上的回测: 胜率 78.5%,而盈亏平衡胜率 = 平均成交价 74.8% —— 95% 置信区间 (69.1%~85.6%)跨过平衡点,这段历史分不出正负。 完整报告(含滑点与盘口深度的影响):<https://api.wanminguo.top/download/backtest-report.txt>
- 429 时(
rate_limited,同一秒请求过多):按Retry-After: 1退避 1 秒后重试。
订阅制下无请求配额(额度按成交股数扣),不会再有 daily_quota_exceeded。
示例客户端见 examples/pm_api_client.py。