tutorial

How to Paginate Football Fixtures from an Odds API

When you need to fetch a large volume of pre-match football fixtures, hitting a single API endpoint usually isn't enough. APIs implement pagination to manage response sizes, prevent server overload, and ensure efficient data transfer. Understanding how to paginate football fixtures from an odds API is crucial for reliably collecting comprehensive datasets, especially when dealing with a UK bookmaker odds API that provides extensive coverage.

This guide will walk you through the process of how to paginate football fixtures from an odds API using ukoddsapi.com, focusing on Python and JavaScript examples. We'll show you how to fetch data page by page, collect all available fixtures, and handle the API response structure. This approach lets you gather all the pre-match football odds JSON you need without resorting to unreliable scraping methods.

Prerequisites

Before you start integrating pagination into your application, ensure you have the following:

  • An API Key: You'll need an X-Api-Key from ukoddsapi.com. You can sign up for a free account to get started.
  • Programming Environment: This tutorial provides examples in Python and JavaScript (Node.js).
  • HTTP Client Libraries: For Python: The requests library. Install it with pip install requests. For JavaScript: Node.js (version 18+ for native fetch) or a library like axios.

This setup will allow you to send authenticated requests and process the API responses effectively.

Step 1: Fetching the First Page of Fixtures

The first step in pagination is to make an initial request to the endpoint that lists fixtures. Most APIs, including ukoddsapi.com, use query parameters like page and per_page to control the data returned. The per_page parameter defines how many items you want per response, and page specifies which page number you're requesting.

For ukoddsapi.com, the /v1/football/events endpoint provides pre-match football fixtures. We'll use schedule_date to filter for a specific day and has_odds=true to ensure we only get events with available odds.

Here's how to fetch the first page of fixtures:

Python Example

python import os import requests from datetime import date

API_KEY = os.environ.get("UKODDSAPI_KEY", "YOUR_API_KEY") # Replace with your actual key or env var BASE_URL = "https://api.ukoddsapi.com" headers = {"X-Api-Key": API_KEY}

