Predixdata v1.0

Predixdata API

Documentação para integrar o feed de mercados de previsão ao seu sistema. Consuma mercados em tempo real e receba notificações automáticas via webhook.

O que você consegue fazer

RecursoDescrição
Feed de mercadosTodos os mercados ativos agrupados por evento, com odds e análise de IA
FiltrosFiltre por categoria, sub-categoria ou evento específico
WebhooksNotificações push quando um mercado fecha ou tem resultado final

BASE URL

https://predixdata.com/api

HEADERS OBRIGATÓRIOS

X-Public-Key: pub_sua_chave_aqui
X-Secret-Key: sec_sua_chave_aqui

Autenticação

Todas as requests exigem autenticação via headers. Você receberá suas chaves ao se cadastrar como agente.

HeaderFormatoDescrição
X-Public-Keypub_xxxxxxxx...Identificador único do agente
X-Secret-Keysec_xxxxxxxx...Chave secreta de autenticação
Nunca exponha sua X-Secret-Key no frontend. Use-a apenas no backend.

EXEMPLO DE REQUEST

cURL
curl https://predixdata.com/api/v1/get-markets \
  -H "X-Public-Key: pub_sua_chave" \
  -H "X-Secret-Key: sec_sua_chave"

Quick Start

Integre em 3 passos:

Receba suas chaves

Entre em contato para receber seu X-Public-Key e X-Secret-Key.

Busque os mercados

Faça um GET /api/v1/get-markets com os headers de autenticação. Pronto — você recebe todos os mercados ativos.

Configure os webhooks

Crie duas rotas no seu backend para receber notificações automáticas quando mercados fecham ou têm resultado.

PHP / LARAVEL

Laravel / PHP
$response = Http::withHeaders([
    'X-Public-Key' => 'pub_sua_chave',
    'X-Secret-Key' => 'sec_sua_chave',
])->get('https://predixdata.com/api/v1/get-markets');

$markets = $response->json()['data'];

JAVASCRIPT

Node.js / Fetch
const res = await fetch('https://predixdata.com/api/v1/get-markets', {
  headers: {
    'X-Public-Key': 'pub_sua_chave',
    'X-Secret-Key': 'sec_sua_chave'
  }
});
const { data } = await res.json();

ROTAS DE WEBHOOK

POST /webhook/predixdata/notify-closed
POST /webhook/predixdata/final-result
POST /webhook/predixdata/market-refund
GET Autenticado

Feed de Mercados

/api/v1/get-markets

Endpoint principal. Retorna todos os mercados ativos agrupados por evento, com odds em tempo real e análise de IA. Sem parâmetros retorna tudo; use query params para filtrar.

Query Parameters

ParâmetroTipoDescrição
categorystringFiltra por categoria (ex: cripto, politica)
sub_categorystringFiltra por sub-categoria (ex: bitcoin, futebol)
event_idstringFiltra por evento específico

Campos do Response

CampoTipoDescrição
event_idstringID do evento que agrupa os mercados
event_titlestringTítulo do evento em PT-BR
categorystringCategoria do evento
sub_categorystringSub-categoria
total_volumefloatVolume total de todos os mercados do evento (USD)
total_marketsintegerQuantidade de mercados no evento
market_idstringID do mercado use para vincular com webhooks
odds.yes.oddfloatOdd decimal para SIM (ex: 1.35)
odds.yes.ai_estimatefloatEstimativa da IA para SIM (0 a 1)
odds.no.oddfloatOdd decimal para NÃO
odds.no.ai_estimatefloatEstimativa da IA para NÃO (0 a 1)
ai.confidencefloatConfiança da IA (0 a 1)
ai.reasoningstringExplicação textual da análise
volumefloatVolume de negociação (USD)
closedbooleanSe o mercado já fechou
activebooleanSe está ativo
end_datestringData de encerramento (ISO 8601)
Try it

RESPONSE 200

JSON
{
  "success": true,
  "agent": "Meu Site",
  "total": 85,
  "events": 12,
  "generated_at": "2026-03-12T10:30:00.000Z",
  "data": [
    {
      "event_id": "248137",
      "event_title": "Preço do Bitcoin em Março 2026",
      "category": "cripto",
      "sub_category": "bitcoin",
      "total_volume": 5000000.00,
      "total_markets": 4,
      "markets": [
        {
          "market_id": "1508196",
          "slug": "o-preco-do-bitcoin-estara-entre-74000",
          "title": "O preço do Bitcoin estará entre...",
          "image_url": "https://...",
          "odds": {
            "yes": { "odd": 1.35, "ai_estimate": 0.72 },
            "no":  { "odd": 3.85, "ai_estimate": 0.28 }
          },
          "ai": {
            "confidence": 0.85,
            "reasoning": "Análise do modelo de IA..."
          },
          "volume": 1500000.00,
          "closed": false,
          "active": true,
          "end_date": "2026-03-15T16:00:00.000Z"
        }
      ]
    }
  ]
}
GET Autenticado

Categorias

/api/v1/get-categories

Retorna todas as categorias e subcategorias disponíveis com a contagem de mercados ativos em cada uma. Use para montar filtros ou menus dinâmicos no seu frontend.

Response

CampoTipoDescrição
successbooleanSempre true
totalintegerQuantidade de categorias
generated_atstringTimestamp ISO 8601
data[].categorystringNome da categoria
data[].total_marketsintegerTotal de mercados ativos na categoria
data[].sub_categories[].namestringNome da subcategoria
data[].sub_categories[].totalintegerTotal de mercados na subcategoria
Use os valores de category e sub_category como filtro no endpoint /api/v1/get-markets?category=esportes&sub_category=futebol.
Request
curl https://predixdata.com/api/v1/get-categories \
  -H "X-Agent-Public-Key: SUA_PUBLIC_KEY" \
  -H "X-Agent-Secret-Key: SUA_SECRET_KEY"
Response 200
{
  "success": true,
  "total": 6,
  "generated_at": "2026-03-16T02:00:00.000Z",
  "data": [
    {
      "category": "esportes",
      "total_markets": 59,
      "sub_categories": [
        { "name": "futebol", "total": 58 },
        { "name": "ufc-mma", "total": 1 }
      ]
    },
    {
      "category": "geopolitica",
      "total_markets": 80,
      "sub_categories": [
        { "name": "oriente-medio", "total": 33 },
        { "name": "global", "total": 40 },
        { "name": "americas", "total": 2 }
      ]
    },
    {
      "category": "entretenimento",
      "total_markets": 17,
      "sub_categories": [
        { "name": "reality-show", "total": 16 },
        { "name": "outros-entretenimento", "total": 1 }
      ]
    }
  ]
}
POST Autenticado

