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
  • Use these docs with AI

Get started

Quickstart

Get a sandbox key, search, price and book a flight, then check the booking, in about ten minutes.

This guide takes about ten minutes. You will get a sandbox key, make a first call, then search, price and book a flight and read the booking back. Nothing here costs money: sandbox flight bookings are made on the suppliers' test systems, so no balance is debited and no real ticket exists.

  1. 1

    Create an account and a sandbox key

    Sign up at fx-port.com/register, open the API page of the dashboard and create a key. Choose Read + Write for this guide, because booking needs it. A sandbox key starts with fxp_test_.

    Keep the key on your server, in an environment variable, never in front-end code:

    export FXP_KEY="fxp_test_YOUR_KEY"
  2. 2

    Check that the key works

    GET /suppliers is free and quick. It answers with the suppliers your key can reach, which proves the key, the environment and the network path in one call.

    curl https://api.fx-port.com/api/v1/suppliers \
      -H "Authorization: Bearer fxp_test_YOUR_KEY"
    {
      "environment": "sandbox",
      "success": true,
      "products": [
        {
          "product": "flights",
          "suppliers": [
            { "id": "amadeus_aqc_dz_1", "name": "Amadeus Algeria", "technology": "amadeus_aqc", "routes": ["DZ"], "capabilities": { "hold": true, "void": true, "flexible_dates": true, "ancillaries": false } },
            { "id": "duffel_1", "name": "Duffel", "technology": "duffel", "routes": ["*"], "capabilities": { "hold": false, "void": false, "flexible_dates": false, "ancillaries": true } }
          ]
        }
      ]
    }

    "environment": "sandbox" is in every JSON answer. It tells you which environment your key is in, so you cannot confuse the two.

  3. 3

    Search, price, book and read the booking back

    The program below does the whole flight flow. It searches Algiers to Paris 45 days ahead on Duffel, takes the cheapest offer, prices it, books it as an instant ticket and reads the booking back.

    import os
    from datetime import date, timedelta
    
    import requests
    
    API = "https://api.fx-port.com/api/v1"
    HEADERS = {"Authorization": f"Bearer {os.environ['FXP_KEY']}", "Content-Type": "application/json"}
    
    
    def post(path, body):
        response = requests.post(API + path, headers=HEADERS, json=body, timeout=90)
        data = response.json()
        if not response.ok:
            raise SystemExit(f"{response.status_code} {data.get('errors') or data}")
        return data
    
    
    # 1. Search (Duffel works on every sandbox key; leave supplier_id out to search every supplier)
    search = post("/get_flights", {
        "origin": "ALG",
        "destination": "CDG",
        "departure_date": (date.today() + timedelta(days=45)).isoformat(),
        "cabin_class": "economy",
        "passengers": {"adults": 1},
        "supplier_id": "duffel_1",
    })
    supplier = search["suppliers"][0]
    offer = min(supplier["results"]["offers"], key=lambda o: o["pricing"]["finalPrice"])
    search_id = supplier.get("search_id") or supplier["searchId"]
    print("cheapest offer:", offer["id"], offer["pricing"]["finalPrice"], offer["pricing"]["agencyCurrency"])
    
    # 2. Price: confirms the fare with the supplier before you book
    priced = post("/flights/price_flight", {"search_id": search_id, "offer_id": offer["id"]})
    print("priced at:", priced["results"]["offers"][0]["pricing"]["finalPrice"])
    
    # 3. Book (sandbox bookings never debit money; send_email false keeps the traveler's inbox quiet)
    booking = post("/flights/book_flight", {
        "search_id": search_id,
        "offer_id": offer["id"],
        "booking_type": "issue",
        "send_email": False,
        "passengers": [{
            "type": "adult",
            "gender": "MALE",
            "first_name": "John",
            "last_name": "Smith",
            "date_of_birth": "1988-06-15",
            "identity_documents": [{"type": "passport", "number": "A12345678", "issuing_country": "DZ", "expiry_date": "2031-04-18", "nationality": "DZ"}],
        }],
        "contact_details": {"email": "[email protected]", "phone_number": "+213554657687"},
    })
    pnr = booking["booking"]["pnr"]
    print("booked:", pnr, booking["booking"]["status"])
    
    # 4. Read the booking back
    details = post("/flights/get_booking", {"booking_id": pnr})
    print("stored booking:", details["booking"]["status"], details["booking"]["total_price"], details["booking"]["currency"])
    // Node 18 or later (fetch is built in); run it as an ES module: node quickstart.mjs
    const API = "https://api.fx-port.com/api/v1";
    const headers = { Authorization: `Bearer ${process.env.FXP_KEY}`, "Content-Type": "application/json" };
    
    async function post(path, body) {
      const response = await fetch(API + path, { method: "POST", headers, body: JSON.stringify(body) });
      const data = await response.json();
      if (!response.ok) throw new Error(`${response.status} ${JSON.stringify(data.errors ?? data)}`);
      return data;
    }
    
    // 1. Search (Duffel works on every sandbox key; leave supplier_id out to search every supplier)
    const departure = new Date(Date.now() + 45 * 86_400_000).toISOString().slice(0, 10);
    const search = await post("/get_flights", {
      origin: "ALG",
      destination: "CDG",
      departure_date: departure,
      cabin_class: "economy",
      passengers: { adults: 1 },
      supplier_id: "duffel_1",
    });
    const supplier = search.suppliers[0];
    const offer = supplier.results.offers.reduce((best, o) => (o.pricing.finalPrice < best.pricing.finalPrice ? o : best));
    const searchId = supplier.search_id ?? supplier.searchId;
    console.log("cheapest offer:", offer.id, offer.pricing.finalPrice, offer.pricing.agencyCurrency);
    
    // 2. Price: confirms the fare with the supplier before you book
    const priced = await post("/flights/price_flight", { search_id: searchId, offer_id: offer.id });
    console.log("priced at:", priced.results.offers[0].pricing.finalPrice);
    
    // 3. Book (sandbox bookings never debit money; send_email false keeps the traveler's inbox quiet)
    const booking = await post("/flights/book_flight", {
      search_id: searchId,
      offer_id: offer.id,
      booking_type: "issue",
      send_email: false,
      passengers: [
        {
          type: "adult",
          gender: "MALE",
          first_name: "John",
          last_name: "Smith",
          date_of_birth: "1988-06-15",
          identity_documents: [{ type: "passport", number: "A12345678", issuing_country: "DZ", expiry_date: "2031-04-18", nationality: "DZ" }],
        },
      ],
      contact_details: { email: "[email protected]", phone_number: "+213554657687" },
    });
    const pnr = booking.booking.pnr;
    console.log("booked:", pnr, booking.booking.status);
    
    // 4. Read the booking back
    const details = await post("/flights/get_booking", { booking_id: pnr });
    console.log("stored booking:", details.booking.status, details.booking.total_price, details.booking.currency);
    # 1. Search. Copy "searchId" of the supplier and the "id" of the offer you want from the answer.
    curl -s -X POST https://api.fx-port.com/api/v1/get_flights \
      -H "Authorization: Bearer $FXP_KEY" -H "Content-Type: application/json" \
      -d '{"origin":"ALG","destination":"CDG","departure_date":"2026-12-20","cabin_class":"economy","passengers":{"adults":1},"supplier_id":"duffel_1"}'
    
    # 2. Price
    curl -s -X POST https://api.fx-port.com/api/v1/flights/price_flight \
      -H "Authorization: Bearer $FXP_KEY" -H "Content-Type: application/json" \
      -d '{"search_id":"fs_dfx_...","offer_id":"fx_offer_..."}'
    
    # 3. Book
    curl -s -X POST https://api.fx-port.com/api/v1/flights/book_flight \
      -H "Authorization: Bearer $FXP_KEY" -H "Content-Type: application/json" \
      -d '{"search_id":"fs_dfx_...","offer_id":"fx_offer_...","booking_type":"issue","send_email":false,
           "passengers":[{"type":"adult","gender":"MALE","first_name":"John","last_name":"Smith","date_of_birth":"1988-06-15",
             "identity_documents":[{"type":"passport","number":"A12345678","issuing_country":"DZ","expiry_date":"2031-04-18","nationality":"DZ"}]}],
           "contact_details":{"email":"[email protected]","phone_number":"+213554657687"}}'
    
    # 4. Read the booking back (booking_id is the "pnr" of step 3)
    curl -s -X POST https://api.fx-port.com/api/v1/flights/get_booking \
      -H "Authorization: Bearer $FXP_KEY" -H "Content-Type: application/json" \
      -d '{"booking_id":"J2RRA3"}'

    Run it and you get something like:

    cheapest offer: fx_offer_a7df9202 21708.29 DZD
    priced at: 21708.29
    booked: 2LL5ZF confirmed
    stored booking: confirmed 21708.29 DZD

    Prices are in the currency of your agency (here DZD), whatever currency the supplier charges.

