amarorn / docs /api-referencia.md
beAnalytic's picture
feat: sync main with feature/superbet-live-inplay
16c19b8 verified
|
Raw
History Blame Contribute Delete
9.07 kB

Referência da API

Base URL: http://localhost:8000
Documentação interativa: /docs
Versão: 0.2.0

Todas as respostas são JSON. CORS habilitado (*).

Autenticação (API key)

Quando API_KEY está definida no ambiente do servidor, todas as rotas exigem chave, exceto:

  • GET /health/live (liveness Fly)
  • /docs, /redoc, /openapi.json

Envie a chave de uma destas formas:

Forma Exemplo
Header X-API-Key: sua-chave
Bearer Authorization: Bearer sua-chave

Várias chaves válidas: API_KEY=chave-antiga,chave-nova (rotação).

Sem API_KEY no servidor, a API permanece aberta (desenvolvimento local).

# Fly.io
fly secrets set API_KEY="$(openssl rand -hex 32)" -a api-noticia

curl -H "X-API-Key: $API_KEY" https://api-noticia.fly.dev/news/all

Frontend (build/deploy):

VITE_API_KEY=mesma-chave-do-servidor

Saúde e meta

GET /health

Status do datalake e contadores.

curl -s http://localhost:8000/health

GET /data/pulse

Pulso do datalake (mesmo uso no navegador): contadores, latest_silver_at, última coleta em _meta/collections.jsonl e links das ações (POST /news/sync, GET /news/all).

Em cada requisição (exceto /health/live e docs), a API inclui headers de pulso na resposta:

Header Conteúdo
X-Data-Pulse-At ISO8601 do snapshot
X-Articles-Silver Artigos no silver
X-Fixtures Fixtures (Brasileirão)
X-WC-Models-Ready true / false
X-Collections-Last-Run Última linha em _meta/collections.jsonl
X-Latest-Silver-At mtime do parquet silver mais recente

O frontend lê esses headers em todo apiFetch e atualiza o badge de status.

curl -sI http://localhost:8000/news/all | grep -i x-data-pulse
curl -s http://localhost:8000/data/pulse

GET /

Lista de endpoints disponíveis.


Notícias

POST /news/sync

Dispara coleta RSS → bronze → silver (até ~800 artigos: RSS_MAX_ENTRIES_PER_SOURCE=160 × 5 fontes).

Query opcional:

  • full_rebuild=true — reprocessa todo o bronze no silver.
  • fetch_body=true — baixa o HTML de cada matéria (corpo completo no body_preview; sync bem mais lento).

Por padrão, body_preview e summary usam o maior texto no silver sem limite de caracteres. O feed RSS costuma trazer só um trecho; para texto integral use fetch_body=true e depois full_rebuild=true.

GET /news/feed

Feed agregado de artigos silver (filtros via query params — ver Swagger).

GET /news/all

Retorna todas as notícias do silver em uma única resposta (mesmo formato do /news/feed).

Query Descrição Padrão
days Janela em dias omitido = histórico completo no lake
offset Deslocamento se passar de 5000 itens 0
source, q, team, home_team, away_team, teams Mesmos filtros do feed/cards

Teto interno: 5000 artigos por requisição (proteção de memória).

GET /news/cards

Notícias formatadas para cards no frontend (NewsArticleCard).

Query Descrição Padrão
limit Máx. cards 12 (máx. 48)
offset Paginação 0
days Janela em dias 14
team Um time/seleção
home_team / away_team Filtro por confronto
teams Lista Brasil,Marrocos
source, q Fonte e busca textual

Resposta: { total, limit, offset, teams, cards[] } — cada item tem title, body_preview, sentiment_label, teams_mentioned, etc.


Brasileirão (notícias + heurística)

POST /context

Contexto de notícias para um jogo.

{
  "home_team": "Flamengo",
  "away_team": "Palmeiras",
  "round_number": 1,
  "competition": "Brasileirão",
  "season": 2024
}

Resposta: context_text, contagens de notícias, sentimento, posição, forma.

POST /predict

Igual /context + palpite baseline (1/X/2), confiança e motivo.

GET /round/predict

Palpites da rodada em data/rounds/current.json.


Copa do Mundo — previsão

POST /worldcup/predict

Palpite avulso com ensemble completo.

Body mínimo:

{
  "home_team": "Brasil",
  "away_team": "Marrocos",
  "phase": "group"
}

Body com FEPT automático (Sofascore):

{
  "home_team": "Brasil",
  "away_team": "Argentina",
  "phase": "group",
  "sofascore_event_id": 11774480
}

Quando sofascore_event_id é enviado e kxl_match.fept está ausente, a API busca escalação + notas no Sofascore (requer curl_cffi no servidor). Metadados em model_breakdown.kxl_fept.

Estatísticas Sofascore (escanteios, chutes, posse)

