Skyscanner Flight API Integration: Developer Guide
Implement Skyscanner Flights Live Prices with x-api-key authentication, create/poll lifecycle, request-response models, itinerary/leg/segment mapping, agents, pricing options, rate limits and observability.
- 2026-09-26 — Provider companion standard applied; rate limits, refresh-price lifecycle, pagination and evidence boundaries clarified.
The core architecture decision in a Skyscanner flight integration is to model asynchronous search sessions + a canonical flight model + provider handoff, instead of exposing upstream JSON directly to the UI.
Skyscanner's current Flights Live Prices flow uses two endpoints:
POST /flights/live/search/create
|
v
initial / partial results
+ sessionToken
|
v
POST /flights/live/search/poll/{sessionToken}
|
v
progressively richer results
|
v
completedThis is not a classic one-request/one-response integration.
Provider companion snapshot
| Area | Status |
|---|---|
| Access | Approved Skyscanner partnership + API key |
| Auth | Server-side x-api-key |
| Primary contracts | Flights Live Prices create/poll, itinerary refresh, Autosuggest |
| Data direction | Client/backend initiated create + poll |
| Pagination | Live Prices is not a paginated collection; completeness progresses through session polling |
| Polling | Core part of the contract after create |
| Public default rate limits | Flights Live Pricing Create: 100/sec and 100/min; Poll: 100/sec and 500/min. Partner agreements may differ |
| Reprice/refresh | itineraryrefresh/create + itineraryrefresh/poll refresh selected-itinerary pricing |
| Booking ownership | Downstream airline/OTA after pricing-option/deeplink handoff |
| Evidence level | Official public docs reviewed; no claim of an authorized partner-account test |
| Code examples | Illustrative |
Capability boundary
Place discovery -> Flights Autosuggest
Initial live search -> /flights/live/search/create
Search completion -> /flights/live/search/poll/{sessionToken}
Selected price refresh-> /flights/live/itineraryrefresh/create
Refresh completion -> /flights/live/itineraryrefresh/poll/{refreshSessionToken}
Booking handoff -> pricing option / agent deep linkSearch session, selected-itinerary refresh and downstream booking are different state machines.
Why pagination is N/A
Flights Live Prices is not a conventional page=2 collection. Result completeness evolves through create/poll on the same search session. Do not bolt on generic pagination; manage session progress and merge semantics instead.
Official rate-limit defaults
Skyscanner's current public Rate Limits page lists these standard Flights defaults:
| API | Per second | Per minute |
|---|---|---|
| Flights Live Pricing — Create | 100 | 100 |
| Flights Live Pricing — Poll | 100 | 500 |
| Flights Indicative Prices | 100 | 500 |
Skyscanner states that limits are API-key/partner specific and can be adjusted. Runtime 429 metrics and the active partner quota therefore remain the operational source of truth.
Price refresh / reprice lifecycle
Do not treat the first live-search pricing option as checkout truth. Skyscanner documents a selected-itinerary refresh flow:
search/create
-> search/poll
-> user selects itinerary
-> itineraryrefresh/create
-> itineraryrefresh/poll
-> refreshed pricing options
-> agent deeplinkIf the refresh changes price, the UI should not keep the stale search price as confirmed state. A safer internal lifecycle is:
SEARCH_PRICE
REFRESHING
REFRESHED
PRICE_CHANGED
HANDOFF_READYFor platform responsibilities, commercial boundaries and operational decisions, see the Skyscanner profile. This guide covers implementation contracts and request/response handling.
1. Access and authentication
Access requires an approved Skyscanner partnership/API key.
Authentication:
x-api-key: <your-api-key>Never expose the key in browser code or public repositories.
interface SkyscannerCredentials {
apiKeyRef: string;
}Recommended boundary:
Browser
-> Your backend
-> Skyscanner API2. Flights Live Prices create endpoint
POST https://partners.api.skyscanner.net/apiservices/v3/flights/live/search/create
Content-Type: application/json
x-api-key: <api-key>Minimal request:
{
"query": {
"market": "UK",
"locale": "en-GB",
"currency": "GBP",
"queryLegs": [
{
"originPlaceId": { "iata": "LHR" },
"destinationPlaceId": { "iata": "SIN" },
"date": { "year": 2026, "month": 10, "day": 12 }
}
],
"adults": 1,
"cabinClass": "CABIN_CLASS_ECONOMY"
}
}Required request concepts include market, locale, currency, query legs and adults. Optional fields include children ages, carrier/agent inclusion or exclusion, sustainability data, nearby airports and baggage-related options when enabled.
3. Internal search request
Do not make provider enums your domain contract.
interface FlightSearchRequest {
market: string;
locale: string;
currency: string;
legs: Array<{
origin: { iata?: string; entityId?: string };
destination: { iata?: string; entityId?: string };
departureDate: string;
}>;
passengers: {
adults: number;
childAges: number[];
};
cabinClass:
| "economy"
| "premium_economy"
| "business"
| "first";
}Internal request
-> Skyscanner mapper
-> create payload4. Handling the create response
The create call returns initial results and a sessionToken. Skyscanner documents the create response as a fast initial/incomplete subset used for time to first result.
Conceptual response:
{
"sessionToken": "session-token",
"status": "RESULT_STATUS_INCOMPLETE",
"content": {
"results": {
"itineraries": {
"itinerary-1": {
"pricingOptions": []
}
}
}
}
}A successful create call does not mean the search is complete.
type SearchStatus =
| "created"
| "partial"
| "polling"
| "complete"
| "failed"
| "expired";5. Poll endpoint
POST https://partners.api.skyscanner.net/apiservices/v3/flights/live/search/poll/{sessionToken}
x-api-key: <api-key>Model session state explicitly:
interface FlightSearchSession {
id: string;
provider: "skyscanner";
providerSessionToken: string;
requestHash: string;
status: SearchStatus;
createdAt: string;
lastPolledAt?: string;
completedAt?: string;
pollCount: number;
}6. Polling strategy
Avoid tight fixed polling loops.
Example:
poll 1 -> 300 ms
poll 2 -> 500 ms
poll 3 -> 800 ms
poll 4 -> 1200 ms
poll 5+ -> 1500-2000 msStop on completion, client disconnect, max duration, unrecoverable errors or invalid sessions.
interface PollPolicy {
maxDurationMs: number;
maxPollCount: number;
initialDelayMs: number;
maxDelayMs: number;
}7. Progressive results
Render first useful results before the whole supplier fan-out matures.
interface FlightSearchViewState {
status: "searching" | "partial" | "complete" | "error";
itineraries: FlightItinerary[];
lastUpdatedAt: string;
}create
-> render first results
-> poll
-> merge/deduplicate
-> rerank
-> render
-> poll
-> complete8. Canonical flight model
Keep itinerary, leg, segment, carrier, agent and pricing option separate.
Itinerary
-> Leg
-> Segment
-> Carrier
Itinerary
-> PricingOption
-> Agent
-> DeepLinkinterface FlightItinerary {
id: string;
legIds: string[];
pricingOptions: FlightPricingOption[];
score?: number;
}
interface FlightLeg {
id: string;
origin: string;
destination: string;
departure: string;
arrival: string;
durationMinutes: number;
segmentIds: string[];
stopCount: number;
}
interface FlightSegment {
id: string;
origin: string;
destination: string;
departure: string;
arrival: string;
marketingCarrierId?: string;
operatingCarrierId?: string;
flightNumber?: string;
}9. Carrier and agent are different
Carriers market or operate flights. Agents sell/book itineraries.
interface FlightAgent {
id: string;
name: string;
type?: "airline" | "ota";
rating?: number;
}One itinerary may be sold by several agents with different prices, fees, baggage terms or deep links.
10. Pricing options
interface FlightPricingOption {
id: string;
agentIds: string[];
price: {
amount: number;
currency: string;
};
deepLink?: string;
transferType?: string;
farePolicy?: string;
}itinerary != offer11. Normalize response entities
Skyscanner JSON
|
v
Provider DTO
|
v
Entity dictionaries
|
+--> itineraries
+--> legs
+--> segments
+--> carriers
+--> agents
|
v
Canonical flight modelThe frontend should consume your model, not Skyscanner DTOs.
12. Merge poll results
Do not blindly append every poll response.
function mergeSearchResults(
current: FlightSearchResult,
incoming: FlightSearchResult
): FlightSearchResult {
// upsert by provider-stable id
// update pricing options
// retain newest provider state
return current;
}13. Autosuggest
Use Skyscanner's Flights Autosuggest for place resolution rather than guessing IATA codes from free text.
POST https://partners.api.skyscanner.net/apiservices/v3/autosuggest/flights{
"query": {
"market": "UK",
"locale": "en-GB",
"searchTerm": "London",
"includedEntityTypes": [
"PLACE_TYPE_CITY",
"PLACE_TYPE_AIRPORT"
]
},
"limit": 10,
"isDestination": false
}interface FlightPlace {
id: string;
entityId?: string;
iata?: string;
name: string;
type: "airport" | "city" | "country";
}14. Cache strategy
A live price cache key should include route, dates, passengers, cabin, market, locale and currency.
origin
+ destination
+ dates
+ adults
+ child ages
+ cabin
+ market
+ locale
+ currencyUse short-lived caching for duplicate suppression and burst control, not as permanent booking truth.
15. Indicative vs Live
Discovery
-> indicative / aggregated data
Booking intent
-> Flights Live PricesDo not run a full live create/poll cycle for every flexible-date discovery cell.
16. Rate limits and 429
The API reference documents 400, 401, 403, 404, 429, 500 and 503 response classes.
For 429:
backoff
+ jitter
+ quota metrics
+ duplicate suppression
+ short-lived cachePotential retry candidates:
429
500
503
network timeoutDo not blindly retry 400/401/403.
17. Error taxonomy
SKYSCANNER_AUTH
SKYSCANNER_FORBIDDEN
SKYSCANNER_BAD_REQUEST
SKYSCANNER_RATE_LIMIT
SKYSCANNER_SESSION_INVALID
SKYSCANNER_TIMEOUT
SKYSCANNER_UPSTREAM
SKYSCANNER_PARSE
SKYSCANNER_EMPTY_RESULT
SKYSCANNER_DEEPLINK18. Deep-link handoff
Validate the itinerary, dates, passenger context, currency, final price and mobile redirect after click.
interface FlightClickEvent {
searchId: string;
itineraryId: string;
pricingOptionId: string;
agentId: string;
displayedPrice: number;
currency: string;
clickedAt: string;
}19. Observability
Track search-session metrics:
search_created
time_to_first_result
time_to_complete
poll_count
itinerary_count
agent_count
api_429
api_5xx
empty_result
deeplink_click
deeplink_failureExample context:
{
"searchId": "srch_123",
"provider": "skyscanner",
"market": "UK",
"currency": "GBP",
"route": "LHR-SIN",
"pollCount": 4,
"providerStatus": "complete"
}20. Example SLOs
Calibrate to your own traffic and partnership limits.
create success >= 99%
time to first useful result <= product budget
poll completion >= 98%
429 rate < quota threshold
deeplink success >= 99%
search error rate < 1%21. Test matrix
| Test | Expected |
|---|---|
| one-way | one leg |
| return | two legs |
| direct | zero stops |
| multi-segment | correct segment chain |
| multiple agents | one itinerary, multiple offers |
| child passenger | age context preserved |
| business cabin | correct cabin |
| different currency | correct currency |
| create partial | partial UI |
| repeated polls | no duplicate entities |
| completed | polling stops |
| 429 | backoff |
| 401 | no blind retry |
| 503 | controlled retry |
| click handoff | itinerary preserved |
22. Go-live checklist
- API key kept server-side
- partnership access ready
- create mapper tested
- poll orchestration tested
- session state persisted
- cancellation/timeout policy defined
- itinerary/leg/segment models separated
- carrier/agent distinction correct
- pricing options normalized
- autosuggest mapping implemented
- poll merge idempotent
- 429/backoff implemented
- observability dashboard available
- deep-link tests complete
- correlation IDs available
- client disconnect stops polling
Summary
The correct integration boundary is:
Search Request
-> Skyscanner Adapter
-> create
-> partial normalize
-> poll
-> merge
-> canonical itinerary
-> pricing options
-> agent deep link
-> post-click quality measurementDeveloper success means managing the asynchronous lifecycle correctly, normalizing provider-specific entities, and preserving itinerary and price context through the provider handoff.
Idempotency and ordering
Correlate create, poll and itinerary-refresh events by provider session/token identity. Reprocessing the same poll response must not create duplicate itineraries or pricing options. Upsert incoming entities by provider-stable IDs and prevent older poll snapshots from overwriting newer refresh state.
same session + same entity revision -> idempotent upsert
older snapshot -> ignore
newer pricing state -> replace/updatePlanning a similar integration?
We can review requirements, feed/API design and the production approach with you.