The identifiers that travel from call to call

You get it fromFieldYou send it toAs
Searchsuppliers[].searchId (twin search_id)price and booksearch_id
Searchsuppliers[].results.offers[].id (twin offer_id)price and bookoffer_id
Bookbooking.pnrget booking, tickets, cancel, ACD documentsbooking_id
Bookbooking.reference (the supplier's order id)pay, cancel a holdorder_id

A search is valid for a limited time and a fare can change or sell out, so always price right before you book. Read Conventions and naming for the rules every endpoint follows (names, ids, money, dates, pagination) and Errors for what to do when a call fails.

Hotels and eSIMs in the sandbox

Hotels. You can search and price any hotel in the sandbox, but you can only book the supplier's test hotels. Booking a random hotel does not work. Use test_hotel_do_not_book (any residency) or test_hotel (residency dz, ru or us) and cancel what you book. The hotel sandbox guide has a complete, tested script, and Hotel data: mini and full shows which hotel payload to use for a result list and which for a details page.

eSIMs. List the packages of a country, order one with the traveler's e-mail and phone, then top it up later:

curl 'https://api.fx-port.com/api/v1/esims/packages?country=france&limit=2' \
  -H "Authorization: Bearer fxp_test_YOUR_KEY"

Continue with the eSIM guide.

Going live

The code you wrote does not change. Swap the key for a fxp_live_ key and the same base URL answers with live data. From then on a Read + Write key issues real tickets and debits your agency balance, so read Security first, keep keys on your server, and check Environments for what differs (live needs a funded balance, the sandbox does not).

What to read next

Conventions and namingNames, ids, money, dates and the rules every endpoint follows.ErrorsThe error body, every code and what to do about each kind.Rate limits and fair useThe limits per minute and how to back off.Streaming search resultsFirst offers in about two seconds instead of waiting for the slowest supplier.Flight booking flowsInstant, hold and ACD flows with every status.API referenceEvery endpoint with examples in nine languages.Use these docs with AIllms.txt, OpenAPI and Markdown for assistants.
PreviousIntroductionNext Security

On this page

  • The identifiers that travel from call to call
  • Hotels and eSIMs in the sandbox
  • Going live
  • What to read next

© 2026 FX-Port

TermsPrivacy[email protected]