Skip to main content

Overview

Flight offers are time-sensitive and can expire within minutes of being fetched. Every offer includes an expires_at timestamp that defines the exact window during which the offer is valid for booking.
{
  "expires_at": "2026-04-01T12:00:00Z"
}
Always treat expires_at as the source of truth. Do not assume an offer is still valid based on when it was fetched or how recently you searched.

Why Expiration Matters

Flight pricing and availability are highly dynamic. An offer can become invalid at any moment due to:
  • Fare changes β€” airline pricing rules updated in real time
  • Seat availability β€” another passenger books the last seat
  • Airline inventory changes β€” capacity adjustments on the route
Attempting to book an expired offer will result in a failed order request. Handling expiration gracefully protects both your users and your integration.
Follow this four-step pattern to safely manage offer expiration end-to-end.

1. Store the Expiration Timestamp

Persist expires_at alongside every offer you retrieve so it is available at booking time.
type Offer = {
  id: string;
  expires_at: string; // ISO 8601 UTC timestamp
};

2. Validate Before Booking

Before calling /v1/air/orders, check whether the offer is still within its validity window.
function isOfferValid(expiresAt: string): boolean {
  return new Date(expiresAt).getTime() > Date.now();
}

3. Block Expired Offers at Both Layers

Enforce expiration checks at the frontend and the backend β€” never rely on just one.
// Disable the booking button when the offer is expired
const canBook = isOfferValid(offer.expires_at);

<button disabled={!canBook}>
    Book Now
</button>

4. Refresh Offers When Needed

If an offer has expired, do not attempt to reuse the offer ID. Fetch a fresh set of results instead.
  1. Re-run the search via /v1/air/search to get new offers based on the user’s original criteria
  2. Present the refreshed options to the user
Never attempt to reuse an expired offer ID β€” it will always fail. Always fetch fresh offers from the search endpoint.

Handling Edge Cases

Race Condition β€” Offer Expires During Booking

Even if you validate the offer immediately before booking, it can expire in the gap between:
  • User clicking Book Now β†’ your API request reaching our servers
  • Network latency β†’ order processing delay
Handle this gracefully by catching the OFFER_EXPIRED error code and prompting the user to refresh:
try {
  await createOrder(offer.id);
} catch (error) {
  if (error.errorCode === "<ExpiredOfferErrorCode>") {
    // Notify the user and trigger a new search
    showAlert("This offer has expired. Please search again for available flights.");
    redirectToSearch();
  }
}

Near-Expiry UX

For offers approaching expiration, proactively improve the user experience:
  • Show a countdown timer β€” surface how much time remains
  • Highlight urgency β€” use visual cues (e.g. amber/red indicators) when under 2 minutes
  • Apply a safety buffer β€” disable booking when fewer than 30 seconds remain to avoid race conditions
const SAFETY_BUFFER_MS = 30_000; // 30 seconds

function isOfferSafeToBook(expiresAt: string): boolean {
  return new Date(expiresAt).getTime() - Date.now() > SAFETY_BUFFER_MS;
}

Best Practices

Validate Twice

Always check expiration both client-side (before showing the button) and server-side (before creating the order). Never rely on a single layer.

Use a Safety Buffer

Treat offers as expired slightly before expires_at β€” a 30-second buffer reduces the risk of race conditions during high-latency requests.

Recover Gracefully

When an offer expires mid-flow, catch the error and guide users back to search results. Avoid dead ends or cryptic error messages.

Never Cache Stale Offers

Do not cache or store offers beyond their expiration window. Always surface fresh results from the search endpoint.