API v1 estável

Integre dados esportivos em minutos.

A SportsAPI oferece uma interface REST para consultar jogos, placares, horários, ligas e transmissões de diferentes fontes em um único formato.

1. Crie uma chave

No dashboard da sua conta.

2. Faça a chamada

Envie a chave no header.

3. Use os dados

Receba JSON normalizado.

Guia de integração para o seu agente de IA

Baixe (ou copie) um guia Markdown com um prompt de implementação organizado, os fluxos principais, autenticação, dados opcionais, odds, limites e erros. Use-o como contexto para o Claude Code, Cursor, Copilot ou outro agente; o contrato OpenAPI publicado complementa a referência quando precisar de detalhes completos de qualquer endpoint.

Autenticação

Envie sua API key no header X-API-Key. Mantenha a chave somente no servidor e nunca publique em código do navegador.

X-API-Key header
X-API-Key: sapi_xxxxxxxxxxxx

Use chaves separadas para desenvolvimento e produção. Se uma chave for exposta, revogue-a imediatamente no dashboard.

Primeira requisição

Consulte os jogos de futebol ao vivo com uma chamada GET. A referência completa está em /docs (OpenAPI).

terminal
curl "https://sportsapi.com.br/api/v1/games?sport=football&status=live&limit=20" \
  -H "X-API-Key: sapi_xxxx"

Para jogos agendados em uma data específica, combine date=YYYY-MM-DD com status=scheduled.

jogos agendados por data
curl "https://sportsapi.com.br/api/v1/games?sport=football&date=2026-08-20&status=scheduled&limit=100&offset=0" \
  -H "X-API-Key: sapi_xxxx"

Resposta 200 OK

A resposta inclui partidas paginadas (`matches`, `total`, `limit`, `offset`) e se os dados vieram do cache.

status da conta
curl "https://sportsapi.com.br/api/v1/account" \
  -H "X-API-Key: sapi_xxxx"
GET

Consultar jogos

Monte uma consulta e copie o código para sua linguagem.

Parâmetros

GET/api/v1/games?sport=football&status=live&limit=20
curl "https://sportsapi.com.br/api/v1/games?sport=football&status=live&limit=20" \
  -H "X-API-Key: sapi_xxxx"
Sua chave nunca é enviada neste playgroundGerar chave no dashboard

Filtros avançados

Use GET /api/v1/games/filter para consultar somente as partidas necessárias sem perder campos do objeto Match. Você pode enviar um único filtro ou combinar vários na mesma URL.

Combinação AND

Todos os filtros enviados precisam corresponder. region=Brasil + team=Flamengo retorna jogos do Flamengo no Brasil.

Texto flexível

q, competition, region e team aceitam trechos e ignoram diferenças entre maiúsculas, minúsculas e acentos.

Uma data por chamada

date usa YYYY-MM-DD. Sem date, a API consulta hoje em UTC; para intervalos, repita a chamada por data.

exemplo completo · ao vivo
curl "https://sportsapi.com.br/api/v1/games/filter?sport=football&region=Brasil&competition=Serie%20A&team=Flamengo&status=live&limit=20" \
  -H "X-API-Key: sapi_xxxx"
sportstring

Modalidade (football, basketball, tennis...). Padrão: football. Veja a lista completa liberada no seu plano em GET /sports.

Exemplo: football

dateYYYY-MM-DD

Data exata consultada. Para um intervalo, faça uma chamada por data. Padrão: hoje em UTC.

Exemplo: 2026-07-17

qstring (2–100)

Busca geral por trecho no nome do campeonato, mandante ou visitante. Use quando você ainda não sabe em qual campo o texto está.

Exemplo: flamengo

statusenum

Estado exato: live (ao vivo), scheduled (agendado), finished (finalizado), postponed (adiado) ou cancelled (cancelado).

Exemplo: live

competitionstring (1–100)

Trecho do nome ou id do campeonato. A busca ignora caixa e acentos. league continua aceito como alias legado.

Exemplo: brasileirao

regionstring (1–100)

Trecho do país da competição ou do país do local da partida. A busca ignora caixa e acentos.

Exemplo: brasil

teamstring (1–100)

Trecho do nome ou id do mandante ou visitante. Não é necessário informar se o time joga em casa ou fora.

Exemplo: flamengo

statusInCSV de status

Vários estados separados por vírgula. Não combine com status.

Exemplo: live,scheduled

matchIdstring

Id canônico exato da partida retornado pela SportsAPI.

Exemplo: a3f9c1e6b2d84f70

competitionIdstring

Id exato do campeonato, como devolvido em league.id (ou nos facets).

Exemplo: Qx7mP2aVn9sK

teamIdstring

Id exato de qualquer um dos times, como devolvido em homeTeam.id / awayTeam.id.

Exemplo: Rk3tY8wLc0dE

homeTeam / awayTeamstring

Trecho do nome ou id, restringindo especificamente mandante ou visitante.

Exemplo: Flamengo

homeTeamId / awayTeamIdstring

Id exato do mandante ou visitante, como devolvido pela API.

Exemplo: Rk3tY8wLc0dE

