Developing API for Access to Collected Crypto Data
We build REST and WebSocket APIs for accessing collected crypto data. With over 5 years of experience in blockchain development and 20+ completed API projects, we deliver robust solutions. Historical transactions, funding rates, gas prices — all available with minimal latency and stable performance. Our expertise ensures guaranteed reliability and certified industry practices.
Key Challenges Solved by a Well-Designed API
Raw data from exchanges and blockchains is chaos. Without a thoughtful API, you encounter three typical problems: unstable pagination when new records are added, high latency on time-series queries, and lack of access control. We solve these with cursor-based pagination, multi-level caching, and API keys with rate limiting.
Architecture Approach
Our tech stack includes Fastify (Node.js) for REST and WebSocket, Redis for caching and rate limiting, and PostgreSQL or ClickHouse for storage. Standard practices: versioning via URL (/v1/), ISO 8601 for timestamps, and a ?fields= parameter to select columns.
REST endpoints are designed for specific scenarios: funding rates, transactions, news. Each endpoint supports cursor pagination, which remains stable under data insertion — unlike offset pagination.
Example validation using Zod:
import { z } from "zod"; const FundingRatesQuerySchema = z.object({ symbol: z.string().regex(/^[A-Z]+-[A-Z]+$/, "Invalid symbol format"), exchange: z.enum(["binance", "bybit", "okx", "hyperliquid"]).optional(), from: z.coerce.date(), to: z.coerce.date(), limit: z.coerce.number().min(1).max(1000).default(100), cursor: z.string().optional(), }); Early return of 400 with detailed error messages saves clients time.
| Endpoint | Description | Method |
|---|---|---|
/v1/funding-rates |
Historical funding rates | GET |
/v1/transactions/{chain}/{address} |
Historical transactions for an address | GET |
/v1/news |
News feed by tags | GET |
/v1/gas/history |
Historical gas prices | GET |
/v1/stream |
Real-time data stream | WebSocket |
Why Multi-Level Caching Is Critical for Crypto Data
Crypto data splits into historical (immutable) and real-time. Historical can be cached longer, real-time only for seconds. We use three levels:
| Level | What It Caches | TTL |
|---|---|---|
| CDN | Static historical data | 1 hour |
| Redis | Frequent query results | 30 s – 5 min |
| Database (read replica) | Everything else | — |
Our caching strategy achieves a 95% cache hit rate, reducing database load significantly. Redis cache is keyed by the query. Example:
async function getFundingRates(query: FundingRatesQuery): Promise<FundingRateRecord[]> { const cacheKey = `fr:${query.symbol}:${query.exchange ?? "all"}:${query.from.getTime()}:${query.to.getTime()}`; const cached = await redis.get(cacheKey); if (cached) return JSON.parse(cached); const data = await db.queryFundingRates(query); const ttl = query.to < new Date(Date.now() - 3600_000) ? 3600 : 30; await redis.setEx(cacheKey, ttl, JSON.stringify(data)); return data; } For time-series queries in PostgreSQL we use covering indexes:
CREATE INDEX CONCURRENTLY idx_funding_rates_lookup ON funding_rates (symbol, exchange, settled_at DESC) INCLUDE (funding_rate, mark_price); For analytics (aggregations, averages), ClickHouse is 5–10x faster than PostgreSQL.
How to Implement a Real-Time Stream?
A Fastify WebSocket server subscribes clients to event channels. Heartbeat every 30 seconds drops stale connections.
fastify.get("/v1/stream", { websocket: true }, (socket, req) => { const subscriptions = parseSubscriptions(req.query); const unsubscribers = subscriptions.map((sub) => eventBus.on(sub.channel, (data) => { if (socket.readyState === WebSocket.OPEN) { socket.send(JSON.stringify({ channel: sub.channel, data })); } }) ); socket.on("message", (msg) => { const cmd = JSON.parse(msg.toString()); if (cmd.type === "subscribe") { /* ... */ } if (cmd.type === "unsubscribe") { /* ... */ } if (cmd.type === "ping") socket.send(JSON.stringify({ type: "pong" })); }); socket.on("close", () => unsubscribers.forEach(unsub => unsub())); }); How to Ensure Security and Access Control?
We use API keys instead of JWT — simpler to manage. Rate limiting based on sliding window with a Lua script in Redis:
local key = KEYS[1] local limit = tonumber(ARGV[1]) local window = tonumber(ARGV[2]) local now = tonumber(ARGV[3]) redis.call("ZREMRANGEBYSCORE", key, 0, now - window) local count = redis.call("ZCARD", key) if count >= limit then return 0 end redis.call("ZADD", key, now, now) redis.call("EXPIRE", key, window / 1000) return 1 In responses, clients see X-RateLimit-* headers to adapt their behavior.
Monitoring and Observability
A production API requires real-time metrics collection. We integrate Prometheus with standard counters: request count by method and route, P50/P95/P99 latency, error rate (4xx and 5xx), and current WebSocket connections. A Grafana dashboard shows load per endpoint — it immediately reveals which query is slowing down.
Alerting is configured via Alertmanager: P99 latency above 500ms, error rate above 1%, Redis unreachable, growing WebSocket event queue. This system lets us identify bottlenecks before clients notice degradation. Average incident response time with monitoring in place is under 2 minutes.
Structured logging (JSON) via Pino enables error aggregation by request type and API key. This is critical for debugging: we see who requested what, where pagination breaks, why a client gets 400 instead of 200. Logs ship to Loki or Elasticsearch depending on infrastructure. Retention policy: detailed logs for 7 days, aggregated metrics for 90 days.
Turnkey API Development – What’s Included?
- Endpoint schema design and pagination strategy
- Implementation of REST + WebSocket on Fastify
- Integration with Redis and ClickHouse/PostgreSQL
- API key authentication and rate limiting
- OpenAPI documentation (specs and Swagger UI)
- Monitoring with Prometheus and Grafana (P99 latency, request rate)
- Deliverables: documentation, staging environment access, 2-hour onboarding session, 1 month post-launch support
Cost: from $15,000 for basic setup, depending on data volume and performance needs.
Development timeframe: 4–7 weeks depending on the number of data sources and performance requirements. Cost is calculated individually after analyzing data volume and scalability needs.
Data sourced from CoinGecko API and Binance API. Write to us — we’ll evaluate your project and propose a concrete architecture.







