Skip to content
PerpFinder

Free API for perp market data

The API needs no key and is CORS-open. It gives perpetual-futures volume, open interest, funding rates, fees and liquidations across 50+ exchanges. Fetch the data from any site, server or browser.

26 endpointsKeyless · CORS-openas of 2026-09-27Source: OpenAPI

Quick start

// Free, keyless, CORS-open — call it from any site, server or browser
const res = await fetch('https://perpfinder.com/api/data/funding-rates')
const { rows } = await res.json()
// rows = live funding rates, asset × exchange

Please keep a “Data by PerpFinder” credit linking to perpfinder.com when you republish. The data is free; the attribution funds it.

Rate limits and conventions

Every route on this page is free and needs no key. PerpFinder does not issue API keys. Each route accepts GET only, and any site can call it (CORS is open). The rate limit per route table gives the per-IP limit of each route, per 60 seconds.

The limiter counts per server instance, and CDN hits do not reach it. A short burst above a limit can pass, but sustained traffic above it gets 429 with Retry-After and X-RateLimit-* headers. Plan for the listed limit.

Filters: /api/data/funding-rates accepts ?asset=BTC,ETH, ?venue=Binance,Bybit and ?limit=50. The full matrix is close to 1 MB, so ask for the rows you need. A venue filter recomputes each row’s max/min rate, arb spread and total open interest over the venues you asked for. With no parameters the response is unchanged.

Conventions: a missing value is null, never 0. Every response carries updatedAt (UTC). Self-collected datasets additionally expose methodologyVersion, maturity and dataStatus — treat collecting/preliminary series accordingly. Pipeline health: /status. Machine-readable resources: OpenAPI and API manifest.

License: the DATA is free to republish with attribution (“Data: PerpFinder” + link) — see the data-license clause in our terms. Editorial content (reviews, guides) remains all-rights-reserved.

Rate limit per route

Requests per IP per minute
RouteLimit per minute
/api/data/perps60
/api/data/slippage30
/api/data/slippage-spot30
/api/data/dex-enrichment60
/api/data/perps-chart60
/api/data/volume40
/api/data/open-interest40
/api/data/oi-long-short60
/api/data/funding-rates40
/api/data/fees60
/api/data/liquidations60
/api/data/liquidations-history40
/api/data/options40
/api/data/options-history40
/api/data/volume-quality40
/api/data/rwa-markets60
/api/data/mica30
/api/data/premium60
/api/data/fear-greed60
/api/data/funding-history40
/api/data/funding-prediction-accuracy30
/api/data/volume-quality-history40
/api/data/volume-ranking40
/api/data/venue-history40
/api/data/builder-fees40
/api/data/cost-history40
/api/mcp60

The read-only MCP server for AI agents is at /api/mcp. See MCP server.

Market-data endpoints

26 endpoints · full list at /llms-full.txt
GET/api/data/perps

Perp DEX volume overview (24h/7d/30d, change %, per protocol).

Fields: protocols[], dataStatus, generatedAt, upstreamTimestamp, schemaVersion, sources, ETag

GET/api/data/slippage?asset=BTC&size=100000&feeType=taker|maker&side=buy|sell

Live perp execution-cost ladder from order books and supported oracle/pool models. Total cost is fee + half-spread + size-dependent impact beyond the best quote. A venue that cannot fill the order publishes totalBps null and keeps its partial-fill numbers under partial. depthConfidence low marks displayed depth the plausibility gate could not confirm; such rows rank after confirmed rows. An oracle venue has no book: GMX publishes the size-dependent GMX v2 position impact read from the on-chain DataStore through the GMX Reader, in priceImpactBps, with slippageModel onchain-position-impact-v2 and slippageConfidence medium; slippageSourceUrl names the contract that was read. Caching: a healthy answer carries Cache-Control public, max-age=10, s-maxage=25, must-revalidate, so one measurement serves every reader for the engine TTL; partial and stale answers use max-age=0, s-maxage=5, must-revalidate so readers see a completed background refresh before its fresh window expires. X-Cache states where the answer came from: HIT (this instance), SHARED (the shared result store, inside the TTL), STALE-SHARED or STALE (a labelled older result, with staleAgeSeconds, refreshed behind the response), MISS (measured now) or UNAVAILABLE (nothing inside the stale cap).