venuestring

Trecho do nome do estádio, ginásio, pista ou quadra.

Exemplo: Maracanã

citystring

Trecho da cidade do local da partida.

Exemplo: Rio de Janeiro

stagestring

Trecho da fase ou estágio, incluindo fases internas de esportes com sets, períodos ou sessões.

Exemplo: Semifinal

roundstring

Trecho do nome da rodada.

Exemplo: Rodada

roundNumberinteger

Número exato da rodada.

Exemplo: 15

tvNetworkstring

Trecho do nome, país ou id do canal de transmissão.

Exemplo: ESPN

eventTypestring

Tipo de evento presente na partida, quando disponível.

Exemplo: goal

startsAfter / startsBeforeepoch ms

Limites inclusivos para o horário de início dentro da data consultada.

Exemplo: 1784311200000

minHomeScore / maxHomeScoreinteger

Faixa inclusiva do placar do mandante. Jogos sem placar não correspondem.

Exemplo: 1

minAwayScore / maxAwayScoreinteger

Faixa inclusiva do placar do visitante. Jogos sem placar não correspondem.

Exemplo: 0

minTotalScore / maxTotalScoreinteger

Faixa inclusiva da soma dos dois placares.

Exemplo: 3

hasScoreboolean

Exige (true) ou exclui (false) partidas com algum placar disponível.

Exemplo: true

hasVenueboolean

Exige ou exclui dados do local.

Exemplo: true

hasBroadcastboolean

Exige ou exclui canais de transmissão.

Exemplo: true

hasOfficialsboolean

Exige ou exclui árbitros/oficiais.

Exemplo: true

hasEventsboolean

Exige ou exclui eventos da partida.

Exemplo: true

hasStandingsboolean

Exige ou exclui classificação/leaderboard embutido.

Exemplo: true

hasLiveClockboolean

Exige ou exclui cronômetro, período ou tempo ao vivo.

Exemplo: true

sortByenum

Ordena por startTime, homeScore, awayScore ou totalScore. Padrão: startTime.

Exemplo: startTime

sortOrderasc | desc

Direção da ordenação. Valores nulos de placar ficam por último.

Exemplo: asc

limitnumber

Itens por página (1–100). Padrão: 50.

Exemplo: 20

offsetnumber

Quantidade de itens ignorados antes da página atual. Comece em 0 e depois use pagination.nextOffset.

Exemplo: 0

agendados de um time em uma data
curl "https://sportsapi.com.br/api/v1/games/filter?sport=football&date=2026-08-20&team=Palmeiras&status=scheduled&limit=100" \
  -H "X-API-Key: sapi_xxxx"
busca geral por texto
curl "https://sportsapi.com.br/api/v1/games/filter?sport=football&q=libertadores&limit=50&offset=0" \
  -H "X-API-Key: sapi_xxxx"

Resposta completa do filtro

matches traz os objetos completos da página; filters confirma o que foi aplicado; pagination controla as próximas páginas; e facets permite montar seletores de campeonato, região e time com contagens.

filter-response.json
{
  "matches": [
    {
      "id": "a3f9c1e6b2d84f70",
      "sport": "football",
      "homeTeam": {
        "id": "5981",
        "name": "Flamengo",
        "logo": "/api/v1/images/media/team/5981",
        "color": "#D2122E"
      },
      "awayTeam": {
        "id": "1963",
        "name": "Palmeiras",
        "logo": "/api/v1/images/media/team/1963"
      },
      "homeScore": 2,
      "awayScore": 1,
      "status": "live",
      "startTime": 1784311200000,
      "league": {
        "id": "325",
        "name": "Brasileirão Série A",
        "country": "Brazil",
        "logo": "/api/v1/images/media/tournament/325"
      },
      "gameTimeDisplay": "74'",
      "period": "Segundo tempo",
      "roundName": "Rodada",
      "roundNum": 15,
      "venue": {
        "name": "Maracanã",
        "capacity": 78838
      },
      "officials": [
        "Anderson Daronco"
      ],
      "tvNetworks": [
        {
          "id": 42,
          "name": "ESPN",
          "logo": "/api/v1/images/media/tv-channel/42"
        }
      ]
    }
  ],
  "filters": {
    "sport": "football",
    "date": "2026-07-17",
    "query": null,
    "competition": "Serie A",
    "region": "Brasil",
    "team": "Flamengo",
    "status": "live"
  },
  "pagination": {
    "total": 1,
    "limit": 20,
    "offset": 0,
    "returned": 1,
    "hasNext": false,
    "nextOffset": null
  },
  "statusCounts": {
    "live": 1,
    "scheduled": 0,
    "finished": 0,
    "postponed": 0,
    "cancelled": 0
  },
  "facets": {
    "competitions": [
      {
        "id": "325",
        "name": "Brasileirão Série A",
        "country": "Brasil",
        "count": 1
      }
    ],
    "regions": [
      {
        "name": "Brasil",
        "count": 1
      }
    ],
    "teams": [
      {
        "id": "5981",
        "name": "Flamengo",
        "count": 1
      }
    ],
    "venues": [
      {
        "name": "Maracanã",
        "city": "Rio de Janeiro",
        "country": "Brasil",
        "count": 1
      }
    ],
    "broadcasters": [
      {
        "id": 42,
        "name": "ESPN",
        "country": "Brasil",
        "count": 1
      }
    ],
    "stages": [
      {
        "name": "Rodada 15",
        "count": 1
      }
    ]
  },
  "cached": true,
  "timestamp": 1784315678123
}

