Public BNB Smart Chain RPC endpoints are convenient but can fail or time out under load due to saturation, request size, unsupported methods, and rate limits. Production applications need sane timeouts, retries that avoid duplicate writes, health monitoring, and a fallback chain from public to commercial to dedicated nodes. This article explains the mechanisms and provides a Node.js example.
Direct Answer: Why Public BSC RPC Endpoints Fail and What to Do
Public BNB Smart Chain (BSC) RPC endpoints are free and easy to use, but they are not designed for production workloads. Under heavy load, they frequently return HTTP 429 (Too Many Requests) or 5xx errors, and requests can time out. This is documented in the BNB Chain GitHub issue tracker, where users report that public RPC node requests fail more than 80% of the time during peak periods. While that specific figure is a community report, not an official measurement, it highlights a real reliability problem.
To build a reliable production setup, you must implement per-call timeouts, retries that do not duplicate writes, health monitoring, and a fallback chain that starts with public endpoints, moves to a commercial provider, and ultimately uses a dedicated BSC node. This article explains the underlying causes and provides a runnable Node.js example.
- Public endpoints are shared and rate-limited; they can be saturated by other users.
- Large requests (e.g., eth_getLogs with wide ranges) can cause timeouts.
- Unsupported methods like debug_traceTransaction or archive data requests may return errors.
- Production apps should use a fallback chain and monitor endpoint health.
Mechanism: Why Public BSC RPC Endpoints Fail or Time Out
Public BSC RPC endpoints are operated by the BNB Chain team and community volunteers. They sit behind load balancers that enforce per-second rate limits (often 10,000 requests per second per IP, but this is not officially documented). When the limit is exceeded, the server returns HTTP 429. Under extreme load, the backend nodes may become overwhelmed, leading to 5xx errors or dropped connections.
Another common cause is request size. Methods like eth_getLogs can scan a large block range, causing the node to do heavy work. Public endpoints often cap the block range (e.g., 10,000 blocks) and return an error if exceeded. Similarly, debug_* methods are usually disabled on public endpoints for security reasons, so they return an error like 'method not found'.
The BNB Chain documentation on node configuration recommends setting appropriate timeouts and using WebSocket for real-time data. It also suggests that developers should not rely on public endpoints for production and should consider running their own node or using a commercial provider. These are documented recommendations, not our own measurements.
- Rate limiting: 429 responses when exceeding per-second limits.
- Saturation: 5xx or timeouts when the backend node is overloaded.
- Request size: Large eth_getLogs ranges can be rejected.
- Unsupported methods: debug_* and archive methods are often disabled.
Setting Sane Timeouts and Retries Without Duplicating Writes
A timeout is the maximum time you wait for a response. For BSC, a typical block time is 3 seconds, so a read call like eth_blockNumber should respond in under 1 second. For heavier calls like eth_getLogs, you might allow 10-15 seconds. Set a timeout that is generous enough for the method but not so long that your app hangs.
Retries are tricky for write operations (e.g., eth_sendRawTransaction). If you retry a transaction that was already accepted, you might get a 'nonce too low' error, but the transaction is already in the mempool. To avoid duplicates, you should not blindly retry writes. Instead, check the transaction receipt or use the same nonce and gas price. For reads, retries are safe.
A common pattern is to use a library like axios with a timeout and a retry mechanism that only retries on network errors or 5xx, not on 429 (unless you respect Retry-After). For writes, you can retry only if you are sure the transaction was not broadcast (e.g., connection error before response).
- Set per-method timeouts: 1s for eth_blockNumber, 10s for eth_getLogs.
- Retry reads on network errors or 5xx, but not on 429 unless you back off.
- For writes, do not retry blindly; check receipt or use idempotency.
- Use exponential backoff with jitter to avoid thundering herd.
Monitoring Endpoint Health: eth_blockNumber Lag and eth_syncing
To know if an endpoint is healthy, you can monitor two key metrics: eth_blockNumber and eth_syncing. eth_blockNumber returns the latest block number the node has processed. If this number lags behind the network's latest block (which you can get from a trusted source like a block explorer), the node is behind and may serve stale data.
eth_syncing returns an object if the node is syncing, or false if it is fully synced. If it returns an object, the node is not ready to serve accurate data. You should treat a syncing node as unhealthy and route traffic away from it.
In your monitoring, you can periodically call eth_blockNumber on each endpoint and compare it to a reference. If the lag exceeds a threshold (e.g., 5 blocks), mark the endpoint as degraded. This is a simple health check that can be automated.
- eth_blockNumber: compare to a reference to detect lag.
- eth_syncing: if not false, the node is syncing and should be avoided.
- Automate health checks every 30-60 seconds.
- Use a threshold like 5 blocks for lag.
Designing a Fallback Chain: Public -> Commercial -> Dedicated
A robust production setup uses a fallback chain. Start with a public endpoint (e.g., https://bsc-dataseed.bnbchain.org), then fall back to a commercial provider like OnFinality's BNB Chain network page or RPC Assistant, and finally to your own dedicated BSC node. This ensures high availability.
Commercial providers offer higher rate limits, dedicated resources, and SLAs. OnFinality's api service provides managed endpoints with monitoring and scaling. Dedicated nodes give you full control and no rate limits, but require maintenance.
When implementing the fallback, you should try the primary endpoint first. If it fails (timeout, 5xx, or 429), try the next. You can also use a health check to skip endpoints that are known to be down. The example below demonstrates this pattern.
- Public endpoints: free but unreliable.
- Commercial providers: better reliability and support.
- Dedicated nodes: maximum control and performance.
- Fallback order: primary -> secondary -> tertiary.
Runnable Example: Node.js Health Check and Fallback with Timeout and Retry
Below is a self-contained Node.js script that demonstrates how to implement a fallback chain with timeouts and retries. It uses axios and the built-in http module. The script defines two endpoints (you can replace with your own), a health check function, and a request function that tries each endpoint in order.
To run it, save the code to a file (e.g., bsc-rpc-fallback.js) and run node bsc-rpc-fallback.js. It will make a simple eth_blockNumber call and print the result. The script includes a timeout of 5 seconds per request and retries up to 2 times on network errors or 5xx, but not on 429 (it will back off).
- Uses axios with timeout and retry logic.
- Health check compares eth_blockNumber to a reference.
- Fallback chain tries endpoints in order.
- Retries only on network errors or 5xx, not on 429.
const axios = require('axios');
const endpoints = [
'https://bsc-dataseed.bnbchain.org',
'https://bsc-dataseed1.bnbchain.org'
];
const TIMEOUT = 5000; // 5 seconds
const MAX_RETRIES = 2;
async function callRpc(endpoint, method, params) {
const url = endpoint;
const data = { jsonrpc: '2.0', method, params, id: 1 };
let lastError;
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
try {
const response = await axios.post(url, data, { timeout: TIMEOUT });
if (response.data.error) {
throw new Error(response.data.error.message);
}
return response.data.result;
} catch (error) {
lastError = error;
if (error.response && error.response.status === 429) {
// Rate limited, wait and retry (but not too many times)
await new Promise(resolve => setTimeout(resolve, 1000 * (attempt + 1)));
} else if (error.response && error.response.status >= 500) {
// Server error, retry
await new Promise(resolve => setTimeout(resolve, 500 * (attempt + 1)));
} else if (error.code === 'ECONNABORTED') {
// Timeout, retry
await new Promise(resolve => setTimeout(resolve, 500 * (attempt + 1)));
} else {
// Other error, don't retry
break;
}
}
}
throw lastError;
}
async function getLatestBlock(endpoint) {
return await callRpc(endpoint, 'eth_blockNumber', []);
}
async function checkHealth(endpoint) {
try {
const block = await getLatestBlock(endpoint);
return { healthy: true, block: parseInt(block, 16) };
} catch (error) {
return { healthy: false, error: error.message };
}
}
async function main() {
for (const endpoint of endpoints) {
const health = await checkHealth(endpoint);
console.log(`${endpoint}: ${health.healthy ? 'healthy, block ' + health.block : 'unhealthy: ' + health.error}`);
if (health.healthy) {
// Use this endpoint for your actual calls
const block = await getLatestBlock(endpoint);
console.log(`Using ${endpoint}, latest block: ${block}`);
break;
}
}
}
main().catch(console.error);Expected Results and How to Verify
When you run the script, you should see output like:
https://bsc-dataseed.bnbchain.org: healthy, block 12345678
Using https://bsc-dataseed.bnbchain.org, latest block: 0xbc614e
The block number is in hex. You can verify it against a block explorer like BscScan. If the first endpoint is down, the script will try the second. You can test this by temporarily using an invalid endpoint.
To verify the timeout and retry behavior, you can set the TIMEOUT to a very low value (e.g., 1 ms) and observe that it retries. You can also simulate a 429 by using a mock server, but that is beyond this example.
- Output shows health status and block number.
- Verify block number on BscScan.
- Test fallback by using a bad endpoint.
- Test timeout by lowering TIMEOUT.
Common Failures and Fixes
One common failure is that public endpoints return 429 even with a low request rate. This can happen if the IP is shared (e.g., behind a corporate NAT). The fix is to use a commercial provider that offers dedicated IPs or higher limits.
Another failure is that eth_getLogs returns an error like 'query returned more than 10000 results'. This is a documented limit on BSC nodes. The fix is to narrow the block range or use pagination.
If you see 'method not found' for debug_traceTransaction, it means the endpoint does not support that method. Use a dedicated node or a provider that supports debug methods.
Finally, timeouts can occur if your request is too large. For example, eth_getBlockByNumber with full transaction objects can be heavy. Use the 'false' parameter to get only hashes, or use a lighter method.
- 429: use a commercial provider or reduce request rate.
- eth_getLogs limit: narrow the range or paginate.
- Unsupported methods: use a dedicated node or provider that supports them.
- Large responses: request only necessary data.
Tradeoffs and Limitations
Using a fallback chain adds complexity. You need to manage multiple endpoints and health checks. However, the reliability gain is significant. Public endpoints are free but unreliable; commercial providers cost money but offer SLAs; dedicated nodes require maintenance but give full control.
There is also a tradeoff between timeout length and user experience. A short timeout may cause false failures, while a long timeout can make your app feel slow. You should tune timeouts based on your use case.
Finally, note that the 'public RPC fails' references are issue/bug-tracker records, not our own measurements. You should always test endpoints in your own environment to determine their actual reliability.
- Fallback adds complexity but improves reliability.
- Timeouts need tuning per use case.
- Community reports are not official measurements.
- Test endpoints yourself.
Next Steps and Further Reading
To build a production-grade BSC RPC setup, start by implementing the fallback pattern shown above. Then, consider using a commercial provider like OnFinality's BNB Chain network page or RPC Assistant for higher reliability. You can also explore pricing to find a plan that fits your needs.
For more in-depth troubleshooting, read our generic RPC timeout diagnosis and fixes guide. You can also browse other articles on OnFinality Learn to improve your blockchain infrastructure skills.
Remember to monitor your endpoints continuously and adjust your fallback chain as needed. With the right setup, you can minimize downtime and provide a smooth experience for your users.
- Implement the fallback pattern in your production code.
- Consider a commercial provider for better reliability.
- Read the generic RPC timeout guide for more tips.
- Monitor and adjust your setup regularly.