Fields: asset, sizeUsd, side, results[].{exchange,feeBps,feeBpsMin,feeBpsMax,feeModel,slippageBps,halfSpreadBps,spreadBps,totalBps,midPrice,vwap,sufficient,fillStatus,depthFetchedUsd,depthConfidence,depthConfidenceReason,rankingEligible,partial,quoteStatus,maxExecutableSizeUsd}, coverage, referenceMidSource, meta, dataStatus, generatedAt, schemaVersion, sources, ETag

GET/api/data/slippage-spot?asset=BTC&size=10000&feeType=taker|maker&side=buy|sell

Live spot execution-cost ladder using venue order books, pair-specific fees, spread, VWAP impact and explicit depth coverage.

Fields: asset, sizeUsd, side, results[].{exchange,baseAsset,quoteAsset,feeBps,slippageBps,halfSpreadBps,spreadBps,totalBps,midPrice,vwap,sufficient,fillStatus,depthFetchedUsd,depthConfidence,depthConfidenceReason,partial}, coverage, meta, dataStatus, generatedAt, schemaVersion, sources, ETag

GET/api/data/dex-enrichment

Direct DEX market enrichment: OI, volume, funding, prices, protocol stats and venue coverage. fetchedAt is the time the upstreams were read — cross-venue funding predictions are judged against it, not the serve clock.

Fields: protocols[], crossVenueFunding, fetchedAt, coverage.{requested,succeeded,failed,rejected,retired}, dataStatus, generatedAt, upstreamTimestamp, schemaVersion, sources, ETag

GET/api/data/perps-chart?days=90

Aggregate daily perp-volume time series (DefiLlama) plus total open interest per day from our own snapshots. oi is absent on days with no capture — never zero-filled.

Fields: chart[{date,volume,oi?}], oiCoverage.{daysWithOi,days,note}, dataStatus, generatedAt, schemaVersion, sources

GET/api/data/volume

24h volume per venue from direct venue-reported perp tickers (CEX) and perp-DEX protocol APIs, with top symbols. Each row carries venueType so consumers can split CEX from DEX. exchanges[] is sorted by volume24h and holds the CEX rows plus Hyperliquid and dYdX (both venueType dex); every other perp DEX sits in dexVenues[], and dexTotalVolume is the sum of dexVenues[] only. Two hold-out rules keep a row visible while keeping its figure out of totalVolume. spikeFlagged[] names venues whose reported 24h volume is above 3x their own 7-day median (row carries volumeSpike true; the hold-out ends when the print holds). unverified[] names venues whose reported turnover PerpFinder cannot verify against an open-interest feed or an order book of matching size (row carries volumeUnverified true; the hold-out is permanent until an editorial review removes the entry). heldOut[] is the union, one {name, reason} per excluded venue, and each row carries heldOut set to the string unverified, the string spike, or null. change_1d is the day-over-day change of our captured snapshot total between the two newest calendar-adjacent captures, with every heldOut venue removed from both days, so it sits on the same basis as totalVolume; null when the two captures are not one day apart.

Fields: exchanges[].{name,volume24h,symbolCount,topSymbols[],venueType,volumeSpike?,volumeUnverified?,heldOut}, totalVolume, spikeFlagged[], unverified[], heldOut[].{name,reason}, dexVenues[], dexTotalVolume, noFeed[], change_1d, failed[], venueSlugs, updatedAt, dataStatus, generatedAt, upstreamTimestamp, schemaVersion, sources, ETag

GET/api/data/open-interest

Open interest per venue and the aggregate total. byExchange[] keeps the historical CEX list (plus Hyperliquid and dYdX); other perp DEXes are additive under dexVenues[]. Three totals, all in USD: totalOI is the sum of byExchange[] only (unchanged for existing consumers), cexTotalOI is the sum of the byExchange[] rows tagged venueType cex, and totalOIAllVenues is totalOI + dexTotalOI. coverage.cex and coverage.dex count the venues in THIS response; the /open-interest page replaces its initial server snapshot with this feed on each successful refresh.

