Historical Odds History

GET/tennis/v2/extend/api/historical-odds/history
Retrieve historical tennis odds using either a match identifier or both player names. All matching records are returned, grouped by market and bookmaker, with the newest odds first.

Query Parameter

ParameterRequiredTypeDescription
market_idNostring
Market — Optional market ID to filter the results. E.g market_id=1. You can see available market list
bookmaker_idNostring
Return only the selected bookmaker. Omit to include all available bookmakers (Pinnacle, Marathon) Only.
match_idIf player names are omittedstring
Historical match identifier, such as 32703-29462-17157-5. Maximum 255 characters & Order doesn't matter.
yearNointeger
Year to search when using player names. Defaults to the current year—2026 at present. Accepted range: 1970–9998
player1When match_id is omitted.string
First player display name. E.g: Jannik Sinner
player2When match_id is omitted.string
Second player display name. E.g: Carlos Alcaraz
lineNostring
Filter by an exact handicap or totals line, such as -0.5 or 22.5 etc. Maximum 255 characters.

How it works

  • When match_id is supplied, it takes priority. Player names and year are ignored.
  • Otherwise, both player1 and player2 are required.
  • Player-name matching ignores capitalization and accepts either player order. Use full names; partial-name searches are not supported.
  • For name searches, year filters the odds’ addTime from January 1 inclusive to the following January 1 exclusive, using UTC.
  • Market, bookmaker, and line filters work with either search method.
  • There is no pagination or record limit.
Example Request: CURL
curl --request GET \
	--url 'https://tennis-api-atp-wta-itf.p.rapidapi.com/tennis/v2/extend/api/historical-odds/history?match_id=32703-29462-17157-5&market_id=1' \
	--header 'X-RapidAPI-Host: tennis-api-atp-wta-itf.p.rapidapi.com' \
	--header 'X-RapidAPI-Key: YOUR_RAPIDAPI_KEY'

Response Properties

Shared objects in this response (defined once in Reusable response schemas): Odds entry.

FieldTypeDescription
successbooleantrue when the request succeeded (also when nothing matched: result is then {}).
matchIdstringThe match_id you sent (match_id searches). Name searches return player1, player2 and year instead, and result gains one level keyed by match ID: result → matchId → market → bookmaker.
resultobjectOdds grouped by market, then bookmaker. Empty {} when no records match.
result.{market}objectOne market, keyed by its name, e.g. "Full Time Result", "Over Under", "Asian Handicap".
result.{market}.{bookmaker}arrayEvery stored quote from this bookmaker (Pinnacle or Marathonbet), newest first.
result.{market}.{bookmaker}[].od1stringDecimal odds for outcome 1 (player 1, or over on Over Under), as a string.
result.{market}.{bookmaker}[].od2stringDecimal odds for outcome 2 (player 2, or under on Over Under), as a string.
result.{market}.{bookmaker}[].addTimeintegerWhen this quote was recorded, as Unix seconds (a number).
result.{market}.{bookmaker}[].linestringHandicap or total line, e.g. 21.5 or -0.5/+0.5; empty string when the market has no line (Full Time Result).

Historical Odds Movements

GET/tennis/v2/extend/api/historical-odds/movements
Retrieve recorded changes in historical tennis odds for a match or for matches between two players. The endpoint compares consecutive quotes separately for each match, market, bookmaker, and line. A record is returned when either od1 or od2 changes. Consecutive quotes with unchanged prices are excluded. By default, the first recorded quote in each series is also included as a starting point.

Query Parameter

ParameterRequiredTypeDescription
market_idNostring
Market — Optional market ID to filter the results. E.g market_id=1. You can see available market list
bookmaker_idNostring
Return only the selected bookmaker. Omit to include all available bookmakers (Pinnacle, Marathon) Only.
match_idIf player names are omittedstring
Historical match identifier, such as 32703-29462-17157-5. Maximum 255 characters & Order doesn't matter.
yearNointeger
Year to search when using player names. Defaults to the current year—2026 at present. Accepted range: 1970–9998
player1When match_id is omitted.string
First player display name. E.g: Jannik Sinner
player2When match_id is omitted.string
Second player display name. E.g: Carlos Alcaraz
fromNonumber
Include movements recorded at or after this Unix timestamp, in seconds.
toNonumber
Include movements recorded at or before this Unix timestamp, in seconds.
include_initialNoboolean
Include the first recorded quote for each series. Defaults to true.
orderNostring
Sort each bookmaker’s records by time: asc for oldest first or desc for newest first. Defaults to asc
lineNostring
Filter by an exact handicap or totals line, such as -0.5 or 22.5 etc. Maximum 255 characters.

How it works

