What I Learned Building a Funding Rate Comparison Tool (and Where Exchange APIs Get Weird)
I built a small tool that compares perpetual funding rates across five exchanges. The project was straightforward until it wasn't. Here's what I learned about crypto exchange APIs — the parts nobody documents. Not all
I built a small tool that compares perpetual funding rates across five exchanges. The project was straightforward until it wasn't. Here's what I learned about crypto exchange APIs — the parts nobody documents.
- Not all exchanges return data in the same order
Binance's funding history endpoint returns entries oldest-first. Bybit's returns newest-first. Assume one ordering and half your data is wrong for weeks without noticing.
The fix:
python
entries.sort(key=lambda e: e[0], reverse=True)
One line. But finding it took a diagnostic run that printed the raw array and its timestamps to see the pattern.
- Geo-blocking is real and deliberate
Binance and Bybit both block requests from cloud infrastructure. GitHub Actions (Azure IPs), Cloudflare Workers, AWS — all blocked. The response codes vary: Binance returns HTTP 451 with a "Service unavailable from a restricted location" message; Bybit returns HTTP 403 through their CloudFront distribution with "The Amazon CloudFront distribution is configured to block access from your country."
This isn't a bug. It's an anti-scraping policy. The workaround is to use exchanges that don't geo-block cloud IPs — OKX, MEXC, Hyperliquid, Gate.io, Bitget all work fine.
I tried a Cloudflare Worker proxy to route around the block. It worked for Bybit from my laptop but failed from the GitHub Actions runner. Eventually I dropped both exchanges and added five that don't block us.
- Funding intervals vary widely
Most exchanges use 8-hour funding. Some use 4-hour. Hyperliquid uses 1-hour. If you annualize assuming 8 hours across the board, you're wrong by a factor of 8 on Hyperliquid.
The correct formula uses the venue's own interval:
python
periods_per_day = 24 / interval_hours
apr = ((1 + rate) ** (periods_per_day * 365) - 1) * 100
Each venue's funding interval comes from the settlement timestamps — compute the median gap between consecutive entries and you have it.
- Always verify response shapes before writing the parser
I nearly shipped a version that used a single per-symbol rate and four fee assumptions to compute "venue-specific" funding. It would have told users "Binance is the best venue" when the conclusion came from Binance's assumed fee, not its actual rate.
The fix: fetch real per-venue data. Which meant verifying every endpoint's response shape before writing the parser. It sounds tedious. It saves you from shipping misleading numbers.
- Static beats live for this use case
Funding rates only change at settlement. A snapshot refreshed every 8 hours is maximally fresh for the entire window. Fetching live at page load burns API quota, adds latency, and introduces a failure point for zero benefit.
The architecture:
GitHub Actions cron runs the fetcher every 8 hours
Fetcher writes to a JSON file, commits it
Cloudflare Pages auto-rebuilds on the commit
Visitor loads a pre-rendered static page in under 50ms
The tool
Source: github.com/sanchopansa1987/feescope
If you're building anything on exchange APIs, the surprises are always in the response shapes and the geo-blocks. Everything else is straightforward.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.