Apostar

/api/v1/transaction

Registra uma aposta em um mercado ativo. O campo odd é obrigatório e deve conter a odd que o usuário viu na tela — se a odd mudou no servidor, a aposta é rejeitada com 409.

Conecte via WebSocket ao canal market.{agent_id}.{market_id} para receber odds em tempo real e manter o valor de odd sempre atualizado. Veja a seção WebSocket.

Body Parameters

CampoTipoObrigatórioDescrição
market_idstringSimID do mercado (mesmo retornado no feed)
typestringSim"yes" ou "no"
amountnumberSimValor da aposta em R$ (min: 1.00, max: 50.000)
oddnumberSimOdd que o usuário viu na tela. Se a odd no servidor subiu, retorna 409

Campos do Response

CampoTipoDescrição
bet_idintegerID único da aposta
oddfloatOdd recebida (estilo bet365: 1.25 = R$12.50 por R$10)
payoutfloatValor total que recebe se ganhar
profitfloatLucro líquido (payout - amount)
odds_after.yesfloatNova odd do YES após a aposta
odds_after.nofloatNova odd do NO após a aposta
Apostas em mercados fechados (closed: true) ou inativos retornam 422.
Try it

RESPONSE 200

JSON
{
  "success": true,
  "bet_id": 42,
  "market_id": "1508196",
  "type": "yes",
  "amount": 10,
  "odd": 1.35,
  "payout": 13.50,
  "profit": 3.50,
  "odds_after": {
    "yes": 1.28,
    "no": 4.12
  }
}

EXEMPLO cURL

cURL
curl -X POST https://predixdata.com/api/v1/transaction \
  -H "X-Public-Key: pub_..." \
  -H "X-Secret-Key: sec_..." \
  -H "Content-Type: application/json" \
  -d '{"market_id":"1508196","type":"yes","amount":10,"odd":1.35}'

RESPONSE 409 — ODD ALTERADA

JSON
{
  "success": false,
  "error": "Odd sofreu alteração. Atualize e tente novamente.",
  "current_odd": 1.28
}
GET Autenticado

Simular Aposta

/api/v1/simulate-bet

Preview da aposta sem executar. Retorna a odd, payout e lucro estimado. Ideal para atualizar a interface do usuário em tempo real conforme ele digita o valor.

Query Parameters

ParâmetroTipoObrigatórioDescrição
market_idstringSimID do mercado
typestringSim"yes" ou "no"
amountnumberSimValor em R$ (min: 0.50)

Campos do Response

CampoTipoDescrição
oddfloatOdd que o apostador receberia
payoutfloatValor total que receberia se ganhar
profitfloatLucro líquido (payout - amount)
Use este endpoint no onInput do campo de valor para mostrar o retorno estimado em tempo real. Não executa aposta nem altera o mercado.
Try it

RESPONSE 200

JSON
{
  "success": true,
  "market_id": "1508196",
  "type": "yes",
  "amount": 100,
  "odd": 1.22,
  "payout": 122.00,
  "profit": 22.00
}

EXEMPLO DE USO NO FRONTEND

JavaScript
// Atualiza preview conforme o usuário digita
amountInput.addEventListener('input', async (e) => {
  const amount = e.target.value;
  if (amount < 0.50) return;

  const res = await fetch(
    `/api/v1/simulate-bet?market_id=\${id}&type=yes&amount=\${amount}`,
    { headers: { 'X-Public-Key': pub, 'X-Secret-Key': sec } }
  );
  const data = await res.json();

  // Atualiza a UI
  oddLabel.textContent = data.odd;
  payoutLabel.textContent = `R$ \${data.payout}`;
  profitLabel.textContent = `R$ \${data.profit}`;
});

WebSocket — Odds ao Vivo

Conecte via WebSocket para receber atualizações de odds em tempo real. Use o /simulate-bet com debounce para calcular o payout conforme o usuário digita o valor.

Fluxo completo da aposta

EtapaO que acontece
1Usuário abre o card/modal de aposta → conecta ao canal market.{agent_id}.{market_id}
2Recebe odds atuais via WS (ou do feed) → exibe na tela
3Usuário digita o valor → chama /simulate-bet com debounce de 300ms → exibe payout
4Se alguém aposta nesse mercado → WS envia odds.updated → atualiza odds na tela
5Usuário clica "Apostar" → POST /transaction com a odd da tela
6Se a odd mudou → servidor retorna 409 → WS já atualizou a tela → usuário tenta de novo

Dados do Canal

CampoDescrição
Hostwss://predixdata.com/app/{APP_KEY}
Canalmarket.{agent_id}.{polymarket_id} (público, sem auth)
Eventoodds.updated

Payload do Evento

CampoTipoDescrição
market_idstringID do mercado
yes_oddfloatOdd atual do YES
no_oddfloatOdd atual do NO
yes_poolfloatVolume do pool YES
no_poolfloatVolume do pool NO

Cálculo client-side (com slippage)

Com os dados do pool você calcula o payout exato no frontend, sem precisar chamar o /simulate-bet. A fórmula é a mesma que o servidor usa (CPMM).

PassoFórmulaDescrição
1K = yes_pool × no_poolConstante do pool (produto invariável)
2newPool = pool_do_lado + amountAposta YES → yes_pool + amount
3unitsOut = pool_oposto − (K / newPool)Unidades que saem do lado oposto
4houseCut = unitsOut × 0.055% de margem da casa
5payout = amount + unitsOut − houseCutValor total que recebe se ganhar
6odd = min(payout / amount, odd_exibida)Cap: nunca paga mais que a odd na tela
7profit = payout − amountLucro líquido
O /simulate-bet continua disponível como alternativa para quem preferir não implementar a fórmula client-side.

EVENTO RECEBIDO VIA WEBSOCKET

JSON
{
  "event": "odds.updated",
  "data": {
    "market_id": "1508196",
    "yes_odd": 1.28,
    "no_odd": 4.12,
    "yes_pool": 37.50,
    "no_pool": 12.50
  }
}

JAVASCRIPT — EXEMPLO COMPLETO

JavaScript
// ── 1. Config ──
const API   = 'https://predixdata.com/api/v1';
const PUB   = 'pub_...';
const SEC   = 'sec_...';
const AGENT = 5;    // seu agent_id
const MKT   = '1508196';

// ── 2. Estado do mercado ──
let market = {
  yesOdd: 0, noOdd: 0,
  yesPool: 0, noPool: 0,
};

