POST /api/v1/hotels/search_advanced
Search hotels by geo-radius or a specific hotel ID, with live availability and indicative pricing. Supports streaming (SSE) for large result sets.
Search for hotels — either around a geographic point (geo search) or for one specific hotel (hotel-ID search) — with live availability and pricing.
Permission: Read
Either latitude+longitude or id is required — not both. Use geo search for browsing a
destination, and hotel-ID search when you already know the exact hotel (e.g. from
name-search or a saved favorite).
Request body
| Field | Type | Required | Description |
|---|---|---|---|
checkin | string | Yes | Check-in date, YYYY-MM-DD |
checkout | string | Yes | Check-out date, YYYY-MM-DD |
guests | array | Yes | Room occupancy — see Guests & rooms |
latitude | float | For geo search | Latitude, from locations/search |
longitude | float | For geo search | Longitude, from locations/search |
radius | integer | No | Search radius in meters. Default 15000. |
id | string | For hotel-ID search | A known hotel ID |
residency | string | No | Guest residency country code. Default "gb". |
language | string | No | Response language. Default "en". |
Do not send a currency field — it returns 400. Currency is always resolved from your
authenticated agency.
Request examples
curl --request POST \
--url https://api.fx-port.com/api/v1/hotels/search_advanced \
--header 'Authorization: Bearer fxp_test_YOUR_KEY' \
--header 'Content-Type: application/json' \
--data '{
"checkin": "2026-11-15",
"checkout": "2026-11-18",
"residency": "gb",
"language": "en",
"guests": [{ "adults": 2, "children": [] }],
"latitude": 48.8566,
"longitude": 2.3522,
"radius": 15000
}'Streaming (SSE)
For large result sets, enable streaming to receive hotels in chunks of 25 as they're ready, instead of waiting for the full response:
POST /api/v1/hotels/search_advanced?stream=trueOr set header Accept: text/event-stream. Standard (non-streaming) is the default and is
recommended for most integrations.
Response example
{
"environment": "sandbox",
"search_id": "45af62b6-a305-48e5-b934-64910cd75f7b",
"status": "complete",
"hotels_details": [
{
"id": "hotel_paris_001",
"name": "Hotel Example Paris",
"address": "123 Rue de Rivoli, Paris",
"starRating": 4,
"images": ["https://cdn.worldota.net/t/{size}/example.jpg"],
"amenities": ["wifi", "parking", "pool"],
"latitude": 48.8566,
"longitude": 2.3522,
"room": {
"name": "Superior Double",
"beddingType": "double-bed",
"capacity": 2
},
"rate": {
"matchHash": "m-abc123",
"meal": { "type": "breakfast", "included": true },
"dailyPrices": [120.50, 120.50, 135.00],
"allotment": 5,
"cancellationPolicy": {
"freeCancellationBefore": "2026-11-13T23:59:59"
}
},
"pricing": {
"supplierCurrency": "EUR",
"agencyCurrency": "DZD",
"originalPrice": 376.00,
"agencyCommission": 2937.69,
"totalPrice": 61691.45,
"b2bPrice": 58753.76
}
}
],
"processing_status": {
"total_hotels": 45,
"cached_hotels": 40,
"queued_hotels": 5
},
"timing": {
"search_duration": 1.23,
"total_duration": 2.45
}
}Field reference
| Field | Description |
|---|---|
search_id | Internal search identifier — useful for support requests |
status | complete, no_hotels_found, or error |
hotels_details[] | One entry per hotel, with the best matching room/rate for your search |
hotels_details[].rate | The cheapest/best matching rate for a quick overview — call get_rates for the full rate list on a chosen hotel |
pricing | See Hotel pricing for every field |
processing_status | How many hotels were served from cache vs. queued for a live supplier fetch |
hotels_details[].rate is a single representative rate, not the full inventory. Always call
get_rates with the chosen id before booking, to see every room/rate
combination and cancellation policy.