# API Reference
The CitizenPlane v4 API is based on REST (opens new window), returns JSON responses and uses standard HTTP codes. The API base url is https://booking-api.citizenplane.com/v4.
This API implements an NDC-inspired layer, providing a standardized interface for distributors to search, offer, order and book flights through CitizenPlane's inventory.
To get started with CitizenPlane v4 API, send us a mail. We'll create your account, and send the credentials to start using our API.
# Authentication
The CitizenPlane v4 API uses API Key authentication. All requests must include a valid API key. Requests made without authentication will fail, as well as requests made over plain HTTP rather than HTTPS.
To get your API key, send us a mail and we'll assign one for you.
# Errors
CitizenPlane uses conventional HTTP response codes to indicate the success or failure of an API request. Codes in the 4xx range indicate a request that failed given the information provided. Codes in the 5xx range indicate an internal error, which is rare.
All error responses include a message and a type field to help identify the issue.
# Error Types
| Type | Description |
|---|---|
OFFER_NOT_AVAILABLE | The requested offer is no longer available or has expired |
OFFER_NOT_FOUND | The specified offer could not be found |
INVALID_REQUEST_PARAMETERS | The request payload is invalid or missing required fields |
INTERNAL_ERROR | An internal server error has occurred |
AUTHENTICATION_ERROR | Authentication failed |
ORDER_NOT_FOUND | The specified order could not be found |
RESERVATION_NOT_FOUND | The specified reservation could not be found |
CRITERIA_NOT_ALLOWED | The search criteria is not supported (e.g. unsupported travel class) |
ROUTE_NOT_OPERATED | The requested route is not operated |
# HTTP Status Codes Summary
| Code | Status | Meaning |
|---|---|---|
| 400 | Bad Request | Your request is invalid (e.g. payload doesn't match expected schema) |
| 401 | Unauthorized | Your API key is wrong |
| 404 | Not Found | The specified resource could not be found (e.g. offer, order, reservation) |
| 500 | Internal Server Error | Error on CitizenPlane's end. Try again later and contact us if the issue remains |
# Key concepts
The v4 API follows a structured booking flow:
- Search - Find available offers matching travel criteria
- Get Offer - Get detailed pricing for a specific flight combination
- Get Baggage - Retrieve available baggage options for an offer
- Get Fares - Retrieve all available fares for an offer
- Create Order - Create an order with passenger details and selected baggage
- Confirm Order - Confirm the order and trigger the booking
- Get Reservation - Retrieve the final reservation details including PNR
# Offers
An offer represents a flight itinerary with pricing. Offers are stored for 30 minutes after being retrieved via the Get Offer endpoint. Any order using an expired offer will fail.
# Orders
An order is created once passengers and baggage are selected. An order must be confirmed to trigger the actual booking. Once confirmed, a reservation is created. Orders must be confirmed within 15 minutes of creation.
# Reservations
A reservation is the final booking record. It contains the PNR, passenger details, and flight information.
# Connectivity Standard
All offers returned by this API are marked with connectivityStandard: "NDC", indicating NDC-inspired connectivity.
# Supported values
# Trip types
| Value | Description |
|---|---|
ONE_WAY | One-way trip |
ROUND_TRIP | Round trip |
# Travel classes
| Value | Description |
|---|---|
ECONOMY | Economy class (currently the only supported class) |
# Passenger types
| Value | Description |
|---|---|
ADULT | Adult passenger |
CHILD | Child passenger |
INFANT | Infant passenger (under 2 years old) |
# Baggage types
| Value | Description |
|---|---|
CHECK_IN | Checked baggage |
CABIN | Cabin baggage |
# Location types
| Value | Description |
|---|---|
AIRPORT | IATA airport code (e.g. CDG) |
CITY | IATA city code (e.g. PAR) |
# Endpoints
# Search Offers
Search for available flight offers matching the given travel criteria. Returns a list of offers with pricing for one-way or round-trip itineraries. This endpoint supports both airport and city codes for origin/destination.
POST
https://booking-api.citizenplane.com/v4/search
WARNING
Only ECONOMY travel class is currently supported. Searching with any other travel class will return an empty result.
# Body parameters
| Parameter | Type | Status | Description |
|---|---|---|---|
| adults | integer | required | Number of adults (min 1, max 9) |
| children | integer | required | Number of children (min 0, max 9) |
| infants | integer | required | Number of infants (min 0, max = adults) |
| tripType | string | required | ONE_WAY or ROUND_TRIP |
| travelClass | string | optional | Travel class. Defaults to ECONOMY |
| outboundSegment | object | required | Outbound segment details. See segment parameters |
| inboundSegment | object | required if round trip | Inbound segment details (required when tripType is ROUND_TRIP). See segment parameters |
# Search segment parameters
| Parameter | Type | Status | Description |
|---|---|---|---|
| origin | object | required | Origin location with code (3-letter IATA code) and type (AIRPORT or CITY) |
| destination | object | required | Destination location with code (3-letter IATA code) and type (AIRPORT or CITY) |
| departureDate | string | required | Departure date. Expected format: YYYY-MM-DD |
Example request
curl 'https://booking-api.citizenplane.com/v4/search' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: {your_api_key}' \
-d '{
"adults": 1,
"children": 0,
"infants": 0,
"tripType": "ONE_WAY",
"travelClass": "ECONOMY",
"outboundSegment": {
"origin": {
"code": "CDG",
"type": "AIRPORT"
},
"destination": {
"code": "AYT",
"type": "AIRPORT"
},
"departureDate": "2025-08-06"
},
"inboundSegment": null
}'
Example response
{
"offers": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"outboundSegment": {
"sections": [
{
"origin": "CDG",
"destination": "AYT",
"departureDate": "2025-08-06T17:45:00",
"arrivalDate": "2025-08-06T22:40:00",
"travelClass": "ECONOMY",
"availableSeats": 10,
"flightNumber": "511",
"operatingCarrierCode": "XQ",
"marketingCarrierCode": "XQ",
"technicalStops": []
}
]
},
"fare": {
"hasPriorityBoardingIncluded": false,
"hasFastTrackIncluded": false,
"hasFreeSeatsAvailable": false,
"passengerPricingByPassengerType": {
"ADULT": {
"fareAmount": {
"amount": 627.27,
"currency": "EUR"
},
"taxAmount": {
"amount": 0,
"currency": "EUR"
},
"numberOfPassengers": 1
}
},
"includedBaggage": {
"checkedBaggageQuantity": 1,
"cabinBaggageQuantity": 1
},
"price": {
"totalAmount": 627.27,
"sourceAmount": 627.27,
"currency": "EUR"
}
},
"paymentOptions": [
{
"type": "cash",
"fee": {
"amount": 0,
"currency": "EUR"
}
}
],
"connectivityStandard": "NDC"
}
]
}
# Get Offer
Retrieve detailed offer information for a specific flight combination. This endpoint verifies availability and pricing with the supplier and returns a complete offer with fare details, passenger information requirements, and an offer ID that can be used to create an order.
The offer is stored in cache for 30 minutes.
POST
https://booking-api.citizenplane.com/v4/offer
# Body parameters
| Parameter | Type | Status | Description |
|---|---|---|---|
| adults | integer | required | Number of adults (min 1, max 9) |
| children | integer | required | Number of children (min 0, max 9) |
| infants | integer | required | Number of infants (min 0, max = adults) |
| tripType | string | required | ONE_WAY or ROUND_TRIP |
| travelClass | string | optional | Travel class. Defaults to ECONOMY |
| isResidentDiscountEligible | boolean | required | Whether the passenger is eligible for resident discount (must be false) |
| outboundSegment | object | required | Outbound segment with sections. See offer segment parameters |
| inboundSegment | object | required if round trip | Inbound segment with sections (required when tripType is ROUND_TRIP) |
# Offer segment parameters
Each segment contains an array of sections:
| Parameter | Type | Status | Description |
|---|---|---|---|
| origin | string | required | 3-letter IATA airport code |
| destination | string | required | 3-letter IATA airport code |
| departureDate | string | required | Departure date. Format: YYYY-MM-DDTHH:mm:ss |
| arrivalDate | string | required | Arrival date. Format: YYYY-MM-DDTHH:mm:ss |
| flightNumber | string | required | Flight number (1-4 digits) |
| operatingCarrierCode | string | required | 2-letter IATA operating carrier code |
| marketingCarrierCode | string | required | 2-letter IATA marketing carrier code |
| travelClass | string | optional | Travel class. Defaults to ECONOMY |
Example request
curl 'https://booking-api.citizenplane.com/v4/offer' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: {your_api_key}' \
-d '{
"adults": 1,
"children": 0,
"infants": 0,
"tripType": "ONE_WAY",
"travelClass": "ECONOMY",
"isResidentDiscountEligible": false,
"outboundSegment": {
"sections": [
{
"origin": "CDG",
"destination": "AYT",
"departureDate": "2025-08-06T17:45:00",
"arrivalDate": "2025-08-06T22:40:00",
"flightNumber": "511",
"operatingCarrierCode": "XQ",
"marketingCarrierCode": "XQ"
}
]
},
"inboundSegment": null
}'
Example response
{
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"outboundSegment": {
"sections": [
{
"origin": "CDG",
"destination": "AYT",
"departureDate": "2025-08-06T17:45:00",
"arrivalDate": "2025-08-06T22:40:00",
"travelClass": "ECONOMY",
"flightNumber": "511",
"departureTerminal": "1",
"arrivalTerminal": "1",
"operatingCarrierCode": "XQ",
"marketingCarrierCode": "XQ",
"technicalStops": []
}
]
},
"cheapestFare": {
"id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"hasPriorityBoardingIncluded": false,
"hasFastTrackIncluded": false,
"hasFreeSeatsAvailable": false,
"passengerPricingByPassengerType": {
"ADULT": {
"fareAmount": {
"amount": 627.27,
"currency": "EUR"
},
"taxAmount": {
"amount": 0,
"currency": "EUR"
},
"numberOfPassengers": 1
}
},
"includedBaggage": [
{
"quantity": 1,
"descriptor": { "weight": 20 },
"type": "CHECK_IN"
},
{
"quantity": 1,
"descriptor": {
"weight": 10,
"dimensions": { "width": 56, "height": 45, "depth": 25 }
},
"type": "CABIN"
}
],
"totalPrice": {
"amount": 627.27,
"currency": "EUR"
}
},
"providerAdditionalPassengerInformationByPassengerType": {
"ADULT": {
"providerIdentificationTypeOptions": ["PASSPORT"],
"providerRequiredAdditionalFields": ["BIRTH_DATE"]
}
},
"paymentOptions": [
{
"type": "cash",
"fee": {
"amount": 0,
"currency": "EUR"
}
}
],
"connectivityStandard": "NDC",
"validatingCarrier": "XQ"
}
# Get All Fares
Retrieve all available fare options for a given offer.
GET
https://booking-api.citizenplane.com/v4/offer/{offerId}/fares
# Path parameters
| Parameter | Type | Status | Description |
|---|---|---|---|
| offerId | string | required | UUID of the offer |
Example request
curl 'https://booking-api.citizenplane.com/v4/offer/b2c3d4e5-f6a7-8901-bcde-f12345678901/fares' \
-H 'Accept: application/json' \
-H 'Authorization: {your_api_key}'
# Get Baggage
Retrieve available baggage options (checked and cabin) for a given offer. This returns paid baggage options that are not included in the base fare.
GET
https://booking-api.citizenplane.com/v4/offer/{offerId}/baggage
# Path parameters
| Parameter | Type | Status | Description |
|---|---|---|---|
| offerId | string | required | UUID of the offer |
Example request
curl 'https://booking-api.citizenplane.com/v4/offer/b2c3d4e5-f6a7-8901-bcde-f12345678901/baggage' \
-H 'Accept: application/json' \
-H 'Authorization: {your_api_key}'
Example response
{
"fareBaggageOptions": [
{
"fareId": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"itineraryOptions": [],
"outboundSegmentOptions": [
{
"type": "CHECK_IN",
"maxPiecesPerPassenger": 1,
"maxKilosPerPiece": 20,
"pricePerPiece": {
"amount": 36.05,
"currency": "EUR"
}
},
{
"type": "CABIN",
"maxPiecesPerPassenger": 1,
"maxKilosPerPiece": 10,
"pricePerPiece": {
"amount": 15.00,
"currency": "EUR"
}
}
],
"inboundSegmentOptions": null
}
]
}
# Create Order
Create an order for a previously retrieved offer. This step validates the offer, creates internal booking requests, and returns an order with pricing and passenger details.
POST
https://booking-api.citizenplane.com/v4/order?offerId={offerId}&fareId={fareId}
Offer expiration
The offer must still be valid (within 30 minutes of retrieval). If the offer has expired, a new one must be obtained via the Get Offer endpoint.
# Query parameters
| Parameter | Type | Status | Description |
|---|---|---|---|
| offerId | string | required | UUID of the offer |
| fareId | string | required | UUID of the fare within the offer |
# Body parameters
| Parameter | Type | Status | Description |
|---|---|---|---|
| passengers | array | required | Array of passenger objects. See passenger parameters |
| selectedBaggage | array | required | Array of baggage selections per passenger. See baggage parameters |
| selectedSeats | array | required | Array of seat selections (currently empty) |
| customerContact | object | required | Contact information. See customer contact parameters |
| paymentMethod | object | required | Payment method. Must be { "cash": true } |
# Order passenger parameters
| Parameter | Type | Status | Description |
|---|---|---|---|
| number | integer | required | Passenger number (sequential) |
| type | string | required | ADULT, CHILD, or INFANT |
| title | string | required | MR, MRS, or MS |
| name | string | required | Passenger's first name |
| firstLastName | string | required | Passenger's first last name |
| secondLastName | string | optional | Passenger's second last name |
| gender | string | required | MALE or FEMALE |
| birthDate | string | optional | Date of birth. Format: YYYY-MM-DD. Required for children and infants. Required for adults unless the supplier allows optional DOB. |
| nationalityCountryCode | string | optional | 2-letter country code |
| identification | object | optional | Identification document. See identification parameters |
| phone | object | optional | Phone number with number and countryCode fields |
# Identification parameters
| Parameter | Type | Status | Description |
|---|---|---|---|
| type | string | required | PASSPORT, NIE, NIF, NATIONAL_ID_CARD, or BIRTH_DATE |
| identificationNumber | string | required | The identification document number |
| expirationDate | string | required | Expiration date. Format: YYYY-MM-DD. Must be in the future |
| issueCountryCode | string | required | 2-letter country code of issue |
# Order baggage parameters
| Parameter | Type | Status | Description |
|---|---|---|---|
| passengerNumber | integer | required | The passenger number this baggage selection applies to |
| itineraryBaggageSelection | array | required | Baggage for the whole itinerary (usually empty) |
| outboundSegmentBaggageSelection | array | required | Baggage for outbound segment |
| inboundSegmentBaggageSelection | array | optional | Baggage for inbound segment (for round trips) |
Each baggage selection item:
| Parameter | Type | Status | Description |
|---|---|---|---|
| type | string | required | CHECK_IN or CABIN |
| numberOfPieces | integer | required | Number of bags |
| kilosPerPiece | integer | required | Weight per bag in kg |
# Customer contact parameters
| Parameter | Type | Status | Description |
|---|---|---|---|
| name | string | required | Contact first name |
| lastNames | string | required | Contact last names |
| string | required | Contact email | |
| countryCode | string | required | 2-letter country code |
| phone | object | required | Phone with number and countryCode fields |
| address | string | required | Contact address |
| cityName | string | required | Contact city |
| zipCode | string | required | Contact postal code |
Example request
curl 'https://booking-api.citizenplane.com/v4/order?offerId=b2c3d4e5-f6a7-8901-bcde-f12345678901&fareId=c3d4e5f6-a7b8-9012-cdef-123456789012' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: {your_api_key}' \
-d '{
"passengers": [
{
"number": 1,
"type": "ADULT",
"title": "MR",
"name": "John",
"firstLastName": "Doe",
"secondLastName": null,
"gender": "MALE",
"birthDate": "1990-01-15",
"nationalityCountryCode": "FR",
"identification": {
"type": "PASSPORT",
"identificationNumber": "ABCDE123",
"expirationDate": "2030-01-01",
"issueCountryCode": "FR"
},
"phone": {
"number": "+33600000000",
"countryCode": "FR"
}
}
],
"selectedBaggage": [
{
"passengerNumber": 1,
"itineraryBaggageSelection": [],
"outboundSegmentBaggageSelection": [
{
"type": "CHECK_IN",
"numberOfPieces": 1,
"kilosPerPiece": 20
}
],
"inboundSegmentBaggageSelection": null
}
],
"selectedSeats": [],
"customerContact": {
"name": "John",
"lastNames": "Doe",
"email": "john.doe@example.com",
"countryCode": "FR",
"phone": {
"number": "+33600000000",
"countryCode": "FR"
},
"address": "123 Rue de Paris",
"cityName": "Paris",
"zipCode": "75001"
},
"paymentMethod": {
"cash": true
}
}'
Example response
{
"id": "d4e5f6a7-b8c9-0123-defg-234567890123",
"offer": {
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"outboundSegment": {
"sections": [
{
"origin": "CDG",
"destination": "AYT",
"departureDate": "2025-08-06T17:45:00",
"arrivalDate": "2025-08-06T22:40:00",
"travelClass": "ECONOMY",
"flightNumber": "511",
"departureTerminal": "1",
"arrivalTerminal": "1",
"operatingCarrierCode": "XQ",
"marketingCarrierCode": "XQ",
"technicalStops": []
}
]
},
"fare": {
"id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"hasPriorityBoardingIncluded": false,
"hasFastTrackIncluded": false,
"hasFreeSeatsAvailable": false,
"passengerPricingByPassengerType": {
"ADULT": {
"fareAmount": {
"amount": 627.27,
"currency": "EUR"
},
"taxAmount": {
"amount": 0,
"currency": "EUR"
},
"numberOfPassengers": 1
}
},
"includedBaggage": [
{
"quantity": 1,
"descriptor": { "weight": 20 },
"type": "CHECK_IN"
}
],
"totalPrice": {
"amount": 627.27,
"currency": "EUR"
}
},
"connectivityStandard": "NDC"
},
"passengers": [
{
"number": 1,
"type": "ADULT",
"title": "MR",
"name": "John",
"firstLastName": "Doe",
"gender": "MALE",
"birthDate": "1990-01-15",
"nationalityCountryCode": "FR",
"identification": {
"type": "PASSPORT",
"identificationNumber": "ABCDE123",
"expirationDate": "2030-01-01",
"issueCountryCode": "FR"
}
}
],
"selectedBaggage": [
{
"passengerNumber": 1,
"itineraryBaggageSelection": [],
"outboundSegmentBaggageSelection": [
{
"type": "CHECK_IN",
"numberOfPieces": 1,
"kilosPerPiece": 20,
"pricePerPiece": {
"amount": 36.05,
"currency": "EUR"
}
}
],
"inboundSegmentBaggageSelection": []
}
],
"selectedSeats": [],
"customerContact": {
"name": "John",
"lastNames": "Doe",
"email": "john.doe@example.com",
"countryCode": "FR",
"phone": {
"number": "+33600000000",
"countryCode": "FR"
},
"address": "123 Rue de Paris",
"cityName": "Paris",
"zipCode": "75001"
},
"status": "OPEN",
"totalPrice": {
"amount": 663.32,
"currency": "EUR"
}
}
# Confirm Order
Confirm a previously created order. This triggers the actual booking process. The order status will change to CONFIRMING and a reservation ID will be generated.
POST
https://booking-api.citizenplane.com/v4/order/{orderId}/confirm
WARNING
An order can only be confirmed once. Duplicate confirmation requests will be rejected.
# Path parameters
| Parameter | Type | Status | Description |
|---|---|---|---|
| orderId | string | required | UUID of the order to confirm |
Example request
curl -X POST 'https://booking-api.citizenplane.com/v4/order/d4e5f6a7-b8c9-0123-defg-234567890123/confirm' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: {your_api_key}'
Example response
{
"id": "d4e5f6a7-b8c9-0123-defg-234567890123",
"offer": {
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"outboundSegment": { "sections": [...] },
"fare": { ... },
"connectivityStandard": "NDC"
},
"passengers": [...],
"selectedBaggage": [...],
"selectedSeats": [],
"customerContact": { ... },
"status": "CONFIRMING",
"reservationId": "e5f6a7b8-c9d0-1234-efgh-345678901234",
"totalPrice": {
"amount": 663.32,
"currency": "EUR"
}
}
# Get Order
Retrieve the current status and details of an order.
GET
https://booking-api.citizenplane.com/v4/order/{orderId}
# Path parameters
| Parameter | Type | Status | Description |
|---|---|---|---|
| orderId | string | required | UUID of the order |
Example request
curl 'https://booking-api.citizenplane.com/v4/order/d4e5f6a7-b8c9-0123-defg-234567890123' \
-H 'Accept: application/json' \
-H 'Authorization: {your_api_key}'
# Get Reservation by ID
Retrieve reservation details using the reservation ID. The reservation contains the PNR, passenger details, flight segments, baggage, and fare information.
GET
https://booking-api.citizenplane.com/v4/reservation/{reservationId}
# Path parameters
| Parameter | Type | Status | Description |
|---|---|---|---|
| reservationId | string | required | UUID of the reservation |
Example request
curl 'https://booking-api.citizenplane.com/v4/reservation/e5f6a7b8-c9d0-1234-efgh-345678901234' \
-H 'Accept: application/json' \
-H 'Authorization: {your_api_key}'
Example response
{
"id": "e5f6a7b8-c9d0-1234-efgh-345678901234",
"orderId": "d4e5f6a7-b8c9-0123-defg-234567890123",
"pnr": "CPABCD",
"email": "john.doe@example.com",
"outboundSegment": {
"sections": [
{
"origin": "CDG",
"destination": "AYT",
"departureDate": "2025-08-06T17:45:00",
"arrivalDate": "2025-08-06T22:40:00",
"travelClass": "ECONOMY",
"flightNumber": "511",
"departureTerminal": "1",
"arrivalTerminal": "1",
"operatingCarrierCode": "XQ",
"marketingCarrierCode": "XQ",
"technicalStops": []
}
]
},
"passengers": [
{
"number": 1,
"type": "ADULT",
"title": "MR",
"name": "John",
"firstLastName": "Doe",
"gender": "MALE",
"birthDate": "1990-01-15",
"nationalityCountryCode": "FR"
}
],
"selectedBaggage": [...],
"selectedSeats": [],
"customerContact": { ... },
"fare": { ... }
}
# Get Reservation by Order ID
Retrieve reservation details using the order ID instead of the reservation ID.
GET
https://booking-api.citizenplane.com/v4/reservation?orderId={orderId}
# Query parameters
| Parameter | Type | Status | Description |
|---|---|---|---|
| orderId | string | required | UUID of the order |
Example request
curl 'https://booking-api.citizenplane.com/v4/reservation?orderId=d4e5f6a7-b8c9-0123-defg-234567890123' \
-H 'Accept: application/json' \
-H 'Authorization: {your_api_key}'
The response format is the same as the Get Reservation by ID endpoint.