Como paginar

Enquanto pagination.hasNext for verdadeiro, repita a mesma consulta usando offset=pagination.nextOffset. Não some valores manualmente.

Como usar as facetas

As contagens consideram todo o resultado depois dos filtros, antes de limit/offset. Há facetas de competições, regiões, times, locais, canais e fases. Use o id ou name retornado como próximo filtro.

paginação automática · JavaScript
const params = new URLSearchParams({ sport: 'football', region: 'Brasil', status: 'scheduled', limit: '100', offset: '0' });
const allMatches = [];

do {
  const response = await fetch(`https://sportsapi.com.br/api/v1/games/filter?${params}`, {
    headers: { 'X-API-Key': 'sapi_xxxx' },
  });
  if (!response.ok) throw new Error(`SportsAPI: ${response.status}`);
  const data = await response.json();
  allMatches.push(...data.matches);
  if (!data.pagination.hasNext) break;
  params.set('offset', String(data.pagination.nextOffset));
} while (true);
Uma resposta com matches: [] e HTTP 200 significa que nenhum jogo correspondeu à combinação informada. Remova um filtro por vez ou consulte as facetas de uma busca mais ampla. Parâmetros inválidos retornam HTTP 400; chave ausente/inválida retorna 401; esporte fora do plano retorna 403; cota do plano esgotada retorna 402; rate limit retorna 429.

Formato da resposta

Todo dado é normalizado para o mesmo contrato. Nem todo campo aparece em toda partida — eles variam por esporte, então sempre trate campos ausentes como opcionais em vez de assumir que estarão sempre presentes.

response.json
{
  "matches": [
    {
      "id": "a3f9c1e6b2d84f70",
      "sport": "football",
      "homeTeam": {
        "id": "5981",
        "name": "Flamengo",
        "logo": "/api/v1/images/media/team/5981",
        "color": "#D2122E"
      },
      "awayTeam": {
        "id": "1963",
        "name": "Palmeiras",
        "logo": "/api/v1/images/media/team/1963"
      },
      "homeScore": 2,
      "awayScore": 1,
      "status": "live",
      "startTime": 1784311200000,
      "league": {
        "id": "325",
        "name": "Brasileirão Série A",
        "country": "Brazil",
        "logo": "/api/v1/images/media/tournament/325"
      },
      "gameTimeDisplay": "74'",
      "period": "Segundo tempo",
      "roundName": "Rodada",
      "roundNum": 15,
      "venue": {
        "name": "Maracanã",
        "capacity": 78838
      },
      "officials": [
        "Anderson Daronco"
      ],
      "tvNetworks": [
        {
          "id": 42,
          "name": "ESPN",
          "logo": "/api/v1/images/media/tv-channel/42"
        }
      ]
    }
  ],
  "total": 1,
  "limit": 50,
  "offset": 0,
  "cached": false,
  "timestamp": 1784315678123
}

Referência completa de campos

idstring

Id único e opaco da partida.

sportstring

Modalidade da partida.

statusstring

scheduled · live · finished · postponed · cancelled.

startTimenumber

Horário de início em epoch ms (UTC).

homeScore / awayScorenumber | null

Placar atual. null antes do jogo começar.

gameTimeDisplaystring (opcional)

Cronômetro legível, ex.: "74'" ou "Q3 08:12".

periodstring (opcional)

Tempo/quarto/set atual, quando informado.

roundName / stageNamestring (opcional)

Fase do torneio, ex.: "Quarterfinal", "Grupo A". Em automobilismo, roundName é a sessão do fim de semana ("Treino Livre 1", "Classificação", "Corrida") — cada sessão vem como uma partida separada.

roundNumnumber (opcional)

Número da rodada dentro da fase atual, quando a competição usa esse formato.

homeTeam.id / awayTeam.idstring

Id do time.

homeTeam.name / awayTeam.namestring

Nome do time.

homeTeam.logo / awayTeam.logostring (opcional)

Caminho relativo — veja a seção "Imagens" abaixo antes de usar.

homeTeam.color / awayColorstring (opcional)

Cor do time em hex, quando disponível.

league.id / league.name / league.countrystring

Dados da competição.

league.logostring (opcional)

Caminho relativo — veja a seção "Imagens" abaixo.

venue.name / venue.capacitystring / number (opcional)

Local da partida — populado para a maioria dos jogos, mesmo sem transmissão.

venue.city / venue.country / venue.courtstring (opcional)

Detalhes adicionais do local, quando disponíveis (court é a quadra específica, comum em tênis).

officials[]string[] (opcional)

Árbitro(s)/oficiais da partida, quando reportado.

tvNetworks[].name / tvNetworks[].logostring (opcional)

