OFFCODEDOCSAbrir plataforma

API pública

Quatro rotas de leitura, sem chave e sem cookie, com os nomes de campo que os agregadores (CoinGecko, CoinMarketCap) e as bibliotecas de mercado (CCXT) esperam encontrar. São feitas para robô: quem monta painel, alimenta índice ou reconcilia preço lê daqui. Não existe rota de ordem nesta API — nada aqui move fundos nem exige conta.

Base e convenções

Tudo pende de https://offcode.pro/api/public/, só aceita GET e responde application/json. O ticker_id é BASE_COTACAO (BTC_USDT): base antes do sublinhado, cotação depois, letras maiúsculas e dígitos, de 2 a 15 caracteres de cada lado. Par cujo símbolo não cabe nesse formato não é publicado.

Todo número viaja como string decimal, nunca como ponto flutuante — moeda de oito casas perde casa ao passar por float. Instantes são milissegundos desde a época Unix.

RotaO que devolveParâmetros
GET /pairsTodo par que este feed endereça, com a fonte do preço em cada item
GET /tickersPreço, volume de 24 h, máxima, mínima e as duas pontas dos pares spot
GET /orderbookLivro de ofertas de um particker_id · depth
GET /tradesNegócios recentes de um par, do mais novo para o mais velhoticker_id · type · limit
GET /historical_tradesA mesma coisa que /trades — dois nomes, uma implementaçãoidem

Pares — /pairs

A lista de tudo que este feed endereça, e o portão das outras rotas: ticker_id que não sai daqui responde 404 no livro e nos negócios. O kind separa o mercado à vista da casa (spot) do perpétuo (perp); quando o mesmo nome serve aos dois, o spot fica com ele. O price_source diz de onde o número sai, no próprio item.

GET /api/public/pairs
[
  { "ticker_id": "BTC_USDT", "base": "BTC", "target": "USDT", "kind": "spot", "price_source": "offcode-perp" },
  { "ticker_id": "1000PEPE_USDT", "base": "1000PEPE", "target": "USDT", "kind": "perp", "price_source": "offcode-perp" }
]

Tickers — /tickers

Preço, volume de 24 horas, máxima, mínima e as duas pontas de cada par spot. Os pares perp não entram: este é o recorte que o padrão dos agregadores descreve.

GET /api/public/tickers
[
  {
    "ticker_id": "BTC_USDT", "base_currency": "BTC", "target_currency": "USDT",
    "last_price": "…", "base_volume": "…", "target_volume": "…",
    "bid": "…", "ask": "…", "high": "…", "low": "…", "timestamp": 1756800000000
  }
]
ticker_idO par, no formato BASE_COTACAO.
base_currency · target_currencyAs duas pernas do par, separadas.
last_priceÚltimo preço negociado no perpétuo.
base_volumeVolume de 24 h em moeda-base, medido pela venue quando ela o publica. Só na ausência dele a rota divide target_volume por last_price e corta em 8 casas para baixo — conta declarada, não número da corretora.
target_volumeVolume de 24 h em moeda de cotação, cru da fonte.
bid · askIguais entre si e iguais ao mid do perpétuo — não são topo de livro. Ver Fonte dos dados.
high · lowMáxima e mínima das últimas 24 h.
timestampInstante da leitura, em milissegundos desde a época Unix.

Livro — /orderbook

O livro de ofertas de um par. bids e asks são pares [preço, tamanho], os dois em string decimal, do melhor preço para o pior. O timestamp é o relógio da fonte, não o nosso: o local faria um livro velho passar por fresco.

depth é opcional e vale até 100 níveis por lado. Omitido, devolve o teto: 100. Pedido maior não é recusado — é limitado a 100.

GET /api/public/orderbook?ticker_id=BTC_USDT&depth=100
{
  "ticker_id": "BTC_USDT",
  "timestamp": 1756800000000,
  "bids": [["…", "…"], ["…", "…"]],
  "asks": [["…", "…"], ["…", "…"]]
}

Negócios — /trades e /historical_trades

