athenum

How Much Crypto Funding Rate History Can You Actually Get? Four Venues, Four Silent Limits

TLDR. Two of these four venues hold more than a year of Bitcoin perpetual funding history and none of them will hand you a year in a single call. Measured on 2026-08-15 at 11:40 UTC, unauthenticated, from an ordinary connection, against the public REST endpoints of Binance USD-M, Bybit, OKX and Bitget. Every one of them answered HTTP 200 with a success code. None of them said it had truncated anything. Binance returns 500 records or 1,000 records for the same limit=1000, depending on whether you also set a parameter you had no reason to set. Bybit hands back 200 whatever you ask for. OKX returns everything it holds, which today is 279 records, and will not page one record past a floor 92.8 days deep even though the contract has been listed since 2019. Bitget defaults to 20. And because every cap is counted in records while the thing you actually need is time, the exchange rate between them is the funding interval, which is a per-symbol property: 433 of Binance's 570 perpetuals fund every four hours, not every eight, so the same 500-record ceiling buys 83 days there rather than 167.

How far back does each venue's public funding rate history actually go?

Two different limits are at work and they are routinely confused. The first is the page size, meaning how many records one call will return. The second is the archive depth, meaning how far back the venue keeps any funding history at all. A page size you can page around costs you an extra loop. An archive floor costs you the data permanently. Measured on the BTC USDT-margined perpetual at the timestamp above:

Venue Records with no parameters Most records in one call Oldest record reachable by any route History available
Binance USD-M 100 500 without startTime, 1,000 with it 2019-09-10 08:00 UTC 2,531 days
Bybit v5 200 200 2020-03-25 16:00 UTC 2,334 days
OKX v5 279 279 2026-05-14 16:00 UTC 92.8 days
Bitget v2 20 100, and 270 across all pages 2026-05-17 16:00 UTC 89.8 days

Binance and Bybit keep the record and make you page for it. OKX and Bitget do not keep it. That is the practical split, and it is not a small one: the same query that yields nearly seven years of Binance funding yields under three months of OKX funding, and no amount of client-side cleverness closes the gap.

Note what the last column does and does not say. Two venues hold more than a year. Zero of them serve a year in one request, because the largest single response any of them will produce is Binance's 1,000 records, and at an eight-hour interval 1,000 records is 333.3 days. A year of history from these endpoints is always a paging loop, never a call.

The OKX floor deserves its own sentence, because a reasonable person would assume it is the contract's listing date. It is not. OKX's own public instruments endpoint reports BTC-USDT-SWAP with a listTime of 2019-11-12 11:16 UTC and a state of live. The contract is six and three quarter years old and the funding history behind it is ninety-three days old. Whether that is a deliberate retention policy or a limit on what this particular route can reach, the practical effect on you is the same. Bitget lands within three days of the same floor from an entirely separate codebase, and roughly three months is a common enough default for a hot-storage window that the agreement is worth noting without being read as proof of anything.

Is a retention window a rolling window? Not on every venue

This is the part you cannot see by measuring once, and it is the reason a retention number copied from any article, including this one, belongs in a monitoring job rather than in a config file. The same two endpoints were read on three consecutive days.

Measured at OKX oldest record OKX records in one call OKX history Bitget oldest record Bitget history
2026-08-13 11:49 UTC 2026-05-11 16:00 UTC 282 93.8 days 2026-05-15 16:00 UTC 89.8 days
2026-08-14 12:02 to 12:10 UTC 2026-05-14 16:00 UTC 276 91.8 days 2026-05-16 16:00 UTC 89.8 days
2026-08-15 11:40 UTC 2026-05-14 16:00 UTC 279 92.8 days 2026-05-17 16:00 UTC 89.8 days

Read the two venues against each other. Bitget's floor advanced exactly one day on each of the two intervals between the three readings, and its available history stayed flat at 89.8 days throughout. That is a true rolling window: old records leave the back of the queue at the same rate new ones arrive at the front, and 89.8 days is a constant you can rely on.

OKX does not behave that way. Its floor jumped forward three days between the first and second reading, then did not move at all between the second and third, and its page size went 282, then 276, then 279. Between the first and second reading its available history shrank by two days in the space of one, even as new settlements kept arriving at the front.