Searching by match ID
GET /tennis/v2/extend/api/historical-odds/movements?match_id=32703-29462-17157-5

Providing only match_id returns the available movements across all markets, bookmakers, and lines for that match.

There is no default year restriction for this search. If supplied, player1, player2, and year do not affect match selection.

The response is grouped as:

result → market → bookmaker → movement records
Searching by player names
GET /tennis/v2/extend/api/historical-odds/movements?player1=Gijs%20Brouwer&player2=Michael%20Geerts&year=2021

When match_id is omitted:

  • Both player names are required.
  • Matching ignores capitalization and leading/trailing spaces in the supplied names.
  • Either player order is accepted.
  • Names must match the stored full names; partial-name searches are not supported.
  • Omitting year searches the current year.
  • Multiple matches are separated by their historical match identifiers.

The response gains one additional grouping level:

result → match_id → market → bookmaker → movement records

The year is based on the odds’ recorded timestamp, using January 1 at 00:00:00 UTC inclusive through the following January 1 exclusive. It is not based on the database import date.

Filtering markets and bookmakers

Request one market and bookmaker:

GET /tennis/v2/extend/api/historical-odds/movements?match_id=32703-29462-17157-5&market_id=2&bookmaker_id=20
How movements are detected

Quotes are compared within each match, market, bookmaker, and line:

Examples of quotes included or excluded from odds movements
Recorded timeod1od2Included by default?
10:00"1.87""1.87"Yes — initial quote
10:01"1.87""1.87"No — unchanged
10:02"1.90""1.84"Yes — prices changed
10:03"1.900""1.840"No — numerically equal
10:04"1.87""1.87"Yes — prices changed back

A movement requires a change in either price, not necessarily both.

Returning to an earlier price still counts as a movement. Numeric formatting differences, such as "1.90" and "1.900", do not.

previousOd1, previousOd2, and previousAddTime refer to the immediately preceding stored quote in the same series. That quote may have been excluded from the response because its prices were unchanged.

Initial quotes

By default:

include_initial=true

The first recorded quote for each series is returned with:

{
  "previousOd1": null,
  "previousOd2": null,
  "previousAddTime": null,
  "isInitial": true
}

To return changes only:

GET /tennis/v2/extend/api/historical-odds/movements?match_id=32703-29462-17157-5&include_initial=false

An initial quote means the first quote available in the stored data. It is not necessarily the bookmaker’s actual opening price.

Time-range filtering
GET /tennis/v2/extend/api/historical-odds/movements?match_id=32703-29462-17157-5&from=1624528991&to=1624532591

Both bounds are inclusive:

from ≤ addTime ≤ to

Change detection uses preceding quotes before applying the lower time bound. Therefore:

  • A movement inside the range can reference a previous quote outside the range.
  • The first quote inside the range is not automatically treated as an initial quote.
  • An initial quote is returned only if its own timestamp falls inside the range.
  • This endpoint does not add a snapshot of the price already in effect at from.

For player-name searches, the time range also intersects with the selected year.

Example Request: CURL
curl --request GET \
	--url 'https://tennis-api-atp-wta-itf.p.rapidapi.com/tennis/v2/extend/api/historical-odds/movements?match_id=32703-29462-17157-5&market_id=2&bookmaker_id=20&include_initial=false' \
	--header 'X-RapidAPI-Host: tennis-api-atp-wta-itf.p.rapidapi.com' \
	--header 'X-RapidAPI-Key: YOUR_RAPIDAPI_KEY'

Response Properties

Shared objects in this response (defined once in Reusable response schemas): Odds entry.

FieldTypeDescription
successbooleantrue when the request succeeded (also when nothing matched: result is then {}).
matchIdstringThe match_id you sent (match_id searches). Name searches return player1, player2 and year instead, and result gains one level keyed by match ID: result → matchId → market → bookmaker.
resultobjectOdds grouped by market, then bookmaker. Empty {} when no records match.
result.{market}objectOne market, keyed by its name, e.g. "Full Time Result", "Over Under", "Asian Handicap".
result.{market}.{bookmaker}arrayQuotes where od1 or od2 changed from the previous stored quote of the same line, oldest first by default (order=desc for newest first).
result.{market}.{bookmaker}[].od1stringDecimal odds for outcome 1 (player 1, or over on Over Under), as a string.
result.{market}.{bookmaker}[].od2stringDecimal odds for outcome 2 (player 2, or under on Over Under), as a string.
result.{market}.{bookmaker}[].addTimeintegerWhen this quote was recorded, as Unix seconds (a number).
result.{market}.{bookmaker}[].linestringHandicap or total line; each line is compared only with itself. Empty string when the market has no line.
result.{market}.{bookmaker}[].isInitialbooleantrue for the first stored quote of its series (match, market, bookmaker, line); its previous* fields are then null. Not necessarily the bookmaker's opening price.
result.{market}.{bookmaker}[].previousOd1stringod1 of the immediately preceding stored quote in the same series (null on the initial quote).
result.{market}.{bookmaker}[].previousOd2stringod2 of the immediately preceding stored quote in the same series (null on the initial quote).
result.{market}.{bookmaker}[].previousAddTimeintegeraddTime of that preceding quote, Unix seconds (null on the initial quote). It can fall outside from/to.