// ── 3. WebSocket — recebe odds + pools ──
const pusher = new Pusher('APP_KEY', {
  wsHost: 'predixdata.com',
  wsPort: 443, wssPort: 443,
  forceTLS: true,
  disableStats: true,
  enabledTransports: ['ws', 'wss'],
});

pusher.subscribe(`market.${AGENT}.${MKT}`)
  .bind('odds.updated', (d) => {
    market = {
      yesOdd: d.yes_odd,
      noOdd: d.no_odd,
      yesPool: d.yes_pool,
      noPool: d.no_pool,
    };
    updateUI();
});

// ── 4. Cálculo CPMM client-side ──
function calcPayout(type, amount) {
  const yp = market.yesPool;
  const np = market.noPool;
  const K  = yp * np;

  let newPool, otherPool;
  if (type === 'yes') {
    newPool   = yp + amount;
    otherPool = np;
  } else {
    newPool   = np + amount;
    otherPool = yp;
  }

  const unitsOut = otherPool - (K / newPool);
  const houseCut = unitsOut * 0.05;
  let payout = amount + unitsOut - houseCut;
  let odd = payout / amount;

  // Cap: nunca paga mais que a odd exibida
  const maxOdd = type === 'yes'
    ? market.yesOdd : market.noOdd;
  if (odd > maxOdd) {
    odd = maxOdd;
    payout = amount * maxOdd;
  }

  return {
    odd:    +odd.toFixed(2),
    payout: +payout.toFixed(2),
    profit: +(payout - amount).toFixed(2),
  };
}

// ── 5. Atualiza UI quando user digita ──
function onAmountChange(amount, type) {
  const r = calcPayout(type, amount);
  oddLabel.textContent    = r.odd + 'x';
  payoutLabel.textContent = `R$ ${r.payout}`;
  profitLabel.textContent = `R$ ${r.profit}`;
}

// ── 6. Apostar ──
async function placeBet(type, amount) {
  const odd = type === 'yes'
    ? market.yesOdd : market.noOdd;

  const r = await fetch(`${API}/transaction`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-Public-Key': PUB,
      'X-Secret-Key': SEC,
    },
    body: JSON.stringify({
      market_id: MKT, type, amount, odd
    })
  });
  const d = await r.json();

  if (r.status === 409) {
    // Odd mudou — WS já atualizou a tela
    showToast('Odd atualizada, confira');
    return;
  }
  showToast(`R$${d.amount} a ${d.odd}x = R$${d.payout}`);
}

PHP LARAVEL — EXEMPLO COMPLETO

PHP
// ── config/services.php ──
'predixdata' => [
    'url'    => 'https://predixdata.com/api/v1',
    'pub'    => env('PREDIX_PUB'),
    'sec'    => env('PREDIX_SEC'),
    'agent'  => env('PREDIX_AGENT_ID'),
],

// ── PredixService.php ──
use Illuminate\Support\Facades\Http;

class PredixService
{
    private string $url;
    private array $headers;

    public function __construct()
    {
        $cfg = config('services.predixdata');
        $this->url = $cfg['url'];
        $this->headers = [
            'X-Public-Key' => $cfg['pub'],
            'X-Secret-Key' => $cfg['sec'],
        ];
    }

    /**
     * Cálculo CPMM client-side — mesmo resultado do servidor.
     * $market = dados do feed ou do WS (yes_odd, no_odd, yes_pool, no_pool)
     */
    public static function calcPayout(
        array  $market,
        string $type,
        float  $amount
    ): array {
        $yp = $market['yes_pool'];
        $np = $market['no_pool'];
        $K  = $yp * $np;

        if ($type === 'yes') {
            $newPool   = $yp + $amount;
            $otherPool = $np;
        } else {
            $newPool   = $np + $amount;
            $otherPool = $yp;
        }

        $unitsOut = $otherPool - ($K / $newPool);
        $houseCut = $unitsOut * 0.05;
        $payout   = $amount + $unitsOut - $houseCut;
        $odd      = $payout / $amount;

        // Cap: nunca paga mais que a odd exibida
        $maxOdd = $type === 'yes'
            ? $market['yes_odd']
            : $market['no_odd'];
        if ($odd > $maxOdd) {
            $odd = $maxOdd;
            $payout = $amount * $maxOdd;
        }

        return [
            'odd'    => round($odd, 2),
            'payout' => round($payout, 2),
            'profit' => round($payout - $amount, 2),
        ];
    }

    // Registra aposta — envia odd que o user viu
    public function bet(
        string $marketId,
        string $type,
        float  $amount,
        float  $odd
    ): array {
        $r = Http::withHeaders($this->headers)
            ->post("{$this->url}/transaction", [
                'market_id' => $marketId,
                'type'      => $type,
                'amount'    => $amount,
                'odd'       => $odd,
            ]);

        if ($r->status() === 409) {
            // Odd mudou — pega a odd atual
            return [
                'error' => 'odd_changed',
                'current_odd' => $r['current_odd']
            ];
        }
        return $r->json();
    }
}

Webhooks

O Predixdata envia notificações automáticas via POST para seu servidor quando eventos importantes acontecem.

Fluxo de entrega

EtapaDescrição
1Mercado novo aprovado, fechado ou com resultado → webhook enfileirado
2Dispatcher envia POST para sua URL
3Seu servidor retorna 2xx → marcado como enviado
4Falha → retry automático (1 min, 5 min, 15 min)
54 tentativas sem sucesso → marcado como falha

Headers enviados pelo Predixdata

HeaderDescrição
X-Predixdata-KeySua public_key — identifica a origem
X-Predixdata-SignatureHMAC-SHA256 do payload
Content-Typeapplication/json
Seu endpoint deve retornar status 2xx para confirmar recebimento. Caso contrário, retries serão feitos automaticamente.

ROTAS QUE VOCÊ PRECISA CRIAR

Seus endpoints
POST {sua_url}/webhook/predixdata/new-market
POST {sua_url}/webhook/predixdata/notify-closed
POST {sua_url}/webhook/predixdata/final-result
POST {sua_url}/webhook/predixdata/market-refund

POLÍTICA DE RETRY

Tentativa 1: imediata
Tentativa 2: +1 minuto
Tentativa 3: +5 minutos
Tentativa 4: +15 minutos
Depois:      marcado como falha
POST

new-market

{sua_url}/webhook/predixdata/new-market

Enviado automaticamente a cada ~6 horas quando novos mercados são processados pela IA e aprovados. O payload segue o mesmo formato do GET /api/v1/get-markets, incluindo odds e pools prontos para uso.