Canais de transmissão — cobertura naturalmente esparsa fora dos jogos de maior perfil.

stages[]array (opcional)

Parciais por set/quarto/tempo extra — comum em tênis, vôlei, basquete.

events[]array (opcional)

Linha do tempo de lances (gols, cartões, etc.), quando reportado.

standings[]array (opcional)

Classificação completa para golfe (todos os jogadores) e automobilismo (todos os pilotos da sessão) — essas modalidades não são "A vs B"; homeTeam/awayTeam trazem só os 2 primeiros, standings[] traz o campo inteiro (position, name, logo, isWinner, score).

GET

Detalhes da partida e odds

Use o mesmo id recebido em /games para buscar estatísticas, escalações, incidentes, histórico, previsões, odds e narração. Os blocos são opcionais: a fonte pode não fornecer todos os dados para todos os esportes.

Nem todos os jogos têm odds, estatísticas ou informações detalhadas publicadas. A cobertura depende do que nossos provedores disponibilizam; partidas de menor audiência ou sem interesse em apostas podem não ter esses dados. Quando um campo ou mercado não aparece, significa que a fonte não forneceu essa informação — isso, por si só, não é erro da API.

Se uma partida de grande relevância estiver sem dados, informe ao suporte o ID da partida, o endpoint consultado e o cabeçalho X-Request-Id da resposta. Vamos analisar o caso imediatamente para evitar impacto no seu produto.

detalhes da partida
curl "https://sportsapi.com.br/api/v1/games/a3f9c1e6b2d84f70/details?sport=football" \
  -H "X-API-Key: sapi_xxxx"

O campo matchId identifica a partida. O campo timestamp identifica o momento em que a resposta foi gerada. O cache da API pode manter dados por alguns segundos, especialmente em partidas ao vivo.

