PinBook Odds API - Quickstart
🚀 Connect
To connect the API to a project, you must have an API key to authenticate your request.
Connect To PinBook Odds API
This page demonstrates some of the available functions.
Learn how to use the PinBook API to make your first API call and quickly integrate it into your application.
Before Use
If you are not logged in to the Pinnacle site, you will see delayed data on the Pinnacle site. This API has no data delay.
Quickstart
1. Getting the Sport List
Use the @List of sports endpoint.
curl -X GET "https://pinbook-odds.p.rapidapi.com/kit/v1/sports" \
-H "x-rapidapi-key: YOUR_API_KEY" \
-H "x-rapidapi-host: pinbook-odds.p.rapidapi.com"curl -X GET "https://pinnacle-odds-api.hgapi.top/kit/v1/sports" \
-H "x-portal-apikey: YOUR_API_KEY"2. Getting the List of Markets
Use the @List of markets endpoint by sport_id.
List of Markets (Prematch):
curl -X GET "https://pinbook-odds.p.rapidapi.com/kit/v1/markets?sport_id=1&is_have_odds=1&event_type=prematch" \
-H "x-rapidapi-key: YOUR_API_KEY" \
-H "x-rapidapi-host: pinbook-odds.p.rapidapi.com"curl -X GET "https://pinnacle-odds-api.hgapi.top/kit/v1/markets?sport_id=1&is_have_odds=1&event_type=prematch" \
-H "x-portal-apikey: YOUR_API_KEY"List of Markets (Live):
curl -X GET "https://pinbook-odds.p.rapidapi.com/kit/v1/markets?sport_id=1&is_have_odds=1&event_type=live" \
-H "x-rapidapi-key: YOUR_API_KEY" \
-H "x-rapidapi-host: pinbook-odds.p.rapidapi.com"curl -X GET "https://pinnacle-odds-api.hgapi.top/kit/v1/markets?sport_id=1&is_have_odds=1&event_type=live" \
-H "x-portal-apikey: YOUR_API_KEY"You can pass the event_type and is_have_odds parameters.
is_have_odds parameter: Filters events that have betting periods. Note that markets may still be absent or closed for betting. Check the meta->open_... fields to verify.
Please note that prematch and live events are different.
Response:
{
"sport_id": 1, // Sport ID
"sport_name": "Soccer", // Sport name
"last": 1745671020, // Last modified timestamp. Use it for the next query as the "since" parameter. You will only get matches that have had changes since "since".
"last_call": 1745671020, // Last modified timestamp.
"events": [ // List of events
{
"event_id": 1607709101, // Event ID
"sport_id": 1, // Sport ID
"league_id": 1842, // League ID
"league_name": "Germany - Bundesliga", // League Name
"starts": "2025-04-26T13:30:00", // Start Time
"last": 1745671014, // Last modified timestamp.
"home": "Bayern Munich", // Home team name
"away": "Mainz 05", // Away team name
"event_type": "prematch", // Event type: live or prematch
"live_status_id": 2, // Live status ID: 0 = No live betting will be offered on this event, 1 = Live betting event, 2 = Live betting will be offered on this match, but on a different event.
"parent_id": null, // If event is linked to another event, parent_id will be populated. Live event would have pre-game event as parent_id.
"resulting_unit": "Regular", // Specifies the basis for event settlement, e.g., Corners, Bookings.
"is_actual": true, // The match is in the future or has recently finished.
"is_have_odds": true, // [Alias: is_have_periods] Match has periods. Markets may be absent or closed for betting. Check [meta][open_...].
"is_have_periods": true, // Match has periods. Markets may be absent or closed for betting. Check [meta][open_...].
"is_have_open_markets": true, // Match has one or more open markets.
"periods": {
"num_0": {
"line_id": 3080353239, // Line ID
"number": 0, // Period number. See endpoint @/kit/v1/meta-periods
"description": "Match", // Period name
"cutoff": "2025-04-26T13:30:00", // Period's wagering cutoff date in UTC.
"period_status": 1, // 1 = Online, period is open for betting. 2 = Offline, period is not open for betting.
"money_line": { // Money line, 1X2
"home": 1.228,
"draw": 7.38,
"away": 11.4
},
"spreads": { // Spreads
"-2.0": {
"hdp": -2.0,
"alt_line_id": null,
"home": 1.99,
"away": 1.917,
"max": 10000.0
},
...
},
"totals": { // Totals
"3.75": {
"points": 3.75,
"alt_line_id": null,
"over": 1.877,
"under": 2.02,
"max": 5000.0
},
...
},
"team_total": { // Team total
"home": {
"points": 2.5,
"over": 1.636,
"under": 2.31
},
"away": {
"points": 0.5,
"over": 1.558,
"under": 2.49
}
},
"meta": { // Meta information
"number": 0, // Period number. See endpoint @/kit/v1/meta-periods
"max_spread": 10000.0, // Max bet for Spread
"max_money_line": 10000.0, // Max bet for Money Line
"max_total": 5000.0, // Max bet for Total
"max_team_total": 1500.0, // Max bet for Team Total
"open_money_line": true, // Money Line market open for betting
"open_spreads": true, // Spreads market open for betting
"open_totals": true, // Totals market open for betting
"open_team_total": true // Team Total market open for betting
}
},
"num_1": {...}
}
}
]
}3. Getting the List of Special Markets
Examples: Player Props, Futures, Both Teams To Score.
Use the @List of Special markets endpoint by sport_id.
curl -X GET "https://pinbook-odds.p.rapidapi.com/kit/v1/markets-special?sport_id=1" \
-H "x-rapidapi-key: YOUR_API_KEY" \
-H "x-rapidapi-host: pinbook-odds.p.rapidapi.com"curl -X GET "https://pinnacle-odds-api.hgapi.top/kit/v1/markets-special?sport_id=1" \
-H "x-portal-apikey: YOUR_API_KEY"Response:
{
"sport_id": 1, // Sport ID
"sport_name": "Soccer", // Sport name
"last": 1745671020, // Last modified timestamp. Use it for the next query as the "since" parameter. You will only get matches that have had changes since "since".
"last_call": 1745671020, // Last modified timestamp.
"specials": [ // List of special lines
{
"special_id": 1608338379, // Special ID
"sport_id": 1, // Sport ID
"league_id": 1980, // League ID
"event_id": 1607803934, // Event ID. Null if the market is not related to an event.
"last": 1745732750, // Last modified timestamp.
"live_status": "prematch", // Live status: live or prematch
"live_status_id": 2, // 0 = No live betting will be offered on this event, 1 = Live betting event, 2 = Live betting will be offered on this match, but on a different event.
"bet_type": "MULTI_WAY_HEAD_TO_HEAD", // MULTI_WAY_HEAD_TO_HEAD, SPREAD, OVER_UNDER
"units": null, // Measurement in the context of the special. This is applicable to special bet types spread and over/under. In a hockey special, this could be goals.
"name": "3-Way Handicap Bournemouth -2", // Special name
"starts": "2025-04-27T13:00:00", // Date of the special in UTC.
"cutoff": "2025-04-27T13:00:00", // Wagering cutoff date in UTC.
"category": "Team Props", // The category that the special falls under.
"status": "O", // Status of the Special: O = Lines are open for betting, H = Lines are temporarily unavailable for betting, I = One or more lines have a red circle (lower maximum bet amount).
"event": { // Event data. Null if the market is not related to an event.
"id": 1607803934,
"period_number": 0,
"home": "Bournemouth",
"away": "Manchester United"
},
"is_actual": true, // The special line is in the future or has recently finished.
"max_bet": 500.0, // Maximum bet volume amount.
"is_have_odds": true, // [Alias: is_have_lines]
"is_have_lines": true, // Specials market has lines.
"open": true, // Special market is open for betting.
"lines": {
"c_1608338380": {
"id": 1608338380, // Line ID
"name": "Bournemouth (-2)", // Line name
"rot_num": 2824, // Rotation Number
"line_id": 4941359540, // Line identifier required for placing a bet.
"price": 5.93, // Price of the line
"handicap": null // A number indicating the spread, over/under, etc.
},
"c_1608338381": {}
...
}
}
]
}4. Event List, Archive, or Schedule
Use the @List of archive events endpoint by sport_id.
curl -X GET "https://pinbook-odds.p.rapidapi.com/kit/v1/archive?sport_id=1" \
-H "x-rapidapi-key: YOUR_API_KEY" \
-H "x-rapidapi-host: pinbook-odds.p.rapidapi.com"curl -X GET "https://pinnacle-odds-api.hgapi.top/kit/v1/archive?sport_id=1" \
-H "x-portal-apikey: YOUR_API_KEY"5. Getting a History of Odds
Use the @Event details endpoint by event_id.
curl -X GET "https://pinbook-odds.p.rapidapi.com/kit/v1/details?event_id=1607709101" \
-H "x-rapidapi-key: YOUR_API_KEY" \
-H "x-rapidapi-host: pinbook-odds.p.rapidapi.com"curl -X GET "https://pinnacle-odds-api.hgapi.top/kit/v1/details?event_id=1607709101" \
-H "x-portal-apikey: YOUR_API_KEY"List of Object Statuses
Period Status:
1= Online, period is open for betting2= Offline, period is not open for betting
Period Special Status:
O= Lines are open for bettingH= Lines are temporarily unavailable for bettingI= One or more lines have a red circle (lower maximum bet amount)
Period Results:
Endpoints: @kit/v1/archive and @/kit/v1/details
1= Event period is settled2= Event period is re-settled3= Event period is cancelled4= Event period is re-settled as cancelled5= Event is deleted
How to Use the since Parameter?
Call the @List of markets endpoint WITHOUT the since parameter. You will get a list of ALL events and odds for that sport.
Example:
/kit/v1/markets?sport_id=1In the response, you will get the last property — for example,
1658948800. The last property is a UTC timestamp.Use the last value as the since parameter in the next call to the @List of markets endpoint.
Example:
/kit/v1/markets?sport_id=1&since=1658948800You will now get ONLY those events (full event data) that were changed after this timestamp, plus the NEW last value.
Use steps 3-4 in your loop. You must always use the since parameter after the initial request. To avoid rate-limiting errors, limit requests without the since parameter to no more than 15 calls per 5 minutes.
Code Sample Python 🐍
import requests
import json
import time
headers = {
"x-rapidapi-key": "__YOUR_KEY__",
"x-rapidapi-host": "pinbook-odds.p.rapidapi.com"
}
base_url = "https://pinbook-odds.p.rapidapi.com"
s = requests.Session()
s.headers.update(headers)
since_items = {}
all_events = {}
while True:
for sport_id in [1]: # endpoint /kit/v1/sports
# is_have_odds = 0 or 1, event_type = prematch, live
params = {
'sport_id': sport_id,
'is_have_odds': True,
'since': since_items.get(sport_id)
}
print('[REQUEST] %s' % params)
response = s.get(base_url + '/kit/v1/markets', params=params)
if response.status_code != 200:
raise Exception(response.status_code, response.text)
result = json.loads(response.text)
since_items[sport_id] = result['last']
for event in result['events']:
all_events[str(event['event_id'])] = event
try:
print(' %s: %s — %s %s' % (event['league_name'], event['home'], event['away'], event['periods']['num_0']['money_line']))
except KeyError:
pass
print('Sport: %s' % result['sport_name'])
print('Number of changes: %s' % len(result['events']))
print(' ')
time.sleep(3)FAQ
Is the First Team the Home or Away Team?
Is the API returning opposite results?
The first team is not always the home team. To determine this, use the endpoint @/kit/v1/leagues.
Example:
"home_team_type": "Team1"— First team is home"home_team_type": "Team2"— Second team is home
What Are num_0 and num_1?
Use the endpoint @kit/v1/meta-periods.
Examples:
num_0→ Matchnum_1→ 1st Halfnum_2→ 2nd Half- ...
How to Handle Duplicate Parent Events?
If a parent event was created with incorrect information in the immutable properties (participant names, league, etc.), a new parent event will be created with the correct information.
When the client detects a duplicate, by default you should use the event with the greater identifier value. Additionally, monitor the settled fixtures endpoint and discard any event that is deleted or settled.
When Is the Market Open for Betting?
First Method:
A straight market in a period is open for betting if, in the Get Odds response, all of these conditions are true:
- Period status = 1
- Market (period) has odds.
- Period cutoff is in the future.
Second Method:
Check the value ['periods'] -> ['num_...'] -> ['meta'] -> ['open...']Example:
{
"events": [
{
"event_id": 1608004986,
...
"periods": {
...
"num_0": {
"money_line": {},
"team_total": {},
"meta": {
"number": 0,
"max_spread": 250.0,
"max_total": 1000.0,
...
"open_money_line": false,
"open_spreads": true,
"open_totals": true,
"open_team_total": false
}
}
}
}
]
}What Time Zone Is Used for the API?
All times are GMT (0).
How to Know If an Event Is Finished?
Use the @/kit/v1/archive and @/kit/v1/details endpoints to find out if the event's period was settled or if the event was deleted. Check the "cutoff" values.
