Skyscanner Flight API Entegrasyonu: Developer Rehberi
Skyscanner Flights Live Prices API entegrasyonunu x-api-key auth, create/poll lifecycle, request-response modelleri, itinerary/leg/segment mapping, agent/pricing option, rate limit, polling ve observability ile developer gözüyle uygulayın.
- 2026-09-26 — Provider companion standard applied; rate limits, refresh-price lifecycle, pagination and evidence boundaries clarified.
Skyscanner flight entegrasyonunda temel mimari karar, upstream response'u doğrudan UI modeli yapmak yerine async search session + canonical flight model + provider handoff katmanları oluşturmaktır.
Skyscanner'ın güncel Flights Live Prices akışı iki endpoint üzerine kuruludur:
POST /flights/live/search/create
|
v
initial / partial results
+ sessionToken
|
v
POST /flights/live/search/poll/{sessionToken}
|
v
progressively richer results
|
v
completedBu yapı klasik tek-request/tek-response API gibi ele alınmamalıdır.
Provider companion özeti
| Alan | Durum |
|---|---|
| Erişim | Onaylı 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 search lifecycle paginated collection değildir; sonuçlar session polling ile olgunlaşır |
| Polling | Contract'ın temel parçasıdır; create sonrası poll gerekir |
| Public default rate limits | Flights Live Pricing Create: 100/sn ve 100/dk; Poll: 100/sn ve 500/dk. Partner agreement farklı olabilir |
| Reprice/refresh | itineraryrefresh/create + itineraryrefresh/poll ile selected itinerary için güncel price refresh |
| Booking ownership | Pricing option/deeplink sonrası downstream airline/OTA |
| Evidence level | Official public docs reviewed; partner account ile canlı test iddiası yok |
| 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 ve downstream booking aynı state machine değildir.
Pagination neden N/A?
Flights Live Prices response modeli klasik page=2 collection pagination değildir. Sonuç completeness'i aynı search session üzerinde create/poll lifecycle ile ilerler. Bu nedenle generic pagination implementasyonu eklemeyin; session progress ve merge semantics'i yönetin.
Resmi rate limit sınırları
Skyscanner'ın güncel public Rate Limits sayfasındaki standard Flights değerleri:
| API | Per second | Per minute |
|---|---|---|
| Flights Live Pricing — Create | 100 | 100 |
| Flights Live Pricing — Poll | 100 | 500 |
| Flights Indicative Prices | 100 | 500 |
Bu değerler default limitlerdir. Skyscanner limitlerin API key / partner ihtiyacına göre özelleştirilebildiğini açıkça belirtir. Bu nedenle runtime'da 429 metriği ve account-specific quota bilgisi source of truth olmalıdır.
Price refresh / reprice lifecycle
İlk live-search pricing option'ını checkout truth gibi kabul etmeyin. Skyscanner seçilen itinerary için ayrı refresh flow dokümante eder:
search/create
-> search/poll
-> user selects itinerary
-> itineraryrefresh/create
-> itineraryrefresh/poll
-> refreshed pricing options
-> agent deeplinkRefresh sonucu değişirse UI eski fiyatı confirmed state gibi göstermemelidir. Internal modelde:
SEARCH_PRICE
REFRESHING
REFRESHED
PRICE_CHANGED
HANDOFF_READYgibi explicit state kullanmak daha güvenlidir.
Platform sorumlulukları, ticari sınırlar ve operasyon kararları için Skyscanner profiline bakın. Bu rehber uygulama sözleşmelerini ve request/response işlemlerini ele alır.
1. Erişim ve authentication
Skyscanner API'lerine erişim için partnership başvurusu ve onaylanmış API key gerekir.
Authentication header:
x-api-key: <your-api-key>API key client-side JavaScript'e veya public repo'ya konmamalıdır.
Önerilen boundary:
interface SkyscannerCredentials {
apiKeyRef: string;
}Frontend:
Browser
-> Your backend
-> Skyscanner APIDoğrudan browser -> Skyscanner çağrısı yapmayın.
2. Flights Live Prices create endpoint
Endpoint:
POST https://partners.api.skyscanner.net/apiservices/v3/flights/live/search/create
Content-Type: application/json
x-api-key: <api-key>Minimal örnek request:
{
"query": {
"market": "TR",
"locale": "tr-TR",
"currency": "TRY",
"queryLegs": [
{
"originPlaceId": {
"iata": "IST"
},
"destinationPlaceId": {
"iata": "LHR"
},
"date": {
"year": 2026,
"month": 10,
"day": 12
}
}
],
"adults": 1,
"cabinClass": "CABIN_CLASS_ECONOMY"
}
}Skyscanner dokümantasyonunda create request için temel alanlar:
- market,
- locale,
- currency,
- queryLegs,
- adults.
Ek olarak child ages, include/exclude carrier/agent, sustainability, nearby-airports ve baggage gibi seçenekler bulunabilir.
3. Internal search request modeli
Provider contract'ını domain modeliniz yapmayın.
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";
}Adapter:
Internal FlightSearchRequest
|
v
Skyscanner request mapper
|
v
Skyscanner create payloadBöylece provider enum'ları ürün domain'inize yayılmaz.
4. create response nasıl ele alınmalı?
Skyscanner create çağrısı ilk sonuçları ve sessionToken döndürür. Dokümantasyona göre create sonucu hızlı time-to-first-result amacıyla incomplete/cached subset olabilir.
Conceptual response:
{
"sessionToken": "session-token",
"status": "RESULT_STATUS_INCOMPLETE",
"content": {
"results": {
"itineraries": {
"itinerary-1": {
"pricingOptions": []
}
}
}
}
}Buradaki önemli nokta:
create başarılı oldu = search tamamlandı demek değildir.
Internal state:
type SearchStatus =
| "created"
| "partial"
| "polling"
| "complete"
| "failed"
| "expired";5. poll endpoint
Endpoint:
POST https://partners.api.skyscanner.net/apiservices/v3/flights/live/search/poll/{sessionToken}
x-api-key: <api-key>Request body gerektirmeyen poll çağrısı session token üzerinden devam eder.
Internal session:
interface FlightSearchSession {
id: string;
provider: "skyscanner";
providerSessionToken: string;
requestHash: string;
status: SearchStatus;
createdAt: string;
lastPolledAt?: string;
completedAt?: string;
pollCount: number;
}6. Polling strategy
Fixed 250ms loop gibi agresif polling yapmayın.
Örnek:
poll 1 -> 300 ms
poll 2 -> 500 ms
poll 3 -> 800 ms
poll 4 -> 1200 ms
poll 5+ -> 1500-2000 msStop conditions:
- API status completed,
- client disconnect,
- maximum search duration,
- unrecoverable provider error,
- expired/invalid session.
Örnek policy:
interface PollPolicy {
maxDurationMs: number;
maxPollCount: number;
initialDelayMs: number;
maxDelayMs: number;
}7. Progressive results
Live flight search'te ilk sonuç ile mature result set arasında fark vardır.
Frontend state:
interface FlightSearchViewState {
status: "searching" | "partial" | "complete" | "error";
itineraries: FlightItinerary[];
lastUpdatedAt: string;
}UI:
create
-> first results render
-> poll
-> merge/deduplicate
-> rerank
-> render
-> poll
-> completeTüm ekranı spinner'da tutmak yerine usable first result erken gösterilebilir.
8. Canonical flight domain modeli
En az şu entity'leri ayırın:
Itinerary
-> Leg
-> Segment
-> Carrier
Itinerary
-> PricingOption
-> Agent
-> DeepLinkItinerary
interface FlightItinerary {
id: string;
legIds: string[];
pricingOptions: FlightPricingOption[];
score?: number;
}Leg
interface FlightLeg {
id: string;
origin: string;
destination: string;
departure: string;
arrival: string;
durationMinutes: number;
segmentIds: string[];
stopCount: number;
}Segment
interface FlightSegment {
id: string;
origin: string;
destination: string;
departure: string;
arrival: string;
marketingCarrierId?: string;
operatingCarrierId?: string;
flightNumber?: string;
}9. Carrier ve Agent aynı şey değildir
Carrier uçuşu market eden veya operate eden airline'dır.
Agent ise itinerary'yi satan booking provider olabilir.
interface FlightAgent {
id: string;
name: string;
type?: "airline" | "ota";
rating?: number;
}Aynı itinerary farklı agent'larda:
- farklı fiyat,
- farklı baggage,
- farklı booking fee,
- farklı payment option,
- farklı deep link
ile bulunabilir.
10. Pricing option modeli
interface FlightPricingOption {
id: string;
agentIds: string[];
price: {
amount: number;
currency: string;
};
deepLink?: string;
transferType?: string;
farePolicy?: string;
}Important:
itinerary != offerTek itinerary'nin birden fazla seller/price alternatifi olabilir.
11. Response normalization
Skyscanner response'u çoğunlukla normalized entities + ID references şeklinde okunmalıdır.
Önerilen pipeline:
Skyscanner JSON
|
v
Provider DTO
|
v
Entity dictionaries
|
+--> itineraries
+--> legs
+--> segments
+--> carriers
+--> agents
|
v
Canonical flight modelFrontend provider DTO bilmemeli.
12. Search result merge
Poll sonucunda önceki itinerary'ler tekrar gelebilir veya pricing option değişebilir.
Blind append yapmayın.
function mergeSearchResults(
current: FlightSearchResult,
incoming: FlightSearchResult
): FlightSearchResult {
// upsert entities by provider-stable id
// replace/update pricing options
// preserve newest provider state
return current;
}Merge key:
- provider itinerary ID,
- leg/segment IDs,
- agent + price option identity.
13. Autosuggest entegrasyonu
Flight search input'unda free-text'i doğrudan IATA'ya çevirmeye çalışmayın.
Skyscanner Flights Autosuggest endpoint:
POST https://partners.api.skyscanner.net/apiservices/v3/autosuggest/flightsÖrnek:
{
"query": {
"market": "TR",
"locale": "tr-TR",
"searchTerm": "Istanbul",
"includedEntityTypes": [
"PLACE_TYPE_CITY",
"PLACE_TYPE_AIRPORT"
]
},
"limit": 10,
"isDestination": false
}Internal place:
interface FlightPlace {
id: string;
entityId?: string;
iata?: string;
name: string;
type: "airport" | "city" | "country";
}14. Cache stratejisi
Live search response'u booking-intent data'dır.
Cache key en az:
origin
+ destination
+ dates
+ adults
+ child ages
+ cabin
+ market
+ locale
+ currencyCache'in amacı:
- duplicate request suppression,
- burst control,
- short-lived UX acceleration.
Ama eski live price'ı booking truth gibi kullanmayın.
15. Indicative Prices boundary
Discovery/flexible-date use case'ini Live Prices'tan ayırın.
Discovery
-> indicative/aggregated pricing
Booking intent
-> Flights Live PricesAylık calendar ekranındaki her hücre için live create/poll yapmak hem quota hem latency açısından yanlış olabilir.
16. Rate limit ve 429
API reference şu response sınıflarını dokümante eder:
- 400 Bad Request,
- 401 Unauthorized,
- 403 Forbidden,
- 404 Not Found,
- 429 Too Many Requests,
- 500 Internal Server Error,
- 503 Service Unavailable.
429 için:
do not hammer retry
-> exponential backoff
-> jitter
-> quota metric
-> request suppression/cacheÖrnek:
interface RetryPolicy {
retryable: number[];
maxAttempts: number;
baseDelayMs: number;
maxDelayMs: number;
}Retryable adaylar:
429
500
503
network timeout400/401/403 genellikle blind retry edilmemelidir.
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_DEEPLINKProvider error'u UI mesajı haline doğrudan çevirmeyin.
18. Deep link handoff
Pricing option içindeki booking/deep link, kullanıcıyı downstream provider'a taşır.
Kontrol edin:
- itinerary aynı mı,
- dates aynı mı,
- passenger context korunuyor mu,
- currency tutarlı mı,
- final price aynı mı,
- mobile redirect doğru mu.
Internal handoff event:
interface FlightClickEvent {
searchId: string;
itineraryId: string;
pricingOptionId: string;
agentId: string;
displayedPrice: number;
currency: string;
clickedAt: string;
}19. Observability
Search-session bazlı metrik tutun.
search_created
time_to_first_result
time_to_complete
poll_count
itinerary_count
agent_count
api_429
api_5xx
empty_result
deeplink_click
deeplink_failureLog context:
{
"searchId": "srch_123",
"provider": "skyscanner",
"market": "TR",
"currency": "TRY",
"route": "IST-LHR",
"pollCount": 4,
"providerStatus": "complete"
}20. SLO önerileri
Kendi trafiğinize göre kalibre edin.
create success >= 99%
time to first useful result <= product budget
poll completion >= 98%
429 rate < agreed quota threshold
deeplink success >= 99%
search error rate < 1%21. Test matrisi
| Test | Beklenen |
|---|---|
| one-way | tek leg |
| return | iki leg |
| direct | 0 stop |
| multi-segment | doğru segment chain |
| multiple agents | aynı itinerary, farklı offers |
| child passenger | age context korunuyor |
| business cabin | doğru cabin |
| different currency | doğru price currency |
| first create partial | UI partial gösteriyor |
| multiple polls | merge duplicate üretmiyor |
| completed status | polling duruyor |
| 429 | backoff |
| 401 | retry yok / credential alert |
| provider 503 | controlled retry |
| click handoff | itinerary korunuyor |
22. Go-live checklist
- API key server-side
- partnership/access hazır
- create request mapper test edildi
- poll orchestration test edildi
- session state persisted
- cancellation/timeout policy var
- itinerary/leg/segment model ayrı
- carrier/agent ayrımı doğru
- pricing option normalize
- autosuggest place mapping hazır
- poll merge idempotent
- 429/backoff uygulanmış
- observability dashboard hazır
- deep-link testleri tamam
- search/session correlation ID var
- client disconnect polling'i durduruyor
Sonuç
Skyscanner Flights API'yi yalnız endpoint entegrasyonu olarak kurmak eksik kalır.
Doğru boundary:
Search Request
-> Skyscanner Adapter
-> create
-> partial normalize
-> poll
-> merge
-> canonical itinerary
-> pricing options
-> agent deep link
-> post-click quality measurementDeveloper açısından başarı, yalnız JSON response almak değil; async search lifecycle'ını doğru yönetmek, provider-specific entity'leri normalize etmek ve kullanıcıyı tutarlı itinerary/price context'iyle booking provider'a taşımaktır.
Idempotency ve ordering
Search create, poll ve itinerary refresh event'lerini provider session/token identity ile correlate edin. Aynı poll response'un tekrar işlenmesi duplicate itinerary veya pricing option üretmemelidir. Incoming entity state provider-stable ID ile upsert edilmeli; eski poll snapshot'ı daha yeni refresh sonucunu ezmemelidir.
same session + same entity revision -> idempotent upsert
older snapshot -> ignore
newer pricing state -> replace/updateBenzer bir entegrasyon mu planlıyorsunuz?
Gereksinim, feed/API tasarımı ve production yaklaşımını birlikte değerlendirebiliriz.