details-response.json
{
  "matchId": "a3f9c1e6b2d84f70",
  "info": {
    "intro": "Flamengo recebe o Palmeiras pela 15ª rodada.",
    "referee": {
      "name": "Anderson Daronco",
      "yellowCardsPerGame": 4.2,
      "redCardsPerGame": 0.2
    },
    "weather": {
      "conditionCode": 1,
      "temperature": "24°C",
      "wind": "12 km/h",
      "humidity": "72%",
      "pressure": "1015 hPa"
    }
  },
  "statistics": [
    {
      "key": "possession",
      "label": "Possession",
      "home": "55%",
      "away": "45%"
    },
    {
      "key": "shots",
      "label": "Shots",
      "home": "10",
      "away": "7"
    },
    {
      "key": "corners",
      "label": "Corners",
      "home": "6",
      "away": "3"
    }
  ],
  "periodStatistics": [
    {
      "period": "1ST",
      "stats": [
        {
          "key": "goals",
          "label": "Goals",
          "home": "1",
          "away": "0"
        }
      ]
    },
    {
      "period": "2ND",
      "stats": [
        {
          "key": "goals",
          "label": "Goals",
          "home": "1",
          "away": "1"
        }
      ]
    }
  ],
  "incidents": [
    {
      "type": "goal",
      "minute": "35'",
      "side": "home",
      "playerName": "Pedro",
      "assistName": "Arrascaeta",
      "homeScore": 1,
      "awayScore": 0
    },
    {
      "type": "yellow_card",
      "minute": "61'",
      "side": "away",
      "playerName": "Gustavo Gómez",
      "homeScore": 1,
      "awayScore": 0
    },
    {
      "type": "substitution",
      "minute": "70'",
      "side": "home",
      "playerInName": "Everton",
      "playerOutName": "Bruno Henrique",
      "homeScore": 1,
      "awayScore": 0
    }
  ],
  "lineups": {
    "confirmed": true,
    "home": {
      "formation": "4-3-3",
      "manager": "Filipe Luís",
      "teamRating": 7.2,
      "starters": [
        {
          "playerName": "Pedro",
          "shirtNumber": 9,
          "position": "F",
          "rating": 7.8,
          "isCaptain": false,
          "substitute": false,
          "minutesPlayed": 90,
          "isBest": true
        }
      ],
      "substitutes": [
        {
          "playerName": "Everton",
          "shirtNumber": 11,
          "position": "F",
          "rating": 6.8,
          "substitute": true,
          "minutesPlayed": 20
        }
      ]
    },
    "away": {
      "formation": "4-2-3-1",
      "manager": "Abel Ferreira",
      "teamRating": 6.9,
      "starters": [
        {
          "playerName": "Gustavo Gómez",
          "shirtNumber": 15,
          "position": "D",
          "rating": 6.7,
          "isCaptain": true,
          "substitute": false,
          "minutesPlayed": 90
        }
      ],
      "substitutes": []
    },
    "injuries": [
      {
        "side": "away",
        "playerName": "Nome do Jogador",
        "reason": "Lesão muscular"
      }
    ]
  },
  "playerStats": [
    {
      "playerName": "Pedro",
      "playerId": 123,
      "side": "home",
      "minutesPlayed": 90,
      "rating": 7.8,
      "goals": 1,
      "assists": 0,
      "shots": 4,
      "shotsOnTarget": 2,
      "expectedGoals": 0.72,
      "expectedAssists": 0.18,
      "passes": 28,
      "tackles": 1,
      "interceptions": 0,
      "duelsWon": 5,
      "yellowCards": 0,
      "redCards": 0
    }
  ],
  "standings": [
    {
      "name": "Classificação Geral",
      "groupName": "Série A",
      "rows": [
        {
          "position": 1,
          "teamName": "Flamengo",
          "played": 14,
          "won": 10,
          "drew": 2,
          "lost": 2,
          "goalsFor": 30,
          "goalsAgainst": 12,
          "goalDiff": 18,
          "points": 32,
          "note": "Classificação para a Libertadores"
        }
      ]
    }
  ],
  "headToHead": {
    "totalMatches": 10,
    "homeWins": 5,
    "draws": 2,
    "awayWins": 3,
    "homeGoals": 16,
    "awayGoals": 12,
    "meetings": [
      {
        "startTime": 1780000000000,
        "competitionName": "Brasileirão Série A",
        "homeTeamName": "Flamengo",
        "awayTeamName": "Palmeiras",
        "homeScore": 2,
        "awayScore": 1
      }
    ]
  },
  "form": [
    {
      "side": "home",
      "matches": 5,
      "wins": 3,
      "draws": 1,
      "losses": 1,
      "goals": 9,
      "conceded": 5,
      "avgPossessionPercent": 56,
      "avgShots": "12.4",
      "winStreak": 2,
      "unbeatenRun": 3,
      "overGoalsPercent": 60,
      "bothTeamsScorePercent": 80,
      "underCornersPercent": 40,
      "recent": []
    },
    {
      "side": "away",
      "matches": 5,
      "wins": 2,
      "draws": 2,
      "losses": 1,
      "goals": 7,
      "conceded": 6,
      "avgPossessionPercent": 48,
      "avgShots": "9.8",
      "winStreak": 1,
      "unbeatenRun": 2,
      "overGoalsPercent": 40,
      "bothTeamsScorePercent": 60,
      "underCornersPercent": 50,
      "recent": []
    }
  ],
  "prediction": {
    "h2h": {
      "totalMatches": 10,
      "homeWinPercent": 50,
      "drawPercent": 20,
      "awayWinPercent": 30
    },
    "homeForm": {
      "matches": 5,
      "wins": 3,
      "draws": 1,
      "losses": 1,
      "goals": 9,
      "conceded": 5,
      "avgPossessionPercent": 56,
      "avgShots": "12.4",
      "winStreak": 2,
      "unbeatenRun": 3,
      "overGoalsPercent": 60,
      "bothTeamsScorePercent": 80,
      "underCornersPercent": 40
    },
    "awayForm": {
      "matches": 5,
      "wins": 2,
      "draws": 2,
      "losses": 1,
      "goals": 7,
      "conceded": 6,
      "avgPossessionPercent": 48,
      "avgShots": "9.8",
      "winStreak": 1,
      "unbeatenRun": 2,
      "overGoalsPercent": 40,
      "bothTeamsScorePercent": 60,
      "underCornersPercent": 50
    },
    "goalTiming": [
      {
        "side": "home",
        "scored": [
          {
            "fromMinute": 0,
            "toMinute": 15,
            "percent": 20
          }
        ],
        "conceded": [
          {
            "fromMinute": 76,
            "toMinute": 90,
            "percent": 30
          }
        ]
      },
      {
        "side": "away",
        "scored": [],
        "conceded": []
      }
    ]
  },
  "odds": {
    "markets": [
      {
        "key": "1x2",
        "positions": [
          "home",
          "draw",
          "away"
        ],
        "quotes": [
          {
            "companyName": "Bet365",
            "opening": [
              2.1,
              3.4,
              3.2
            ],
            "latest": [
              1.95,
              3.5,
              3.6
            ]
          },
          {
            "companyName": "Betano",
            "latest": [
              2,
              3.4,
              3.55
            ]
          }
        ]
      },
      {
        "key": "handicap",
        "positions": [
          "home",
          "line",
          "away"
        ],
        "quotes": [
          {
            "companyName": "Bet365",
            "opening": [
              1.9,
              -0.5,
              1.95
            ],
            "latest": [
              1.85,
              -0.5,
              2
            ]
          }
        ]
      },
      {
        "key": "over_under",
        "positions": [
          "over",
          "line",
          "under"
        ],
        "quotes": [
          {
            "companyName": "Bet365",
            "opening": [
              1.9,
              2.5,
              1.9
            ],
            "latest": [
              1.8,
              2.5,
              2.05
            ]
          }
        ]
      },
      {
        "key": "corners",
        "positions": [
          "over",
          "line",
          "under"
        ],
        "quotes": [
          {
            "companyName": "Bet365",
            "latest": [
              1.9,
              9.5,
              1.9
            ]
          }
        ]
      }
    ],
    "impliedProbabilities": {
      "homePercent": 48.7,
      "drawPercent": 27.1,
      "awayPercent": 24.2
    }
  },
  "commentary": [
    {
      "chapter": 1,
      "minute": "35'",
      "content": "Gol do time da casa",
      "scoreLine": "1-0",
      "isGoal": true
    }
  ],
  "ballByBall": [
    {
      "over": 1,
      "inning": 1,
      "runs": 6,
      "wickets": 0,
      "balls": 6
    }
  ],
  "scorecards": [
    {
      "number": 1,
      "battingTeamName": "Time Exemplo",
      "runs": 180,
      "ballsFaced": 120,
      "fours": 15,
      "sixes": 4,
      "runRate": "9.00",
      "bowlingTeamName": "Time Adversário",
      "overs": 20,
      "wickets": 5
    }
  ],
  "timestamp": 1784315678123
}
statistics / periodStatistics

