Price a flight offer
Price a cached flight offer to confirm the current fare. Required before booking: book_flight reads the priced offer from this cache.
POST/api/v1/flights/price_flight
Price an offer from a search to get its current, confirmed fare. This step is required before book_flight: the booking reads the priced offer, and an offer that was never priced is refused with 404 offer_not_priced.
Permission: Read. Rate group: default (6,000 requests per minute per key).
What it does
- Duffel: fetches the offer again from the supplier, so you get the live price (0.2 to 10 seconds).
- Amadeus Algeria: re-prices the saved offer through the supplier's pricing service (1 to 2 seconds).
- The priced offer, with your agency's commission, is saved for 12 hours under the same offer id. Pricing the same offer again is allowed and returns the current price.
supportHold,supportVoidandvoidDeadlineare evaluated again: read them here before you choose betweenholdandissue. An offer that could be held at search time may not be holdable now.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
search_id | string | Yes | suppliers[n].searchId of the search response. |
offer_id | string | Yes | id of the offer, fx_offer_ and 8 hex characters. |
Both fields are ids of up to 128 letters, digits and - _ . : = ~. Nothing else is read. The offer must belong to that search.
Legacy names
searchId and offerId still work and are answered with a deprecated_field warning.
Request example
curl https://api.fx-port.com/api/v1/flights/price_flight \
-H "Authorization: Bearer fxp_test_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"search_id": "fs_dfx_a3aeaa36-74ad-424b-b88d-e6bdc9a4ba94",
"offer_id": "fx_offer_89d6592c"
}'Response example
A Duffel offer: the supplier prices in USD and the agency settles in DZD, so exchangeRate is at the top level (the pricing block never holds a rate). The itinerary and the traveler pricing are the same objects as in Search flights and are shortened here.
{
"environment": "sandbox",
"success": true,
"requestId": "76f8fc58",
"offerId": "fx_offer_89d6592c",
"offerType": "priced",
"processingTime": 0.177,
"supplierCurrency": "USD",
"agencyCurrency": "DZD",
"exchangeRate": 250.0,
"results": {
"totalOffers": 1,
"supplierCurrency": "USD",
"agencyCurrency": "DZD",
"offers": [
{
"id": "fx_offer_89d6592c",
"type": "flight_offer",
"supplier": { "id": "duffel_1", "name": "Duffel", "originalId": "off_034q6JKduVe2mSW1fH8lge" },
"source": "duffel",
"bookableSeats": null,
"supportHold": false,
"supportVoid": false,
"voidDeadline": null,
"filtersApplied": {},
"warnings": [],
"instantTicketingRequired": false,
"pricing": {
"supplierCurrency": "USD",
"agencyCurrency": "DZD",
"basePrice": 16975.0,
"taxesAndFees": 5366.8,
"agencyCommission": 22.35,
"finalPrice": 22364.15,
"b2bPrice": 22341.8,
"additionalServices": []
},
"itinerary": [{ "direction": "outbound", "duration": "PT2H24M", "stops": 0, "segments": ["..."], "conditions": { "...": "..." } }],
"travelerPricing": [{ "travelerId": "pas_0000BC0a5iH5UE0bmBqWkl", "travelerType": "ADULT", "price": { "...": "..." }, "fareDetailsBySegment": ["..."] }],
"conditions": { "refundBeforeDeparture": { "allowed": true }, "changeBeforeDeparture": { "allowed": true } },
"fareRules": { "refund": { "allowed": true }, "exchange": { "allowed": true }, "supplierCurrency": "USD", "agencyCurrency": "DZD" },
"validatingAirline": [{ "code": "BA", "name": "British Airways" }],
"additionalServices": {
"baggage": [
{
"id": "ase_0000BC0aGG1cCZZ8LCieTb",
"type": "checked",
"quantity": 1,
"maxQuantity": 1,
"maxWeightKg": 23,
"maxWeightUnit": "kg",
"priceAmount": 20.0,
"convertedPriceAmount": 5000.0,
"priceCurrency": "USD",
"applicablePassengers": ["pas_0000BC0a5iH5UE0bmBqWkl"],
"applicableSegments": ["seg_0000BC0a5iVGdVLqUATqqr"],
"bookableByItinerary": false,
"metadata": { "name": "CHECKED", "description": "Additional checked" }
}
]
}
}
]
}
}The offer is the search offer plus source (duffel or GDS) and the priced additionalServices (extra baggage the traveler can buy: a list of priced services under baggage), and minus lastTicketingDate and lastTicketingDateTime.
Response fields
| Field | Type | Meaning |
|---|---|---|
success | boolean | true when the offer was priced. |
requestId | string | The id of this call, for support. |
offerId | string | Echoes your offer_id. |
offerType | string | priced. |
supplierCurrency, agencyCurrency | string | The two currencies; see Flight pricing. |
exchangeRate | number | Only when the two currencies differ: the rate used. |
processingTime | number | Seconds spent calling the supplier. |
results.offers[0] | object | The priced offer. Its id is the offer_id for book_flight. |
results.offers[0].supportHold, .supportVoid, .voidDeadline | Re-evaluated at pricing time: decide hold or issue from these. | |
results.offers[0].pricing | object | The confirmed price. Compare finalPrice with the search: it can differ. |
results.offers[0].travelerPricing | array | The confirmed price of each traveler, as in search. |
results.offers[0].conditions, .fareRules | object | Refund and change eligibility of this exact fare (indicative). |
Next to the keys above the response carries the snake_case twins request_id, offer_id, exchange_rate, processing_time and, in the offer, offer_id, support_hold, support_void, void_deadline, pricing.base_price, pricing.taxes_and_fees, pricing.agency_commission, pricing.final_price, pricing.b2b_price, supplier.supplier_id, supplier.supplier_name, supplier.supplier_offer_id.
What the response does not say: when the offer expires, and the searchId (you must keep it). Duffel offers expire after 15 to 60 minutes: in our sandbox an offer was priced without a problem 26 minutes after the search and was gone at 38 minutes. Book soon after you price.
Errors
| Status | Code | When | What to do |
|---|---|---|---|
400 | bad_request | search_id or offer_id is missing: Missing required parameters: search_id and offer_id are required. | Send both. |
400 | invalid_identifier | An id is not a valid token (letters, digits and - _ . : = ~, up to 128 characters). | Send the id exactly as you received it. |
400 | invalid_json, invalid_body | The body is not a JSON object. | Send a JSON object. |
404 | search_not_found | The search_id is unknown, older than 24 hours, a requestId, or belongs to another agency (the answer is the same). | Search again. |
404 | offer_not_found | The offer is not in that search. | Check that the offer comes from the supplier entry of this searchId. |
410 | offer_expired | The supplier says the offer is gone (supplier_offer_id is in the body). | Search again. |
410 | offer_incomplete, supplier_not_found, technology_type_unknown | The saved offer is unusable. | Search again. |
503 | supplier_temporarily_unavailable | The supplier is being skipped after repeated failures (SUPPLIER_CIRCUIT_OPEN). | Wait a little, then retry. |
503 | supplier_api_error | The supplier refused or failed. | Retry once; otherwise search again. |
503 | supplier_busy | FX-Port's capacity towards the supplier is full for a moment. The supplier was not called and nothing was priced. The answer carries Retry-After (seconds). | Wait for Retry-After, then send the same request again: it is safe. |
See Errors for the body of an error and Search flights for the 24-hour life of a searchId.
Next
Book the flight with the same search_id and the id of the priced offer. For what each price field means, see Flight pricing.