Live and bookable — pre-production, and no payment
These endpoints work against real American Airlines inventory: a search returns real offers and you can create a real booking with a carrier PNR. Two limits are worth knowing before you build against them.
- Orders are pre-production. They are genuine reservations in the airline's pre-production system, not commercial tickets — nobody flies on them. Cancel what you create; anything left over expires on its own payment time limit.
- Payment and ticketing cannot complete. An order is created as an unpaid hold, which is the normal agency flow, but ticketing is inhibited on this IATA number, so
POST /v1/orders/{orderID}/commitreturns502. Everything up to and including the reservation works.
Shopping
Price offer
Confirm the price with the airline and mint the bookable offer ID. Mandatory before booking.
POST
/v1/offers/priceRequires X-API-KeyThis step is not optional. AirShopping returns *transient* offers; the airline will not book one.
|---|---|
| Sent (shopped) |
| Returned (priced) |
| Guaranteed until | ~20 minutes |Booking with the *shopped* ID fails with
OfferPrice re-confirms availability and price and mints a new offer ID, and that new ID is the only one POST /v1/orders accepts.With American Airlines the change is visible in the ID itself — the shopped offer starts AA-X… and the priced offer comes back AA-P…:| | ||---|---|
| Sent (shopped) |
AA-X2E80EC16-51FD-48AD-924B.1-1\|X2E80EC16-…-1-1 || Returned (priced) |
AA-P2E80EC16-51FD-48AD-924B-1\|P2E80EC16-…-1-1 || Guaranteed until | ~20 minutes |Booking with the *shopped* ID fails with
502 and [230000002] Invalid or Expired Offer. This is the single most common integration mistake.Routing. airline is optional — it is inferred from the offer ID prefix (everything before the first -). Send it explicitly if your offer IDs do not carry a prefix.Caching. Priced results are cached per exact set of offer IDs, so a repeat call inside the TTL does not hit the airline again.The response is a full Offer plus price_guaranteed_until. In practice AA returns the same total it shopped — a confirmed price is a confirmation, not usually a re-quote.Request example
curl --request POST \
--url https://api.norba.io/v1/offers/price \
--header "X-API-Key: $NORBA_KEY" \
--header "Content-Type: application/json" \
--data '{ "offer_ids": ["AA-X2E80EC16-51FD-48AD-924B.1-1|X2E80EC16-51FD-48AD-924B.1-1-1"], "airline": "AA" }'Response example
Response example
{
"offer_id": "AA-P2E80EC16-51FD-48AD-924B-1|P2E80EC16-51FD-48AD-924B-1-1",
"airline_offer_id": "P2E80EC16-51FD-48AD-924B-1",
"airline": "AA",
"source": "ndc_direct",
"source_version": "NDC-21.3",
"currency": "EUR",
"total_amount": 380.62,
"base_fare": 193,
"taxes_fees": 187.62,
"cabin_class": "economy",
"baggage_included": false,
"expires_at": "2026-08-14T09:45:23Z",
"price_guaranteed_until": "2026-08-14T09:45:23Z",
"passengers": [
{
"type": "ADT",
"count": 1,
"base_fare": 193,
"taxes": 187.62,
"total": 380.62
}
],
"slices": [
{
"origin": "BCN",
"destination": "JFK",
"stops": 0,
"segments": [
{
"flight_number": "AA67",
"aircraft": "772"
}
]
}
]
}Request body
offer_idsarray<string>RequiredOne or more offer IDs from a shopping response. Required — an empty array returns
400 offer_ids required.Example:
["AA-X2E80EC16-…|X2E80EC16-…-1-1"]airlinestringIATA code. Inferred from the first offer ID's prefix when omitted.
Example:
AAResponse fields
offer_idstringRequiredThe bookable offer ID. Pass this — not the shopped one — to
POST /v1/orders.price_guaranteed_untilstring (RFC 3339)RequiredDeadline for creating the order at this price. Roughly 20 minutes with AA. Past it, price again.
(all Offer fields)objectThe response embeds the full canonical
Offer — totals, slices, segments, fare family, fare rules, passenger breakdown.Error codes
400offer_ids required, or unknown airline (check offer_id prefix).404offer no longer available at airline.502The airline rejected the offer — usually expired, or already priced once.