Campos do Payload

CampoTipoDescrição
eventstringSempre "new-market"
event_idstringID do evento que agrupa este mercado
event_titlestringTítulo do evento em PT-BR
categorystringCategoria (ex: politica, esportes)
sub_categorystringSub-categoria (ex: futebol, exterior)
marketobjectObjeto com todos os dados do mercado (ver abaixo)
sent_atstringQuando o webhook foi gerado (ISO 8601)

Campos do objeto market

CampoTipoDescrição
market_idstringID único do mercado — use como chave no seu banco
slugstringSlug em PT-BR para URL
titlestringTítulo em PT-BR
descriptionstringDescrição traduzida
image_urlstring|nullImagem do mercado
icon_urlstring|nullÍcone do mercado
odds.yes.oddfloatOdd decimal do SIM (com margem embutida)
odds.yes.poolfloatPool de liquidez do YES — use para montar seu CPMM
odds.yes.ai_estimateint|nullProbabilidade estimada pela IA (%)
odds.no.oddfloatOdd decimal do NÃO
odds.no.poolfloatPool de liquidez do NO
odds.no.ai_estimateint|nullProbabilidade estimada pela IA (%)
ai.confidencestringhigh, medium ou low
ai.reasoningstringJustificativa da IA para a estimativa
volumefloatVolume negociado na fonte original (USD)
relevance_scoreintScore de relevância para público brasileiro (0-100)
end_datestring|nullData de encerramento (ISO 8601)
Ao receber este webhook, salve o mercado no seu banco, capture odds.yes.pool e odds.no.pool, calcule k = yes_pool × no_pool, e o mercado já está pronto para receber apostas. Não precisa chamar o GET.

PAYLOAD

JSON
{
  "event": "new-market",
  "event_id": "248137",
  "event_title": "Eleições USA 2028",
  "category": "politica",
  "sub_category": "exterior",
  "market": {
    "market_id": "1508196",
    "slug": "trump-vence-eleicao-2028",
    "title": "Trump vence a eleição de 2028?",
    "description": "Resolve SIM se Trump vencer...",
    "image_url": "https://polymarket.com/...",
    "icon_url": "https://polymarket.com/...",
    "odds": {
      "yes": {
        "odd": 2.35,
        "pool": 21.27,
        "ai_estimate": 42
      },
      "no": {
        "odd": 1.74,
        "pool": 28.73,
        "ai_estimate": 58
      }
    },
    "ai": {
      "confidence": "medium",
      "reasoning": "Trump lidera pesquisas..."
    },
    "volume": 145230.50,
    "relevance_score": 85,
    "end_date": "2028-11-05T23:00:00+00:00"
  },
  "sent_at": "2026-03-15T18:30:00+00:00"
}

EXEMPLO DE HANDLER

PHP — Laravel
Route::post('/webhook/predixdata/new-market',
  function (Request $request) {
    $data = $request->all();
    $m = $data['market'];

    Market::updateOrCreate(
      ['predixdata_id' => $m['market_id']],
      [
        'title'     => $m['title'],
        'slug'      => $m['slug'],
        'category'  => $data['category'],
        'yes_odd'   => $m['odds']['yes']['odd'],
        'no_odd'    => $m['odds']['no']['odd'],
        'yes_pool'  => $m['odds']['yes']['pool'],
        'no_pool'   => $m['odds']['no']['pool'],
        'end_date'  => $m['end_date'],
        'active'    => true,
      ]
    );

    return response()->json(['received' => true]);
});
POST

notify-closed

{sua_url}/webhook/predixdata/notify-closed

Enviado quando um mercado fecha para novas apostas (5 minutos antes do end_date).

Campos do Payload

CampoTipoDescrição
eventstringSempre "notify-closed"
market_idstringID do mercado — mesmo market_id retornado no feed
event_idstringID do evento que agrupa este mercado
titlestringTítulo em PT-BR
end_datestringData de encerramento (ISO 8601)
closedbooleanSempre true
closed_atstringQuando o mercado foi fechado

PAYLOAD

JSON
{
  "event": "notify-closed",
  "market_id": "1508196",
  "event_id": "248137",
  "title": "O preço do Bitcoin estará entre...",
  "end_date": "2026-03-12T16:00:00+00:00",
  "closed": true,
  "closed_at": "2026-03-12T15:55:12+00:00"
}

EXEMPLO DE HANDLER

PHP — Laravel
Route::post('/webhook/predixdata/notify-closed',
  function (Request $request) {
    $data = $request->all();

    $market = Market::where(
      'predixdata_id', $data['market_id']
    )->first();

    if ($market) {
      $market->update([
        'closed'    => true,
        'closed_at' => $data['closed_at'],
      ]);
    }

    return response()->json(['received' => true]);
});
POST

final-result

{sua_url}/webhook/predixdata/final-result

Enviado quando o resultado final do mercado é confirmado.

Campos do Payload

CampoTipoDescrição
eventstringSempre "final-result"
market_idstringID do mercado — mesmo market_id retornado no feed
event_idstringID do evento que agrupa este mercado
titlestringTítulo em PT-BR
end_datestringData de encerramento
resultstring"yes" ou "no" — sempre lowercase
resolved_atstringQuando o resultado foi confirmado
O campo result será "yes" ou "no" — sempre lowercase.

PAYLOAD

JSON
{
  "event": "final-result",
  "market_id": "1508196",
  "event_id": "248137",
  "title": "O preço do Bitcoin estará entre...",
  "end_date": "2026-03-12T16:00:00+00:00",
  "result": "yes",
  "resolved_at": "2026-03-12T18:30:00+00:00"
}

EXEMPLO DE HANDLER

PHP — Laravel
Route::post('/webhook/predixdata/final-result',
  function (Request $request) {
    $data = $request->all();

    $market = Market::where(
      'predixdata_id', $data['market_id']
    )->first();

    if ($market) {
      $market->update([
        'result'      => $data['result'],
        'active'      => false,
        'resolved_at' => $data['resolved_at'],
      ]);

      // Processa pagamentos, notifica usuários...
    }

    return response()->json(['received' => true]);
});
POST

market-refund

{sua_url}/webhook/predixdata/market-refund

Enviado quando um mercado é reembolsado automaticamente. Isso acontece quando o resultado não é confirmado dentro de 24 horas após o fechamento.

Campos do Payload