A fita recente de um par, do negócio mais novo para o mais velho. As duas rotas são a mesma implementação e o mesmo cache: o padrão dos agregadores pede historical_trades, o CCXT costuma pedir trades, e assim nunca existe o dia em que uma responde diferente da outra.

type é opcional (buy ou sell, pelo lado agressor) e limit vale até 100, também limitado em vez de recusado. Omitir type devolve os dois lados, e omitir limit devolve o teto: 100. O trade_id é derivado — instante, lado, preço e tamanho —, estável para o mesmo negócio entre duas leituras, e não é o identificador do negócio na venue.

GET /api/public/trades?ticker_id=BTC_USDT&type=buy&limit=100
[
  { "trade_id": "…", "price": "…", "base_volume": "…", "target_volume": "…", "trade_timestamp": 1756800000000, "type": "buy" }
]

Fonte dos dados

Preço, livro, negócios e volume de 24 horas deste feed vêm do mercado perpétuo — inclusive nos pares rotulados spot. O spot da OFFCODE não tem livro próprio: ele cota o mid do perpétuo de mesmo símbolo. Por isso cada item de /pairs carrega price_source: quem consome o feed lê a fonte no próprio feed, em vez de deduzi-la do rótulo — e deduzir errado.

bid e ask são o mesmo mid. Não há livro spot próprio de onde tirar topo de livro, então ask − bid daqui dá zero. Spread zero é impossível num livro real: não trate esses dois campos como as pontas de um livro.

base_volume é o volume de 24 h em moeda-base do perpétuo, medido pela venue quando ela o publica. Só na ausência dele a rota divide target_volume por last_price e corta em 8 casas para baixo — uma conta declarada, não um número da corretora.

Um par só é publicado onde existe mercado negociável de mesmo símbolo: publicar é prometer servir, e o ticker_id que sai de /pairs é o que o livro e os negócios têm de responder logo em seguida. Moeda custodiada sem par negociável fica de fora do feed, e o mesmo vale para o par cujo nome na venue tem fator — o perpétuo 1000PEPE_USDT é publicado como ele mesmo, nunca como PEPE_USDT.

Limites, cache e CORS

600 requisições por minuto por IP, em cada rota. Acima disso vem 429.

Resposta 2xx sai com Cache-Control: public, max-age=5; o que não é 2xx sai com no-store, porque um 404 guardado por cinco segundos sobrevive ao par recém-listado, e um 502 guardado devolve a venue fora do ar depois que ela já voltou.

Por dentro, o livro é relido no máximo a cada 350 ms por par e profundidade, e os negócios a cada 1 segundo por par: o teto de idas à venue é o cache, não o número de quem pergunta. Falha de leitura nunca entra no cache.

Access-Control-Allow-Origin: *, sem credenciais — não há cookie, chave nem cabeçalho de autenticação, e nada na resposta é personalizado.

Erros

CódigoQuando aconteceCorpo
400ticker_id fora do formato, type que não é buy nem sell, depth ou limit que não é inteiro{"code": "VALIDATION_ERROR", "message": "…"}
404ticker_id bem formado que não está em /pairs{"error": "UNKNOWN_TICKER"}
429Acima do limite de requisições deste IP{"statusCode": 429, "code": "RATE_LIMITED", "message": "…"}
502A venue não respondeu, ou respondeu com falha{"code": "UPSTREAM_UNAVAILABLE", "message": "…"}

502 e não 500, de propósito: falha da venue não é erro nosso, e quem lê de fora precisa dessa diferença para saber se vale tentar de novo.

Agregadores e acesso automatizado

O domínio fica atrás da Cloudflare. Um navegador passa; uma chamada de linha de comando sem cookie pode receber um desafio no lugar do JSON — o que não é a rota recusando, e sim a borda perguntando quem chegou. Se o seu robô precisa varrer o feed de forma contínua, peça a liberação antes de ligar a esteira: informe o user-agent, as faixas de IP de saída e a frequência pretendida em Contato.

Taxas e regras de negociação não estão nesta API: elas moram em /api/public/fees e em Taxas. O estado dos serviços fica em Status da plataforma.