Betamigos / Desenvolvedores

Referência da API

API do Betamigos

Registre suas apostas no Betamigos a partir do seu próprio sistema: um bot, um script de planilha, uma ferramenta interna. Toda aposta enviada cai no mesmo lugar de uma digitada no app, então CLV, P&L, exposição e o extrato da carteira continuam funcionando igual.

É uma API pequena de propósito. Ela cobre o ciclo que importa (registrar, corrigir, liquidar, reconciliar) e mais nada.

Pegue um token

Abra Perfil > Acesso por API no app e crie um token. O segredo aparece uma única vez, na criação, porque guardamos só o hash dele. Se você perder, revogue aquele token e crie outro.

Mande ele em toda requisição, no header X-Service-Token:

X-Service-Token: bmst_xxxxxxxxxxxxxxxxxxxxxxxx

O token age como você: lê e escreve só os seus dados, e nunca enxerga os de outra conta. Revogar vale na hora.

URL base

https://betamigos.io

Todo caminho aqui embaixo já começa com /api, então a URL completa do primeiro endpoint é https://betamigos.io/api/bets. Todos os corpos são JSON. Todas as datas são ISO 8601 em UTC.

Registrar uma aposta

POST /api/bets

Há duas formas de registrar, e a diferença entre elas não é de formato: é o quanto de trabalho fica com você depois.

Aposta solta. Você descreve a aposta em texto e pronto. Serve para qualquer coisa, inclusive mercado que a gente nem acompanha. O preço é que ninguém liquida por você: quando o jogo acabar, você chama o endpoint de liquidação com o resultado, e o CLV só existe se você mandar a odd de fechamento junto.

Aposta ligada ao mercado. Você inclui o selection_id da seleção real. A partir daí a aposta se liquida sozinha quando o jogo termina, e a linha de fechamento é capturada por nós, o que dá CLV sem você fazer nada. Como achar esse id está na seção seguinte.

A recomendação é óbvia: sempre que a aposta existir no nosso catálogo, mande o selection_id. Quando não existir, a aposta solta é o caminho, e nada se perde além da automação.

# Aposta solta: só rótulos. Você liquida depois.
curl -X POST https://betamigos.io/api/bets \
  -H "X-Service-Token: $BETAMIGOS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "stake": 25,
    "odds": 2.10,
    "sport": "Soccer",
    "league": "Premier League",
    "event_label": "Arsenal vs Chelsea",
    "market": "Match winner",
    "selection": "Arsenal",
    "starts_at": "2026-08-09T19:00:00Z",
    "external_source": "meu-bot",
    "external_ref": "trade-8814"
  }'
# Ligada ao mercado: liquida e calcula CLV sozinha.
curl -X POST https://betamigos.io/api/bets \
  -H "X-Service-Token: $BETAMIGOS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "account_id": 3,
    "stake": 25,
    "odds": 2.10,
    "blv_odds": 1.96,
    "blv_devig": "conservative",
    "root_event_id": 45012,
    "selection_id": 918273,
    "starts_at": "2026-08-09T19:00:00Z",
    "sport": "Soccer",
    "league": "Premier League",
    "event_label": "Arsenal vs Chelsea",
    "market": "Asian Handicap -0.5",
    "selection": "Arsenal",
    "tag_names": ["bot", "valuebet"],
    "external_source": "meu-bot",
    "external_ref": "trade-8814"
  }'

Devolve 201 com a aposta criada. Ela nasce pending.

stake e odds são obrigatórios. O resto deixa os números mais ricos:

CampoPor que enviar
account_idCarteira da aposta. Sem ele, cai na sua carteira favorita.
selection_id, root_event_idAmarram a aposta ao mercado real. É o que habilita a liquidação automática e a captura da linha de fechamento.
sport, league, event_label, market, selectionOs rótulos que você vai ler depois no dashboard. Mande mesmo com selection_id: é o que aparece na tabela.
starts_atHorário de início. Define quando a linha é considerada fechada.
oddsA odd que você realmente pegou. Em exchange, mande a líquida, já com a comissão.
gross_odds, commission_type, commission_pctA comissão que você pagou, se preferir que a gente faça a conta. Os três juntos ou nenhum: com eles, o servidor calcula a odd líquida a partir da bruta e ignora o odds que você mandou. commission_type é on_profit, on_stake ou on_return.
blv_oddsA odd justa (sem vig) no momento da aposta, se você calcula. Alimenta a métrica de valor.
originalert, explorer ou manual. Marca de onde a aposta veio.
tag_namesAnexa suas tags por nome, sem precisar dos ids. Nome inexistente é ignorado.
external_source, external_refSeus identificadores. Veja idempotência abaixo.

Idempotência

Mande sempre external_source e external_ref. Repetir o POST do mesmo par devolve a aposta que já existe, com status 200 em vez de 201, e não cria nada novo.

Isso pesa mais do que parece: sem esse par, um retry depois de um timeout registra a aposta duas vezes e lança o dinheiro duas vezes no extrato da carteira. Com ele, tentar de novo sai de graça.

Conferência de consistência