Be careful about what that does and does not establish, because it is the same trap the rest of this piece is about. What it establishes is that OKX's floor is not advancing smoothly: the readings are 24.3 and 23.6 hours apart, and a floor delta of 3.00 days and then 0.00 days cannot be reconciled with a window that rolls continuously, no matter where inside those hours each sample landed. What it does NOT establish is the mechanism. A periodic batch purge fits, but so does a fixed daily cutoff that my drifting sample times straddled inconsistently, and so does a one-off backfill or reindex. Three samples cannot separate those, and I did not get to see OKX's job scheduler.

One narrow alternative the readings do rule out is a single frozen snapshot of the whole response. Between the second and third reading the page grew from 276 records to 279, which is exactly the three settlements the clock predicts for those 23.6 hours on an eight-hour schedule, while the tail did not move at all. A response served whole from one stale instant would not have advanced at the head like that. What this does NOT rule out is any arrangement in which the head and the tail are produced on different schedules, and it is worth being clear that a periodic purge job is exactly such an arrangement: a floor updated by an occasional batch, sitting behind a head that updates every settlement, is precisely the pattern above. So this observation narrows the field by one and is entirely consistent with the purge hypothesis rather than evidence against it.

So the honest statement is the observation and not the cause. OKX's archive floor moves in steps rather than continuously; Bitget's advances one day per day, three readings running. Only the second is a rolling window you can quote as a constant.

The consequence is practical and it cuts against the way this kind of number is normally used. A single-instant reading of OKX can be wrong by three days, so "about 90 days" is honest and "92.8 days" is not, unless it carries the timestamp it was taken at. And the cheap general test needs no privileged access at all: read the floor on three consecutive days and compare each move against the time that actually passed. Two readings give you one delta, which is a rate; three tell you whether that rate is steady, which is the question you actually care about. If the floor moves further than the clock did, or stalls while the clock does not, the figure needs an as-of stamp rather than a place in your config.

Why does the same Binance request return 500 records one time and 1,000 the next?

Because the ceiling depends on a parameter that has nothing to do with the ceiling. Asking for limit=1000 with no startTime returns 500 records, covering 2026-03-02 00:00 to 2026-08-15 08:00 UTC. Adding a startTime and asking for the identical limit=1000 returns 1,000 records. Same endpoint, same symbol, same limit value, exactly twice the data, and the only difference is a parameter that a caller who just wants "the most recent history you have" would never think to set.

The ceiling is firm and it is quiet. Probing it with no startTime: asking for 400 returns 400, asking for 500 returns 500, and asking for 501, 600, 750, 999 or 1,000 all return 500. There is no warning field, no truncation flag, no difference in the response shape. A client that logs "requested 1000" and moves on has a log entry that is wrong by half.

What does each venue do when you ask for more than it will give?

Four venues, four behaviours, and only one of them is loud about it. This is the table worth pinning above a data pipeline.

Venue Ask above the cap What comes back How you would notice
Binance USD-M limit=501 to 1000 500 records, HTTP 200 You would not
Binance USD-M limit=1001 HTTP 200 carrying an error object Only if you check the body
Bybit v5 limit=201, limit=1000 200 records, retCode 0, retMsg "OK" You would not
OKX v5 limit=500 279 records, code "0" You would not
Bitget v2 pageSize=101, pageSize=200 100 records, code "00000" You would not

The Binance row in the middle is the one that will actually break something. Asking for limit=1001 does not return HTTP 400. It returns HTTP 200, and the body is this:

{"status":"ERROR","type":"GENERAL","code":"99099990","errorData":"illegal params.","data":null,"subData":null,"params":null}

The success path for that endpoint is a JSON array. That is a JSON object. So a client that parses the response and iterates it does not crash in any obvious way: it iterates seven key names, and a client that calls len() on it gets 7 rather than a record count. The failure is silent, it is typed as success at the transport layer, and it produces a plausible small number. Checking the HTTP status is not enough here.

Do not generalise that into an error-object check and think it covers the rest. Of these four, only Binance put an actual error object inside a 200 on this endpoint. The other three did something categorically different, and arguably worse: they returned a perfectly ordinary success payload that was simply short, with no error field anywhere to catch. A client hardened against the Binance shape is no better protected against the other three than it was before.