CampoTipoDescrição
eventstringSempre "market-refund"
market_idstringID do mercado
event_idstringID do evento
titlestringTítulo em PT-BR
activebooleanSempre false
closedbooleanSempre true
resultstringSempre "refund"
refunded_atstringQuando o refund foi processado
Ao receber este webhook, devolva o valor apostado aos usuários. O campo result será "refund" em vez de "yes"/"no".

PAYLOAD

JSON
{
  "event": "market-refund",
  "market_id": "1508196",
  "event_id": "248137",
  "title": "O preço do Bitcoin estará entre...",
  "active": false,
  "closed": true,
  "result": "refund",
  "refunded_at": "2026-03-13T18:00:00+00:00"
}

EXEMPLO DE HANDLER

PHP — Laravel
Route::post('/webhook/predixdata/market-refund',
  function (Request $request) {
    $data = $request->all();

    $market = Market::where(
      'predixdata_id', $data['market_id']
    )->first();

    if ($market) {
      $market->update([
        'result' => 'refund',
        'active' => false,
      ]);

      // Devolver valor apostado aos usuários
      $market->bets()->each(function ($bet) {
        $bet->user->increment('balance', $bet->amount);
        $bet->update(['status' => 'refunded']);
      });
    }

    return response()->json(['received' => true]);
});

Validação HMAC

Todo webhook inclui uma assinatura HMAC-SHA256 no header X-Predixdata-Signature. Valide para garantir que a request veio do Predixdata.

Como funciona

EtapaDescrição
1Predixdata gera HMAC-SHA256 do body JSON usando sua secret_key
2Assinatura enviada no header X-Predixdata-Signature
3Você recalcula o HMAC e compara com a assinatura
Sempre valide a assinatura antes de processar o webhook. Rejeite requests inválidas com 401.

PHP

Laravel
$signature = $request->header('X-Predixdata-Signature');
$secret    = config('services.predixdata.secret_key');

$expected = hash_hmac(
  'sha256',
  $request->getContent(),
  $secret
);

if (!hash_equals($expected, $signature)) {
  return response()->json(
    ['error' => 'Invalid signature'], 401
  );
}

// Assinatura válida — processar

NODE.JS

Express
const crypto = require('crypto');

const signature = req.headers['x-predixdata-signature'];
const expected  = crypto
  .createHmac('sha256', process.env.PREDIXDATA_SECRET)
  .update(JSON.stringify(req.body))
  .digest('hex');

if (signature !== expected) {
  return res.status(401).json({
    error: 'Invalid signature'
  });
}

// Assinatura válida — processar

Trânsito ao Vivo

Mercados de previsão baseados em câmeras de trânsito em tempo real. A cada 5 minutos, uma nova rodada é criada automaticamente com uma linha Over/Under de veículos. Os usuários apostam se a contagem será acima ou abaixo da linha.

Como funciona

EtapaTempoO que acontece
1. Abertura0:00Nova rodada criada — mercado chega via webhook new-market com source: "traffic"
2. Apostas0:00 → 3:00Apostadores escolhem Over (SIM) ou Under (NÃO). Odds se movem via CPMM igual aos outros mercados
3. Fechamento3:00Apostas travadas — webhook notify-closed é enviado
4. Resultado5:00Contagem final de veículos enviada — webhook final-result ou market-refund (empate na linha)
5. Nova rodada5:05Ciclo recomeça automaticamente
Mercados de trânsito usam a mesma infraestrutura dos outros mercados — mesmos webhooks, mesmas odds CPMM, mesmas APIs de aposta (POST /transaction) e simulação (POST /simulate-bet). A única diferença é que eles vêm com source: "traffic" e incluem um campo extra video_url.

CARACTERÍSTICAS

source:       "traffic"
category:     "live"
sub_category: "transito"
duração:      5 minutos por rodada
apostas:      3 minutos (fecha 2 min antes)
tipo:         Over / Under (veículos)
video_url:    URL do stream ao vivo

CICLO VISUAL

0:00 ─── Rodada abre ──────────────────
 ▸ Apostas habilitadas
 ▸ Odds se movem com cada aposta
3:00 ─── Apostas fechadas ────────────
 ✕ Novas apostas bloqueadas
5:00 ─── Resultado ───────────────────
 ✓ Contagem final → Over/Under/Push
5:05 ─── Nova rodada ─────────────────

Vídeo ao Vivo

Cada mercado de trânsito inclui um campo video_url que aponta para uma página hospedada no Predixdata com o stream ao vivo da câmera (MJPEG via HTTPS). A URL real da câmera nunca é exposta — você sempre recebe uma URL nossa.

Formato da URL

CampoExemplo
video_urlhttps://predixdata.com/live/xK9mP2nQr7sT4u8v.html

Como usar

MétodoDescrição
iframeEmbedar a página video_url direto no seu site — já vem com o stream fullscreen e fundo preto
LinkAbrir em nova aba para o usuário assistir enquanto aposta

EMBED COM IFRAME

HTML
<div class="video-container">
  <iframe
    src="{{ video_url }}"
    width="100%"
    height="400"
    frameborder="0"
    style="border-radius: 8px"
  ></iframe>
</div>

REACT

JSX
{market.video_url && (
  <iframe
    src={market.video_url}
    className="w-full aspect-video rounded-lg"
    frameBorder="0"
  />
)}

Ciclo de Vida (5 min)

Cada rodada passa por 4 webhooks — os mesmos que você já usa para mercados normais. A diferença é a velocidade: tudo acontece em 5 minutos.

Webhooks por ordem

#WebhookQuandoAção no seu sistema
1new-marketRodada abre (0:00)Salvar mercado, exibir vídeo, habilitar apostas Over/Under
2notify-closed2 min antes do fim (3:00)Bloquear novas apostas, mostrar countdown
3afinal-resultFim da rodada (5:00)Mostrar resultado, liquidar apostas (Over=yes / Under=no)
3bmarket-refundEmpate na linhaReembolsar todas as apostas

Mapeamento Over/Under → Yes/No

Aposta do usuárioTipo na APIResultado ganhador
Over (acima da linha)type: "yes"result: "yes"
Under (abaixo da linha)type: "no"result: "no"
Exato na linha (Push)result: "refund"

EXEMPLO: new-market (TRÂNSITO)

