Availability
Get tour availability and pricing
/v1/return_availabilityReturns availability slots and pricing for a specific product.
Response Structure: The response contains pricing_model, pricing_rates, and an
availability object. The availability object uses date strings as keys (format MM/DD/YYYY),
with each date mapping to an object containing time slots and pricing.
Important: Pricing parameters depend on the product’s pricing model.
Check the pricing_model field to determine which parameters to use:
"Per Person"— use parameter names matching the product’s pricing schemes. Supported schemes:participants,adults,children,youth,senior,students,infants. The endpoint dynamically maps whichever schemes the product has configured (visible in the response’spricing_rates); send only the schemes returned for that product. Names are case-insensitive, and+, space, and.in keys are normalized to_."Unit"— send one param per rate name shown inpricing_rates. This single value covers By Time Based (duration slots, e.g.2_hours=2), By Occupancy (room/group names, e.g.2 Participants (2+) Shared Tour=2), and By API (supplier rate names, e.g.Customer (4+ Years)=2, sometimes with&description=). Use the exact rate name, URL-encoded.
The response pricing_model is only ever "Per Person" or "Unit" — it is a
hint derived from the rate names, not one of the four internal models
(By Person Type / By Time Based / By Occupancy / By API). Always read
pricing_rates in the response to learn the exact param names a product accepts.
Omitting them, or sending names that don’t match, returns availability dates with
price: 0 (a common integration mistake).
Understanding Pricing Fields:
pricing_model- Indicates whether the product uses “Per Person” or “Unit” pricingpricing_rates(top-level) - Product-level rate definitions withmin_age/max_agerangespricing_rates(per-slot, inside each time_with_description entry) - Rate definitions withmin_quantity/max_quantitylimits plusmin_age/max_age
Partial-payment products (reserve_for): A subset of products take only a
reservation fee at booking, with the balance paid directly to the operator on
tour day. This covers both deposit-only products and products with a
min_payment override. For those products, each AvailabilityDay entry
includes a reserve_for field equal to the amount due at booking. The
remaining balance is price - reserve_for.
reserve_for is omitted entirely on full-payment products — absence of the
field means charge price in full at booking. Partners can detect deposit-only
products up front via booking_meta.deposit_only on return_tours; products
with a min_payment override may also surface reserve_for here without that
flag being set.
Maximum Range: 31 days from start date.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | Required | Product ID (e.g., “PE12031” or “12031”) |
date | string (date) | Optional | Start date for availability check (YYYY-MM-DD format). Important: This date is used for both availability checking AND pricing validation. Products with seasonal pricing that starts in the future may return $0 pricing if this parameter is omitted (defaults to current system date). Best Practice: Always provide this parameter when checking availability for future bookings to ensure accurate pricing information. |
range | integer | Optional | Number of days to check (max 31) - Default: 31 |
option_id | string | Optional | OCTO only. Supplier option identifier — same value as id on each entry under options in the OCTO product resource (GET .../products/{productId} in OCTO terms). Fetches availability for one option. Strongly recommended for multi-option OCTO products (much faster than omitting this). Non-OCTO products ignore this parameter. How to obtain the value: It is the option UUID/string returned by the supplier’s OCTO product payload. Project Expedition’s public V1 tour schemas do not yet expose a dedicated octo_options list; integrators working with multi-option OCTO products should obtain option IDs from PE integration/support or from any partner-specific metadata PE provides out-of-band. Single-option OCTO products may omit this parameter. |
currency | string | Optional | Currency for pricing - Values: USD, GBP, EUR, AUD - Default: USD |
participants | integer | Optional | Total number of participants (By Person Type pricing) |
adults | integer | Optional | Number of adults (By Person Type pricing) |
children | integer | Optional | Number of children (By Person Type pricing) |
youth | integer | Optional | Number of youth travelers (By Person Type pricing) |
senior | integer | Optional | Number of seniors (By Person Type pricing) |
students | integer | Optional | Number of students (By Person Type pricing). Use students; legacy student is unsupported. |
infants | integer | Optional | Number of infants (By Person Type pricing) |
private | boolean | Optional | Request private tour pricing |
description | string | Optional | Filter by time slot description (required for some API-priced products with multiple time slots) |
Code Examples
# PE129761 - response pricing_model "Per Person". Send one param per# scheme name in pricing_rates: adults, children, infants, youth,# senior, students, participants. Keys are case-insensitive.curl -H "access-token: {{token}}" \ "https://apistage.projectexpedition.com/v1/return_availability?id=PE129761&date=2026-09-01&range=5&adults=2&children=1¤cy=EUR"const response = await fetch('https://apistage.projectexpedition.com/v1/return_availability?id=PE129761&date=2026-09-01&range=5&adults=2&children=1¤cy=EUR', { headers: { 'access-token': 'YOUR_TOKEN' }});
const data = await response.json();<?php$context = stream_context_create([ 'http' => [ 'method' => 'GET', 'header' => "access-token: YOUR_TOKEN" ]]);
$response = file_get_contents('https://apistage.projectexpedition.com/v1/return_availability?id=PE129761&date=2026-09-01&range=5&adults=2&children=1¤cy=EUR', false, $context);$data = json_decode($response, true);# PE113598 - response pricing_model "Unit". Send the duration as# "<amount>_<units>", e.g. 2_hours (spaces normalize to "_").curl -H "access-token: {{token}}" \ "https://apistage.projectexpedition.com/v1/return_availability?id=PE113598&date=2026-09-01&range=5&2_hours=2"const response = await fetch('https://apistage.projectexpedition.com/v1/return_availability?id=PE113598&date=2026-09-01&range=5&2_hours=2', { headers: { 'access-token': 'YOUR_TOKEN' }});
const data = await response.json();<?php$context = stream_context_create([ 'http' => [ 'method' => 'GET', 'header' => "access-token: YOUR_TOKEN" ]]);
$response = file_get_contents('https://apistage.projectexpedition.com/v1/return_availability?id=PE113598&date=2026-09-01&range=5&2_hours=2', false, $context);$data = json_decode($response, true);# PE136751 - response pricing_model "Unit". Send the exact room_type# name from pricing_rates, URL-encoded:# "2 Participants (2+) Shared Tour" -> 2%20Participants%20%282%2B%29%20Shared%20Tourcurl -H "access-token: {{token}}" \ "https://apistage.projectexpedition.com/v1/return_availability?id=PE136751&date=2026-09-01&range=5&2%20Participants%20%282%2B%29%20Shared%20Tour=2"const response = await fetch('https://apistage.projectexpedition.com/v1/return_availability?id=PE136751&date=2026-09-01&range=5&2%20Participants%20%282%2B%29%20Shared%20Tour=2', { headers: { 'access-token': 'YOUR_TOKEN' }});
const data = await response.json();<?php$context = stream_context_create([ 'http' => [ 'method' => 'GET', 'header' => "access-token: YOUR_TOKEN" ]]);
$response = file_get_contents('https://apistage.projectexpedition.com/v1/return_availability?id=PE136751&date=2026-09-01&range=5&2%20Participants%20%282%2B%29%20Shared%20Tour=2', false, $context);$data = json_decode($response, true);# PE48898 - response pricing_model "Unit". Send the rate name from# pricing_rates; add &description=... when a slot lists priced descriptions.curl -H "access-token: {{token}}" \ "https://apistage.projectexpedition.com/v1/return_availability?id=PE48898&date=2026-09-01&range=5&Customer%20%284%2B%20Years%29=2"const response = await fetch('https://apistage.projectexpedition.com/v1/return_availability?id=PE48898&date=2026-09-01&range=5&Customer%20%284%2B%20Years%29=2', { headers: { 'access-token': 'YOUR_TOKEN' }});
const data = await response.json();<?php$context = stream_context_create([ 'http' => [ 'method' => 'GET', 'header' => "access-token: YOUR_TOKEN" ]]);
$response = file_get_contents('https://apistage.projectexpedition.com/v1/return_availability?id=PE48898&date=2026-09-01&range=5&Customer%20%284%2B%20Years%29=2', false, $context);$data = json_decode($response, true);Responses
200 Availability data with pricing. pricing_model and pricing_rates are top-level
fields alongside availability. The availability object contains only date-keyed
entries (format MM/DD/YYYY).
{ "pricing_model": "Per Person", "pricing_rates": [ { "pricing_rate": "Adult", "min_age": 18, "max_age": null }, { "pricing_rate": "Child", "min_age": 3, "max_age": 17 } ], "availability": { "03/15/2025": { "description": "Adult 12 hours", "currency": "EUR", "price": 65, "time": [ "7:00 am", "10:00 am" ], "time_with_description": [ { "time": "7:00 am", "description": "Morning Departure", "currency": "EUR", "price": 65, "pricing_rates": [ { "pricing_rate": "Adult", "min_age": 18, "max_age": null, "min_quantity": 1, "max_quantity": 50 }, { "pricing_rate": "Child", "min_age": 3, "max_age": 17, "min_quantity": 0, "max_quantity": 10 } ] }, { "time": "10:00 am", "description": "Afternoon Departure", "currency": "EUR", "price": 65, "pricing_rates": [ { "pricing_rate": "Adult", "min_age": 18, "max_age": null, "min_quantity": 1, "max_quantity": 50 } ] } ] }, "03/16/2025": { "description": "Adult 12 hours", "currency": "EUR", "price": 65, "time": [ "7:00 am" ], "time_with_description": [ { "time": "7:00 am", "description": "Morning Departure", "currency": "EUR", "price": 65, "pricing_rates": [ { "pricing_rate": "Adult", "min_age": 18, "max_age": null, "min_quantity": 1, "max_quantity": 50 } ] } ] } }}