PolyNode uses per-key rate limiting to ensure fair usage. Limits scale with your plan.
Tier limits
Firehose = a WebSocket subscription with no filters applied. It receives every event (all settlements, trades, blocks, etc.) at full throughput. Filtered subscriptions — where you specify wallets, tokens, slugs, or size thresholds — use far less bandwidth and don’t count toward your firehose limit.
| Free | Starter ($50/mo) | Growth ($200/mo) | Enterprise ($750/mo) |
|---|
| Rate limit | 120 req/min (2 QPS) | 6,000 req/min (100 QPS) | 18,000 req/min (300 QPS) | 60,000 req/min (1,000 QPS) |
| WebSocket connections | 1 | 500 | 3,000 | Unlimited |
| Firehose connections | 1 | 100 | 750 | Unlimited |
| Orderbook connections | 1 | 2 | 10 | Unlimited |
| Orderbook market subs | Unlimited | Unlimited | Unlimited | Unlimited |
| API keys | 1 | 3 | 10 | 25 |
| Snapshot size | 20 | 100 | 200 | 500 |
| Key generation | 1 per IP per day | Via dashboard | Via dashboard | Via dashboard |
| Support | Community | Email | Priority | Dedicated + Slack |
Enterprise includes dedicated server infrastructure. Contact josh@quantish.live before activation.
How it works
- Rate limits are tracked per API key using a sliding window.
- When you exceed the limit, you’ll receive a
429 Too Many Requests response.
- The error message includes a Unix timestamp for when you can retry.
Best practices
Use WebSocket for real-time data
Instead of polling REST endpoints, connect via WebSocket for live updates:
// Instead of polling /v1/markets every 5 seconds (12 req/min)...
// Use WebSocket (1 connection, unlimited events)
const ws = new WebSocket("wss://ws.polynode.dev/ws?key=pn_live_YOUR_KEY");
ws.send(JSON.stringify({
action: "subscribe",
type: "settlements",
}));
Market metadata (question, slug, outcomes) changes infrequently. Cache it and only refresh periodically:
// Fetch full market list once
const markets = await fetch("/v1/markets?count=1000", { headers })
.then((r) => r.json());
// Cache by token_id
const cache = new Map(markets.markets.map((m) => [m.token_id, m]));
// Use cache for lookups, refresh every 5 minutes
Batch where possible
Use /v1/markets?count=1000 instead of individual /v1/markets/:id calls to reduce requests.
Use filtered subscriptions
WebSocket subscriptions with filters reduce message volume and processing overhead:
{
"action": "subscribe",
"type": "settlements",
"filters": {
"tokens": ["specific-token-id"],
"min_size": 100
}
}
Expected WebSocket throughput
WebSocket subscriptions are not rate-limited by requests/min, but message volume varies significantly by subscription type:
| Subscription | Typical messages/sec | Notes |
|---|
| Firehose (no filters) | 50–150 | ~0.5–1.5 Mbps uncompressed |
| Filtered (1 market) | 1–20 | Depends on market activity |
| Filtered (1 wallet) | 0–5 | Most wallets trade infrequently |
| Blocks only | ~0.5 | One per Polygon block (~2s) |
status_update events arrive in bursts (30–80 per block). If you don’t need confirmation tracking, exclude them from your event_types filter to reduce volume.
Data endpoint limits
The wallet and market data endpoints proxy to upstream sources and have their own rate limits, separate from your per-key limit.
Standard data endpoints
These endpoints share your plan’s normal rate limit:
/v1/wallets/{addr}/positions
/v1/wallets/{addr}/trades
/v1/wallets/positions (multi-wallet)
/v1/markets/{id}/positions
/v1/markets/{id}/trades
Batch requests (multiple wallets) count each wallet as one request toward your limit. For example, a 5-wallet batch counts as 5 requests.
Heavy endpoints RATE LIMITED
Some data endpoints fan out many upstream requests per call and have a stricter per-key limit:
| Endpoint | Limit | Why |
|---|
/v1/markets/{id}/positions?includeTrades=true | 60 req/min per key | Each call fetches trade history for every position holder in the market (can be 50-100+ upstream calls) |
When this limit is exceeded, you’ll receive a 429 response with:
{
"error": "includeTrades rate limit exceeded (60/min). Retry after 1774108000."
}
If you only need position sizes and P&L, omit includeTrades to use the standard rate limit. Only add includeTrades=true when you specifically need firstTradeAt and lastTradeAt timestamps.
For real-time data, use the WebSocket stream instead of polling these endpoints.
Higher limits
Upgrade your plan at polynode.dev/pricing for higher rate limits and more WebSocket connections.