Docs
Get an API key

Book your first flight

Your first flight booking: offers, prebook, book, read back.

In short

4 calls, in the order this page makes themFor people and agents
  1. 1POST/v1/flight/offersGet flight offers
  2. 2POST/v1/flight/prebookPrebook a flight offer
  3. 3POST/v1/flight/bookings/{id}/bookBook: pay and ticket
  4. 4GET/v1/flight/bookings/{id}Get a booking

The same list heads this page's Markdown:/flight/flight-getting-started.md

You book a Duffel fare with a saved OEX instrument in this tutorial. For LiteAPI, follow Pay for a flight with LiteAPI, because its checkout collects payment before book. Every sample uses $BASE_URL for the API host and $API_KEY for your API key.

If a call does not succeed, stop the lesson and use Retry flight calls safely and Flight API errors.

Get offers

Describe the trip as journeys, one for a one-way and two for a return, and the party with an age for anyone who is not an adult.

Request
curl -X POST "$BASE_URL/v1/flight/offers" \
  -H "x-api-key: $API_KEY" -H "Content-Type: application/json" \
  -d '{
    "supplier": "DUFFEL",
    "journeys": [
      { "origin": "LHR", "destination": "CDG", "date": "2026-11-10" },
      { "origin": "CDG", "destination": "LHR", "date": "2026-11-14" }
    ],
    "passengers": { "adults": 1, "children": 1, "children_ages": [9] },
    "cabin_class": "ECONOMY",
    "filters": { "max_stops": 1 },
    "limit": 20
  }'
Response
{ "data": [ { "offer_reference": "eyJ…", "total_amount": { "amount": 220.67, "currency": "USD" }, "expires_at": "2026-11-01T12:34:56.000Z", "fare_family": "Basic", "refundable": false, "fare_conditions": { "refund": { "allowed": false }, "change": { "allowed": true, "penalty": { "amount": 75.00, "currency": "USD" } } }, "journeys": [ … ], "total_duration_minutes": 274 } ], "meta": { "supplier": "DUFFEL" } }

Each offer is one fare for the whole party and the whole trip. journeys come in travel order, each with its segments; a journey with two segments has a connection, and its stops counts that connection. total_amount is what the traveller pays. Read expires_at: a flight offer lives for tens of minutes, so prebook promptly.

Search for the real party. The passenger mix is bound here; a prebook with a different number of adults, children or infants is refused.

Prebook

Send x-idempotency-key. We mint the booking id.

Request
curl -X POST "$BASE_URL/v1/flight/prebook" \
  -H "x-api-key: $API_KEY" -H "x-idempotency-key: flight-lhr-cdg-1" -H "Content-Type: application/json" \
  -d '{
    "offer_reference": "<offer_reference from step 1>",
    "contact": {
      "first_name": "Amira", "last_name": "Haddad", "email": "amira.haddad@example.com",
      "phone_country_code": "44", "phone_number": "2080160509"
    },
    "passengers": [
      { "type": "ADULT", "first_name": "Amira", "last_name": "Haddad", "birthday": "1988-04-12", "gender": "F", "nationality": "GB",
        "document": { "type": "PASSPORT", "number": "123456789", "issue_country": "GB", "expiry": "2031-04-11" } },
      { "type": "CHILD", "first_name": "Leila", "last_name": "Haddad", "birthday": "2017-06-30", "gender": "F", "nationality": "GB",
        "document": { "type": "PASSPORT", "number": "987654321", "issue_country": "GB", "expiry": "2030-06-29" } }
    ],
    "use_payment_sdk": false
  }'

The fare is re-priced and bound, the itinerary and passengers are stored, and the booking answers with status: "READY_FOR_COMPLETE". total_amount is what book will charge. fare_amount and services_amount add up to it, and fare_tax_amount is the tax inside fare_amount. No inventory is held before book, so expires_at is a real deadline.

A 201 means this call created the booking. A 200 means the key already had one. The response id is the handle for every later call.

use_payment_sdk: false, the default, means book charges a saved instrument.

Send passenger details as they appear on the document: real names, dates of birth and document numbers. The airline rejects a ticket that does not match the passport at the gate. Read the offer's documents_required: when it is false, as on many US domestic fares, you may leave out document and the traveller flies on the ID the airline checks at the airport. When it is true, every passenger needs one, or prebook answers 422 document_required.

Book: pay and ticket

Request
curl -X POST "$BASE_URL/v1/flight/bookings/bkg_0192f3a4b5c64d7e8f9a0b1c2d3e4f5b/book" \
  -H "x-api-key: $API_KEY" -H "Content-Type: application/json" \
  -d '{ "payment_instrument_id": "pi_01J8Z6Q0M6RVW4B9Z8N3XK2T7A" }'

The instrument is authorized, the airline issues the ticket, and the authorization is captured, in that order, so an airline refusal releases the hold instead of refunding a charge. The 200 is the booking as CONFIRMED, with supplier_reference, confirmed_at and, when the airline provides one, airline_pnr.

Read it back

Request
curl "$BASE_URL/v1/flight/bookings/bkg_0192f3a4b5c64d7e8f9a0b1c2d3e4f5b" \
  -H "x-api-key: $API_KEY"

operating_carrier appears only when a different airline flies the segment than the one that sold it; show it, so the traveller goes to the right desk. Each entry in passengers has a stable id, which is how a bag bought later names its traveller.

Keep learning

On this page