Polymarket WebSocket API 是流式传输层,它向已连接的客户端推送市场数据(订单簿、价格、交易)和私有账户数据(订单、持仓、余额),无需反复轮询 REST 接口。它围绕独立的市场端点和私有端点组织,每个端点都有自己的订阅类型和身份验证要求。本文解释其心智模型,展示可运行的客户端代码,说明如何验证数据流确实处于活跃状态,并涵盖生产环境中至关重要的故障模式。具体的端点和提供商选择属于 RPC Assistant 的范畴;本页重点介绍流式集成的工作原理以及如何衡量它。
Polymarket WebSocket API 的用途
Polymarket WebSocket API 为市场信息和私有用户数据提供实时流式传输。客户端无需定时轮询 REST 端点,而是打开一个持久连接并订阅自己关心的数据流。随后服务器在更新发生时推送更新。当你需要低延迟地获取订单簿变化、交易通知或账户状态变化时,这就是正确的模型。
该 API 分为两个逻辑层面。市场流承载公开数据,例如完整订单簿快照、轻量价格数据和交易。私有流承载账户范围内的数据,例如订单更新、持仓变化和余额更新。私有流需要身份验证;在文档所述的美国 API 模型中,市场流是公开的,但根据你所集成的部署环境,仍可能需要 API 密钥请求头。
本文假设你已拥有 Polymarket API 密钥或 SDK 凭据,知道要跟踪的市场 slug 或代币标识符,并且能够在 Node.js 或 Python 中运行 WebSocket 客户端。本文不假设任何特定的托管提供商。如果你仍在为延迟敏感的机器人选择基础设施,RPC 端点服务概览和专用节点选项是相关的起点。
- 当你需要持续的订单簿或交易更新时,使用 WebSocket。
- 一次性查询、历史数据和数据核对使用 REST。
- 将私有订阅和公开订阅放在不同的连接上。
连接模型与端点结构
文档所述的 Polymarket WebSocket 部署为私有流和市场流暴露了不同的路径。美国文档描述了 wss://api.polymarket.us/v1/ws/private 和 wss://api.polymarket.us/v1/ws/markets 下的端点。其他文档和社区参考资料描述了位于 wss://ws-subscriptions-clob.polymarket.com/ws 的 CLOB 订阅端点。请将确切的主机和路径视为特定于部署,并在编写生产代码之前,对照你所针对环境的文档进行确认。
重要的架构要点不是字面上的 URL,而是关注点分离。私有流按用户进行身份验证,承载订单、持仓和余额事件。市场流是共享的,承载订单簿、价格和交易事件。将它们混在同一个连接上会使身份验证、错误处理和重连逻辑变得复杂。请将它们分开。
# Conceptual connection layout (confirm exact hosts in current docs)
# Private stream: orders, positions, balances
wss://api.polymarket.us/v1/ws/private
# Market stream: order book, prices, trades
wss://api.polymarket.us/v1/ws/markets
# Handshake headers for authenticated streams
X-PM-Access-Key: <your-api-key-id>
X-PM-Timestamp: <timestamp-in-milliseconds>
X-PM-Signature: <base64-encoded-signature>身份验证与签名握手
私有 WebSocket 连接使用与 REST API 相同的 API 密钥身份验证模型。文档所述的方法在 WebSocket 握手期间传递三个请求头:访问密钥 ID、毫秒时间戳和 base64 编码的签名。签名由时间戳、HTTP 方法和请求路径构造而成。对于私有流,方法是 GET,路径是 /v1/ws/private;对于市场流,路径是 /v1/ws/markets。
由于签名包含时间戳,时钟偏差是一个真实的故障模式。如果你的主机时钟发生漂移,即使你的密钥有效,服务器也可能拒绝握手。请保持机器与 NTP 同步,并在任何握手失败时记录你发送的时间戳。签名必须基于你连接的确切路径计算,因此末尾斜杠或不同的主机路径都会导致不匹配。
- 对
timestamp + "GET" + path进行签名,而不是完整 URL。 - 使用毫秒时间戳并保持主机时钟同步。
- 切勿在生产日志中记录密钥或完整签名。
订阅类型与消息格式
所有 WebSocket 消息都是使用 snake_case 字段名的 JSON。订阅请求包含唯一的 request_id、数字类型的 subscription_type,以及你要跟踪的市场 slug 或标识符。文档所述的私有订阅类型包括订单更新、持仓变化和账户余额更新。文档所述的市场订阅类型包括完整市场数据(订单簿和统计信息)、轻量价格数据和实时交易通知。
响应会回显 request_id 和 subscription_type,以便你将响应与产生它的请求关联起来。快照响应包含 eof 标志,用于指示初始快照的结束。如果订阅失败,响应会包含 error 字段。取消订阅使用原始的 request_id,这就是唯一 ID 很重要的原因:它们是在不拆除连接的情况下取消特定数据流的唯一可靠方式。
{
"subscribe": {
"request_id": "book-btc-100k-001",
"subscription_type": 1,
"market_slugs": ["btc-100k-2025"]
}
}一个验证数据流的最小 Node.js 客户端
以下客户端连接到市场流,订阅完整市场数据,并打印前几条消息。它刻意保持精简,以便你在构建完整的数据摄取管道之前确认连接性和消息结构。请将主机和路径替换为你所在环境文档中的值,并将 slug 替换为你实际想要跟踪的市场。
运行它,并留意回显你 request_id 的响应。如果你看到心跳消息但没有订阅响应,你的订阅载荷很可能格式有误。如果连接立即关闭,请检查握手请求头和主机路径。
import WebSocket from "ws";
const WS_URL = process.env.POLYMARKET_WS_MARKETS; // e.g. wss://api.polymarket.us/v1/ws/markets
const MARKET_SLUG = process.env.MARKET_SLUG || "btc-100k-2025";
const ws = new WebSocket(WS_URL, {
headers: {
"X-PM-Access-Key": process.env.POLYMARKET_KEY_ID,
"X-PM-Timestamp": String(Date.now()),
"X-PM-Signature": process.env.POLYMARKET_SIGNATURE,
},
});
let received = 0;
ws.on("open", () => {
console.log("connected");
ws.send(JSON.stringify({
subscribe: {
request_id: "book-" + MARKET_SLUG,
subscription_type: 1,
market_slugs: [MARKET_SLUG],
},
}));
});
ws.on("message", (raw) => {
const msg = JSON.parse(raw.toString());
if (msg.heartbeat) {
console.log("heartbeat");
return;
}
received += 1;
console.log("message", received, Object.keys(msg));
if (received >= 5) ws.close();
});
ws.on("error", (err) => console.error("ws error", err.message));
ws.on("close", (code, reason) => console.log("closed", code, reason.toString()));用于订单簿和交易事件的 Python 客户端
Python 客户端遵循相同的模式:连接、发送订阅载荷,并按类型处理消息。下面的示例使用 websockets 库,并演示了大多数实现容易出错的两件事:响应心跳以及区分快照消息和增量更新。
快照响应中的 eof 标志告诉你初始状态何时加载完成。只有在 eof 之后,你才应将后续消息视为增量变化。如果你在快照完成之前应用增量,你的本地订单簿将不一致。
import asyncio, json, os
import websockets
WS_URL = os.environ["POLYMARKET_WS_MARKETS"]
SLUG = os.environ.get("MARKET_SLUG", "btc-100k-2025")
async def main():
headers = {
"X-PM-Access-Key": os.environ["POLYMARKET_KEY_ID"],
"X-PM-Timestamp": str(int(asyncio.get_event_loop().time() * 1000)),
"X-PM-Signature": os.environ["POLYMARKET_SIGNATURE"],
}
async with websockets.connect(WS_URL, extra_headers=headers) as ws:
await ws.send(json.dumps({
"subscribe": {
"request_id": f"book-{SLUG}",
"subscription_type": 1,
"market_slugs": [SLUG],
}
}))
async for raw in ws:
msg = json.loads(raw)
if "heartbeat" in msg:
continue
if "error" in msg:
print("subscription error:", msg["error"])
break
print("keys:", list(msg.keys()))
asyncio.run(main())验证数据流确实处于活跃状态
连接保持打开并不能证明数据正在流动。要验证数据流,请在固定时间窗口内记录一小组可观察信号,并将它们与预期进行比较。下表是一个操作流程,而不是一组实测结果:请根据你自己的运行情况填写右列。不要发布你未测量过的数字。
| 信号 | 如何捕获 | 预期行为 | 你的结果 |
|---|---|---|---|
| 握手成功 | 记录连接打开事件 | 无错误打开 | (填写) |
| 订阅确认 | 在响应中匹配 request_id | 以相同类型回显 | (填写) |
| 快照完成 | 观察 eof: true | 在增量之前到达 | (填写) |
| 心跳节奏 | 为每个心跳打时间戳 | 规律间隔 | (填写) |
| 消息速率 | 统计每分钟消息数 | 活跃市场期间非零 | (填写) |
| 重连恢复 | 断开连接并观察 | 重新订阅并恢复 | (填写) |
请对正在活跃交易的市场运行此流程。冷清的市场可能在消息之间产生长时间间隔,这看起来像数据流已死,但实际并非如此。如果你需要比较基线,请在一天中的两个不同时间运行相同的流程并记录两者。
重连、心跳与状态恢复
服务器发送周期性心跳消息以保持连接活跃。客户端应响应心跳或实现自己的保活机制。在实践中,最安全的设计是将每次断开都视为完整的状态重置:重新连接、重新订阅、等待新的快照,然后才恢复应用增量。在重连后试图修补过时的本地订单簿是幻影流动性的常见来源。
重连应使用带抖动的指数退避。针对限速端点的紧密重连循环会使中断变得更糟。记录每次断开的关闭代码和原因,以便区分网络故障、身份验证失败和订阅错误。
- 每次重连时重置本地状态。
- 在应用增量之前等待
eof。 - 使用带抖动的退避;设置最大延迟上限。
- 将重复的身份验证失败与网络故障分开告警。
排查常见故障
大多数集成问题都属于少数几类。立即失败的握手通常指向签名不匹配、过期时间戳或错误路径。连接打开但从未收到订阅响应通常指向格式错误的载荷或不支持的订阅类型。数据流提供快照但没有更新通常意味着市场不活跃或订阅范围指向了错误的标识符。
如果你在代理或负载均衡器后面运行客户端,请确认 WebSocket 升级请求头被转发,并且空闲超时长于心跳间隔。许多看起来像 API 故障的生产事故实际上是基础设施超时。有关节点和端点可靠性的更广泛指导,请参阅 RPC 端点指南和多链 RPC 端点指南。
- 签名不匹配:验证方法、路径和时间戳。
- 无订阅响应:验证 JSON 结构和类型值。
- 静默数据流:确认市场活跃且 slug 正确。
- 频繁断开:检查代理空闲超时和心跳处理。
局限性与权衡
WebSocket 流式传输不能替代 REST。快照可能很大,同时订阅多个市场的完整订单簿会消耗内存和带宽。轻量价格订阅的存在正是为了在你只需要最优买卖价或最新价格时降低这一成本。选择与你的决策逻辑相匹配的订阅类型,而不是默认使用最重的数据流。
流式传输还引入了轮询所避免的排序和一致性问题。你必须决定如何处理乱序消息、重复更新以及重连后的间隙。一种常见模式是从数据流维护本地订单簿,并定期与 REST 快照进行核对。这一核对步骤成本低廉,并能在偏差影响交易决策之前发现它。
最后,确切的端点主机、路径和身份验证细节因部署而异。不要硬编码你在博客文章中看到的主机。请阅读你所集成环境的当前文档,并将主机保存在配置中,以便无需代码部署即可更改。
生产集成的后续步骤
一旦最小客户端可以工作,后续步骤就是运维层面的:为连接生命周期事件添加结构化日志,为消息速率和重连次数添加指标,并为身份验证失败添加告警。将私有流和市场流放在具有不同重试策略的独立连接上。在短时间窗口内持久化原始消息,以便重放故障。
如果你的机器人还需要来自 Polygon 或其他网络的链上数据,请将流式集成与可靠的 RPC 层配对使用。RPC 定价页面和支持的网络列表描述了相关选项,区块链节点托管指南涵盖了基础设施方面。如需具体的端点推荐而非方法论,请使用 RPC Assistant。
- 在添加功能之前先添加指标。
- 定期将数据流状态与 REST 进行核对。
- 将凭据和主机保存在配置中,而不是代码中。