Historical Odds Summary

GET/tennis/v2/extend/api/historical-odds/summary
Retrieve a statistical summary of historical tennis odds. For each market, bookmaker, and line, the endpoint returns: - First and last recorded quotes. - Lowest and highest recorded prices for each outcome. - Total number of stored odds records. The first and last quotes represent the available data within the requested period. They are not necessarily the bookmaker’s actual opening and closing prices.

Query Parameter

ParameterRequiredTypeDescription
market_idNostring
Market — Optional market ID to filter the results. E.g market_id=1. You can see available market list
bookmaker_idNostring
Return only the selected bookmaker. Omit to include all available bookmakers (Pinnacle, Marathon) Only.
match_idIf player names are omittedstring
Historical match identifier, such as 32703-29462-17157-5. Maximum 255 characters & Order doesn't matter.
yearNointeger
Year to search when using player names. Defaults to the current year—2026 at present. Accepted range: 1970–9998
player1When match_id is omitted.string
First player display name. E.g: Jannik Sinner
player2When match_id is omitted.string
Second player display name. E.g: Carlos Alcaraz
fromNonumber
Include movements recorded at or after this Unix timestamp, in seconds.
toNonumber
Include movements recorded at or before this Unix timestamp, in seconds.
lineNostring
Filter by an exact handicap or totals line, such as -0.5 or 22.5 etc. Maximum 255 characters.

How it works

This endpoint summarizes historical odds separately for each match, market, bookmaker, and line.

  • First: the earliest recorded quote.
  • Last: the latest recorded quote.
  • Minimum odds: the lowest numeric price recorded for each outcome.
  • Maximum odds: the highest numeric price recorded for each outcome.
  • Record count: the number of stored quotes, including unchanged prices.

First and last recorded quotes are not necessarily the bookmaker’s actual opening and closing prices.

Searching by match ID
GET /tennis/v2/extend/api/historical-odds/summary?match_id=32703-29462-17157-5

Providing only match_id returns summaries for all available markets, bookmakers, and lines for that match.

There is no default year restriction. When match_id is supplied, player1, player2, and year do not affect match selection.

The response is grouped as:

result → market → bookmaker → line summaries[]
Searching by player names
GET /tennis/v2/extend/api/historical-odds/summary?player1=Gijs%20Brouwer&player2=Michael%20Geerts&year=2021

When match_id is omitted:

  • Both player names are required.
  • Matching ignores capitalization and leading/trailing spaces in the supplied names.
  • Either player order is accepted.
  • Use the stored full names; partial-name searches are not supported.
  • Omitting year searches the current year.
  • Multiple matches are separated by their historical match identifiers.

The response gains one additional grouping level:

result → match_id → market → bookmaker → line summaries[]

The year filter uses the odds’ recorded timestamp, from January 1 at 00:00:00 UTC inclusive through the following January 1 exclusive. It does not use the database import date.

Reversing the supplied player names does not swap od1 and od2. The stored market-outcome order is preserved.

Filtering markets and bookmakers

Request one market and bookmaker:

GET /tennis/v2/extend/api/historical-odds/summary?match_id=32703-29462-17157-5&market_id=2&bookmaker_id=20
Time-range filtering
GET /tennis/v2/extend/api/historical-odds/summary?match_id=32703-29462-17157-5&from=1624528991&to=1624532591

from and to are Unix timestamps in seconds. Both bounds are inclusive:

from ≤ addTime ≤ to

The selected range applies to every calculation:

  • first is the earliest quote inside the range.
  • last is the latest quote inside the range.
  • minOdds and maxOdds use only prices inside the range.
  • recordCount counts only records inside the range.

For player-name searches, the time range also intersects with the selected year.

Understanding the calculations

Suppose one bookmaker has these quotes for the same market and line:

Example quotes used to calculate a historical odds summary
Timeod1od2
10:001.871.87
10:011.871.87
10:021.951.80
10:031.901.84
  • First: 1.87 / 1.87 at 10:00.
  • Last: 1.90 / 1.84 at 10:03.
  • Minimum: od1 = 1.87; od2 = 1.80.
  • Maximum: od1 = 1.95; od2 = 1.87.
  • Record count: 4, including the unchanged quote at 10:01.