JSON — Webhook Payload
{
  "event": "new-market",
  "event_id": "traffic_rod-br-101-km42",
  "event_title": "Trânsito ao Vivo - BR-101 KM 42",
  "category": "live",
  "sub_category": "transito",
  "market": {
    "market_id": "traffic_rod-br-101-km42_15",
    "source": "traffic",
    "slug": "transito-br-101-km-42-rodada-15",
    "title": "Rodada #15 - Mais de 14.5 veículos irão cruzar a linha?",
    "description": "Mercado resolve SIM se a contagem de veículos for superior a 14.5 ao final da rodada.",
    "video_url": "https://predixdata.com/live/xK9mP2nQr7sT4u.html",
    "image_url": "https://s2.glbimg.com/...rodovia-dutra-divulgacao.jpg",
    "icon_url": "https://s2.glbimg.com/...rodovia-dutra-divulgacao.jpg",
    "odds": {
      "yes": { "odd": 1.85, "pool": 43.20, "ai_estimate": 54 },
      "no":  { "odd": 2.12, "pool": 36.80, "ai_estimate": 46 }
    },
    "ai": {
      "confidence": "low",
      "reasoning": "Linha de 14.5 calculada com base no histórico da câmera..."
    },
    "volume": 0,
    "closed": false,
    "active": true,
    "relevance_score": 75,
    "end_date": "2026-03-23T15:05:00+00:00"
  },
  "sent_at": "2026-03-23T15:00:00+00:00"
}

Over / Under

A linha (threshold) de cada rodada é calculada automaticamente com base no histórico da câmera. O sistema considera 3 fatores:

Cálculo da Linha

FatorPesoDescrição
Últimas 5 rodadas50%Tendência recente — se o trânsito está mais ou menos movimentado
Mesma hora ontem30%Padrão horário — rush, almoço, madrugada (com margem de ±30 min)
Média geral20%Baseline da câmera

Se a média da mesma hora de ontem diverge mais de 20% da média recente, o sistema aumenta o peso dela para 40% — isso significa que detectou um padrão horário forte (ex: rush das 18h).

Regras da linha

RegraDescrição
ArredondamentoLinha sempre em .0 ou .5 (ex: 12.5, 15.0)
Sem históricoPadrão de 10.0 na primeira rodada

A linha pode ser extraída do título do mercado (ex: "Rodada #15 - Mais de 14.5 veículos irão cruzar a linha?").

TÍTULO E DESCRIPTION

Exemplo
"title": "Rodada #15 - Mais de 14.5 veículos irão cruzar a linha?"
"description": "Mercado resolve SIM se a contagem de veículos for superior a 14.5 ao final da rodada."

EXTRAINDO A LINHA (REGEX)

JavaScript
// Extrair a linha do título
const match = title.match(
  /Mais de ([\d.]+)/
);
const line = match ? parseFloat(match[1]) : null;

ODDS E PROBABILIDADES

// odds.yes = Over  (acima da linha)
// odds.no  = Under (abaixo da linha)
//
// ai_estimate = probabilidade em % (ex: 55, 45)
// Mesmo padrão dos outros mercados
//
// Com histórico: baseado nas últimas 10 rodadas
// Sem histórico: assimetria leve aleatória (40-60%)
// Nunca 50/50 — sempre um lado ligeiramente favorito
//
// Pool inicial: R$80 (maior que padrão)
// Mercado rápido = mais liquidez = estabilidade

Integração Completa

Exemplo passo a passo de como integrar mercados de trânsito no seu sistema.

Receber o mercado

No seu handler de webhook new-market, verifique se sub_category === "transito". Se sim, salve o mercado com o video_url e exiba na UI com os botões Over e Under.

Exibir vídeo + apostas

Embede a video_url via iframe na sua página — a página já contém o stream MJPEG via HTTPS. Mostre a linha, as odds atuais e botões SIM/NÃO. Use POST /simulate-bet para preview em tempo real.

Executar aposta

Use POST /api/v1/transaction com type: "yes" para Over ou type: "no" para Under. As odds se movem igual CPMM — a mesma API dos outros mercados.

Receber fechamento

Webhook notify-closed chega 2 min antes do fim. Bloqueie o botão de apostar e mostre um countdown.

Receber resultado

Webhook final-result com result: "yes" (Over ganhou) ou "no" (Under ganhou). Ou market-refund se empate na linha. Liquide as apostas e mostre próxima rodada.

HANDLER COMPLETO — LARAVEL

PHP — Laravel
// routes/api.php
Route::post('/webhook/predixdata/new-market',
  function (Request $r) {
    $data = $r->all();
    $m = $data['market'];

    // Mercado de trânsito?
    $isTraffic = $data['sub_category'] === 'transito';

    Market::updateOrCreate(
      ['market_id' => $m['market_id']],
      [
        'title'     => $m['title'],
        'source'    => $m['source'] ?? 'polymarket',
        'video_url' => $m['video_url'] ?? null,
        'yes_odd'   => $m['odds']['yes']['odd'],
        'no_odd'    => $m['odds']['no']['odd'],
        'yes_pool'  => $m['odds']['yes']['pool'],
        'no_pool'   => $m['odds']['no']['pool'],
        'end_date'  => $m['end_date'],
      ]
    );

    if ($isTraffic) {
      // Broadcast para exibir na tela de trânsito
      broadcast(new TrafficRoundStarted($m));
    }

    return response()->json(['ok' => true]);
});

APOSTAR OVER (YES)

cURL
curl -X POST "https://predixdata.com/api/v1/transaction" \
  -H "X-Public-Key: pub_..." \
  -H "X-Secret-Key: sec_..." \
  -H "Content-Type: application/json" \
  -d '{
    "market_id": "traffic_rod-br-101-km42_15",
    "type": "yes",
    "amount": 50.00,
    "odd": 1.85
  }'

Jogos ao Vivo

Mercados de previsão baseados em jogos 3D ao vivo transmitidos via stream. Dois grupos (YES e NO) se enfrentam e os usuários apostam em quem vence. Usa a mesma infraestrutura de webhooks e apostas dos outros mercados.

Como funciona

EtapaTempoO que acontece
1. Abertura0:00Nova rodada criada — mercado chega via webhook new-market com source: "game"
2. Apostas0:00 → 0:15Apostadores escolhem YES ou NO. Odds se movem via CPMM
3. Fechamento0:15Apostas travadas — webhook notify-closed é enviado
4. Jogo0:15 → fimJogo roda até um grupo vencer (máx ~10 min)
5. ResultadofimWebhook final-result ou market-refund (empate/timeout)
Mercados de jogos usam a mesma infraestrutura dos outros mercados — mesmos webhooks, mesmas APIs de aposta (POST /transaction) e simulação (POST /simulate-bet). A diferença é que eles vêm com source: "game", sub_category: "game" e o resultado é binário: YES vence ou NO vence.

CARACTERÍSTICAS