Fields: totalOI, cexTotalOI, totalOIAllVenues, byExchange[].{name,oi,venueType}, dexVenues[], dexTotalOI, failed[], venueSlugs, coverage.{cex,dex,failed,noFeed,rejected,symbolCoverage}, updatedAt, dataStatus, generatedAt, upstreamTimestamp, schemaVersion, sources, ETag

GET/api/data/oi-long-short

DEX long vs short open-interest split per protocol.

Fields: protocols[], totalLong, totalShort, updatedAt, dataStatus, generatedAt, schemaVersion, sources

GET/api/data/funding-rates?asset=BTC,ETH&venue=Binance,Bybit&limit=50

Live funding-rate matrix: asset × exchange (normalized 1h). Missing OI/price are null with per-venue field support and explicit feed coverage. Venues that report a per-market interval (Binance, Bybit, OKX, Bitget, MEXC, Gate.io, BingX, KuCoin, Phemex, HTX, BloFin, Orderly, Aster) keep the raw rate and that interval; a market with no reported interval gets no rate. When only the interval request fails, the last reported interval of the market (at most 24 h old) is used and marked intervalSource "last-known" with intervalAgeMs. Optional asset/venue/limit filters narrow the matrix; a venue filter recomputes the per-row aggregates.

Fields: rows[].exchanges.{rate1h,rawRate?,intervalHours?,intervalSource?,intervalAgeMs?,oi,price}, exchanges[], coverage, updatedAt, meta.fieldSupport, meta.venueSlugs, dataStatus, generatedAt, upstreamTimestamp, schemaVersion, sources, ETag

GET/api/data/fees

Protocol fees (DEX, 24h/7d/30d per protocol) — aggregator-reported protocol fees, one row per Derivatives protocol. Fees paid and protocol-retained revenue are different measures. It carries no CEX rows: a CEX figure on /fees/generated is that venue’s 24h volume from /api/data/volume multiplied by a maker/taker rate. The volume rule therefore decides the CEX estimate too — a venue that /api/data/volume holds out, either as volumeSpike (24h volume above 3x its own 7-day median) or as volumeUnverified (reported turnover we cannot verify), keeps its row and its chip on /fees/generated, and stays out of the 24h fee total and the share bars, exactly as it stays out of totalVolume.

Fields: protocols[], dataStatus, generatedAt, upstreamTimestamp, schemaVersion, sources, ETag

GET/api/data/liquidations

Liquidations 24h/4h/1h, long vs short split. Binance and Bybit come from our own capture of their forced-close WebSockets; OKX, Gate.io, HTX and GMX come from their public REST feeds. meta.coverage states how much of the last 24 hours the capture actually covered and which venues have no continuous capture. Captured-venue windows are summed from stored five-minute slots, so a window edge includes the slot it falls inside.

Fields: events[], summary, meta.{venues,missingVenues,windowHours,coverage.{status,collector24h,collectorPerVenue,venuesWithoutCollector,collectorSince}}, updatedAt, dataStatus, generatedAt, upstreamTimestamp, schemaVersion, sources

GET/api/data/liquidations-history?hours=24|168&format=json|csv

Hourly liquidation history for Binance and Bybit from our own socket capture. Ledger v2 uses Bybit position sides and observed heartbeat windows; v1 remains separate. An hour with no capture returns null totals, never 0, and every bucket carries its coverage share.

Fields: collectorVersion, methodologyNote, series[].{t,longUsd,shortUsd,events,coverage,byVenue}, coverage.{fraction,perVenue,hoursWithCoverage,hoursRequested}, collectorSince, observedTotalUsd, hours, venues[], updatedAt, dataStatus, generatedAt, upstreamTimestamp, schemaVersion, sources

GET/api/data/options

BTC/ETH options market state: mark IV per strike/expiry, ATM term structure, put/call, Deribit DVOL (attributed), expiries.

Fields: status, updatedAt, assets.{BTC,ETH}.{asset,venue,status,updatedAt,spotIndex,dvol,summary,termStructure[],skew[],skewExpiry,expiries[]}, dataStatus, generatedAt, schemaVersion, sources

