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.
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.
| Rota | O que devolve | Parâmetros |
|---|---|---|
GET /pairs | Todo par que este feed endereça, com a fonte do preço em cada item | — |
GET /tickers | Preço, volume de 24 h, máxima, mínima e as duas pontas dos pares spot | — |
GET /orderbook | Livro de ofertas de um par | ticker_id · depth |
GET /trades | Negócios recentes de um par, do mais novo para o mais velho | ticker_id · type · limit |
GET /historical_trades | A mesma coisa que /trades — dois nomes, uma implementação | idem |
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.
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.
ticker_id | O par, no formato BASE_COTACAO. |
base_currency · target_currency | As duas pernas do par, separadas. |
last_price | Último preço negociado no perpétuo. |
base_volume | Volume 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_volume | Volume de 24 h em moeda de cotação, cru da fonte. |
bid · ask | Iguais entre si e iguais ao mid do perpétuo — não são topo de livro. Ver Fonte dos dados. |
high · low | Máxima e mínima das últimas 24 h. |
timestamp | Instante da leitura, em milissegundos desde a época Unix. |
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.
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.
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.
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.
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.
| Código | Quando acontece | Corpo |
|---|---|---|
| 400 | ticker_id fora do formato, type que não é buy nem sell, depth ou limit que não é inteiro | {"code": "VALIDATION_ERROR", "message": "…"} |
| 404 | ticker_id bem formado que não está em /pairs | {"error": "UNKNOWN_TICKER"} |
| 429 | Acima do limite de requisições deste IP | {"statusCode": 429, "code": "RATE_LIMITED", "message": "…"} |
| 502 | A 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.
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.