Estatísticas gerais e separadas por período. Valores home/away são strings porque podem conter percentuais, médias ou textos da fonte.

incidents / commentary

Incidentes estruturados e narração textual. Gol, cartões e substituições podem ter campos diferentes.

lineups / playerStats

Escalações, reservas, lesões e estatísticas individuais quando disponíveis.

headToHead / form / prediction

Confrontos anteriores, forma recente, tendências e sinais estatísticos. Não são garantia de resultado.

odds

Mercados por casa de aposta, com odds de abertura e mais recentes quando a fonte informa ambas.

ballByBall / scorecards

Dados específicos para críquete e beisebol; normalmente ausentes no futebol.

Estrutura de odds

Cada posição do array latest ou opening corresponde à mesma posição em positions. Em 1x2, por exemplo, a ordem é casa, empate, visitante. impliedProbabilities são probabilidades normalizadas sem a margem da casa, calculadas a partir das melhores odds 1X2 recentes.

Se a sua aplicação só precisa de cotações, chame GET /games/:id/odds no lugar desta: a resposta é menor e o bloco odds aqui vem junto de estatísticas, escalações e narração que você não vai usar. A lista completa de mercados está na seção Odds por partida.

GET

Odds por partida

Chame GET /api/v1/games/:id/odds quando você quiser apenas as cotações de uma partida — odds de abertura e as mais recentes de cada casa de aposta, mercados de handicap, gols, escanteios e cartões, e as probabilidades sem margem. Basta o id devolvido em /games; na maioria dos casos você nem precisa informar data ou esporte.

Mais leve que /details

Só a seção de odds é coletada. É a chamada certa para telas de cotação, comparadores e pré-jogo.

Nome de mercado estável

Cada mercado vem com uma chave canônica em markets[].key — sempre a mesma, independentemente de como a fonte escreve o nome do mercado.

Time só com id e nome

homeTeam e awayTeam trazem id e name — sem logo, cor ou placar. Busque esses campos em /games.

odds da partida
curl "https://sportsapi.com.br/api/v1/games/a3f9c1e6b2d84f70/odds?sport=football" \
  -H "X-API-Key: sapi_xxxx"
filtrando mercado e casa de aposta
curl "https://sportsapi.com.br/api/v1/games/a3f9c1e6b2d84f70/odds?sport=football&market=over_under&bookmaker=bet365" \
  -H "X-API-Key: sapi_xxxx"

Parâmetros

idpath

Id da partida, exatamente como devolvido em /games. É o mesmo id usado em /games/:id e /games/:id/details.

Exemplo: a3f9c1e6b2d84f70

sportenum

Esporte da partida. Padrão: football. Acelera a resolução quando você já sabe o esporte.

Exemplo: football

dateYYYY-MM-DD

Dia da partida. Sem esse parâmetro, a API procura hoje, os sete dias anteriores e o dia seguinte — por isso partidas finalizadas há pouco continuam resolvendo.

Exemplo: 2026-08-20

marketstring (1–60)

Mantém apenas mercados cuja key contenha esse texto. A comparação ignora caixa e acentos: 1x2, handicap, over_under, corners, cards, both_teams_score, double_chance, first_goal.

Exemplo: over_under

bookmakerstring (1–80)

Mantém apenas cotações de casas de aposta cujo nome contenha esse texto. A comparação ignora caixa e acentos.

Exemplo: bet365

