Error Handling
Handle errors, retries, and request IDs before you rely on the API in production.
Treat errors as part of the API contract, not as a fallback path.
Every failed request returns a structured JSON body, request identifiers, and enough context to classify the issue or retry safely.
Error body
Capture the response body and the request identifier together. They are the fastest path to support or incident triage.
{ "error": "Invalid API key", "code": 401, "request_id": "req_abc123xyz"}HTTP status codes
| Code | Name | What it means |
|---|---|---|
| 400 | Bad Request | Invalid request parameters or malformed request body |
| 401 | Unauthorized | Missing or invalid API key |
| 403 | Forbidden | API key lacks permission for this resource or tier limit exceeded |
| 404 | Not Found | Endpoint or resource does not exist |
| 429 | Too Many Requests | Rate limit exceeded. Check X-RateLimit-Reset header |
| 500 | Internal Server Error | Server error. Retry with exponential backoff |
| 503 | Service Unavailable | Service temporarily unavailable. Retry later |
Rate-limit headers
Read the response headers on every request so 429 handling is deterministic instead of reactive.
X-RateLimit-Limit: 50 # Requests per second allowedX-RateLimit-Remaining: 47 # Requests remaining this secondX-RateLimit-Reset: 1704067200 # Unix timestamp when limit resetsX-Credits-Limit: 10000000 # Monthly credit limitX-Credits-Used: 500000 # Credits used this monthX-Credits-Remaining: 9500000 # Credits remaining this monthX-Credits-Reset: 1706745600 # Unix timestamp for credit resetRetry strategy
Retry only transient failures. Authentication, authorization, and validation issues should fail fast and return clear errors.
import timeimport requests
def request_with_retry(url, headers, max_retries=3): for attempt in range(max_retries): response = requests.get(url, headers=headers)
if response.status_code == 200: return response.json()
if response.status_code == 429: # Rate limited - wait and retry reset_time = int(response.headers.get('X-RateLimit-Reset', 0)) wait = max(reset_time - time.time(), 1) time.sleep(wait) elif response.status_code >= 500: # Server error - exponential backoff time.sleep(2 ** attempt) else: # Client error - don't retry response.raise_for_status()
raise Exception("Max retries exceeded")Common issues
I get 403 when querying ETH but BTC works fine
The free tier only allows BTC (and km:US500 for HIP-3). All other symbols require Build tier or higher. Upgrade at /pricing or start a free 14-day Build trial.
I get 403 on HIP-3 orderbook endpoints
HIP-3 orderbook and orderbook history require Pro tier or higher. Other HIP-3 endpoints (trades, candles, funding, OI) work on Build tier. Free tier is limited to km:US500.
I get 400 "Maximum request range is 30 days on your plan"
Free tier limits each request to a 30-day time window. Split large queries into 30-day chunks with cursor pagination, or upgrade to Build+ for unlimited per-request ranges.
I get 429 but I'm well under 15 requests/second
You may be hitting the concurrent query limit (3 for Free, 10 for Build). This limits how many requests can be in-flight at the same time. Wait for pending requests to complete before sending new ones, or reduce parallelism.
My WebSocket connection keeps disconnecting
The server sends ping frames every 30 seconds. Your client must respond with pong frames (most libraries do this automatically). Connections without a pong response within 60 seconds are terminated. If your library doesn't handle pings, send {"op": "ping"} periodically.
WebSocket replay says "Channel does not support historical replay"
The ticker, all_tickers, l4_diffs, l4_orders, hip3_l4_diffs, and hip3_l4_orders channels are real-time only and cannot be replayed. Use the REST API for historical L4 data.
Candles data returns empty for dates before May 2025
Hyperliquid candles are available from May 2025 onwards. For earlier price data, use the /prices endpoint which derives from orderbook snapshots (available from April 2023).
Lighter orderbook data returns only checkpoint granularity
Higher granularity (30s, 10s, 1s, tick) requires Build tier or higher. Free tier only gets checkpoint-level snapshots. The granularity parameter is tier-restricted, not credit-restricted.
My monthly credits reset but I'm still getting 429
Credits and rate limits are separate. Credits reset monthly, but rate limits (requests/second) and concurrent query limits apply continuously. Check the X-RateLimit-Remaining header to distinguish between credit exhaustion and rate limiting.
Pre-March 2025 trades are missing maker addresses
Fills before March 2025 were backfilled from Hyperliquid's REST API which only returns taker-side data. Maker/taker attribution with both addresses is available from March 2025 onward (S3 source data).