Documentação da API
Referência completa da API pública da ExBull para quem quer conectar bots, scripts ou integrações próprias.
Visão Geral
A API da ExBull permite consultar saldo, payout de ativos e criar ordens de negociação de forma programática. Ela foi feita para bots e scripts externos — tudo que você vê aqui pode ser usado por uma IA ou por um desenvolvedor júnior sem precisar olhar o código-fonte do servidor.
| Base URL | https://ex-bull.com/api/v1 |
|---|---|
| Formato | JSON (Content-Type: application/json) |
| Autenticação | API Key + API Secret via headers |
/ping) exigem autenticação. Toda resposta segue o mesmo formato:
{ "success": true|false, ... }. Sempre confira o campo success antes de usar o resto do corpo.
Autenticação
Gere uma chave em /account/api-keys (aba "API" do painel). Você recebe um par
apiKey / apiSecret — o secret só aparece uma vez, na criação.
Se perder, revogue a chave e gere outra. Nunca compartilhe ou versione o secret em repositórios públicos.
Envie os dois em todas as requisições autenticadas, como headers HTTP:
X-Api-Key: ak_live_xxx
X-Api-Secret: sk_live_xxx
Cada chave tem um ou mais escopos, que definem o que ela pode fazer:
| Escopo | Permite |
|---|---|
| read | Consultar saldo, ordens, histórico e payout. Nenhuma rota de leitura muda dados. |
| trade | Criar ordens (POST /orders). Uma chave com escopo trade move dinheiro de verdade — trate como uma senha. |
Se a autenticação falhar ou o escopo for insuficiente, a API responde:
| HTTP | Quando acontece |
|---|---|
| 401 | Faltou header, key não existe ou secret errado. |
| 403 | Key válida, mas sem o escopo necessário para aquela rota (ex: key só-leitura tentando POST /orders). |
Rate Limiting
Existem dois limitadores independentes: um para rotas de leitura (readLimiter) e outro,
mais restritivo, para criação de ordens (tradeLimiter). Ao estourar o limite, a API responde
429. Seu bot deve tratar esse status com backoff (espere e tente
de novo, não em loop imediato) — nunca assuma que 429 significa erro de negócio.
Formato de Resposta
Toda resposta é um JSON com um campo success booleano:
// sucesso
{ "success": true, ... }
// erro
{ "success": false, "error": "mensagem legível do que deu errado" }
Regra prática para bots: sempre cheque success primeiro. Se for false,
trate error como texto para log/alerta, não tente parsear ele para decidir lógica de negócio.
Como Funciona a Criação de Ordens (Importante)
POST /orders é assíncrono. Ele não cria a ordem na hora — ele enfileira um job
e responde imediatamente com 202 e um jobId. Isso existe
porque a criação real da ordem envolve outra camada interna (fila + worker), então seu bot precisa
consultar o resultado depois, não assumir que a ordem já existe.
POST /orders → 202 + jobIdGET /orders/status/:jobId a cada ~500ms–1sstatus vira completed ou failed, pare de consultar
Estados possíveis de status no polling: waiting, active, delayed
(ainda processando — continue tentando), completed (deu certo — veio a ordem criada) ou
failed (rejeitada por regra de negócio — pare de tentar, o erro está em error).
waiting/active/delayed,
trate como falha de infraestrutura e não repita o POST /orders automaticamente — isso pode
criar uma ordem duplicada se o primeiro job só estava lento, não perdido.
Endpoints
/ping
sem autenticação
Verifica se a API está no ar. Útil para healthcheck do seu bot antes de começar a operar.
Resposta 200:
{ "success": true, "timestamp": 1735689600000 }
/account/balance
read
Retorna o saldo da conta associada à chave (demo ou real, conforme a chave).
Resposta 200:
{ "success": true, "accountType": "demo", "balance": 985.32 }
Erros: 404 conta não encontrada.
/account/stats
read
Estatísticas da semana atual da conta (vitórias, derrotas, volume, etc — mesmo formato usado no painel).
{ "success": true, "stats": { ... } }
/payout
read
Lista o payout atual de todos os ativos disponíveis para negociação no momento.
{
"success": true,
"payout": [
{ "symbol": "EURUSD", "payoutPercentage": 87 },
{ "symbol": "GBPUSD", "payoutPercentage": 82 }
]
}
Nota: GET /payouts (com "s") também existe e retorna algo parecido, mas com
mais campos internos por linha — para bots, prefira /payout, que é o formato enxuto pensado
para consumo externo.
/payout/:symbol
read
Payout de um ativo específico. Use antes de POST /orders para confirmar que o ativo está ativo — é exatamente a mesma checagem que a criação de ordem faz internamente.
| Parâmetro | Tipo | Descrição |
|---|---|---|
symbol path | string | Ex: EURUSD. Não é case-sensitive. |
{ "success": true, "payout": { "symbol": "EURUSD", "payoutPercentage": 87 } }
Erros: 404 ativo não encontrado ou indisponível.
/orders
trade
Enfileira a criação de uma ordem. Responde 202 imediatamente — o resultado real vem do
polling em GET /orders/status/:jobId (veja a seção "Como Funciona a Criação de Ordens" acima).
Body:
| Campo | Tipo | Descrição |
|---|---|---|
symbol | string | Ativo, ex: EURUSD. |
direction | string | Somente "up" ou "down". |
amount | number | Valor da entrada. Precisa ser maior que 0. |
expirationSeconds | number | Somente um destes valores: 30, 60, 120, 180, 300. |
expirationSeconds aceita somente 30, 60, 120, 180 ou 300. Qualquer outro
valor (ex: 90, 600) retorna 400 — a API não faz arredondamento.
Exemplo de requisição:
{
"symbol": "EURUSD",
"direction": "up",
"amount": 10,
"expirationSeconds": 60
}
Resposta 202:
{
"success": true,
"status": "queued",
"jobId": "12345",
"statusUrl": "/api/v1/orders/status/12345"
}
Erros:
| 400 | symbol/direction/amount/expirationSeconds inválidos, ou ativo indisponível. |
| 403 | Chave sem escopo trade. |
| 429 | Rate limit de trade estourado. |
/orders/status/:jobId
read
Consulta o resultado do job criado por POST /orders. Só retorna jobs que pertencem à sua própria chave.
// ainda processando
{ "success": true, "status": "active" }
// concluído
{ "success": true, "status": "completed", "order": { "id": "...", "status": "active", "expires_at": "...", ... } }
// rejeitado
{ "success": false, "status": "failed", "error": "motivo da rejeição" }
Erros: 404 job não encontrado, expirado, ou pertence a outro usuário.
/orders
read
Lista as ordens atualmente ativas (ainda não fechadas) da conta.
{ "success": true, "orders": [ { "id": "...", "symbol": "EURUSD", "status": "active", ... } ] }
/orders/history
read
Histórico de ordens já fechadas, com filtro opcional por período.
| Parâmetro | Tipo | Descrição |
|---|---|---|
startDate query | string | Opcional. Data inicial do filtro. |
endDate query | string | Opcional. Data final do filtro. |
GET /orders/history?startDate=2026-06-01&endDate=2026-06-30
/orders/:id
read
Busca uma ordem específica pelo id (o mesmo id retornado dentro de
order no polling de /orders/status/:jobId). Use para checar se uma ordem já
fechou e ver exit_price/profit.
{ "success": true, "order": { "id": "...", "status": "won", "exit_price": 1.0842, "profit": 8.7, ... } }
Erros: 404 ordem não encontrada ou pertence a outro usuário.
Referência Rápida
| Método | Rota | Escopo | Descrição |
|---|---|---|---|
| GET | /ping | — | Healthcheck |
| GET | /account/balance | read | Saldo da conta |
| GET | /account/stats | read | Estatísticas da semana |
| GET | /payout | read | Payout de todos os ativos |
| GET | /payout/:symbol | read | Payout de um ativo |
| POST | /orders | trade | Enfileira criação de ordem |
| GET | /orders/status/:jobId | read | Resultado do job de criação |
| GET | /orders | read | Ordens ativas |
| GET | /orders/history | read | Histórico de ordens fechadas |
| GET | /orders/:id | read | Detalhe de uma ordem |
Exemplo Completo (Node.js)
Fluxo mínimo para um bot: checar payout, abrir ordem e aguardar o resultado. Requer Node 18+ (usa
fetch nativo, sem dependências).
const BASE_URL = 'https://ex-bull.com/api/v1';
const headers = {
'Content-Type': 'application/json',
'X-Api-Key': process.env.API_KEY,
'X-Api-Secret': process.env.API_SECRET,
};
async function criarOrdem(symbol, direction, amount, expirationSeconds) {
// 1. Confirma que o ativo está disponível antes de tentar operar
const payoutRes = await fetch(`${BASE_URL}/payout/${symbol}`, { headers });
const payoutData = await payoutRes.json();
if (!payoutData.success) throw new Error(`Ativo indisponível: ${payoutData.error}`);
// 2. Enfileira a ordem
const orderRes = await fetch(`${BASE_URL}/orders`, {
method: 'POST',
headers,
body: JSON.stringify({ symbol, direction, amount, expirationSeconds }),
});
const orderData = await orderRes.json();
if (orderRes.status !== 202) throw new Error(`Falha ao enfileirar: ${orderData.error}`);
// 3. Faz polling até sair de waiting/active/delayed
const deadline = Date.now() + 20_000;
while (Date.now() < deadline) {
const statusRes = await fetch(`${BASE_URL}/orders/status/${orderData.jobId}`, { headers });
const statusData = await statusRes.json();
if (statusData.status === 'completed') return statusData.order;
if (statusData.status === 'failed') throw new Error(`Ordem rejeitada: ${statusData.error}`);
await new Promise((r) => setTimeout(r, 500));
}
throw new Error('Timeout aguardando confirmação da ordem');
}
Checklist de Boas Práticas para Bots
- Sempre confira
successantes de ler qualquer outro campo da resposta. - Trate
POST /orderscomo assíncrono — nunca assuma que a ordem existe só porque veio 202. - Defina um timeout de polling e não reenvie o mesmo
POST /ordersautomaticamente ao estourar — isso pode duplicar ordens. - Trate 429 com espera/backoff, nunca com retry imediato em loop.
- Guarde
apiSecretem variável de ambiente, nunca em código versionado. - Use uma chave com escopo
readpara qualquer bot que só consulta dados — reserve o escopotradesó para o que realmente opera.
--trade para testar
a criação real de uma ordem.
Precisa de Ajuda?
E-mail: support@ex-bull.com
Horário: Segunda a sexta, das 9h às 18h