Error Handling

Handle errors, retries, and request IDs before you rely on the API in production.

Errors
HTTP plus JSON bodies
Recovery
Retry guidance included
Request IDs
Included in responses

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 responseJSON
{
"error": "Invalid API key",
"code": 401,
"request_id": "req_abc123xyz"
}

HTTP status codes

CodeNameWhat it means
400Bad RequestInvalid request parameters or malformed request body
401UnauthorizedMissing or invalid API key
403ForbiddenAPI key lacks permission for this resource or tier limit exceeded
404Not FoundEndpoint or resource does not exist
429Too Many RequestsRate limit exceeded. Check X-RateLimit-Reset header
500Internal Server ErrorServer error. Retry with exponential backoff
503Service UnavailableService temporarily unavailable. Retry later

Rate-limit headers

Read the response headers on every request so 429 handling is deterministic instead of reactive.

Rate-limit headersHTTP
X-RateLimit-Limit: 50 # Requests per second allowed
X-RateLimit-Remaining: 47 # Requests remaining this second
X-RateLimit-Reset: 1704067200 # Unix timestamp when limit resets
X-Credits-Limit: 10000000 # Monthly credit limit
X-Credits-Used: 500000 # Credits used this month
X-Credits-Remaining: 9500000 # Credits remaining this month
X-Credits-Reset: 1706745600 # Unix timestamp for credit reset

Retry strategy

Retry only transient failures. Authentication, authorization, and validation issues should fail fast and return clear errors.

Exponential backoffPython
import time
import 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).

Keep the client safe