today = date.today().isoformat() params = { "schedule_date": today, "has_odds": "true", "per_page": 20, # Request 20 events per page "page": 1 # Start with the first page }

try: response = requests.get( f"{BASE_URL}/v1/football/events", headers=headers, params=params, timeout=30, ) response.raise_for_status() # Raise an HTTPError for bad responses (4xx or 5xx) first_page_data = response.json() print(f"Fetched page 1. Events on this page: {len(first_page_data.get('events', []))}") print(f"Total pages available: {first_page_data.get('pagination', {}).get('total_pages')}")

except requests.exceptions.RequestException as e: print(f"An error occurred: {e}")


### JavaScript Example

```javascript
import fetch from 'node-fetch'; // For Node.js versions < 18, otherwise native fetch is available
import 'dotenv/config'; // For loading environment variables

const API_KEY = process.env.UKODDSAPI_KEY || "YOUR_API_KEY"; // Replace with your actual key or env var
const BASE_URL = "https://api.ukoddsapi.com";

const today = new Date().toISOString().slice(0, 10); // YYYY-MM-DD
const params = new URLSearchParams({
    schedule_date: today,
    has_odds: "true",
    per_page: 20, // Request 20 events per page
    page: 1       // Start with the first page
});

async function fetchFirstPage() {
    try {
        const response = await fetch(
            `${BASE_URL}/v1/football/events?${params.toString()}`,
            {
                headers: { "X-Api-Key": API_KEY },
                timeout: 30000 // 30 seconds
            }
        );

        if (!response.ok) {
            throw new Error(`HTTP error! status: ${response.status}`);
        }

        const firstPageData = await response.json();
        console.log(`Fetched page 1. Events on this page: ${firstPageData.events ? firstPageData.events.length : 0}`);
        console.log(`Total pages available: ${firstPageData.pagination ? firstPageData.pagination.total_pages : 'N/A'}`);
        return firstPageData;

    } catch (error) {
        console.error(`An error occurred: ${error}`);
        return null;
    }
}

// fetchFirstPage(); // Uncomment to run

code snippet showing API request and JSON response, with abstract data flow lines

This code sends a GET request to the /v1/football/events endpoint. We specify per_page=20 to get 20 events per response and page=1 for the first page. The response.json() method parses the JSON content. Error handling is included to catch network issues or bad HTTP responses.

## Step 2: Extracting Pagination Details

Once you receive the response from the first page, you need to examine its structure to understand how to paginate football fixtures from an odds API further. ukoddsapi.com includes a `pagination` object in its response, which contains crucial information like `total_pages`, `current_page`, and `total_events`. This metadata tells you how many pages of data are available and where you currently are in the sequence.

Here's a typical (truncated) JSON response structure for the `/v1/football/events` endpoint, highlighting the pagination details:
```json
{
  "schema_version": "1.0",
  "count": 20,
  "events": [
    {
      "event_id": "EVT001",
      "league_name": "Premier League",
      "home_team": "Arsenal",
      "away_team": "Chelsea",
      "kickoff_utc": "2026-04-29T19:00:00Z",
      "markets_with_odds": ["match_betting"],
      "unique_bookmaker_codes": ["UO001", "UO002"]
    },
    {
      "event_id": "EVT002",
      "league_name": "Championship",
      "home_team": "Leeds United",
      "away_team": "Leicester City",
      "kickoff_utc": "2026-04-29T19:45:00Z",
      "markets_with_odds": ["match_betting"],
      "unique_bookmaker_codes": ["UO003", "UO004"]
    }
    // ... more events
  ],
  "pagination": {
    "total_events": 123,
    "total_pages": 7,
    "current_page": 1,
    "per_page": 20
  },
  "note": "Example only — response is truncated."
}

The pagination object is key. From this, we can determine total_pages, which is the maximum page number we need to request. The current_page confirms the page we just fetched. Knowing total_pages allows us to construct a loop that reliably fetches all remaining pages. This method is far more robust than trying to guess if more data exists, which is a common pitfall when trying to get pre-match football odds JSON from less structured sources.

Step 3: Looping Through All Pages

With the pagination details in hand, the next step is to iterate through all available pages to collect the complete dataset of scheduled fixtures. This involves setting up a loop that increments the page parameter with each request until current_page equals total_pages.

It's important to introduce a delay between requests to avoid hitting rate limits. ukoddsapi.com's free tier allows 300 requests/month, while paid tiers offer significantly higher limits (e.g., 1,000 requests/hour for Starter, 20,000 requests/hour for Business). Even with high limits, a small delay is good practice to prevent bursts that could temporarily trigger rate limiting.

Python Example for Full Pagination

import os
import requests
import time
from datetime import date

API_KEY = os.environ.get("UKODDSAPI_KEY", "YOUR_API_KEY")
BASE_URL = "https://api.ukoddsapi.com"
headers = {"X-Api-Key": API_KEY}

def get_all_football_fixtures(schedule_date: str):
    all_events = []
    current_page = 1
    total_pages = 1 # Initialize to 1 to ensure the loop runs at least once
    per_page = 20 # Adjust as needed, max is usually 100

    print(f"Starting pagination for {schedule_date}...")

    while current_page <= total_pages:
        params = {
            "schedule_date": schedule_date,
            "has_odds": "true",
            "per_page": per_page,
            "page": current_page
        }

        try:
            print(f"Fetching page {current_page}...")
            response = requests.get(
                f"{BASE_URL}/v1/football/events",
                headers=headers,
                params=params,
                timeout=30,
            )
            response.raise_for_status()
            data = response.json()

            if "pagination" in data:
                total_pages = data["pagination"]["total_pages"]
                current_page = data["pagination"]["current_page"]
                print(f"Page {current_page}/{total_pages} fetched. Events: {len(data.get('events', []))}")

                if data.get("events"):
                    all_events.extend(data["events"])
                
                current_page += 1
                
                if current_page <= total_pages: # Only sleep if there are more pages to fetch
                    time.sleep(1) # Small delay to respect rate limits

            else:
                print("No pagination data found in response. Exiting loop.")
                break

        except requests.exceptions.HTTPError as e:
            print(f"HTTP error fetching page {current_page}: {e}")
            if e.response.status_code == 429: # Rate limit exceeded
                print("Rate limit hit. Waiting longer before retrying...")
                time.sleep(5) # Wait 5 seconds
            break # Exit on other HTTP errors

        except requests.exceptions.RequestException as e:
            print(f"Network error fetching page {current_page}: {e}")
            break

    print(f"Finished fetching all pages for {schedule_date}. Total events collected: {len(all_events)}")
    return all_events

# Example usage:
today_str = date.today().isoformat()
all_todays_fixtures = get_all_football_fixtures(today_str)
# print(all_todays_fixtures) # Uncomment to see all collected data

JavaScript Example for Full Pagination

import fetch from 'node-fetch';
import 'dotenv/config';
import { setTimeout } from 'timers/promises'; // For async sleep

const API_KEY = process.env.UKODDSAPI_KEY || "YOUR_API_KEY";
const BASE_URL = "https://api.ukoddsapi.com";

async function getAllFootballFixtures(scheduleDate) {
    let allEvents = [];
    let currentPage = 1;
    let totalPages = 1; // Initialize to 1 to ensure the loop runs at least once
    const perPage = 20; // Adjust as needed, max is usually 100

    console.log(`Starting pagination for ${scheduleDate}...`);

    while (currentPage <= totalPages) {
        const params = new URLSearchParams({
            schedule_date: scheduleDate,
            has_odds: "true",
            per_page: perPage,
            page: currentPage
        });

        try {
            console.log(`Fetching page ${currentPage}...`);
            const response = await fetch(
                `${BASE_URL}/v1/football/events?${params.toString()}`,
                {
                    headers: { "X-Api-Key": API_KEY },
                    timeout: 30000 // 30 seconds
                }
            );

            if (!response.ok) {
                if (response.status === 429) {
                    console.warn("Rate limit hit. Waiting longer before retrying...");
                    await setTimeout(5000); // Wait 5 seconds
                    continue; // Retry the same page
                }
                throw new Error(`HTTP error! status: ${response.status}`);
            }

            const data = await response.json();

            if (data.pagination) {
                totalPages = data.pagination.total_pages;
                currentPage = data.pagination.current_page;
                console.log(`Page ${currentPage}/${totalPages} fetched. Events: ${data.events ? data.events.length : 0}`);

                if (data.events) {
                    allEvents = allEvents.concat(data.events);
                }
                
                currentPage++;
                
                if (currentPage <= totalPages) { // Only sleep if there are more pages to fetch
                    await setTimeout(1000); // Small delay to respect rate limits (1 second)
                }
            } else {
                console.log("No pagination data found in response. Exiting loop.");
                break;
            }

        } catch (error) {
            console.error(`Network or other error fetching page ${currentPage}: ${error}`);
            break; // Exit on error
        }
    }

    console.log(`Finished fetching all pages for ${scheduleDate}. Total events collected: ${allEvents.length}`);
    return allEvents;
}

// Example usage:
const todayJs = new Date().toISOString().slice(0, 10);
// getAllFootballFixtures(todayJs).then(allTodaysFixtures => {
//     console.log(allTodaysFixtures); // Uncomment to see all collected data
// });

abstract representation of a loop, fetching data in chunks, with a clock icon indicating delays

These functions get_all_football_fixtures (Python) and getAllFootballFixtures (JavaScript) encapsulate the pagination logic. They continuously fetch pages, append the events to a list, and increment the page number until all pages are retrieved. The time.sleep(1) or await setTimeout(1000) call introduces a one-second delay between requests, which is usually sufficient for most use cases and helps manage API consumption. This robust approach ensures you get all the pre-match football odds JSON data you need from the UK bookmaker odds API.

Common mistakes when paginating odds data

Paginating data might seem straightforward, but developers often make a few common mistakes that can lead to incomplete datasets, rate limit issues, or inefficient applications. Avoid these pitfalls when you integrate an odds API without scraping:

  • Ignoring Rate Limits: Making requests too quickly will get your IP temporarily blocked or your API key suspended. Always implement delays (time.sleep or setTimeout) between requests, especially when looping through many pages.
  • Not Checking total_pages: Assuming there's always a next page, or stopping prematurely, can lead to incomplete data. Always use the total_pages field from the API response to determine when to stop.
  • Hardcoding Page Numbers: Don't hardcode page=1, page=2, etc. Increment the page parameter dynamically within your loop based on the current_page and total_pages values from the API's pagination object.
  • Inefficient Data Storage: Appending thousands of events to an in-memory list might consume too much RAM. For very large datasets, consider writing data to a file or database in chunks rather than holding everything in memory.
  • Not Handling Errors: Network issues, API downtime, or invalid parameters can cause requests to fail. Implement try-except blocks (Python) or try-catch (JavaScript) to gracefully handle errors and prevent your application from crashing.
  • Over-fetching Data: Requesting per_page=100 when you only need a few items, or fetching data for dates far in the past that you don't use, wastes API requests. Be specific with your query parameters.
  • Not Validating API Key: A missing or invalid API key will result in 401 Unauthorized errors. Ensure your X-Api-Key header is correctly set for every request.

Options and alternatives for managing large datasets

When dealing with large volumes of pre-match football odds data, pagination is the standard method for an odds API without scraping. However, depending on your scale and specific needs, other strategies or considerations might come into play.

Here's a comparison of common approaches:

Method Description Pros Cons Best Use Case
API Pagination (ukoddsapi.com) Fetching data in chunks using page and per_page parameters from a structured API. Reliable, structured, legal, avoids IP bans, consistent data format. Requires looping and managing rate limits. Most common for developers building odds comparison sites, betting tools, or data pipelines.
Web Scraping Programmatically extracting data directly from bookmaker websites. Potentially "free" (no API cost). Highly unstable, prone to breaking, legally questionable, requires complex anti-bot bypasses, IP bans. Small, personal projects with high tolerance for breakage and maintenance. Not recommended for commercial use.
Data Dumps / Bulk Downloads Some providers offer daily or weekly files containing large datasets. Very efficient for historical data, no rate limits to manage. Data might not be as fresh, requires processing large files, not suitable for near real-time updates. Backtesting models, historical analysis, initial data seeding.
Webhooks / Push APIs Data is pushed to your endpoint when updates occur, rather than you polling for it. Real-time updates, efficient (no polling overhead). Requires a publicly accessible endpoint, more complex to set up, not common for pre-match odds feeds. High-frequency trading, truly real-time applications (not typically needed for pre-match).

For most developers building applications that require current pre-match football odds JSON, using a dedicated UK bookmaker odds API like ukoddsapi.com with proper pagination is the most effective and reliable solution. It provides structured, normalized data and handles the complexities of data collection from multiple bookmakers, allowing you to focus on building your application.

FAQ

How does pagination help with API rate limits?

Pagination helps by breaking a large data request into smaller, manageable chunks. Instead of trying to fetch thousands of events in one go (which might exceed a single request's data limit or timeout), you make multiple smaller requests. By adding a delay between these requests, you spread out your API calls over time, staying within the allowed request rate per minute or hour.

What if an API doesn't offer pagination?

If an API doesn't offer explicit page and per_page parameters, it might use cursor-based pagination (e.g., next_cursor or offset). If no pagination mechanism is provided at all, you might be limited to the default number of items returned per request. In such cases, you'd need to filter your requests more aggressively (e.g., by smaller time windows) to avoid overwhelming the API or missing data.

How fresh is the data when paginating through many pages?

The data freshness depends on how quickly you paginate and the API's update frequency. For pre-match football odds, data typically updates every few minutes. If you paginate through a day's worth of fixtures in a few seconds, the data from the first page will only be slightly older than the last. For critical applications, you might re-fetch specific event odds after collecting the initial list.

Can I retrieve odds for all UK bookmakers through pagination?

Yes, when you paginate football fixtures from an odds API like ukoddsapi.com, the pagination applies to the list of events. Once you have the event_id for a specific fixture, you can then make a separate request to /v1/football/events/{event_id}/odds to get all available pre-match odds from supported UK bookmakers for that event, regardless of which page you found the event on.

What's the maximum per_page value I should use?

The maximum per_page value is typically defined by the API provider. For ukoddsapi.com, you can usually request up to 100 items per page. Using a higher per_page value reduces the total number of requests you need to make, which can be more efficient, but ensure you still respect overall rate limits with appropriate delays. Always check the API documentation for specific limits.

Conclusion

Efficiently collecting pre-match football odds data means understanding how to paginate football fixtures from an odds API. By using the page and per_page parameters, you can systematically retrieve large datasets without hitting rate limits or overwhelming your application. This tutorial showed you how to implement this process using Python and JavaScript, providing a robust method to get comprehensive pre-match football odds JSON. This approach is a far more reliable and scalable alternative to web scraping, ensuring you have the data you need for your projects.

To get started with your own data integration, explore the API documentation and sign up for an API key at ukoddsapi.com.