Getting Offers
Once you've found a journey, the next step is getting pricing options, what we call offers. An offer represents a specific way to travel that journey, with a price, fare type, and service level.
What is an Offer?
An offer is a pricing option for a journey. Each offer includes:
- Price: The total cost for your passengers
- Fare type: How flexible the ticket is (can you change or cancel?)
- Service class: The level of comfort (standard, first class, etc.)
- Reservations: Whether seat reservations are included or required
A single journey typically has multiple offers, giving travelers choices between price, flexibility, and comfort. For example:
- A low-cost, non-refundable option
- A flexible, refundable option (usually more expensive)
- A first-class option with guaranteed seats
Getting Offers for a Journey
Use the journeyOffer query (or subscription) to get pricing for a journey. You'll need:
- The journey ID (from your search results)
- Passenger information (number and types of travelers)
You can include up to five passengers in a single offer request. This works with all train operators.
Renamed from getJourneyOffer
journeyOffer replaces getJourneyOffer, which is deprecated but keeps working. The journey ID argument is called journeyId on the new field, where the old one called it journey. Nothing else changed.
Use subscriptions for better UX
Getting offers can take up to 30 seconds because train operators have slow systems. Use WebSocket subscriptions to stream results as they arrive. This lets you show progress to users and display offers incrementally. See Subscriptions for details.
Example: Get offers for a journey
graphql
subscription JourneyOffer {
journeyOffer(
journeyId: "97e85d9e-5aed-4981-bbc4-4616174d55f5"
passengers: [{ type: ADULT }]
) {
status
itinerary {
__typename
... on SegmentCollection {
status
segments {
departureAt
origin {
name
countryCode
}
}
offers {
id
price {
amount
currency
}
parts {
... on AdmissionPart {
conditions {
__typename
description
... on ReturnCondition {
type
timeframe
}
... on ValidityCondition {
validFrom
validUntil
breakOfJourney
}
... on TravelCondition {
type
url
}
}
flexibility
serviceClass
comfortClass
}
... on ReservationPart {
conditions {
__typename
description
... on ReturnCondition {
type
timeframe
}
}
flexibility
comfortClass
accommodation {
type
}
}
}
}
}
... on Stopover {
location {
name
countryCode
}
}
}
}
}subscription JourneyOffer {
journeyOffer(
journeyId: "bf8d9e47-90cc-4ea2-8470-0ad2111c75b8"
passengers: [{ type: ADULT }]
) {
status
itinerary {
__typename
... on SegmentCollection {
status
segments {
departureAt
origin {
name
countryCode
}
}
offers {
id
price {
amount
currency
}
parts {
... on AdmissionPart {
conditions {
__typename
description
... on ReturnCondition {
type
timeframe
}
... on ValidityCondition {
validFrom
validUntil
breakOfJourney
}
... on TravelCondition {
type
url
}
}
flexibility
serviceClass
comfortClass
}
... on ReservationPart {
conditions {
__typename
description
... on ReturnCondition {
type
timeframe
}
}
flexibility
comfortClass
accommodation {
type
}
}
}
}
}
... on Stopover {
location {
name
countryCode
}
}
}
}
}Round Trips
Some operators sell a genuine return ticket that is cheaper than two singles. They will only quote it if they are asked for both directions at once, so a round trip has to be priced in one call rather than two.
Use combinedJourneyOffer (or its subscription) for this. It takes one journey ID per direction, from two ordinary journeys searches in opposite directions, plus the passengers who are travelling both ways:
Example: Price an outbound and an inbound journey together
graphql
subscription CombinedJourneyOffer {
combinedJourneyOffer(
outboundJourneyId: "bca22f65-ce7f-4418-bcea-9f9b42c797cb"
inboundJourneyId: "91eab254-f629-450a-908d-7226c1a61e88"
passengers: [{ type: ADULT }]
) {
outbound {
status
itinerary {
... on SegmentCollection {
status
offers {
id
price {
amount
currency
}
memberOf {
id
}
}
}
}
}
inbound {
status
itinerary {
... on SegmentCollection {
status
offers {
id
price {
amount
currency
}
memberOf {
id
}
}
}
}
}
}
}subscription CombinedJourneyOffer {
combinedJourneyOffer(
outboundJourneyId: "a0f6ebf0-0562-443e-a533-328b14860f3f"
inboundJourneyId: "b2be9ca5-8fd1-43af-a347-b129518db8c8"
passengers: [{ type: ADULT }]
) {
outbound {
status
itinerary {
... on SegmentCollection {
status
offers {
id
price {
amount
currency
}
memberOf {
id
}
}
}
}
}
inbound {
status
itinerary {
... on SegmentCollection {
status
offers {
id
price {
amount
currency
}
memberOf {
id
}
}
}
}
}
}
}You get back an outbound and an inbound journey offer. Each one is exactly what journeyOffer would have returned for that direction, so anything you already do with a one-way offer works unchanged on either side.
What the round trip buys you shows up in the prices. Where the traveller retraces their outward route, and the operator sells a return fare for it, that fare replaces the two one-way prices for those parts of the journey. Everywhere else — an outbound and inbound that take different routes, or an operator with no return fare to sell — you get the same prices as two separate journeyOffer calls. Either way the call succeeds; there is nothing to detect and no fallback to write.
A return fare is one ticket, priced on one direction
A return fare arrives as a full-price offer on one direction and a companion offer costing nothing on the other. The two are one ticket and are bookable only together, so an offer at no cost here is not a free add-on. Both directions arrive in the same response, so you can match them up: offers sharing a memberOf.id belong to the same set, and you select an offer from that set for every part of the journey it covers, or nothing from it at all. Here that means both halves of the return, or neither. See Offer Sets.
Book the round trip as a single booking: pass the offer IDs you selected from both directions to createBooking in one offerIds list. Two bookings, one per direction, cannot express a return fare — one booking has to cover everything the set covers.
Pricing two directions takes at least as long as pricing one, so prefer the subscription here even more than for a single journey. Each direction streams independently, and a payload arrives whenever either side has something new.
Understanding Offer Parts
Offers consist of parts that describe what's included. The two main types are:
Admission Parts
The right to travel: the basic ticket. This includes:
- Flexibility: Can you change or cancel? (
FULL_FLEX,SEMI_FLEX,NON_FLEX) - Service class: Standard, high-speed, premium, etc.
- Comfort class: First class, second class, etc.
Reservation Parts
A reserved seat or accommodation. This includes:
- Flexibility: Same as admission parts
- Comfort class: The class of the reserved seat
- Accommodation: Type of reservation (seat, couchette, sleeper, etc.)
Most offers include both admission (the right to travel) and reservation (a specific seat). Some journeys only require admission. You can sit anywhere in your class.
Ticket Conditions
Both kinds of part carry conditions: everything the operator stated about the ticket. Every entry has a description — complete, human-readable text that is always safe to show on its own — and a __typename telling you which kind of rule it is:
ReturnCondition— a refund or exchange rule.typesays which of the two it is, orUNKNOWNwhen the operator stated a rule without saying which;timeframesays when it applies —ALWAYS,BEFORE_DEPARTURE,NEVER(never refundable or exchangeable) orUNKNOWN.ValidityCondition— the dates the ticket may be used on.validFromandvalidUntilare inclusive calendar dates in the operator's local time zone, with no time of day;breakOfJourneysays whether the passenger may stop partway and continue later, and isnullwhen the operator did not say.TravelCondition— another rule of use.typesays which:ROUTE(the ticket is tied to particular services or a particular way round),TRAIN_BOUND(only valid on the booked train) orRESTRICTION(limits on the times of day it may be used).urllinks to the page with the full wording when there is one.
Select description bare on Condition, and the fields of each kind through an inline fragment on that kind:
graphql
conditions {
__typename
description
... on ReturnCondition {
type
timeframe
}
... on ValidityCondition {
validFrom
validUntil
breakOfJourney
}
... on TravelCondition {
type
url
}
}conditions {
__typename
description
... on ReturnCondition {
type
timeframe
}
... on ValidityCondition {
validFrom
validUntil
breakOfJourney
}
... on TravelCondition {
type
url
}
}The list holds only what the operator stated. A missing kind means they did not say, never that the rule does not apply: a ticket with no ValidityCondition is not thereby valid indefinitely, it is a ticket whose validity dates the operator left unstated.
New kinds may appear
More kinds of condition may be added over time. Render description for any __typename you do not recognise rather than dropping the entry.
Passenger Requirements
When requesting offers, provide passenger information:
- For adults: Age or birth date is optional
- For youth/seniors: Age or birth date is required (for discount eligibility)
- For card holders: Attach discount cards and rail passes with
travelCards, see Travel Cards
The API uses this information to calculate correct pricing and ensure passengers qualify for age-based discounts.
Offer Availability
Finding a journey doesn't guarantee offers are available. The API checks real-time availability with train operators, and sometimes:
- Tickets aren't on sale yet (too far in advance)
- Tickets are sold out (popular routes, last-minute bookings)
- The exact journey isn't available (but similar alternatives might be)
When offers are available, they may have slight timing variations from your original journey search. The overall route stays the same, but departure times might shift slightly. The API ensures:
- Safe connection times between trains
- The same comfort level
- The same overall itinerary
Choosing Which Journey to Price
When you search for journeys, you'll get multiple options for the same day. Which one should you price? Here are some strategies:
Filter by Journey Type
If you know what type of journey your users want, filter during the search:
SMART(default): Most comfortable, may include overnight stopsNON_STOP: Fastest route, but may be less comfortableBLUEPRINT: Pre-defined expert itineraries
See Finding Journeys for details on filtering.
Use Journey Ratings
The API includes a rating system that scores journeys based on travel time, comfort, connection quality, and more. Ratings range from 0 to 1, where 1 is the best possible journey.
Use getJourneyRatings to compare multiple journeys and pick the highest-rated option:
Example: Compare journey ratings
graphql
query GetJourneyRatings {
getJourneyRatings(
journeys: [
"f0badf2c-4cb7-4616-945f-e42a002f2bce"
"0033b383-2051-43d5-9457-af9447450f9c"
"2a94e8ed-abb0-4065-b86c-e2ff1af4c2bd"
]
)
}query GetJourneyRatings {
getJourneyRatings(
journeys: [
"d64de4bb-1caf-4e4f-b017-caed0712d4ed"
"14f67a96-98c8-44c4-bd38-3e98982e8c0d"
"48b401e1-0349-4d6a-b633-91be95afcf1a"
]
)
}Let Users Choose
For user-facing applications, show multiple journey options and let users pick. Display:
- Departure and arrival times
- Number of changes
- Total travel time
- Journey type (if helpful)
Then price the journey the user selects.
Responsible API usage
The All Aboard API operates on fair use principles. Only request offers for journeys you're reasonably confident users will book. See API Usage and Look-to-Book for details on our fair use policy.
Next Steps
Once you have offers, the next step is creating a booking. See Booking Tickets to learn how to:
- Create a booking from an offer
- Add passenger details
- Select specific offers and seat preferences
- Complete the purchase