source:       "game"
category:     "live"
sub_category: "game"
duração:      ~2 minutos (variável)
apostas:      15 segundos após abertura
tipo:         YES vence / NO vence
video_url:    URL do stream ao vivo
image_url:    Logo do jogo

CICLO VISUAL

0:00 ─── Rodada abre ──────────────────
 ▸ Grupos YES e NO revelados
 ▸ Apostas habilitadas (15s)
0:15 ─── Apostas fechadas ────────────
 ✕ Novas apostas bloqueadas
 ▸ Jogo em andamento
~2:00 ── Resultado ───────────────────
 ✓ Grupo vencedor → YES ou NO
~2:10 ── Nova rodada ─────────────────

Ciclo de Vida

Cada rodada de jogo passa pelos mesmos 4 webhooks dos mercados normais. A diferença principal é que o tempo de apostas é curto (15 segundos) e a duração do jogo é variável.

Webhooks por ordem

#WebhookQuandoAção no seu sistema
1new-marketRodada abreSalvar mercado, exibir vídeo, habilitar apostas YES/NO
2notify-closed15s após aberturaBloquear novas apostas, mostrar "jogo em andamento"
3afinal-resultJogo terminaMostrar vencedor, liquidar apostas (yes ou no)
3bmarket-refundEmpate/timeoutReembolsar todas as apostas

Mapeamento de Resultado

SituaçãoResultadoWebhook
Grupo YES venceresult: "yes"final-result
Grupo NO venceresult: "no"final-result
Empate / TimeoutReembolsomarket-refund

EXEMPLO: new-market (JOGO)

JSON — Webhook Payload
{
  "event": "new-market",
  "event_id": "traffic_arena-flags-battle",
  "event_title": "Arena ao Vivo - Arena 3D - Bandeiras",
  "category": "live",
  "sub_category": "game",
  "market": {
    "market_id": "traffic_arena-flags-battle_16_KnRAdC",
    "source": "game",
    "slug": "game-arena-3d-bandeiras-rodada-16-knradc",
    "title": "Rodada #16 - Quem vence a batalha?",
    "description": "Mercado resolve SIM se o Grupo YES vencer a rodada.",
    "video_url": "https://predixdata.com/live/xK9mP2nQr7sT4u.html",
    "image_url": "https://mathzdev.b-cdn.net/ch-v2-logo/...",
    "icon_url": "https://mathzdev.b-cdn.net/ch-v2-logo/...",
    "odds": {
      "yes": { "odd": 1.55, "pool": 44.80, "ai_estimate": 56 },
      "no":  { "odd": 1.72, "pool": 35.20, "ai_estimate": 44 }
    },
    "ai": {
      "confidence": "low",
      "reasoning": "Odds calculadas com base no histórico de vitórias..."
    },
    "volume": 0,
    "closed": false,
    "active": true,
    "relevance_score": 75,
    "end_date": "2026-04-09T02:48:33+00:00"
  },
  "sent_at": "2026-04-09T02:46:33+00:00"
}

Odds YES / NO

As odds dos jogos são calculadas de forma diferente dos mercados de trânsito. Não existe "linha" — é simplesmente YES vence ou NO vence.

Cálculo das Odds

CenárioLógica
Com histórico (≥3 rodadas)Baseado na taxa de vitórias YES vs NO das últimas 20 rodadas, com suavização
Sem históricoOdds entre 1.40 e 1.70 com leve variação aleatória
Especial (~5%)Odd de 2.00 em um dos lados — lado oposto entre 1.10 e 1.30

Range de Odds

TipoMínimoMáximo
Normal1.101.90
Especial (~5% das rodadas)1.102.00

As odds se movem com cada aposta via CPMM, igual aos outros mercados. As odds iniciais são apenas o ponto de partida.

TÍTULO E DESCRIPTION

Exemplo
"title": "Rodada #16 - Quem vence a batalha?"
"description": "Mercado resolve SIM se o Grupo YES vencer a rodada."

DIFERENÇAS DO TRÂNSITO

// TRÂNSITO
source:       "traffic"
sub_category: "transito"
resultado:    Over / Under / Push (refund)
odds:         1.01 — 3.00+

// JOGO
source:       "game"
sub_category: "game"
resultado:    YES vence / NO vence / Refund
odds:         1.10 — 1.90 (esporadicamente 2.00)

COMO IDENTIFICAR

JavaScript
// No handler de new-market:
const isGame = data.sub_category === 'game';
const isTraffic = data.sub_category === 'transito';

if (isGame) {
  // Exibir botões YES / NO
  // Mostrar stream do jogo
} else if (isTraffic) {
  // Exibir botões Over / Under
  // Mostrar câmera de trânsito
}

Integração Completa

Exemplo passo a passo de como integrar mercados de jogos ao vivo no seu sistema.

Receber o mercado

No seu handler de webhook new-market, verifique se sub_category === "game". Se sim, salve o mercado com o video_url e exiba na UI com os botões YES e NO.

Exibir vídeo + apostas

Embede a video_url via iframe — mostra o jogo 3D ao vivo. Use a image_url como logo/thumbnail. Mostre as odds atuais e botões YES/NO. Apostas duram apenas 15 segundos — destaque o countdown.

Executar aposta

Use POST /api/v1/transaction com type: "yes" ou type: "no". Mesma API dos outros mercados.

Receber fechamento

Webhook notify-closed chega 15s após abertura. Bloqueie o botão de apostar e mostre "jogo em andamento".

Receber resultado

Webhook final-result com result: "yes" (Grupo YES venceu) ou "no" (Grupo NO venceu). Ou market-refund se empate/timeout. Liquide as apostas e aguarde próxima rodada.

HANDLER — LARAVEL

PHP — Laravel
// routes/api.php
Route::post('/webhook/predixdata/new-market',
  function (Request $r) {
    $data = $r->all();
    $m = $data['market'];

    // Tipo de mercado ao vivo?
    $isGame = $data['sub_category'] === 'game';
    $isTraffic = $data['sub_category'] === 'transito';

    Market::updateOrCreate(
      ['market_id' => $m['market_id']],
      [
        'title'     => $m['title'],
        'source'    => $m['source'],
        'video_url' => $m['video_url'] ?? null,
        'image_url' => $m['image_url'] ?? null,
        'yes_odd'   => $m['odds']['yes']['odd'],
        'no_odd'    => $m['odds']['no']['odd'],
        'end_date'  => $m['end_date'],
      ]
    );

    if ($isGame) {
      broadcast(new GameRoundStarted($m));
    } elseif ($isTraffic) {
      broadcast(new TrafficRoundStarted($m));
    }

    return response()->json(['ok' => true]);
});

