tkTickOdds

Prematch endpoints

REST endpoints for prematch odds. Available on both plans.

GET /api/v1/sports

GET/api/v1/sports

Returns all sports with their competition and match counts. icon is a relative path — see Icons.

{
  "ok": true,
  "data": [
    {
      "id": "soccer",
      "name": "Soccer",
      "slug": "soccer",
      "icon": "/icons/sports/1.svg",
      "competitionCount": 123,
      "updatedAt": "2026-04-17T12:00:00Z"
    }
  ]
}

GET /api/v1/sports/:sportId/competitions

GET/api/v1/sports/soccer/competitions

List all competitions for a sport.

GET /api/v1/competitions/:sportId/:competitionId/matches

GET/api/v1/competitions/soccer/england-premier-league/matches?page=1&pageSize=50

Paginated matches for a competition. Query params:

  • page (default 1)
  • pageSize (default 50, max 200)
  • includePast (default false)

GET /api/v1/matches/:fixtureId

GET/api/v1/matches/191691460

Single match with full market data (all markets + selections).

GET /api/v1/matches

GET/api/v1/matches?sport=soccer&competition=england-premier-league&upcoming=1

Filter matches across all competitions. Query params:

  • sport — sport id or name
  • competition — competition id or name
  • dateFrom — YYYY-MM-DD
  • dateTo — YYYY-MM-DD
  • upcoming — 1 to hide past matches
  • page, pageSize

Match detail response

Every match detail response returns two complementary views of the same odds:marketGroups[] (typed, hierarchical — best for rendering) andodds[] (flat, row-per-outcome — best for aggregation and odds comparison, compatible with the row format used by The Odds API / OpticOdds).

{
  "ok": true,
  "data": {
    "fixtureId": "191027752",
    "sportIcon": "/icons/sports/1.svg",
    "competitionIcon": "/icons/flags/England.svg",
    "home": "Crystal Palace",
    "away": "Newcastle",
    "homeIcon": "/icons/silks/CrystalPalace_Home_Front_25_26.svg",
    "awayIcon": "/icons/silks/Newcastle_Away_Front_25_26.svg",
    "kickoff": "2026-04-13T15:00:00.000Z",
    "mainOdds": { "market": "match_result", "selections": [...] },
    "marketCategories": [ { "name": "Popular" }, { "name": "Goals" } ],
    "categories": [
      { "category": "popular", "count": 6, "groups": [ ... ] }
    ],
    "marketGroups": [ /* typed shape — see below */ ],
    "odds":         [ /* flat rows — see below */ ]
  }
}

Market groups (typed)

Every market group has a type (determines which fields are present) plus canonical identifiers:

  • key — bet365-normalized snake_case (over_under_2_5, first_half_result)
  • industryKey — The Odds API-compatible alias (totals, h2h, spreads, btts, player_points, suffixed by period: h2h_h1, totals_q1). null when no industry equivalent exists.
  • periodfull_game / 1h / 2h / q1..q4 / p1..p3 / s1..s5 / 5_innings / innings_1 / null
  • category — UI tab (popular, goals, player, …)

Layout types:

  • simple — flat selections[] (1X2, BTTS, Double Chance)
  • lineline + selections[] with Over/Under (Total Goals, Asian Handicap)
  • groupedgroups[] of named sub-markets (Correct Score)
  • gridcolumns × rows + cells[] (Result/BTTS, HT/FT)
  • player_gridplayers[] × statTypes[] + cells[] (Goalscorers, Player Props)
  • range_gridranges[] × outcomes[] + cells[] (Match Goals Range)
  • promo — BetBoost / Bet Builder
  • outright — tournament / group winner

Every selection / cell carries canonical metadata so you don't need to re-parse display names:

{
  "name": "Over 2.5",
  "odds": 1.91,
  "side": "over",          // home|away|draw|over|under|yes|no|null
  "line": 2.5,              // numeric line/handicap
  "team": null,             // "home"|"away"|null
  "isMain": true,           // false for alternate lines
  "suspended": false
}

Simple example (Full Time Result):

{
  "id": "40",
  "name": "Full Time Result",
  "key": "full_time_result",
  "industryKey": "h2h_3_way",
  "period": "full_game",
  "category": "popular",
  "type": "simple",
  "loaded": true,
  "selections": [
    { "name": "Crystal Palace", "odds": 3.00, "side": "home", "team": "home", "isMain": true },
    { "name": "Draw",           "odds": 3.30, "side": "draw", "team": null,   "isMain": true },
    { "name": "Newcastle",      "odds": 2.40, "side": "away", "team": "away", "isMain": true }
  ]
}

Line example (Over/Under 2.5 Goals):

{
  "name": "Over/Under 2.5 Goals",
  "key": "over_under_2_5",
  "industryKey": "totals",
  "period": "full_game",
  "type": "line",
  "line": 2.5,
  "selections": [
    { "name": "Over 2.5",  "odds": 1.91, "line": 2.5, "side": "over",  "isMain": true },
    { "name": "Under 2.5", "odds": 1.91, "line": 2.5, "side": "under", "isMain": true }
  ]
}

Player grid example: every player has a stable id (slug(name@team)), and cells carry playerId / statKey:

{
  "name": "Player Shots on Target",
  "key": "player_shots_on_target",
  "industryKey": "player_shots_on_target",
  "period": "full_game",
  "type": "player_grid",
  "players": [
    { "id": "anthony_gordon@newcastle", "name": "Anthony Gordon", "team": "Newcastle", "side": "away" }
  ],
  "statTypes": ["Over 0.5", "Over 1.5"],
  "statKeys":  ["over_0_5", "over_1_5"],
  "cells": [
    { "player": 0, "playerId": "anthony_gordon@newcastle", "stat": 0, "statKey": "over_0_5", "odds": 1.50, "line": 0.5, "isMain": true }
  ]
}

Flat odds[] view

OpticOdds-style row-per-outcome array. Every row carries enough canonical metadata to diff, compare, and aggregate without walking marketGroups. groupingKey ties complementary pairs together (Over/Under at the same line, home/away spread, Yes/No BTTS).

{
  "id": "191027752:over_under_2_5:full_game:over:2.5",
  "marketKey": "over_under_2_5",
  "marketName": "Over/Under 2.5 Goals",
  "industryKey": "totals",
  "category": "goals",
  "period": "full_game",
  "selection": "Over 2.5",
  "side": "over",
  "team": null,
  "line": 2.5,
  "isMain": true,
  "playerId": null,
  "playerName": null,
  "statKey": null,
  "price": 1.91,
  "suspended": false,
  "groupingKey": "over_under_2_5:full_game:2.5"
}