The OKX row needs one correction that is easy to get backwards, because it changes what you should do about it. OKX does not ignore your limit. It honours it exactly: ask for 1 and you get 1, ask for 50 you get 50, 100 you get 100, 101 you get 101, 200 you get 200. Ask for 500 and you get 279, because 279 is everything it holds. A cap and an exhaustion look identical from a single probe, and only the second one means you have already got all the data there is.

Those rows are quieter, and quiet is the problem: there is nothing for an error handler to fire on. Bybit will tell you retMsg "OK" while handing you a fifth of what you asked for. Bitget will accept a pageSize of 200 and serve 100. In none of these cases does the response carry a field that distinguishes "this is all of it" from "this is all I felt like sending". The comparison that tells them apart has to be written by you, because the API will not do it, and it takes two forms depending on the call. If you set a start boundary, compare the oldest record you received against the oldest you asked for. If you did not set one, and the "records with no parameters" column in the very first table of this piece is exactly that case, there is no requested boundary to compare against, so compare against the venue's known floor or listing date instead, or simply force a boundary so that there is something to check. The unbounded call is the one most likely to fool you, and it is usually the first one an integration makes.

There is one more shape of silence. Ask Bybit for a window it has no data in, such as January 2020, and it returns retCode 0, retMsg "OK", and an empty list. Ask OKX to page earlier than its floor and it returns code "0" and an empty list. An empty success is indistinguishable from "no funding was charged in this window" unless you already know where the floor is, and a backtest that treats it as the latter will quietly model a period of zero funding cost that never existed.

Why is a 200-record cap not a 66-day window?

Because the cap is denominated in records and a backtest is denominated in time, and the conversion rate between them is the funding interval, which is set per symbol rather than per venue.

Be careful about which population that claim is made over, because the two readings are not the same claim and only one of them is quantified. Eight hours is the reference interval: it is the value both venues fall back to, settled at 00:00, 08:00 and 16:00 UTC, and it is what the flagship BTC contract uses on both. That is a statement about the default, and I am not putting a number on it. Count individual listed symbols and you get numbers, and they are not alike: 8 hours covers 136 of 570 Binance perpetuals, about 24 percent, while on Bybit it covers 371 of 781, or 47.5 percent, which is nearer a coin flip than a minority. Calling both of those "the minority case" would flatten two quite different distributions into one soundbite. A pipeline is a symbol-level thing, so the symbol-level count is the one that governs how much history a record cap buys you, and on Binance that difference is large.

Venue Perpetuals trading Fund every 8h Fund every 4h Fund every 1h
Binance USD-M 570 136 433 (76.0%) 1
Bybit linear 781 371 (47.5%) 409 (52.4%) 1

Three quarters of Binance's perpetual book funds every four hours. So the record ceilings above convert into wildly different amounts of history depending on which symbol you point them at:

Venue, one maximum call At 8h At 4h At 1h
Binance, 500 records, no startTime 166.7 days 83.3 days 20.8 days
Binance, 1,000 records, with startTime 333.3 days 166.7 days 41.7 days
Bybit, 200 records 66.7 days 33.3 days 8.3 days
Bitget, 100 records 33.3 days 16.7 days 4.2 days

The trap is the one that looks like a rounding error. A researcher who validates a data loader on BTCUSDT, where the interval is eight hours, and then points the same loader at the majority of the book, where it is four, gets exactly half the intended lookback with no error, no warning and no change in the shape of the output. The same code, the same limit, the same success code, half the history. If a study fixes its window in records rather than in days, its lookback silently becomes a function of which symbols happened to be in the basket.

One symbol makes the point at the extreme, and it makes a second point its author did not plan. COTIUSDT funds every hour on Binance and every eight hours on Bybit: the same ticker, two venues, an eight-fold difference in how many records a fixed number of days costs you. Twenty-four hours before this reading, Bybit was funding COTIUSDT hourly too, and the venue moved it. The interval is not a property of the asset. It is a per-symbol, per-venue setting that changes between one day's measurement and the next, which is exactly why a pipeline should read it rather than assume it.

How do you check all of this yourself?