odds-response.json
{
  "matchId": "a3f9c1e6b2d84f70",
  "sport": "football",
  "status": "scheduled",
  "startTime": 1784311200000,
  "homeTeam": {
    "id": "5981",
    "name": "Flamengo"
  },
  "awayTeam": {
    "id": "1963",
    "name": "Palmeiras"
  },
  "league": {
    "id": "325",
    "name": "Brasileirão Série A"
  },
  "markets": [
    {
      "key": "1x2",
      "label": "Resultado Final",
      "positions": [
        "home",
        "draw",
        "away"
      ],
      "quotes": [
        {
          "companyName": "Bet365",
          "opening": [
            2.1,
            3.4,
            3.2
          ],
          "latest": [
            1.95,
            3.5,
            3.6
          ]
        },
        {
          "companyName": "Betano",
          "latest": [
            2,
            3.45,
            3.55
          ]
        }
      ]
    },
    {
      "key": "double_chance",
      "label": "Dupla Chance",
      "positions": [
        "home",
        "draw_or_away",
        "draw_or_home",
        "away"
      ],
      "quotes": [
        {
          "companyName": "Bet365",
          "opening": [
            1.32,
            1.55,
            1.36,
            2.3
          ],
          "latest": [
            1.28,
            1.6,
            1.32,
            2.45
          ]
        }
      ]
    },
    {
      "key": "both_teams_score",
      "label": "Ambos Marcam",
      "positions": [
        "yes",
        "no"
      ],
      "quotes": [
        {
          "companyName": "Bet365",
          "opening": [
            1.72,
            2.05
          ],
          "latest": [
            1.65,
            2.15
          ]
        }
      ]
    },
    {
      "key": "handicap",
      "label": "Handicap -0.5",
      "positions": [
        "home",
        "line",
        "away"
      ],
      "line": -0.5,
      "quotes": [
        {
          "companyName": "Bet365",
          "opening": [
            1.9,
            -0.5,
            1.95
          ],
          "latest": [
            1.85,
            -0.5,
            2
          ]
        }
      ]
    },
    {
      "key": "over_under",
      "label": "Total de Gols (2.5)",
      "positions": [
        "over",
        "line",
        "under"
      ],
      "line": 2.5,
      "quotes": [
        {
          "companyName": "Bet365",
          "opening": [
            1.9,
            2.5,
            1.9
          ],
          "latest": [
            1.8,
            2.5,
            2.05
          ]
        },
        {
          "companyName": "Betano",
          "latest": [
            1.95,
            2.5,
            1.9
          ]
        }
      ]
    },
    {
      "key": "corners",
      "label": "Escanteios (9.5)",
      "positions": [
        "over",
        "line",
        "under"
      ],
      "line": 9.5,
      "quotes": [
        {
          "companyName": "Bet365",
          "latest": [
            1.9,
            9.5,
            1.9
          ]
        }
      ]
    },
    {
      "key": "cards",
      "label": "Cartões (5.5)",
      "positions": [
        "over",
        "line",
        "under"
      ],
      "line": 5.5,
      "quotes": [
        {
          "companyName": "Bet365",
          "opening": [
            2,
            5.5,
            1.8
          ],
          "latest": [
            1.95,
            5.5,
            1.85
          ]
        }
      ]
    },
    {
      "key": "first_goal",
      "label": "Primeiro Gol",
      "positions": [
        "home",
        "draw",
        "away"
      ],
      "quotes": [
        {
          "companyName": "Bet365",
          "latest": [
            4.5,
            7,
            5
          ]
        }
      ]
    }
  ],
  "impliedProbabilities": {
    "homePercent": 47,
    "drawPercent": 26.9,
    "awayPercent": 26.1
  },
  "bookmakers": [
    "Bet365",
    "Betano"
  ],
  "timestamp": 1784315678123
}

No topo da resposta, bookmakers lista todas as casas de aposta presentes na resposta (sem repetição, em ordem alfabética) e timestamp diz quando ela foi gerada. status e startTime são os mesmos da partida em /games.

Como ler positions, line, opening e latest

Os arrays de cotação são posicionais: o valor na posição i de opening e de latest significa o que estiver na posição i de positions. Nunca assuma a ordem — leia positions e localize o índice.

positions

Nomeia cada posição dos arrays de cotação, sem depender da casa de aposta. Também pode conter "line", o slot da linha do mercado.

line

A linha cotada (handicap ou total, como -0.5 ou 2.5). Quando a fonte manda a linha dentro do array, ela aparece nos dois lugares: em markets[].line e na posição "line".

opening

Preço de abertura em decimal. Uma casa de aposta pode publicar só a abertura, só a cotação mais recente, ou as duas.

latest

Preço mais recente em decimal — o que você mostra na tela. Fica ausente quando a fonte não publica um preço novo.

lendo por posição · JavaScript
const market = data.markets.find((item) => item.key === '1x2');
const quote = market.quotes.find((item) => item.companyName === 'Bet365');

const homeIndex = market.positions.indexOf('home');
const homePrice = quote.latest?.[homeIndex]; // 1.95
const openedAt = quote.opening?.[homeIndex]; // 2.1
const drifted = Number((homePrice - openedAt).toFixed(2)); // -0.15

// Mercado com linha: a linha também vem em markets[].line
const total = data.markets.find((item) => item.key === 'over_under');
console.log(total.line, total.positions); // 2.5 ['over', 'line', 'under']

Mercados canônicos

markets[].key é sempre a mesma palavra-chave para o mesmo mercado, mesmo que a casa de aposta ou a fonte escreva o nome de outro jeito (Resultado Final, Full Time Result, Dupla Chance, +/-, Cantinho...). Use a key para decidir o que renderizar e label para exibir. Um mercado fora deste vocabulário volta com o nome da fonte em forma de slug — nesse caso, use apenas para exibição.

1x2[home, draw, away]

Resultado final: casa, empate e visitante.

double_chance[home, draw_or_away, draw_or_home, away]

Casa ou empate, visitante ou empate, casa ou visitante, e só visitante. Quatro posições.

both_teams_score[yes, no]

Ambos os times marcam.