Se você mandar root_event_id junto com starts_at, comparamos o horário declarado com o real. Diferença maior que 6 horas é recusada com 400, porque indica que a aposta foi ligada ao jogo errado (o caso clássico são duas partidas entre os mesmos times, com dias de distância). Se a base de odds estiver fora naquele instante, deixamos passar em vez de te travar.

Achar o evento e a seleção

Dois passos: procure o jogo, abra o jogo. O primeiro devolve o id do evento, o segundo devolve os selection_id de dentro dele.

1. Procurar o jogo

GET /api/events/upcoming lista o que ainda não começou, do mais próximo para o mais distante. Filtros: q (texto livre, casa com os nomes dos times), sport, league_id, date_from, date_to, limit (até 200) e offset.

curl "https://betamigos.io/api/events/upcoming?q=arsenal&sport=Soccer&limit=10" \
  -H "X-Service-Token: $BETAMIGOS_TOKEN"
{
  "total": 2,
  "limit": 10,
  "offset": 0,
  "items": [
    { "id": 45012, "home": "Arsenal", "away": "Chelsea",
      "starts_at": "2026-08-09T19:00:00Z",
      "league_id": 1, "league": "Premier League", "sport": "Soccer", "n_markets": 74 }
  ]
}

Esse id é o root_event_id da aposta. As irmãs /api/events/pending (em andamento) e /api/events/settled (encerrados, com placar) têm os mesmos filtros, e /api/events/leagues devolve o catálogo de ligas se você preferir filtrar por league_id em vez de texto.

Se o seu sistema fala em nomes de casa de aposta, o passo de casar nome com evento é seu, e vale desconfiar: duas partidas entre os mesmos times, com dias de distância, são o erro clássico. Por isso conferimos o starts_at contra o kickoff real e recusamos divergência acima de 6 horas.

2. Abrir o jogo e escolher a seleção

GET /api/events/{id} devolve o evento com todos os mercados e, dentro de cada um, as seleções.

curl "https://betamigos.io/api/events/45012" -H "X-Service-Token: $BETAMIGOS_TOKEN"
{
  "id": 45012, "home": "Arsenal", "away": "Chelsea", "sport": "Soccer",
  "starts_at": "2026-08-09T19:00:00Z", "status": "open",
  "markets": [
    {
      "market_id": 8801, "code": "spread", "period": 0, "unit": "regular",
      "period_name": "Match", "unit_label": "Goals",
      "team_side": null, "bet_type": null, "status": "open", "settleable": true,
      "selections": [
        { "selection_id": 918273, "label": "Arsenal", "line": -0.5, "price": 2.10,
          "status": "open", "max_limit": 2500,
          "no_vig": { "conservative": 1.96, "equal_margin": 1.95,
                      "log": 1.95, "odds_ratio": 1.94 },
          "margin": 0.038 },
        { "selection_id": 918274, "label": "Chelsea", "line": 0.5, "price": 1.80 }
      ]
    }
  ]
}

Como ler para mapear:

CampoPara que serve
code, period, unitIdentificam o mercado. period: 0 é o jogo todo; unit distingue gols de escanteios, sets de games, e por aí. period_name e unit_label são os rótulos humanos dos mesmos valores.
line, labelIdentificam a seleção dentro do mercado. Em handicap e total, a linha é parte da identidade: Arsenal -0.5 e Arsenal -1 são seleções diferentes.
selection_idO que você manda no POST.
priceA odd atual com margem, do jeito que a casa publica.
no_vigA odd justa nos quatro métodos de de-vig. Escolha o seu e mande em blv_odds, com o nome do método em blv_devig.
max_limitQuanto a fonte aceita nessa linha. Bom termômetro de quão confiável é o preço.
settleablefalse significa que a fonte nunca publica resultado desse mercado (acontece em kills de e-sports, pontos de vôlei). A aposta é aceita, mas quem informa o resultado é você.

O gráfico de movimento de linha não está aberto ao token, e não faz falta aqui: para mapear uma seleção, o detalhe do evento basta.

Liquidar uma aposta

POST /api/bets/{id}/settle

curl -X POST https://betamigos.io/api/bets/1234/settle \
  -H "X-Service-Token: $BETAMIGOS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"outcome": "won"}'

outcome é um de won, lost, void, half_won, half_lost. Liquidar calcula o P&L, lança no saldo da carteira e captura o CLV.

Linha de fechamento: se a aposta tem selection_id e o evento já começou, buscamos a odd de fechamento sozinhos. Se você preferir informar a sua odd justa de fechamento, mande closing_odds e a sua prevalece.

Liquidar de novo com outro resultado é seguro. O extrato estorna o lançamento antigo e lança o novo, então o saldo continua certo.

Registrar uma aposta já decidida

POST /api/bets/settled

Mesmo corpo do POST /api/bets. Tentamos gradear na hora pela base de resultados. Se ainda não der, a aposta fica pendente e nosso worker liquida depois. Este endpoint também é idempotente pelo par external_source e external_ref.

Corrigir uma aposta

PATCH /api/bets/{id}

Mande só o que mudou, por exemplo {"stake": 12.5}. Stake e odd são editáveis enquanto a aposta está pendente; depois de liquidada elas congelam e você recebe 409. Rótulos e métricas de valor seguem editáveis.