APOSTAR YES

cURL
curl -X POST "https://predixdata.com/api/v1/transaction" \
  -H "X-Public-Key: pub_..." \
  -H "X-Secret-Key: sec_..." \
  -H "Content-Type: application/json" \
  -d '{
    "market_id": "traffic_arena-flags-battle_16_KnRAdC",
    "type": "yes",
    "amount": 50.00,
    "odd": 1.55
  }'

Guia de Odds

Cada mercado no Predixdata usa um modelo AMM (Automated Market Maker) com liquidez inicial de R$50. As odds são estilo bet365 (decimal) — uma odd de 2.50 significa que R$10 apostados retornam R$25.

Dados que você recebe no feed

CampoTipoDescrição
odds.yes.oddfloatOdd decimal do YES (ex: 1.35)
odds.yes.poolfloatVolume em R$ no pool YES
odds.no.oddfloatOdd decimal do NO (ex: 4.12)
odds.no.poolfloatVolume em R$ no pool NO

Cálculo simples (sem slippage)

Para apostas pequenas relativas ao pool, a fórmula é direta:

CampoFórmulaExemplo (R$10, odd 1.35)
payoutamount × odd10 × 1.35 = R$13.50
profitpayout − amount13.50 − 10 = R$3.50

Cálculo com slippage (CPMM)

Para apostas maiores, o slippage reduz a odd efetiva. Quanto maior a aposta relativa ao pool, pior a odd. Use esta fórmula para calcular o payout exato:

PassoFórmulaDescrição
1K = yes_pool × no_poolConstante do pool (produto invariável)
2newPool = pool_apostado + amountPool do lado que recebe a aposta
3unitsOut = pool_oposto − (K / newPool)Unidades que saem do lado oposto
4payout = amount + (unitsOut × 0.95)5% de margem da casa sobre o lucro
5odd = min(payout / amount, odd_exibida)Cap: nunca paga mais que a odd na tela
Com liquidez de R$50, apostas de até R$5 (~10% do pool) têm slippage desprezível. Acima disso o slippage aumenta progressivamente — o AMM protege o mercado de apostas muito grandes.

EXEMPLO PRÁTICO

Texto
Mercado: "Brasil ganha a Copa?"
Pool inicial: yes_pool=37.50, no_pool=12.50
Odds: yes=1.28, no=4.12

─── Aposta R$5 no YES ───

K = 37.50 × 12.50 = 468.75

newYesPool = 37.50 + 5 = 42.50
newNoPool  = 468.75 / 42.50 = 11.03

unitsOut = 12.50 − 11.03 = 1.47
houseCut = 1.47 × 0.05 = 0.07
payout   = 5 + 1.47 − 0.07 = 6.40
odd      = 6.40 / 5 = 1.28x

→ Aposta R$5, retorno R$6.40, lucro R$1.40

─── Aposta R$25 no YES (slippage alto) ───

newYesPool = 37.50 + 25 = 62.50
newNoPool  = 468.75 / 62.50 = 7.50

unitsOut = 12.50 − 7.50 = 5.00
houseCut = 5.00 × 0.05 = 0.25
payout   = 25 + 5.00 − 0.25 = 29.75
odd      = 29.75 / 25 = 1.19x

→ Odd caiu de 1.28x pra 1.19x (slippage)

JAVASCRIPT

JavaScript
function calcPayout(market, type, amount) {
  const yp = market.odds.yes.pool;
  const np = market.odds.no.pool;
  const K  = yp * np;

  const isYes    = type === 'yes';
  const betPool  = isYes ? yp : np;
  const other    = isYes ? np : yp;
  const maxOdd   = isYes
    ? market.odds.yes.odd
    : market.odds.no.odd;

  const newPool  = betPool + amount;
  const unitsOut = other - (K / newPool);
  const houseCut = unitsOut * 0.05;
  let   payout   = amount + unitsOut - houseCut;
  let   odd      = payout / amount;

  // Cap pela odd exibida
  if (odd > maxOdd) {
    odd = maxOdd;
    payout = amount * maxOdd;
  }

  return {
    odd:    +odd.toFixed(2),
    payout: +payout.toFixed(2),
    profit: +(payout - amount).toFixed(2),
  };
}

// Uso: dados do feed /api/v1/get-markets
const r = calcPayout(market, 'yes', 10);
console.log(r);
// { odd: 1.25, payout: 12.50, profit: 2.50 }

PHP LARAVEL

PHP
function calcPayout(
    float $yesPool,
    float $noPool,
    float $yesOdd,
    float $noOdd,
    string $type,
    float $amount
): array {
    $K = $yesPool * $noPool;

    if ($type === 'yes') {
        $newPool  = $yesPool + $amount;
        $other    = $noPool;
        $maxOdd   = $yesOdd;
    } else {
        $newPool  = $noPool + $amount;
        $other    = $yesPool;
        $maxOdd   = $noOdd;
    }

    $unitsOut = $other - ($K / $newPool);
    $houseCut = $unitsOut * 0.05;
    $payout   = $amount + $unitsOut - $houseCut;
    $odd      = $payout / $amount;

    if ($odd > $maxOdd) {
        $odd    = $maxOdd;
        $payout = $amount * $maxOdd;
    }

    return [
        'odd'    => round($odd, 2),
        'payout' => round($payout, 2),
        'profit' => round($payout - $amount, 2),
    ];
}

// Uso com dados do feed
$r = calcPayout(
    37.50, 12.50,  // pools
    1.28,  4.12,   // odds
    'yes', 10       // aposta
);

Erros

HTTPSignificadoQuando
200SucessoRequest processada com sucesso
401Não autorizadoChaves ausentes ou inválidas
403ProibidoAgente desativado
404Não encontradoMercado não encontrado (transaction/simulate)
409ConflitoOdd sofreu alteração — atualize via WebSocket e tente novamente
422Não processávelMercado fechado ou dados inválidos

Erros comuns

MensagemCausaSolução
Credenciais não fornecidas.Headers ausentesEnvie X-Public-Key e X-Secret-Key
Credenciais inválidas.Chaves incorretasVerifique suas chaves
Agente desativado.Agente inativoEntre em contato com o suporte
Mercado não encontrado.ID inválidoVerifique o market_id no feed
Mercado fechado para apostas.Mercado closed/inativoSó é possível apostar em mercados ativos

401 UNAUTHORIZED

JSON
{
  "success": false,
  "error": "Credenciais inválidas."
}

403 FORBIDDEN

JSON
{
  "success": false,
  "error": "Agente desativado. Entre em contato com o suporte."
}