Skip to content
FX-Port/Docs
GuidesAPI referenceChangelog
Get API keys
  • Introduction
  • Quickstart
  • Security
  • Authentication
  • Environments and Postman
  • Sandbox
  • Conventions and naming
  • Legacy names and twins
  • Errors
  • Rate limits and fair use
  • Streaming search results

Concepts

  • Overview
  • Booking flows
  • Passengers and ages
  • Pricing
  • Post-booking operations

Search and price

  • Airport autocomplete
  • Search flights
  • Basic routes
  • Passengers and cabins
  • Filters
  • Supplier selection
  • Open-jaw and multi-city
  • Flexible dates
  • Price an offer

Book, pay and cancel

  • Book
  • Quote a held booking
  • Pay a held booking
  • Cancel a held booking
  • Quote a cancellation
  • Confirm a cancellation

After booking

  • Get a booking
  • Get the ticket PDF
  • Get the ticket number
  • ACD passport uploads
  • Use these docs with AI

Flights · Search and price

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).

Use the supplier-level searchId of the entry the offer is in (suppliers[n].searchId), not the top-level requestId. A requestId is answered with 404 search_not_found.

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, supportVoid and voidDeadline are evaluated again: read them here before you choose between hold and issue. An offer that could be held at search time may not be holdable now.

Request body

FieldTypeRequiredDescription
search_idstringYessuppliers[n].searchId of the search response.
offer_idstringYesid 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

FieldTypeMeaning
successbooleantrue when the offer was priced.
requestIdstringThe id of this call, for support.
offerIdstringEchoes your offer_id.
offerTypestringpriced.
supplierCurrency, agencyCurrencystringThe two currencies; see Flight pricing.
exchangeRatenumberOnly when the two currencies differ: the rate used.
processingTimenumberSeconds spent calling the supplier.
results.offers[0]objectThe priced offer. Its id is the offer_id for book_flight.
results.offers[0].supportHold, .supportVoid, .voidDeadlineRe-evaluated at pricing time: decide hold or issue from these.
results.offers[0].pricingobjectThe confirmed price. Compare finalPrice with the search: it can differ.
results.offers[0].travelerPricingarrayThe confirmed price of each traveler, as in search.
results.offers[0].conditions, .fareRulesobjectRefund 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

StatusCodeWhenWhat to do
400bad_requestsearch_id or offer_id is missing: Missing required parameters: search_id and offer_id are required.Send both.
400invalid_identifierAn id is not a valid token (letters, digits and - _ . : = ~, up to 128 characters).Send the id exactly as you received it.
400invalid_json, invalid_bodyThe body is not a JSON object.Send a JSON object.
404search_not_foundThe search_id is unknown, older than 24 hours, a requestId, or belongs to another agency (the answer is the same).Search again.
404offer_not_foundThe offer is not in that search.Check that the offer comes from the supplier entry of this searchId.
410offer_expiredThe supplier says the offer is gone (supplier_offer_id is in the body).Search again.
410offer_incomplete, supplier_not_found, technology_type_unknownThe saved offer is unusable.Search again.
503supplier_temporarily_unavailableThe supplier is being skipped after repeated failures (SUPPLIER_CIRCUIT_OPEN).Wait a little, then retry.
503supplier_api_errorThe supplier refused or failed.Retry once; otherwise search again.
503supplier_busyFX-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.

PreviousFlexible-date searchNext Book a flight

On this page

  • What it does
  • Request body
  • Legacy names
  • Request example
  • Response example
  • Response fields
  • Errors
  • Next

© 2026 FX-Port

TermsPrivacy[email protected]