Exchange Funding Rate Data Scraping

We design and develop full-cycle blockchain solutions: from smart contract architecture to launching DeFi protocols, NFT marketplaces and crypto exchanges. Security audits, tokenomics, integration with existing infrastructure.
Showing 1 of 1 servicesAll 1306 services
Exchange Funding Rate Data Scraping
Medium
from 1 business day to 3 business days
FAQ
Blockchain Development Services
Blockchain Development Stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1217
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    852
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1046
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    823

Funding Rate Data Scraping from Exchanges

Funding rate is a perpetual futures balancing mechanism. Every 8 hours (on most centralized exchanges) or continuously (on some), traders with long positions pay traders with short positions or vice versa, depending on whether the contract trades at premium or discount to spot price. For quant strategies, arbitrageurs, and risk managers, historical and real-time funding rate data is critical — one of the main market sentiment signals.

Data sources: exchanges and their APIs

Centralized exchanges

Binance — largest perpetuals volume:

GET https://fapi.binance.com/fapi/v1/fundingRate
  ?symbol=BTCUSDT&limit=1000&startTime={ms}&endTime={ms}

Returns history 1000 records per request. Rate limit: 2400 weight/minute, this endpoint costs 1 weight. Full history available from listing.

Bybit:

GET https://api.bybit.com/v5/market/funding/history
  ?category=linear&symbol=BTCUSDT&limit=200

Pagination via cursor in response, not by timestamp.

OKX:

GET https://www.okx.com/api/v5/public/funding-rate-history
  ?instId=BTC-USDT-SWAP&limit=100&before={ts}&after={ts}

Hyperliquid (decentralized perpetuals):

POST https://api.hyperliquid.xyz/info
Content-Type: application/json
{"type": "fundingHistory", "coin": "BTC", "startTime": 1700000000000}

WebSocket for real-time

Real-time funding rate (current, before next settlement):

Binance WebSocket:

const ws = new WebSocket("wss://fstream.binance.com/ws/btcusdt@markPrice@1s");
ws.onmessage = (e) => {
  const data = JSON.parse(e.data);
  // data.r — current funding rate
  // data.T — next funding time (ms)
};

Bybit WebSocket:

ws.send(JSON.stringify({
  op: "subscribe",
  args: ["tickers.BTCUSDT"]
}));
// In response: fundingRate and nextFundingTime

Collector implementation

Basic collector with retry and rate limiting

import pLimit from "p-limit";
import axiosRetry from "axios-retry";

class FundingRateCollector {
  private readonly limit = pLimit(5); // max 5 concurrent requests
  
  constructor(private readonly config: ExchangeConfig) {
    axiosRetry(this.http, {
      retries: 3,
      retryDelay: axiosRetry.exponentialDelay,
      retryCondition: (err) =>
        axiosRetry.isNetworkError(err) ||
        err.response?.status === 429 ||
        err.response?.status >= 500,
    });
  }

  async fetchHistorical(
    symbol: string,
    from: Date,
    to: Date
  ): Promise<FundingRateRecord[]> {
    const results: FundingRateRecord[] = [];
    let cursor = from.getTime();

    while (cursor < to.getTime()) {
      const batch = await this.limit(() =>
        this.fetchBatch(symbol, cursor, to.getTime())
      );
      if (batch.length === 0) break;
      results.push(...batch);
      cursor = batch[batch.length - 1].timestamp + 1;
      
      // Respect rate limits
      await delay(this.config.requestDelayMs);
    }
    return results;
  }
}

Data normalization

Each exchange returns different format. Normalized structure:

interface FundingRateRecord {
  exchange: string;       // "binance", "bybit", "okx", "hyperliquid"
  symbol: string;         // normalized: "BTC-USDT"
  originalSymbol: string; // as on exchange: "BTCUSDT", "BTC-USDT-SWAP"
  timestamp: number;      // unix ms — settlement time
  fundingRate: number;    // decimal, e.g. 0.0001 = 0.01%
  annualizedRate: number; // fundingRate * 3 * 365 (3 settlements per day)
  markPrice?: number;     // price at calculation time
}

Annualized rate matters for arbitrage analysis: if annualized funding > 30%, cash-and-carry arbitrage (long spot + short perp) potentially profitable.

Symbol handling

Same asset on different exchanges has different names:

  • Binance: BTCUSDT (futures), BTCUSD_PERP (coin-margined)
  • Bybit: BTCUSDT (linear), BTCUSD (inverse)
  • OKX: BTC-USDT-SWAP, BTC-USD-SWAP

Need symbol mapping table:

CREATE TABLE symbol_mapping (
  normalized_symbol VARCHAR(20) NOT NULL,  -- "BTC-USDT"
  exchange          VARCHAR(20) NOT NULL,
  exchange_symbol   VARCHAR(30) NOT NULL,
  contract_type     VARCHAR(10) NOT NULL,  -- "linear", "inverse"
  PRIMARY KEY (exchange, exchange_symbol)
);

Storage and data access

DB schema

CREATE TABLE funding_rates (
  id             BIGSERIAL,
  exchange       VARCHAR(20)     NOT NULL,
  symbol         VARCHAR(20)     NOT NULL,
  settled_at     TIMESTAMPTZ     NOT NULL,
  funding_rate   DECIMAL(18, 10) NOT NULL,
  mark_price     DECIMAL(24, 8),
  PRIMARY KEY (id),
  UNIQUE (exchange, symbol, settled_at)
) PARTITION BY RANGE (settled_at);

-- Index for typical queries
CREATE INDEX idx_funding_symbol_time 
ON funding_rates (symbol, settled_at DESC);

TimescaleDB hypertable for automatic time-based partitioning and compression of old data.

Anomalies and validation

Funding rate rarely exceeds ±0.3% per 8 hours under normal conditions. Extreme values (FTX last days, LUNA collapse) — up to several percent. Validate on insert:

function validateFundingRate(rate: number, exchange: string, symbol: string): boolean {
  if (Math.abs(rate) > 0.03) {  // > 3% is anomaly
    logger.warn(`Extreme funding rate detected`, { exchange, symbol, rate });
    // Don't discard — may be real extreme event
    // Flag for manual check
    return true; // but save it
  }
  return Math.abs(rate) <= 1.0; // > 100% — obvious data error
}

Collection schedule and monitoring

Historical data: initial backfill at startup (can take hours for full history across all symbols). After — incremental update every 8 hours (after settlement) plus real-time via WebSocket.

Monitoring:

  • Freshness: if last record for symbol older than 9 hours — alert (missed settlement)
  • Gap detection: check sequence continuity of timestamps (step must be exactly 8h or multiple)
  • Collection lag: time between settlement on exchange and appearance in our DB

Realistic development timeline for collector across 5–6 exchanges with historical backfill and API: 4–6 weeks.