Ключевые выводы/api/v3/klines отдаёт 500 свечей по умолчанию и максимум 1 000; на споте вызов стоит плоские 2 веса, поэтому листайте всегда на максимуме.
/api/v3/klinesотдаёт 500 свечей по умолчанию и максимум 1 000; на споте вызов стоит плоские 2 веса, поэтому листайте всегда на максимуме.- Листайте курсором: сдвигайтесь за open time последней полученной свечи — фиксированный шаг ломается на дырах от техработ и делистингов.
- Выбрасывайте недозакрытую свечу (close time в будущем) и дедупите по open time — это официальный идентификатор свечи.
- Год минутных свечей — 526 запросов (~1 050 веса); в нашем замере полная минутная история BTCUSDT (~4,7 млн свечей) скачалась через один адрес примерно за 100 секунд на 16 соединениях; последовательный обход — около 25 минут.
- Спотовая пара 500/1 000 действует и для uiKlines, trades и aggTrades; фьючерсные свечи разрешают 1 500, но взвешивают вызов по
limit— константы читайте из того API, который вызываете. - Историю всего рынка не тяните через API:
data.binance.visionотдаёт официальные месячные и дневные ZIP с контрольными суммами; бэкфилл из архивов, живой хвост — пейджером. - Исторического стакана нет ни в API, ни в архивах — пишите глубину сами со стримов или покупайте у поставщиков данных.
Краткое содержание подготовлено с помощью ИИ.
Что на самом деле возвращает параметр limit
Параметр limit ограничивает число свечей в одном ответе, и два его значения объясняют большую часть путаницы вокруг эндпоинта: без параметра действует дефолт в 500 свечей, а 1 000 — жёсткий потолок; в документации это записано прямо: «Default: 500; Maximum: 1000». То есть «голый» запрос молча отдаёт половину возможного — и первое лекарство от «мало свечей» состоит в том, чтобы просто попросить максимум.
На Spot API нет ни одной причины просить меньше. Вызов свечей стоит плоские 2 веса независимо от limit, так что запрос на 1 000 свечей оплачивается так же, как запрос на 10: страница меньше максимума при выгрузке — это те же деньги за меньший объём данных. С параметрами времени лимит связан ещё одним важным поведением: без startTime и endTime эндпоинт возвращает самые свежие свечи, а не самые старые. Для живого дашборда это ровно то, что нужно; для выгрузки истории — ровно наоборот.
Две мелочи из документации эндпоинта экономят реальные часы отладки. Первая: свечи однозначно идентифицируются по open time — именно на этом времени должны строиться пагинация и дедупликация. Вторая: эндпоинт принимает параметр timeZone, который меняет нарезку интервалов, но startTime и endTime всегда трактуются в UTC независимо от него — перепутав это, вы получите диапазоны со сдвигом в часы, которые выглядят как пропавшие данные.
Как выгрузить длинный диапазон по частям
Паттерн — цикл с курсором по параметрам startTime и endTime: запрашиваем до 1 000 свечей от курсора, сохраняем, сдвигаем курсор сразу за open time последней полученной свечи и повторяем, пока в диапазоне что-то остаётся. Единственное проектное решение, отделяющее надёжный пейджер от хрупкого, — это как именно двигать курсор. Шаг на фиксированную величину («прибавить 1 000 интервалов») предполагает, что в данных нет дыр, — а дыры есть: технические работы и делистинги оставляют пропуски, и фиксированный шаг либо перекачивает пересечения, либо молча перепрыгивает данные. Сдвиг от последней реально полученной свечи переживает всё это.
Вот законченный пейджер на этих правилах — пример запроса свечей, который можно запустить как есть, без API-ключа:
import time, requests
BASE = "https://api.binance.com"
def fetch_history(symbol, interval, start_ms, end_ms):
out, cursor = [], start_ms
while cursor < end_ms:
resp = requests.get(f"{BASE}/api/v3/klines", params={
"symbol": symbol, "interval": interval,
"startTime": cursor, "endTime": end_ms, "limit": 1000})
if resp.status_code == 429: # отступаем и повторяем
time.sleep(int(resp.headers.get("Retry-After", "1")))
continue
batch = resp.json()
if not batch: # в диапазоне пусто
break
for k in batch:
if k[6] > time.time() * 1000: # close time в будущем:
continue # выбрасываем недозакрытую свечу
if not out or k[0] > out[-1][0]: # дедуп по open time
out.append(k)
cursor = batch[-1][0] + 1 # курсор за последний OPEN time
return out
На что смотреть в поведении: курсор встаёт на last open time + 1 мс — это безопасно, потому что open time и есть идентификатор свечи; последняя свеча живого диапазона выбрасывается, пока её close time не наступил, — недозакрытая свеча изменится после сохранения; а 429 обрабатывается с учётом Retry-After, а не повторными запросами без паузы.
Теперь арифметика, которая делает всё это практичным. Год минутных свечей — 525 600 записей: 526 запросов при потолке в 1 000, или около 1 050 веса при цене 2 за вызов. На фоне бюджета Spot API в 6 000 веса в минуту это помещается в лимит одной минуты с запасом. Полную версию этой задачи мы замерили на своей сети: вся минутная история BTCUSDT — примерно 4,7 миллиона свечей, около 4 710 запросов при limit=1000 — стоит 9 420 веса, то есть около полутора минут бюджета одного адреса. Реальное время целиком зависит от конкурентности: последовательный пейджер вроде примера выше идёт около 25 минут (3 запроса в секунду), а 16 одновременных соединений через один выделенный адрес закрывают задачу примерно за 100 секунд без единого 429 — полный замер здесь. Для одной пары API — совсем не то узкое место, которого ждут.
У каких эндпоинтов тот же потолок
Пара «500 по умолчанию, максимум 1 000» — не особенность свечей, а типовой шаблон всей спотовой семьи маркет-данных, поэтому один пейджер работает по всей семье. У спотового эндпоинта свечей есть «витринный» близнец uiKlines — те же параметры, тот же потолок, та же идентификация по open time. Сделки и агрегированные сделки (/api/v3/trades, /api/v3/aggTrades) несут в документации идентичную строку «Default 500; max 1000», причём у aggTrades есть своя особенность: окно startTime/endTime там обязано быть короче одного часа.
Константы тихо меняются на фьючерсах — и скопированный спотовый код из-за этого работает с неверными лимитами. Эндпоинт свечей USDⓈ-M на fapi.binance.com принимает до 1 500 свечей за запрос — потолок выше спотового, — но тарифицирует вызов по размеру: вес запроса растёт вместе с limit, а не остаётся плоским. Логика пейджера переносится без изменений; две константы — размер страницы и цена вызова — должны браться из фьючерсной таблицы эндпоинтов, а не из спотовых привычек. Общее правило из нашей статьи о лимитах Binance API работает здесь в миниатюре: потолки REST-эндпоинта свечей читайте в документации того API, который реально вызываете, а код пусть принимает размер страницы параметром.
Когда готовые архивы данных выгоднее API
Пагинация — правильный инструмент для одной пары и ограниченного диапазона. Она перестаёт быть правильным инструментом, когда задача звучит как «весь рынок, вся история», — и альтернативу даёт сам Binance. Официальные публичные архивы на data.binance.vision (проект binance-public-data) отдают исторические рыночные данные обычными ZIP-файлами: месячные и дневные дампы свечей, сделок и агрегированных сделок по споту и обоим фьючерсным рынкам, каждый с файлом контрольной суммы SHA-256 рядом. Год свечей одной пары превращается в один HTTP GET месячного архива вместо сотен постраничных запросов, а весь рынок — в цикл wget вместо миллионов вызовов API, которые превысили бы любой практический поадресный бюджет запросов.
Для проектирования пайплайна важны два свойства архивов. Свежесть: файлы за прошедший день появляются через несколько минут после 00:00 UTC, то есть архивы всегда отстают от настоящего — стандартный продовый паттерн: бэкфилл из архивов, затем доливка живого хвоста пейджером из раздела выше. Изменяемость: Binance прямо предупреждает, что архивные файлы могут обновляться задним числом при обнаружении проблем, — долгоживущим датасетам стоит перепроверять контрольные суммы, а не считать архив неизменным.
Картину завершает одна граница: исторический стакан — слепая зона обоих инструментов. REST-эндпоинт глубины возвращает только текущий снимок — вызова «стакан по состоянию на прошлый вторник» не существует, — и в публичных спотовых архивах истории стакана тоже нет. Если исследованию нужна историческая глубина, её записывают самостоятельно со стримов WebSocket в момент событий либо покупают у специализированных поставщиков данных. Знание этой границы до проектирования бэктеста экономит недели.
Что остаётся от выбора «API или архивы»? Любую серьёзную ширину бэкфильте из архивов; пейджер оставьте для однопарных задач и для живого хвоста, который архивы ещё не догнали. Поадресные бюджеты по-настоящему становятся ограничением на третьем типе нагрузки — непрерывном многопарном опросе поверх этого бэкфилла, где сотни символов конкурируют за 6 000 веса в минуту одного адреса. Это проблема исходящих адресов, а не пагинации: разнесение опроса по выделенным адресам даёт каждому воркеру собственный бюджет, и ровно это дают наши прокси для Binance — выделенные прокси от одного IP и пакетные планы на тысячи адресов, доступ по IP whitelisting, а также HTTP, HTTPS, SOCKS4 и SOCKS5.
FAQ
Какое максимальное число свечей за один запрос?
1 000 на Spot API; без параметра limit вернётся 500. Фьючерсы USDⓈ-M принимают до 1 500 свечей за запрос, но тарифицируют вызов по размеру: больше limit — больше вес, тогда как спот берёт плоские 2 веса за любой размер. На споте нет причин листать меньше максимума; на фьючерсах сначала сверьтесь с таблицей весов.