Every number above comes from an unauthenticated GET. No key, no account, no library. Six checks, about fifteen requests, reproduce the whole thing.

  1. Binance page size. Call the USD-M fundingRate endpoint for BTCUSDT with limit=1000 and count the array. Then call it again with the identical limit plus any startTime in the past and count again. 500 against 1,000 is the finding.
  2. Binance archive depth. Call it with a startTime early enough to predate the contract, such as 2019-01-01 in milliseconds, and read the fundingTime of the first element. It answers 2019-09-10 08:00 UTC.
  3. Bybit page size and depth. Call the v5 linear funding history endpoint with limit=1000 and count 200. Then walk startTime and endTime windows backward through early 2020 until one comes back empty; the oldest record that survives is 2020-03-25 16:00 UTC.
  4. OKX floor and limit behaviour. Call the v5 public funding-rate-history endpoint with no limit, then with 1, 50, 100, 101, 200 and 500, and compare each count against what you asked for. Read the oldest fundingTime, pass it back as the after parameter, and watch an empty list come back with code "0". Then call the public instruments endpoint for the same instId and compare listTime against that floor.
  5. Bitget floor. Call the v2 mix history-fund-rate endpoint with pageSize=100 and walk pageNo upward. Pages 1 and 2 return 100 each, page 3 returns 70, page 4 is empty. 270 records at eight hours is 90 days, which matches the floor to within a rounding step.
  6. Intervals. Read fundingIntervalHours from the Binance fundingInfo endpoint and intersect it with the trading perpetuals in exchangeInfo, and read fundingInterval in minutes from the Bybit instruments-info endpoint for the linear category. Both are public and unauthenticated.

Run check 4 and check 5 on three consecutive days, not two, and you have the retention table from the second section as well. Two readings give you one delta, which is a rate; the third is what tells you whether that rate is steady, and steadiness is the whole finding. Every one of those calls returns a success status. The finding is never in the status; it is always in the count, and in the oldest timestamp.

What this reading will not tell you

First, the tables above are readings, not constants. The ceiling RULES and the error behaviours were stable across three days of probing: Binance's 500 and 1,000, Bybit's 200, and Bitget's 100 per page did not move, and neither did what any venue does when you ask for more. One ceiling NUMBER did move, and it is worth being precise about why: OKX returned 282, then 276, then 279, not because its cap changed but because it has no cap short of exhaustion, so its one-call count is just the size of an archive that was itself moving. Every archive floor moved during those three days and one of them moved unevenly. All of it was measured against public endpoints at the timestamps printed beside it, and every number is a published parameter a venue may revise whenever it likes. The honest way to use this piece is as a method for checking your own venues rather than as a table to copy into a config file.

Second, it is one symbol for the page-size and depth work, the BTC USDT-margined perpetual, plus a full sweep of both venues' perpetual books for the interval counts. Coin-margined and inverse books were not probed and should not be assumed to match.

Third, the archive floors are what the ordinary public REST endpoint serves. They are not a claim about what a venue holds internally, what it serves under a paid data agreement, or what it publishes through a separate bulk or historical download product. If you need years of funding from a venue whose live endpoint keeps three months, the question to ask its support desk is whether an archive exists elsewhere, not whether the endpoint is broken.

Fourth, the three-day retention table is three points taken at 11:49, then 12:02 to 12:10, then 11:40 UTC. That spread is far too small to manufacture a three-day floor move, so the observation that OKX does not advance smoothly is safe, but three unevenly-spaced samples are not enough to characterise the schedule behind it, and I have said so where the table appears rather than only here.

The practical version is four lines. Never trust the record count you asked for; count what arrived. Compare the oldest timestamp you received against the oldest you asked for when you set a boundary, and against the venue's known floor when you did not, and treat any gap as truncation until proven otherwise. Convert record caps into days using that symbol's funding interval rather than the eight hours you have in your head, and re-read that interval instead of caching it. And before you design a study around a venue, find its archive floor and then find it again on each of the next two days, because on some venues that floor, and not your ambition, is what sets the length of your backtest.

Working out which venue actually has the history a question needs, before building anything on top of it, is the sort of cross-venue groundwork Athenum's explainer on funding intervals covers from the other direction.

Jackson Ly, Athenum Analytics