Четыре маршрута только для чтения, без ключа и без cookie, с именами полей, которые ожидают агрегаторы (CoinGecko, CoinMarketCap) и рыночные библиотеки (CCXT). Они существуют, чтобы их читали роботы: тот, кто строит дашборд, питает индекс или сверяет цену, читает отсюда. Маршрута для ордеров в этом API нет — ничто здесь не двигает средства и не требует счёта.
Всё висит на https://offcode.pro/api/public/, принимает только GET и отвечает application/json. ticker_id имеет вид BASE_TARGET (BTC_USDT): до подчёркивания указывается базовая валюта, после — валюта котировки; допускаются заглавные буквы и цифры, от 2 до 15 символов в каждой части. Пара, символ которой не укладывается в эту форму, не публикуется.
Каждое число передаётся десятичной строкой, никогда числом с плавающей точкой — монета с восемью знаками теряет знаки, проходя через float. Моменты — миллисекунды с начала эпохи Unix.
| Маршрут | Что возвращает | Параметры |
|---|---|---|
GET /pairs | Все пары, которые обслуживает этот фид, каждая с источником цены | — |
GET /tickers | Цена, объём за 24 ч, максимум, минимум и обе стороны для спотовых пар | — |
GET /orderbook | Книга ордеров одной пары | ticker_id · depth |
GET /trades | Последние сделки одной пары, новые первыми | ticker_id · type · limit |
GET /historical_trades | То же, что /trades — два имени, одна реализация | те же |
Список всего, что обслуживает этот фид, и ворота для остальных маршрутов: ticker_id, которого здесь нет, получает 404 на книге и на сделках. kind отделяет спотовый рынок платформы (spot) от бессрочного (perp); когда одно имя обслуживает оба, оно остаётся за спотом. price_source указывает, откуда берётся число, прямо в элементе.
Цена, объём за 24 часа, максимум, минимум и обе стороны для каждой пары spot. Пары perp не включены: это срез, который описывает стандарт агрегаторов.
ticker_id | Пара в форме BASE_TARGET. |
base_currency · target_currency | Базовая валюта и валюта котировки торговой пары, каждая в отдельном поле. |
last_price | Последняя цена сделки на бессрочном контракте. |
base_volume | Объём за 24 ч в базовой валюте по данным площадки, если она его публикует. Только при отсутствии этих данных API делит target_volume на last_price и округляет результат вниз до 8 знаков после запятой. Это явно обозначенное расчётное значение, а не показатель, полученный от биржи. |
target_volume | Объём за 24 ч в котируемой валюте, как есть из источника. |
bid · ask | Равны друг другу и средней цене между лучшими ценами покупки и продажи бессрочного контракта. Эти значения не являются лучшими ценами заявок в стакане. См. Откуда берутся данные. |
high · low | Максимум и минимум за последние 24 часа. |
timestamp | Момент чтения, в миллисекундах с начала эпохи Unix. |
Книга ордеров одной пары. bids и asks — пары [price, size], обе десятичными строками, лучшая цена первой. timestamp — часы источника, не наши: локальные позволили бы устаревшей книге сойти за свежую.
depth необязателен и доходит до 100 уровней на сторону. Без него возвращается потолок: 100. Больший запрос не отклоняется — он обрезается до 100.
Недавняя лента одной пары, новая сделка первой. Два маршрута — одна реализация и один кэш: стандарт агрегаторов просит historical_trades, CCXT обычно просит trades, и так никогда не будет дня, когда один ответит иначе, чем другой.
type необязателен (buy или sell, по агрессивной стороне), а limit доходит до 100 и тоже обрезается, а не отклоняется. Без type возвращаются обе стороны, без limit — потолок: 100. trade_id производный — момент, сторона, цена и размер — стабилен для одной и той же сделки между двумя чтениями, и это не собственный идентификатор сделки площадки.
Цена, книга, сделки и объём за 24 часа в этом фиде приходят с бессрочного рынка — в том числе для пар с меткой spot. У спотового рынка OFFCODE нет собственного стакана: для котировки используется средняя цена между лучшими ценами покупки и продажи бессрочного контракта с тем же символом. Поэтому каждый элемент /pairs несёт price_source: тот, кто читает фид, узнаёт источник в самом фиде, а не выводит его из метки — и не выводит неверно.
ask − bid здесь равен нулю. Нулевой спред невозможен в реальной книге: не считайте эти два поля сторонами одной.base_volume — объём за 24 ч в базовой валюте бессрочного контракта, как его измеряет площадка при публикации. Только в его отсутствие маршрут делит target_volume на last_price и обрезает до 8 знаков вниз — объявленный расчёт, а не число с биржи.
Пара публикуется только там, где существует торгуемый рынок с тем же символом: публикация — это обещание обслуживать, и ticker_id, который выходит из /pairs, должен сразу же отвечать на книге и на сделках. Хранимая монета без торгуемой пары остаётся вне фида, как и пара, чьё имя на площадке несёт множитель — бессрочный 1000PEPE_USDT публикуется как есть, никогда как PEPE_USDT.
600 запросов в минуту на IP, на каждом маршруте. Выше — 429.
Ответ 2xx несёт Cache-Control: public, max-age=5; всё, что не 2xx, несёт no-store, потому что 404, удержанный пять секунд, переживает только что залистингованную пару, а удержанный 502 возвращает площадку, которая лежит, после того как она уже поднялась.
Внизу книга перечитывается не чаще, чем раз в 350 мс на пару и глубину, а сделки — раз в 1 секунду на пару: потолок обращений к площадке — это кэш, а не число спрашивающих. Неудачное чтение никогда не кэшируется.
Access-Control-Allow-Origin: *, без учётных данных — нет cookie, ключа и заголовка аутентификации, и ничто в ответе не персонализировано.
| Код | Когда возникает | Тело |
|---|---|---|
| 400 | ticker_id не по форме, type, который ни buy, ни sell, нецелый depth или limit | {"code": "VALIDATION_ERROR", "message": "…"} |
| 404 | Корректный ticker_id, которого нет в /pairs | {"error": "UNKNOWN_TICKER"} |
| 429 | Превышен лимит запросов для этого IP | {"statusCode": 429, "code": "RATE_LIMITED", "message": "…"} |
| 502 | Площадка не ответила или ответила ошибкой | {"code": "UPSTREAM_UNAVAILABLE", "message": "…"} |
502, а не 500, намеренно: сбой на площадке — не наш сбой, и тому, кто читает снаружи, нужна эта разница, чтобы понять, стоит ли пробовать снова.
Домен стоит за Cloudflare. Браузер проходит; вызов из командной строки без cookie может получить проверку вместо JSON — это не отказ маршрута, это край сети спрашивает, кто пришёл. Если вашему роботу нужно непрерывно обходить фид, попросите разрешение до включения краулера: укажите user-agent, исходящие диапазоны IP и предполагаемую частоту через Контакты.
Комиссий и торговых правил в этом API нет: они живут по адресу /api/public/fees и в разделе Комиссии. Состояние сервисов — на странице Статус платформы.