Both prices in first come from the same first record. Both prices in last come from the same last record.

Minimum and maximum prices are calculated independently for each outcome, so their two prices may come from different timestamps.

When only one record exists, the first and last quotes are identical. If timestamps are equal, the database record ID determines their order.

Reading a line summary

Each bookmaker’s array contains objects with this structure:

{
  "line": "-0.5/+0.5",
  "recordCount": 4,
  "first": {
    "od1": "1.87",
    "od2": "1.87",
    "addTime": 1624528800
  },
  "last": {
    "od1": "1.90",
    "od2": "1.84",
    "addTime": 1624528980
  },
  "minOdds": {
    "od1": "1.87",
    "od2": "1.80"
  },
  "maxOdds": {
    "od1": "1.95",
    "od2": "1.87"
  }
}

Prices are returned as strings or null. addTime is a Unix timestamp in seconds. The line may be empty for markets where no line applies.

Empty results and errors

No matching records returns a successful response with an empty result:

{
  "success": true,
  "match_id": "32703-29462-17157-5",
  "result": {}
}

HTTP 400 is returned when:

  • Neither match_id nor both player names are provided.
  • A parameter has an invalid type or value.
  • from is greater than to.

Records without timestamps are excluded. Null or nonnumeric prices are excluded from minimum and maximum calculations, but their timestamped records still contribute to the record count.

If an outcome has no numeric prices, its minimum and maximum are null.

There is no pagination. All matching timestamped records contribute to the summaries; individual historical quotes are not returned.

Example Request: CURL
curl --request GET \
	--url 'https://tennis-api-atp-wta-itf.p.rapidapi.com/tennis/v2/extend/api/historical-odds/summary?match_id=32703-29462-17157-5&from=1624528991&to=1624532591' \
	--header 'X-RapidAPI-Host: tennis-api-atp-wta-itf.p.rapidapi.com' \
	--header 'X-RapidAPI-Key: YOUR_RAPIDAPI_KEY'

Response Properties

Shared objects in this response (defined once in Reusable response schemas): Odds entry.

FieldTypeDescription
successbooleantrue when the request succeeded (also when nothing matched: result is then {}).
matchIdstringThe match_id you sent (match_id searches). Name searches return player1, player2 and year instead, and result gains one level keyed by match ID: result → matchId → market → bookmaker.
resultobjectOdds grouped by market, then bookmaker. Empty {} when no records match.
result.{market}objectOne market, keyed by its name, e.g. "Full Time Result", "Over Under", "Asian Handicap".
result.{market}.{bookmaker}arrayOne summary per line for this bookmaker (Pinnacle or Marathonbet).
result.{market}.{bookmaker}[].linestringThe line this summary covers; empty string when the market has no line.
result.{market}.{bookmaker}[].recordCountintegerStored quotes in the selected range, including unchanged prices.
result.{market}.{bookmaker}[].firstobjectEarliest stored quote in the range (od1, od2, addTime). Not necessarily the bookmaker's opening price.
result.{market}.{bookmaker}[].first.od1stringDecimal odds for outcome 1 (player 1, or over on Over Under), as a string.
result.{market}.{bookmaker}[].first.od2stringDecimal odds for outcome 2 (player 2, or under on Over Under), as a string.
result.{market}.{bookmaker}[].first.addTimeintegerWhen this quote was recorded, as Unix seconds (a number).
result.{market}.{bookmaker}[].lastobjectLatest stored quote in the range (od1, od2, addTime). Not necessarily the closing price.
result.{market}.{bookmaker}[].last.od1stringDecimal odds for outcome 1 (player 1, or over on Over Under), as a string.
result.{market}.{bookmaker}[].last.od2stringDecimal odds for outcome 2 (player 2, or under on Over Under), as a string.
result.{market}.{bookmaker}[].last.addTimeintegerWhen this quote was recorded, as Unix seconds (a number).
result.{market}.{bookmaker}[].minOddsobjectLowest od1 and lowest od2 in the range, each on its own: the two can come from different quotes. null when there is no numeric price.
result.{market}.{bookmaker}[].maxOddsobjectHighest od1 and highest od2 in the range, each on its own: the two can come from different quotes. null when there is no numeric price.
result.{market}.{bookmaker}[].maxOdds.od1stringDecimal odds for outcome 1 (player 1 / over), as a string.
result.{market}.{bookmaker}[].maxOdds.od2stringDecimal odds for outcome 2 (player 2 / under), as a string.
result.{market}.{bookmaker}[].minOdds.od1stringDecimal odds for outcome 1 (player 1 / over), as a string.
result.{market}.{bookmaker}[].minOdds.od2stringDecimal odds for outcome 2 (player 2 / under), as a string.