How the Odds API Handles Postponed, Delayed and Cancelled Events

August 26, 2026

How the Odds API Handles Postponed, Delayed and Cancelled Events

How the Odds API Handles Postponed, Delayed and Cancelled Events

Most of an odds API integration is straightforward until a game doesn't start on time. Then the question becomes what your code should see, and the answer depends on decisions made upstream of your request. This is the behaviour that generates more support questions than any other part of our event data, so it's worth setting out exactly what happens and why.

Postponement is messier than it looks

Sometimes a postponed game gets a new date within the hour and nothing much is disrupted. Sometimes a fixture sits unscheduled for months. Which of those you get depends on the league, the sport, the region, and occasionally on a governing body that hasn't decided yet.

The hard part is that whether a postponed game will eventually be rescheduled or quietly dropped is often not knowable at the moment it is postponed, and that call can arrive at any point afterwards. An API doesn't get to stay undecided. Every event in the feed has to hold some state, and that state has to be one your code can act on.

What we do

If a postponed game doesn't have a confirmed schedule within a short window of the original postponement, we mark the event as cancelled.

If the game is subsequently rescheduled, we create a new event rather than reinstating the original one.

Why a new event rather than a reinstated one

Reinstating a cancelled event sounds tidier, but it breaks things that have already responded to the cancellation. A bet tracker may have voided its tickets. A cache may have dropped the fixture. A warehouse may have written a final row. Anything that treated the cancellation as settled now has to unwind it, and a record moving from cancelled back to upcoming is a transition most systems are never written to expect.

Creating a new event avoids that entirely. The cancelled record stays accurate about what it describes, which is a fixture that could not be confirmed. The new record describes the game that is actually going to be played. Neither one has to lie about its own history.

What this means in practice

You will sometimes see two entries for the same fixture, one marked cancelled and one upcoming. That is expected behaviour rather than duplicate data.

The important consequence is that the two entries have different eventID values. If your ingestion de-duplicates on something like home team plus away team plus date, the rescheduled game will look like a conflict with the cancelled one. De-duplicate on eventID and the problem disappears.

It also means a stored eventID doesn't follow a game to its new date. If you saved an eventID against a user's bet, a watchlist entry, or a cached row, it will keep resolving to the cancelled record. Anything long-lived that references an event should re-check status.cancelled rather than assuming the ID stays valid.

Filtering cancelled events out

The cheapest place to deal with cancelled events is in the request. /events takes a cancelled parameter, so you can exclude them before the response is assembled:

curl "https://api.sportsgameodds.com/v2/events?leagueID=NFL&cancelled=false&oddsAvailable=true" \
  -H "x-api-key: YOUR_API_KEY"

Pass cancelled=true to look at only the cancelled ones, which is useful when reconciling your own store against ours. Omit the parameter and you get both, which is what you want if you are branching on state yourself.

In the response, event state lives under status:

{
  "status": {
    "started": false,
    "completed": false,
    "cancelled": false,
    "ended": false,
    "live": false,
    "delayed": false,
    "displayLong": "Upcoming",
    "finalized": false
  }
}

status.delayed covers a game that has slipped but is still expected to be played, so it's the flag to watch if you want to surface a delay to users before anything is cancelled. status.cancelled is the one that tells you this record is finished.

Use the event-level flag, not the odds-level one

Individual odds carry a cancelled flag too, at odds.<oddID>.cancelled. It is tempting to reach for it when you care about a specific market, but its meaning is different from the event-level flag and considerably less intuitive, and we have plans to change how it works.

Until then, build against status.cancelled at the event level. It is the flag we recommend, and it is the one whose behaviour will stay stable.

If you settle bets

Grading logic that waits for status.finalized needs a cancelled branch, or a cancelled fixture will sit in your open-tickets queue forever waiting on a result that is never coming. Check status.cancelled in the same pass that checks status.finalized, and void rather than grade when it's set.

The rest of the settlement flow, including reading each market's score and knowing when a result is safe to trust, is covered in the bet tracking and settlement guide.

Read More: Getting started with the odds API | Live and in-play odds | NFL odds API guide

Why do I see two events for the same game?
Because a rescheduled fixture is a new event rather than a revived one. The first entry is the original, marked cancelled once no new date was confirmed in time. The second is the rescheduled game. They carry different eventID values, so anything that de-duplicates on team and date will treat them as a conflict when they are actually two distinct records.
How do I stop cancelled events appearing in my feed?
Pass cancelled=false to /events and they are filtered out before the response is built, which is cheaper than discarding them in your own code. If you need both, omit the parameter and branch on status.cancelled in the response.
Should I use the cancelled flag on an odds market?
Not for now. There is a cancelled flag on individual odds as well as on the event, but it means something different and less intuitive, and we have plans to change how it behaves. Build against the event-level status.cancelled instead.
Does a rescheduled game keep its original eventID?
No. The rescheduled fixture is created as a new event with its own eventID. If you stored the original eventID against a bet, a watchlist entry, or a cached row, it will still resolve to the cancelled record rather than following the game to its new date.
How should my bet grading handle a cancelled event?
Check status.cancelled alongside status.finalized. Grading logic that only waits for finalized will hold a cancelled fixture in its open-tickets queue indefinitely, because that event is never going to produce a result.

Build on data that tells you what happened

Odds, scores, and event status in one feed, with a free tier to test against.