Hyperliquid RPC timeouts are typically caused by the public HyperEVM endpoint's strict per-IP rate cap (~100 req/min), which stalls or drops requests when exceeded. This article explains the two Hyperliquid stacks (L1 info/exchange API vs HyperEVM RPC), how to diagnose timeouts vs 429s vs rejected orders, and how to build resilient clients with caching, batching, explicit timeouts, exponential backoff, and idempotent retries. Includes a runnable Node.js example and a checklist for production.
Direct Answer: Why Hyperliquid RPC Requests Time Out
Hyperliquid RPC timeouts happen because the public HyperEVM RPC endpoint is capped at a strict rate limit of around 100 requests per minute per IP (as reported by Chainstack and other providers). When you exceed that cap, requests are stalled or dropped, which manifests as timeouts on your client side. This is separate from the Hyperliquid L1 info/exchange API, which has its own rate limits and different behavior.
In this guide, you'll learn the architecture behind Hyperliquid's two stacks, how to diagnose whether you're hitting a rate cap or a network issue, and how to build a resilient client that avoids timeouts and handles them gracefully when they do occur.
- The public HyperEVM RPC is rate-limited to ~100 req/min per IP (provider-reported).
- Exceeding the cap causes requests to stall or drop, leading to timeouts.
- The L1 info/exchange API is separate and has different limits.
- Use caching, batching, and WebSocket feeds to stay under caps.
- Implement explicit timeouts, exponential backoff, and idempotent retries.
Two Stacks, Two Different Timeout Behaviors
Hyperliquid operates two distinct interfaces that are often conflated: the native L1 API (info and exchange endpoints) and the HyperEVM RPC (an Ethereum-compatible JSON-RPC endpoint). They have different rate limits, latency profiles, and timeout characteristics.
The L1 info API (e.g., /info, /exchange) is designed for high-frequency market data and order placement. It uses a simple HTTP POST interface and has documented rate limits (see the Hyperliquid API rate limits guide). The HyperEVM RPC, on the other hand, is a standard Ethereum JSON-RPC endpoint that supports eth_call, eth_getBalance, and similar methods. It is subject to a much stricter per-IP rate cap, often cited as ~100 requests per minute.
When you exceed the HyperEVM cap, the endpoint may return HTTP 429 or simply hang until your client times out. In practice, many clients see timeouts before a 429 is returned, because the server queues or drops requests under load. This is why 'timeout' is the most common symptom, not 'rate limit exceeded'.
- L1 info/exchange API: separate rate limits, designed for market data and trading.
- HyperEVM RPC: Ethereum-compatible, strict per-IP cap (~100 req/min).
- Timeouts often occur before a 429 is returned, due to request queuing/dropping.
- Network latency and geo-proximity also affect timeout likelihood.
Diagnosing Timeouts vs 429s vs Rejected Orders
Before fixing timeouts, you need to correctly identify what's happening. A timeout is when your client gives up waiting for a response. A 429 is an explicit rate-limit response. A rejected order is an application-level rejection (e.g., insufficient margin, invalid price). Each requires a different response.
Here's a diagnostic checklist:
- Check the HTTP status code: 429 means rate limit; 5xx means server error; timeout means no response.
- Inspect the response body: Hyperliquid may return a JSON error with a code and message.
- Monitor your request rate: log timestamps and count requests per minute per IP.
- Test with a simple curl to the public endpoint to see if it responds at all.
- Compare latency from different regions: use a tool like ping or a geo-distributed service to see if proximity matters.
- Timeout: no response within your client's timeout window.
- 429: explicit rate limit response (HTTP 429).
- Rejected order: application-level error, often with a reason code.
- Use logging and metrics to distinguish these cases.
Staying Under the Rate Cap: Caching, Batching, and WebSockets
The most effective way to avoid timeouts is to stay under the per-IP rate cap. For the HyperEVM RPC, this means reducing the number of requests you make. Strategies include:
Caching: Cache responses for data that doesn't change frequently (e.g., token balances, contract state). Use a short TTL (e.g., 5-10 seconds) to balance freshness.
Batching: JSON-RPC supports batch requests. Combine multiple eth_call or eth_getBalance calls into a single HTTP request. This counts as one request against the rate limit.
Use WebSocket feeds: For market data, use the native L1 WebSocket feeds (e.g., allMids, l2Book) instead of polling the EVM RPC. This drastically reduces request count.
For the L1 info API, similar principles apply: use the /info endpoints with appropriate parameters to get all data in one call, and use WebSocket subscriptions for real-time updates.
- Cache immutable or slow-changing data.
- Batch multiple JSON-RPC calls into one request.
- Prefer WebSocket feeds for market data.
- Use the native /info endpoints for L1 data.
- Monitor your request rate to stay under caps.
Runnable Example: Capped Request Loop with Backoff and Idempotency
Below is a self-contained Node.js script that demonstrates how to interact with the HyperEVM RPC while respecting rate limits. It includes a simple rate limiter, exponential backoff, and an idempotency guard for order submissions (though order submission is on the L1 API, the pattern applies). The script uses the public endpoint (https://api.hyperliquid.xyz) for L1 and (https://api.hyperliquid.xyz/evm) for EVM RPC.
The script does the following:
- Defines a rate limiter that allows a configurable number of requests per minute.
- Implements a fetchWithRetry function that retries on timeout or 429 with exponential backoff.
- Shows an example of an idempotent order submission using a client-supplied order ID.
Run it with Node.js (v18+). It will output the result of a simple eth_blockNumber call and a simulated order submission.
- Rate limiter: token bucket to enforce requests per minute.
- Exponential backoff: retry with increasing delay (e.g., 1s, 2s, 4s).
- Idempotency: include a unique client order ID to prevent duplicate orders.
- Never auto-retry order placement without idempotency.
// hyperliquid-rpc-timeout-example.js
// Run with: node hyperliquid-rpc-timeout-example.js
const https = require('https');
// Configuration
const EVM_RPC_URL = 'https://api.hyperliquid.xyz/evm';
const L1_API_URL = 'https://api.hyperliquid.xyz';
const RATE_LIMIT_PER_MINUTE = 90; // stay under the ~100 cap
const TIMEOUT_MS = 5000;
const MAX_RETRIES = 3;
// Simple token bucket rate limiter
class RateLimiter {
constructor(ratePerMinute) {
this.rate = ratePerMinute / 60; // per second
this.tokens = ratePerMinute;
this.lastRefill = Date.now();
}
async waitForToken() {
while (true) {
const now = Date.now();
const elapsed = (now - this.lastRefill) / 1000;
this.tokens = Math.min(this.rate, this.tokens + elapsed * this.rate);
this.lastRefill = now;
if (this.tokens >= 1) {
this.tokens -= 1;
return;
}
await sleep(100);
}
}
}
const limiter = new RateLimiter(RATE_LIMIT_PER_MINUTE);
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function postJson(url, body) {
return new Promise((resolve, reject) => {
const data = JSON.stringify(body);
const options = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(data)
},
timeout: TIMEOUT_MS
};
const req = https.request(url, options, (res) => {
let responseBody = '';
res.on('data', chunk => responseBody += chunk);
res.on('end', () => {
resolve({ status: res.statusCode, body: responseBody });
});
});
req.on('timeout', () => {
req.destroy(new Error('Request timed out'));
});
req.on('error', reject);
req.write(data);
req.end();
});
}
async function fetchWithRetry(url, body, idempotencyKey = null) {
let attempt = 0;
while (attempt <= MAX_RETRIES) {
await limiter.waitForToken();
try {
const headers = {};
if (idempotencyKey) headers['X-Idempotency-Key'] = idempotencyKey;
const res = await postJson(url, body);
if (res.status === 429) {
// Rate limited, retry with backoff
const delay = Math.pow(2, attempt) * 1000;
console.log(`Rate limited (429). Retrying in ${delay}ms`);
await sleep(delay);
attempt++;
continue;
}
if (res.status >= 500) {
// Server error, retry
const delay = Math.pow(2, attempt) * 1000;
console.log(`Server error (${res.status}). Retrying in ${delay}ms`);
await sleep(delay);
attempt++;
continue;
}
return JSON.parse(res.body);
} catch (err) {
if (err.message === 'Request timed out') {
const delay = Math.pow(2, attempt) * 1000;
console.log(`Timeout. Retrying in ${delay}ms`);
await sleep(delay);
attempt++;
continue;
}
throw err;
}
}
throw new Error('Max retries exceeded');
}
async function main() {
// Example 1: Simple EVM RPC call (eth_blockNumber)
console.log('Fetching current block number from HyperEVM RPC...');
const blockNumber = await fetchWithRetry(EVM_RPC_URL, {
jsonrpc: '2.0',
method: 'eth_blockNumber',
params: [],
id: 1
});
console.log('Block number (hex):', blockNumber.result);
// Example 2: Simulated order submission with idempotency key
// In production, use the L1 /exchange endpoint with a signed payload.
console.log('\nSimulating order submission with idempotency...');
const orderPayload = {
action: {
type: 'order',
orders: [{
a: 1, // asset index
b: 100, // price
s: '0.1', // size
r: false, // reduce only
t: { limit: { tif: 'Gtc' } }
}]
},
nonce: Date.now(),
signature: '0x...' // would be a real signature
};
const idemKey = `order-${Date.now()}`;
try {
const result = await fetchWithRetry(L1_API_URL + '/exchange', orderPayload, idemKey);
console.log('Order response:', result);
} catch (err) {
console.error('Order failed after retries:', err.message);
}
}
main().catch(err => {
console.error('Fatal error:', err);
process.exit(1);
});
// Expected output (shape):
// Fetching current block number from HyperEVM RPC...
// Block number (hex): 0x123456
// Simulating order submission with idempotency...
// Order response: { status: 'ok', response: { type: 'order', ... } }Common Failures and Fixes
Even with careful design, you may encounter issues. Here are common failure modes and how to fix them:
- Timeout on eth_call: This often happens when the EVM RPC is under load. Reduce the number of eth_call requests by caching results or using batch calls. If you need real-time data, consider using the L1 WebSocket feeds.
- 429 Too Many Requests: This is explicit. Implement exponential backoff and respect the Retry-After header if present. Also, reduce your request rate.
- Order rejection due to nonce or signature issues: This is not a timeout but an application error. Ensure your nonce is unique and your signature is correct. Use idempotency keys to avoid duplicate orders.
- Network latency from distant regions: If you're far from Hyperliquid's servers, latency can cause timeouts. Use a provider with global edge caching or deploy a node closer to the exchange.
- eth_call timeouts: cache and batch.
- 429: backoff and reduce rate.
- Order rejection: check nonce and signature.
- Latency: use geo-distributed providers or colocate.
Tradeoffs and Limitations
While the strategies above help, there are tradeoffs:
Caching introduces staleness. For market data, a few seconds of delay may be acceptable, but for order book data, it's not. Use WebSocket feeds for real-time data.
Batching increases complexity. You need to map responses to requests, and some endpoints may not support batching.
Retries can amplify load. If many clients retry simultaneously, they can cause a thundering herd. Use jitter in your backoff.
The ~100 req/min cap is provider-reported and may vary. Always test your actual limits.
For sustained production load, a dedicated endpoint or colocated node is recommended. See RPC pricing and API service for options.
- Caching: tradeoff between freshness and rate limit.
- Batching: complexity and compatibility.
- Retries: use jitter to avoid thundering herd.
- Rate caps: vary by provider; test your own.
- Production: consider dedicated endpoints.
Next Steps and Further Reading
Now that you understand Hyperliquid RPC timeouts, you can build more reliable applications. Here are some next steps:
Review the Hyperliquid API rate limits guide for a deep dive into L1 limits.
Explore the Hyperliquid RPC endpoints (RPC Assistant) to find the right endpoint for your use case.
Check out the Hyperliquid network page for network details.
If you need a managed solution, see our API service and RPC pricing.
For more troubleshooting guides, visit the OnFinality Learn hub.
- Read the Hyperliquid API rate limits guide.
- Use Hyperliquid RPC endpoints (RPC Assistant) for endpoint selection.
- Visit Hyperliquid network page for network specs.
- Consider API service for managed access.
- See RPC pricing for dedicated options.
- Browse more articles on the OnFinality Learn hub.