Reconciliar

GET /api/bets

curl "https://betamigos.io/api/bets?external_ref=trade-8814" \
  -H "X-Service-Token: $BETAMIGOS_TOKEN"

Filtros: account_id, status (pending ou settled), external_ref, mais limit (até 5000) e offset. Mais recentes no topo. Buscar pela sua própria external_ref é o jeito mais barato de responder "essa aqui entrou?".

GET /api/bets/{id} devolve uma aposta só.

Valor da aposta: EV e CLV

As duas métricas saem da mesma conta, odd que você pegou ÷ odd justa − 1, em dois momentos diferentes. O que muda é qual odd justa entra no denominador.

EV no momento da aposta (chamamos de BLV). Mande blv_odds no POST: a odd justa, sem margem, do mercado quando você apostou. É o número que responde "esta aposta tinha valor quando eu a fiz?", e é a única das duas que depende de você, porque só o seu sistema sabe qual linha ele leu naquele instante. Sem blv_odds, tentamos calcular no servidor quando a aposta traz selection_id; sem os dois, a aposta entra sem EV e nada preenche isso depois.

CLV, na linha de fechamento. Se a aposta tem selection_id, não faça nada: buscamos a odd justa de fechamento sozinhos e gravamos o CLV quando ela liquida. Sem selection_id (aposta em casa que a gente não acompanha, mercado montado por você), mande closing_odds no settle, e a sua prevalece sempre, inclusive sobre a automática.

O GET devolve as duas prontas, junto do que você precisa para conferir:

CampoO que é
blv_odds, blvA odd justa que você enviou, e o EV que ela produziu. 0.05 = 5% de valor.
closing_odds, clvA odd justa de fechamento (sua ou nossa) e o CLV.
clv_statusok tem CLV; closed a linha já estava fechada quando a aposta foi feita, então não inventamos um número; none não houve fechamento. Fica nulo enquanto a aposta está pendente.
pnlResultado líquido, preenchido na liquidação.

Uma consequência que costuma surpreender: se a sua carteira cobra comissão, mande em odds a odd líquida, e aí EV e CLV também saem líquidos. É o número honesto do seu bolso, mas não é comparável com CLV bruto publicado por terceiros.

Leituras de apoio

EndpointO que devolve
GET /api/accountsSuas carteiras, com ids, saldos e qual é a favorita.
GET /api/tagsSuas tags, de sistema e custom, com ids.
GET /api/events/upcoming, /pending, /settledBusca de eventos por texto, esporte, liga e data. Veja "Achar o evento e a seleção".
GET /api/events/leaguesCatálogo de ligas, para filtrar por league_id.
GET /api/events/{id}O evento com mercados, seleções, selection_id e odds justas.
GET /api/alerts/rulesAs regras de alerta de valor que você configurou no app, para o seu bot filtrar igual a você.
GET /api/alertsOs 30 alertas de valor mais recentes que batem com suas regras. Cada leg já traz o selection_id, então dá para apostar direto do alerta sem passar pela busca.

O que um token não faz

Excluir aposta, reabrir aposta liquidada, mover aposta entre carteiras, depósito e saque, e qualquer coisa que toque sua conta, seu plano ou o pagamento continuam exigindo login normal. Um token vazado consegue escrever aposta no seu próprio tracker. Não consegue mover dinheiro, apagar seu histórico nem te trancar para fora.

Erros

StatusSignificado
400Corpo inválido, carteira que não é sua, ou jogo incompatível.
401Token ausente, inválido, expirado ou revogado.
402Sua assinatura não está mais ativa, então o token parou de valer. Ele volta a funcionar assim que ela voltar, sem precisar criar outro.
404A aposta não existe, ou não é sua.
409Edição de aposta já liquidada.
429Escritas demais no último minuto.

Todo erro traz um campo detail com a mensagem legível.

Limites

Escritas são limitadas a 30 por minuto por ação, por conta: registrar aposta é um balde, liquidar é outro, editar é outro. Leituras por token são limitadas a 120 por minuto por grupo de endpoint. As duas janelas são deslizantes, então um 429 se resolve em menos de um minuto; tente de novo com um respiro em vez de martelar.

Duas coisas que valem desenhar com cuidado. Ficar pollando GET /api/alerts em loop apertado é o jeito mais rápido de bater o teto de leitura, e não traz dado mais fresco: os alertas chegam no ritmo deles, então de poucos em poucos segundos já basta.

Toda recusa se explica no detail. Trate 429 como "tenta daqui a pouco", não como bug.

Conselho prático

Guarde o token como senha, no seu gerenciador de segredos, nunca no repositório. Rotacionar é fácil: crie o segundo token, publique, e só então revogue o primeiro. Nada quebra no meio, porque os tokens são independentes.

Mande a odd que você realmente pegou, não a que você viu. Se a sua casa cobra comissão, a odd líquida é a honesta, e toda métrica lá na frente herda essa honestidade.

Dúvida, ou falta algo para o seu caso? Fale com a gente e conte o que você está construindo.