GET /worldcup/sofascore/{event_id}/statistics?refresh=false

Retorna métricas achatadas (home_corners, away_corners, home_possession_pct, home_xg, cartões via incidentes, etc.). Com refresh=false, lê cache em data/lake/sofascore/match_stats.parquet se existir.

CLI equivalente:

ingest-sofascore --event-id 11774480 --stats-only --json
ingest-sofascore --event-id 11774480 --with-stats

Previsão de escanteios (Poisson)

POST /worldcup/corners/predict

{
  "home_team": "Brasil",
  "away_team": "Marrocos",
  "phase": "group"
}

Resposta inclui expected_corners (ex. "5.8x4.2"), expected_total_corners, probabilidades over_9.5 / under_9.5 e fatores do modelo (lambda_home, lambda_away). Treina com histórico em data/lake/sofascore/match_stats.parquet; com poucos jogos, faz blend com λ de gols (proxy).

CLI: predict-corners --home Brasil --away Marrocos --json

Body com entrada dinâmica KXL (opcional):

{
  "home_team": "Brasil",
  "away_team": "Marrocos",
  "phase": "group",
  "kxl_match": {
    "fecl": { "previsao_chuva_pct": 55, "estado_gramado": "Molhado" },
    "feju": { "perfil": "punitivista", "indice_cartao_falta": 0.28 },
    "fede": {
      "desfalques_visitante": [
        { "jogador": "Ziyech", "impacto_nota_elenco": -0.8 }
      ]
    },
    "fept": {
      "esquema_mandante": "4-3-3",
      "mandante_titulares_notas": {
        "goleiro": { "nome": "Alisson", "nota_sofascore": 7.1 },
        "atacantes": [{ "nome": "Vini Jr", "nota_sofascore": 7.9 }]
      }
    },
    "feem": { "contexto_peso_caos": 1.25, "jogo_decisivo": true }
  }
}

Resposta principal:

Campo Descrição
prediction 1, X ou 2
confidence Probabilidade do palpite (0–1)
prob_home, prob_draw, prob_away Probabilidades finais
poisson_score Placar mais provável (Dixon-Coles)
expected_goals Ex.: "1.4x1.0"
context Texto IA (stats + DNA KXL + colisão)
model_breakdown Ver abaixo

model_breakdown:

{
  "dixon_coles": { "1": 0.69, "X": 0.21, "2": 0.10 },
  "logistic": { "1": 0.56, "X": 0.22, "2": 0.22 },
  "dixon_coles_rho": 0.08,
  "poisson_factors": {
    "lambda_home": 1.42,
    "lambda_away": 0.98,
    "home_attack": 1.15,
    "away_attack": 0.92,
    "rho": 0.08
  },
  "ensemble_weights": { "dixon_coles": 0.30, "logistic": 0.70 },
  "kxl_baseline": { "1": 0.45, "X": 0.28, "2": 0.27, "blend_weight": 0.25 },
  "kxl_collision": { },
  "kxl_dynamic": { "blocks_used": ["fecl", "fept"], "engine": "wc_kxl_collision" }
}

GET /worldcup/round

Palpites para todos os jogos em data/rounds/wc_2026.json.

GET /worldcup/teams

Lista de seleções (fixtures + rodada).


Copa — validação histórica

GET /worldcup/editions

Edições disponíveis (1986–2022 no lake típico).

GET /worldcup/editions/{season}/matches

Jogos reais de uma edição (placar, fase, resultado). Mata-mata: group_name: null.

POST /worldcup/validate

Backtest com recorte temporal (só dados anteriores ao jogo).

{ "season": 2022, "home_team": "Brasil", "away_team": "Sérvia" }

ou { "season": 2022, "match_id": "..." }.

Resposta: correct: true/false, actual_result, cutoff_note.


Value bets (odds + EV)

POST /worldcup/value/live

Requer ODDS_API_KEY no .env.

{
  "min_edge": 0.03,
  "schedule_file": "data/rounds/wc_2026.json"
}

Cruza probabilidades do modelo com odds The Odds API. Retorna edges[] com EV, fair odd, Kelly ¼.


Códigos de erro comuns

HTTP Causa
400 Body inválido, chave odds ausente
404 Rodada/arquivo/ jogo não encontrado
502 Falha na Odds API externa

Exemplos curl

# Palpite WC
curl -s -X POST http://localhost:8000/worldcup/predict \
  -H "Content-Type: application/json" \
  -d '{"home_team":"Brasil","away_team":"Marrocos"}'

# Validar Copa 2022
curl -s -X POST http://localhost:8000/worldcup/validate \
  -H "Content-Type: application/json" \
  -d '{"season":2022,"home_team":"Brasil","away_team":"Sérvia"}'

# Via proxy frontend
curl -s http://localhost:5173/api/health