GET/api/data/options-history?asset=BTC&metric=dvol&interval=15m|1d&days=N

Self-collected options time series (no synthetic backfill); allowlisted metrics; maturity + methodologyVersion in every response.

Fields: series[], maturity, methodologyVersion, firstObservedAt, observations, start, end, dataStatus, generatedAt, upstreamTimestamp, schemaVersion, sources, ETag

GET/api/data/volume-quality

Reported vs observable CEX volume signals with separate data-confidence scoring. sweepsInWindow counts the last sweepWindowDays UTC days, not the lifetime total — use volume-quality-history for a longer count. unassessedTopVenues names the top-20-by-reported-volume venues that no sweep measures, with the reason.

Fields: sweep.venues[], maturity, methodologyVersion, datasetStart, firstSweepAt, sweepsInWindow, sweepWindowDays, sweepCountScope, unassessedTopVenues[].{venue,reportedVolume24hUsd,rankByReportedVolume,reason}, unassessedRankSource, dataStatus, generatedAt, schemaVersion, sources

GET/api/data/rwa-markets

RWA perp markets: stocks / forex / commodities across venues. platforms[].funding is an hourly rate fraction; null when the venue or its interval is unknown.

Fields: markets[], updatedAt

GET/api/data/mica

Committed ESMA MiCA register snapshot joined to PerpFinder venue status, service permissions, EU-derivatives scope and fee context.

Fields: register, scope, coverage, authorized[], tracked[], dataStatus, generatedAt, upstreamTimestamp, schemaVersion, sources, ETag

GET/api/data/premium

Binance USDT-M perpetual premium index — (mark − index) / index, single venue. indexPrice is Binance’s own index, not an independent spot reference.

Fields: entries[].{symbol,venue,markPrice,indexPrice,premium,fundingRate}, avgPremium, venue, updatedAt, dataStatus, generatedAt, schemaVersion, sources

GET/api/data/fear-greed

Fear & Greed index (alternative.me, attributed). current is null when the index is unavailable.

Fields: current.{value,classification,timestamp}, history[], updatedAt, staleSince, dataStatus, generatedAt, upstreamTimestamp, schemaVersion, sources

GET/api/data/funding-history?asset=BTC&type=aggregates|venue|realized&days=7&venue=Binance&rateType=current|predicted&methodologyVersion=1|2|3|4&format=csv

Self-collected funding history: aggregates, per-venue sweeps, or the append-only realized-settlement ledger. Rate types never mixed; no synthetic backfill. Sweep history defaults to one joined series: v2 sweeps up to the first v3 sweep (2026-09-23), then v3 up to the first v4 sweep (2026-09-25), then v4 (selectionMode "joined", methodologyVersions [2,3,4], seriesNote, excludedFields). Hourly rates are comparable across the join; v2 Aster and Crypto.com rawRate/rawIntervalMinutes are null; v2/v3 Hyperliquid predicted nextSettlementAt is the venue-reported start of the running hour, v4 points at the next settlement. methodologyVersion=1, 2, 3 or 4 selects one stored version only. Each version has its own dataset start.

Fields: series[]/entries[], coverage, maturity, methodologyVersion, methodologyVersions, selectionMode, seriesNote?, excludedFields?, datasetStart, dataStatus, generatedAt, schemaVersion, sources, ETag

GET/api/data/funding-prediction-accuracy?format=json|csv&detail=pairs

Funding prediction accuracy per venue and asset: stored predicted rates matched to the append-only realized-settlement ledger over one shared window per venue and asset (after the first usable stored prediction, up to the furthest targeted settlement, inside a 30-day read window). Accuracy (MAE and bias in bps per hour of the hourly-normalized rate, sign hit rate, median lead) is returned only when the publication gate passes (>=3 venues x >=30 settlements x >=7 days, <=30% missing); otherwise publication "withheld", accuracy null and the CSV returns 409. Hyperliquid v1-v3 predictions carry a labelled +1 interval settlement-time correction (horizonCorrection).

Fields: publication, gate.{ready,missing}, requirements, readWindow, methodologyVersions, method[], units, readiness[], accuracy[]|null, csv|null, dataStatus, generatedAt, schemaVersion, sources, ETag