handicap[home, line, away]

Handicap: preço do mandante, linha e preço do visitante.

over_under[over, line, under]

Total de gols/pontos: acima, linha e abaixo.

corners[over, line, under]

Total de escanteios: acima, linha e abaixo.

cards[over, line, under]

Total de cartões: acima, linha e abaixo.

first_goal[home, draw, away]

Quem marca o primeiro gol. Aqui draw significa "ninguém marca".

impliedProbabilities

Probabilidades sem a margem da casa (soma 100%), derivadas do melhor preço disponível para cada desfecho do mercado 1X2 — no exemplo, 2.00 · 3.50 · 3.60 geram { homePercent: 47.0, drawPercent: 26.9, awayPercent: 26.1 }. O campo só existe quando a resposta traz o mercado 1X2: filtrando com market=over_under, ele simplesmente não vem.

Odds vazias, limites e cota

Uma partida sem odds publicadas — ou um filtro market= / bookmaker= que eliminou tudo — não é erro: a resposta é HTTP 200 com markets: [] e bookmakers: []. Isso significa que nossos provedores não publicaram cotações para esse jogo. O 404 fica reservado para o id que não pode ser localizado. Não faça retry em laço apertado — trate como "sem cotação" e siga. Se for uma partida de grande relevância, avise o suporte para análise imediata.
Limite de requisições: 60 req/min no trial e no starter, 180 no pro e 600 no scale. Ao estourar, a resposta é 429 e os headers RateLimit-Limit, RateLimit-Remaining e RateLimit-Reset dizem até quando esperar. Cada chamada de odds é uma requisição da sua cota, e respostas 4xx — inclusive o 404 de id inexistente — também contam. Somente falhas internas (HTTP 5xx) não são contabilizadas. Odds mudam o tempo todo durante a partida: cache a resposta por alguns segundos e não faça polling agressivo em todo o evento ao vivo.
odds do 1X2 · JavaScript
const response = await fetch(
  'https://sportsapi.com.br/api/v1/games/a3f9c1e6b2d84f70/odds?sport=football&market=1x2',
  { headers: { 'X-API-Key': 'sapi_xxxx' } }
);

if (response.status === 404) {
  // Id inexistente: não é uma partida válida.
  return null;
}
if (!response.ok) throw new Error(`SportsAPI: ${response.status}`);

const odds = await response.json();
if (!odds.markets.length) {
  // Sem cotação publicada (ou filtro que eliminou tudo): siga sem mercado.
  return null;
}

const oneXTwo = odds.markets.find((market) => market.key === '1x2');
const best = oneXTwo.quotes.map((quote) => ({
  bookmaker: quote.companyName,
  home: quote.latest?.[oneXTwo.positions.indexOf('home')],
  draw: quote.latest?.[oneXTwo.positions.indexOf('draw')],
  away: quote.latest?.[oneXTwo.positions.indexOf('away')],
}));

console.log(best, odds.impliedProbabilities, odds.bookmakers);
GET

Esportes disponíveis

Chame /api/v1/sports para descobrir, em tempo real, quais esportes o seu plano libera — em vez de fixar essa lista no seu código. O resultado muda automaticamente se você fizer upgrade de plano.

terminal
curl "https://sportsapi.com.br/api/v1/sports" \
  -H "X-API-Key: sapi_xxxx"
response.json
{
  "sports": [
    {
      "slug": "football",
      "sport": "football",
      "label": "Futebol"
    },
    {
      "slug": "basketball",
      "sport": "basketball",
      "label": "Basquete"
    },
    {
      "slug": "tennis",
      "sport": "tennis",
      "label": "Tênis"
    }
  ],
  "total": 3
}

Imagens (logos e ícones)

Todo campo logo (time, liga, canal de TV) já vem como um caminho relativo, servido pelo nosso próprio proxy de imagens — nunca uma URL absoluta de terceiros.

Basta concatenar o caminho recebido com a URL base da API para exibir a imagem:
https://sportsapi.com.br + match.homeTeam.logo

Esses endpoints de imagem são públicos (não exigem X-API-Key) e ficam por trás de um limite de requisições próprio, separado da sua cota de dados — então usá-los para renderizar logos não consome sua franquia mensal.

/api/v1/images/proxy?url=

Proxy genérico para uma URL https já conhecida, de um host permitido.

/api/v1/images/media/team/:id

Logo de um time, a partir do id numérico.

/api/v1/images/media/tournament/:id

Logo de um torneio.

/api/v1/images/media/tv-channel/:id

Logo de um canal de TV.

/api/v1/images/media/league?uniqueId=

Logo de competição/categoria (aceita uniqueId, tournamentId ou categoryId).

Erros

400Requisição inválidaRevise os parâmetros enviados.
401Não autorizadoAPI key ausente, inválida ou revogada.
402Assinatura necessáriaTrial expirado ou cota mensal esgotada.
403Plano insuficienteEsporte não incluso no seu plano.
404Não encontradoO recurso solicitado não existe.
429Limite excedidoAguarde a janela indicada nos headers RateLimit-*.
500Erro internoTente novamente e contate o suporte se persistir.