~/arifin
GitHub ↗
~/arifin · Indonesia
WorkWritingAbout
GitHub ↗LinkedIn ↗Email
← work
2026·Solo build·shipped

Earn Aggregator

LIVE ↗OSS ↗
backgroundAPY rates across crypto exchanges change daily and no unified API existed to query them reliably without scraping five separate dashboards.
goalBuild a serverless API aggregating flexible earn APYs from major exchanges with Redis caching and scheduled refresh.
outcomeEarn Aggregator — a production API running on Vercel with near-zero cost, aggregating USDT/USDC/BTC/ETH/SOL earn rates from five exchanges.
FastifyTypeScriptUpstash RedisVercelServerlessCronRate Limiting

I was looking for the best stablecoin yield across exchanges and found myself checking Binance, Bybit, Bitget, MEXC, and Gate.io manually every time. APYs change daily, sometimes hourly. I was spending ten minutes on a task that should take ten seconds, and there was no API that gave me all five in one call. So I built one.

The architecture is straightforward: Fastify + TypeScript on Vercel Serverless Functions, with Upstash Redis for caching at a 7-day TTL. A Vercel Cron job runs every 6 hours to refresh the cache proactively. On a cache miss, the API can optionally trigger a live refresh from the exchanges. Rate limiting via @fastify/rate-limit keeps it from being abused. The whole thing runs on Vercel's free tier for effectively zero cost.

The interesting engineering problem was normalization. Each exchange has a completely different API structure: different authentication models, different response shapes, different field names for the same concept. Binance returns earn products in a flat list; Gate.io nests them inside product categories. Building a per-exchange adapter layer that outputs a consistent { exchange, asset, apy, type } shape took careful design. Gate.io's API was the most divergent and needed the most special-casing.

The 7-day TTL on the cache was a deliberate choice. Earn APYs don't change fast enough to need sub-hour freshness for most use cases, and the long TTL means the API stays functional even if the cron fails several times in a row. In production, the cache hit rate is high enough that exchange APIs rarely get called directly at all — most requests are served entirely from Redis.

← all workwriting →
LIVE ↗OSS ↗
FastifyTypeScriptUpstash RedisVercelServerlessCronRate Limiting
← all workwriting →