GET/api/data/volume-quality-history?days=7&venue=&format=csv

Volume-quality sweep history per venue (reported vs observable signals over time). Measurements, never accusations.

Fields: series[], maturity, methodologyVersion, datasetStart, updatedAt, dataStatus, generatedAt, schemaVersion, sources

GET/api/data/volume-ranking?view=ranking|share|share-monthly&window=24h|7d&date=YYYY-MM-DD&format=csv

Perpetual volume ranking of CEX and DEX venues, reported and adjusted. Adjusted is the reported figure with held-out venues removed (unverified tier, 3x spike rule, failed depth test); it is not a modelled estimate. Each held-out venue keeps its reported figure and a dated reason. view=share gives the daily DEX share, raw and adjusted; view=share-monthly the volume-weighted monthly share. date pins one stored day file for citation. Measurements, not accusations.

Fields: ranking.{window,dates,capturedAt,reported,adjusted,gapUsd,gapShare,venues[].{name,type,reportedUsd,adjustedUsd,reportedRank,adjustedRank,holdOuts[].{rule,reason},spikeMultiple,depth}}, share[], monthly[], dayFile, permalink, depthSweepAt, datasetStart, dataStatus, generatedAt, upstreamTimestamp, schemaVersion, sources

GET/api/data/venue-history?venue=Binance&days=30&format=csv

Per-venue daily volume/OI history from the committed snapshot series (since 2026-06-05) — venue-reported figures, normalized; null never zero-filled. The window is a calendar window, and every day with no capture is listed.

Fields: series[].{date,observedAt,backfilled,volume24h,volume7d,volume30d,openInterestUsd,perpPairs}, venueSlug, datasetStart, windowStart, windowEnd, windowDays, missingDays, gaps[], missingDates[], note, updatedAt, dataStatus, generatedAt, schemaVersion, sources

GET/api/data/builder-fees?format=json|csv

Builder fees that wallets and trading apps add to Hyperliquid perps fills, summed from the public Hyperliquid builder-fill files over the newest stored window of up to 7 UTC days. Each row gives the app, its builder addresses, notional, builder fees, the added fee in bps, and the base-tier taker total against a direct Hyperliquid order on $10,000. The same rows as the /trading-apps table. A missing value is null in JSON and empty in the CSV.

Fields: window.{from,to,dates}, directTakerBps, costModel, rows[].{app,builderAddresses,daysCaptured,fills,notionalUsd,builderFeesUsd,addedFeeBps,takerTotalBps,taker10kUsd,premiumOverDirectPct}, updatedAt, dataStatus, generatedAt, upstreamTimestamp, schemaVersion, sources

GET/api/data/cost-history?market=perp&asset=BTC&size=100000&days=7|all&endDate=YYYY-MM-DD&methodologyVersion=1|2|3|4|5|6|7|8|active|all&venue=&format=json|csv

Execution-cost history by methodology version. While the active version has fewer than 7 days of stored sweeps, the default merges every stored version (selectionMode "merged"); each point still carries its own methodologyVersion and the boundary sweeps are flagged. methodologyVersion=active is the strict active-only series. Use endDate to page an older window.

Fields: series[].{attemptId,ingestedAt,collectedAt,methodologyVersion,versionBoundary,side,feeType,feeTierAssumption,engineDataStatus,coverage,venues[].{halfSpreadBps,priceImpactBps,quoteStatus,slippageModel,executionModel,fillStatus,depthConfidence,depthFetchedUsd}}, lastSampleAgeMinutes, collectorStale, selectionMode, methodologySelectionNote, activeMethodologyMinDays, selectedMethodologyVersions, availableMethodologyVersions, versionBoundaries, globalDatasetStart, datasetStart, maturity, maturityByMethodology, venueCoverage, updatedAt, generatedAt

Notes

  • Methodology. Every metric is defined on /data-definitions.
  • Freshness. Responses carry an updatedAt. Cache your own calls a few seconds to be kind.
  • Coverage. 50+ perp venues (CEX+DEX).
  • No code needed? The same feeds ship as free embeddable widgets — one line of HTML, no API key.