Nexa — API reference
Nexa is a REST API for short-term rental availability, pricing and booking.
Authentication
Access is granted to approved AI assistants; there is nothing for you to supply.
POST /ai/v1/auth
Auth opens the session. An entry that names a source (/?source={brand}) sends that
source as the request body; the session stores it, and no later request re-supplies it. The
response is the session itself: object: "session", the session token every
later request carries, the source, an expires_at, and the first search
request. Example exchange:
POST /ai/v1/auth?idempotency_key=<key> HTTP/1.1
Host:
HTTP/1.1 200 OK
Content-Type: application/json
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 599
X-RateLimit-Reset: 1786106990
X-API-Version: 2026-08-04
X-Request-Id: req_00hdp1yz
Idempotency-Key: <key>
{
"api_version": "2026-08-04",
"endpoint": "/ai/v1/auth",
"method": "POST",
"status": 200,
"request": {
"source": "harbourside"
},
"object": "session",
"session": "<session>",
"source": "harbourside",
"expires_at": "2026-08-08T11:49:50Z",
"self_url": "/ai/v1/auth",
"next_request": {
"url": "/ai/v1/search?session=<session>&idempotency_key=<key>",
"method": "GET"
}
}
POST /ai/v1/search
A validation cycle over up to 6 required
fields: brand_locked, destination, check_in_month, check_in_day, check_out, guests .
The session's source is already in received before the first field is
sent — auth supplied it. brand_locked comes first and sets the search scope:
true limits results to the source brand, false returns them from every
brand on the Nexa network. Its two allowed_values each carry a
description of the condition that value holds under, and destination
lists no values until the scope is set, since the destination list is drawn from whatever is in
scope. Every response is HTTP 200. Until the payload is complete, the body carries
error: incomplete_payload and lists what was received, the
missing_fields, and the allowed_values for each open field. Dependent
fields (depends_on) list no values until their prerequisite is set. A complete
payload returns the result.
Example exchange (operator-scoped session):
POST /ai/v1/search?session=<session>&idempotency_key=<key> HTTP/1.1
Host:
HTTP/1.1 200 OK
Content-Type: application/json
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 599
X-RateLimit-Reset: 1786106990
X-API-Version: 2026-08-04
X-Request-Id: req_00hdp1yz
Idempotency-Key: <key>
{
"api_version": "2026-08-04",
"endpoint": "/ai/v1/search",
"method": "POST",
"status": 200,
"session": "<session>",
"error": "incomplete_payload",
"message": "Missing required field: brand_locked",
"received": {
"source": "harbourside"
},
"missing_fields": [
"brand_locked",
"destination",
"check_in_month",
"check_in_day",
"check_out",
"guests"
],
"allowed_values": {
"brand_locked": [
{
"value": true,
"description": "The user asked for this brand specifically (Harbourside Stays)."
},
{
"value": false,
"description": "The user did not ask for a specific brand."
}
],
"check_in_month": [
"2026-08",
"2026-09",
"..."
],
"guests": [
"1",
"2",
"..."
]
},
"field_order": [
"brand_locked",
"destination",
"check_in_month",
"check_in_day",
"check_out",
"guests"
],
"next_required": "brand_locked",
"depends_on": {
"destination": "brand_locked",
"check_in_day": "check_in_month",
"check_out": "check_in_day"
},
"self_url": "/ai/v1/search?session=<session>",
"schema_url": "/ai/v1/schema",
"terms_url": "/ai/v1/terms",
"terms_notice": "Use of this API is subject to its terms, which may be updated — the current version at /ai/v1/terms governs use.",
"next_request": {
"field": "brand_locked",
"value": "true",
"url": "/ai/v1/search?session=<session>&idempotency_key=<key>"
},
"available_requests": [
{
"field": "brand_locked",
"value": "true",
"url": "/ai/v1/search?session=<session>&idempotency_key=<key>"
},
{
"field": "brand_locked",
"value": "false",
"url": "/ai/v1/search?session=<session>&idempotency_key=<key>"
}
]
}
brand_locked is conditional: it exists only when the session's auth request carried a
source. A session entered directly has no brand to lock to, so its payload has no
brand_locked field at all — field_order is the remaining
5, destination is first and independent, and the search
spans every brand on the network (each result still names its brand). A
single-location storefront is narrower again: it has no destination step, because it
has one place. The order that applies to a live session is published in every search response as
field_order. Example from a direct session:
POST /ai/v1/search?session=<session>&idempotency_key=<key> HTTP/1.1
Host:
HTTP/1.1 200 OK
Content-Type: application/json
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 599
X-RateLimit-Reset: 1786106990
X-API-Version: 2026-08-04
X-Request-Id: req_00hdp1yz
Idempotency-Key: <key>
{
"api_version": "2026-08-04",
"endpoint": "/ai/v1/search",
"method": "POST",
"status": 200,
"session": "<session>",
"error": "incomplete_payload",
"message": "Missing required field: destination",
"received": {},
"missing_fields": [
"destination",
"check_in_month",
"check_in_day",
"check_out",
"guests"
],
"allowed_values": {
"destination": [
"lisbon",
"porto",
"..."
],
"check_in_month": [
"2026-08",
"2026-09",
"..."
],
"guests": [
"1",
"2",
"..."
]
},
"field_order": [
"destination",
"check_in_month",
"check_in_day",
"check_out",
"guests"
],
"next_required": "destination",
"depends_on": {
"check_in_day": "check_in_month",
"check_out": "check_in_day"
},
"self_url": "/ai/v1/search?session=<session>",
"schema_url": "/ai/v1/schema",
"terms_url": "/ai/v1/terms",
"terms_notice": "Use of this API is subject to its terms, which may be updated — the current version at /ai/v1/terms governs use.",
"next_request": {
"field": "destination",
"value": "lisbon",
"url": "/ai/v1/search?session=<session>&idempotency_key=<key>"
},
"available_requests": [
{
"field": "destination",
"value": "lisbon",
"url": "/ai/v1/search?session=<session>&idempotency_key=<key>"
},
{
"field": "destination",
"value": "porto",
"url": "/ai/v1/search?session=<session>&idempotency_key=<key>"
}
]
}
GET /ai/v1/result?session={session}
Once the payload is complete, the result endpoint returns the flow's terminal step: a raw-data
envelope — structured facts only, with the meaning of every field carried by the
machine-readable schema at GET /ai/v1/schema. query states the stay
context once
(destination, check_in, check_out, nights,
guests), and echoes brand_locked on an operator-scoped session.
comparison_set is one complete comparison set — all options_count
options belong together, each described by comparison_fields (the ordered field paths
that constitute the comparison); the set is the response — with total_matches,
sort (total_price ascending, and rank on each option is its
position), currency, totals_include_taxes_and_fees (totals are final,
including all taxes and fees), quotes_valid_until, and user_facing_urls —
the URL fields meant for the user. Two more are ours, which the reference surface has no
pagination to declare: has_more says another page exists, and truncated
says the set publishes a total_matches it cannot walk you through.
Each option carries rank, property_id, name,
brand, location, capacity (whose summary is the
spec line its listing page shows), rating, cancellation,
availability, images (primary plus a gallery),
one url — the user-facing link to this property on the operator's website, carrying
the stay parameters: the user's booking path — and its quote:
amount, nightly_average, currency, the stay it prices, and
line_items alongside line_items_available, which is
false when the operator reconciled no breakdown at source.
Keys the search layer does not publish are present and null rather than omitted, so a
documented key is always where an agent looks for it: amenities,
capacity.bedrooms, capacity.bathrooms,
rating.review_count, cancellation.free_cancellation_until,
check_in_time, check_out_time and all_results_url. So are
quote.signature and quote.verify_url: quote signing and an offline
verification path are not on this surface, and the keys say so rather than being absent. The one
optional next step is the amenities refine in available_requests. The result is
re-readable: GET /ai/v1/result?session={session} is idempotent and returns this
envelope for the session's full life — session_expires_at, aligned with
quotes_valid_until. Example exchange:
GET /ai/v1/result?session=<session>&idempotency_key=<key> HTTP/1.1
Host:
HTTP/1.1 200 OK
Content-Type: application/json
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 599
X-RateLimit-Reset: 1786106990
X-API-Version: 2026-08-04
X-Request-Id: req_00hdp1yz
Idempotency-Key: <key>
{
"api_version": "2026-08-04",
"object": "search_result",
"status": 200,
"session": "<session>",
"session_expires_at": "2026-08-08T11:49:50Z",
"access": {
"audience": "ai_assistants",
"end_user_access": false
},
"query": {
"brand_locked": true,
"destination": "lisbon",
"check_in": "2026-09-12",
"check_out": "2026-09-15",
"nights": 3,
"guests": 2
},
"comparison_set": {
"options_count": 2,
"total_matches": 12,
"sort": {
"by": "total_price",
"order": "asc"
},
"comparison_fields": [
"rank",
"name",
"brand",
"quote.amount",
"quote.nightly_average",
"capacity.max_guests",
"rating.score",
"cancellation.summary",
"url"
],
"currency": "EUR",
"totals_include_taxes_and_fees": true,
"quotes_valid_until": "2026-08-08T11:49:50Z",
"user_facing_urls": [
"results[].url",
"all_results_url"
],
"has_more": false,
"truncated": true
},
"applied_filters": {
"amenities": []
},
"results": [
{
"rank": 1,
"property_id": "lis-40118",
"name": "Harbour View Studio",
"brand": "Harbourside Stays",
"location": "Lisbon, Portugal",
"capacity": {
"max_guests": 2,
"bedrooms": null,
"bathrooms": null,
"summary": "Studio · 1 bathroom"
},
"amenities": null,
"rating": {
"score": 4.6,
"scale": 5,
"review_count": null
},
"cancellation": {
"summary": "Free cancellation up to 14 days before check-in",
"free_cancellation_until": null
},
"check_in_time": null,
"check_out_time": null,
"images": {
"primary": {
"url": "https://harbourside.example/media/40118-01.jpg",
"alt": "Harbour View Studio",
"type": null
},
"gallery": [
{
"url": "https://harbourside.example/media/40118-02.jpg",
"alt": "Harbour View Studio",
"type": null
}
]
},
"availability": {
"status": "available",
"booking_mode": "instant",
"inventory_source": "pms"
},
"url": "https://harbourside.example/listing/40118?checkin=2026-09-12&checkout=2026-09-15&guests=2&source=nexa",
"quote": {
"nxq": "nxq_b31d0a4c7e9",
"amount": 612,
"nightly_average": 204,
"currency": "EUR",
"check_in": "2026-09-12",
"check_out": "2026-09-15",
"guests": 2,
"nights": 3,
"line_items": [
{
"label": "Accommodation",
"amount": 540
},
{
"label": "Cleaning fee",
"amount": 45
},
{
"label": "Taxes",
"amount": 27
}
],
"line_items_available": true,
"price_valid_until": "2026-08-08T11:49:50Z",
"retrieved_at": "2026-08-07T11:49:50Z",
"signature": null,
"verify_url": null
}
},
{
"rank": 2,
"property_id": "lis-40233",
"name": "Old Town Two-Bedroom",
"brand": "Harbourside Stays",
"location": "Lisbon, Portugal",
"capacity": {
"max_guests": 2,
"bedrooms": null,
"bathrooms": null,
"summary": "2 bedrooms · 1 bathroom"
},
"amenities": null,
"rating": {
"score": 4.4,
"scale": 5,
"review_count": null
},
"cancellation": {
"summary": "Free cancellation up to 14 days before check-in",
"free_cancellation_until": null
},
"check_in_time": null,
"check_out_time": null,
"images": {
"primary": {
"url": "https://harbourside.example/media/40233-01.jpg",
"alt": "Old Town Two-Bedroom",
"type": null
},
"gallery": [
{
"url": "https://harbourside.example/media/40233-02.jpg",
"alt": "Old Town Two-Bedroom",
"type": null
}
]
},
"availability": {
"status": "available",
"booking_mode": "instant",
"inventory_source": "pms"
},
"url": "https://harbourside.example/listing/40233?checkin=2026-09-12&checkout=2026-09-15&guests=2&source=nexa",
"quote": {
"nxq": "nxq_c07f21a5d38",
"amount": 738.5,
"nightly_average": 246.17,
"currency": "EUR",
"check_in": "2026-09-12",
"check_out": "2026-09-15",
"guests": 2,
"nights": 3,
"line_items": [
{
"label": "Accommodation",
"amount": 651.5
},
{
"label": "Cleaning fee",
"amount": 45
},
{
"label": "Taxes",
"amount": 42
}
],
"line_items_available": true,
"price_valid_until": "2026-08-08T11:49:50Z",
"retrieved_at": "2026-08-07T11:49:50Z",
"signature": null,
"verify_url": null
}
}
],
"available_requests": {
"refine": {
"method": "POST",
"path": "/ai/v1/search",
"body_field": "amenities",
"required": false,
"href": "/ai/v1/search?session=<session>&idempotency_key=<key>"
}
},
"all_results_url": null,
"self_url": "/ai/v1/result?session=<session>",
"terms_url": "/ai/v1/terms",
"notes": "Public catalogue pages are indicative only. Every price above is a live quote retrieved from the operator's booking system at the retrieved_at time shown, and holds until price_valid_until."
}
Access model
/ai/v1 URLs are an authenticated, agent-only surface: /ai/v1 URLs require assistant
authentication; end users cannot open them. Never pass Nexa URLs to users. User-facing
links are exactly the fields listed in comparison_set.user_facing_urls —
operator-website URLs: each option's url and the all-results link. Every result
envelope states the same facts as data:
access: { audience: "ai_assistants", end_user_access: false }.
Nexa is an information layer for assistants; booking is completed on the operator's
website.
Field semantics
Definitions of the comparison set's fields:
- Results are ranked by total price ascending;
rankis the supplied order (rank 1 is the lowest stay total). quote.amountis the final price for the entire stay and all requested guests, incomparison_set.currency— including all taxes and fees.quote.nightly_averageis the derived per-night average, andquote.line_itemsis the operator's own breakdown where it reconciled one;quote.line_items_availablesays whether it did.quote.price_valid_untilis how long the quote holds, andquote.retrieved_atwhen it was fetched from the operator's booking system. Catalogue prices elsewhere are indicative; these are live.urlis the user-facing link to the property on the operator's website, carrying the stay parameters — the user's booking path. One option, one user-facing link; complete and used as returned.images.primaryis intended for direct user display; gallery images carryaltandtype.rating.scoreis the property's guest rating out ofrating.scale— the same figure the operator's listing page shows.cancellation.summaryis the operator's cancellation policy verbatim.availabilitystates the operator'sstatus, itsbooking_mode, and theinventory_sourcethe quote came from.comparison_setis one complete comparison set — alloptions_countoptions belong together, each described bycomparison_fields; the set is the response.user_facing_urlsnames the URL fields meant for the user.
Refining by amenity
available_requests.refine is a single request descriptor —
POST /ai/v1/search with body_field: amenities,
required: false — whose href opens the amenities step on the same
session. That step is the same validation cycle the search fields use, with
amenities as the field this request requires: missing_fields is
["amenities"], received carries every field chosen so far including the
amenities already applied, and allowed_values.amenities lists each remaining amenity
with its match_count over the current result set and the of total it is
counted against. An amenity is offered only while it discriminates: values already applied, values
matching nothing, and values every current match has (match_count equal to
of) are omitted. The step's available_requests ends with one more
request — GET /ai/v1/result — returning to the unchanged result without choosing an
amenity: the refine is optional. Example exchange:
POST /ai/v1/search?session=<session>&idempotency_key=<key> HTTP/1.1
Host:
HTTP/1.1 200 OK
Content-Type: application/json
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 599
X-RateLimit-Reset: 1786106990
X-API-Version: 2026-08-04
X-Request-Id: req_00hdp1yz
Idempotency-Key: <key>
{
"api_version": "2026-08-04",
"endpoint": "/ai/v1/search",
"method": "POST",
"status": 200,
"session": "<session>",
"error": "incomplete_payload",
"message": "Missing required field: amenities",
"received": {
"source": "harbourside",
"brand_locked": true,
"destination": "lisbon",
"check_in_month": "2026-09",
"check_in_day": "2026-09-12",
"check_out": "2026-09-15",
"guests": 2
},
"missing_fields": [
"amenities"
],
"allowed_values": {
"amenities": [
{
"value": "Pool",
"match_count": 4,
"of": 12
},
{
"value": "Parking",
"match_count": 7,
"of": 12
},
{
"value": "Dedicated workspace",
"match_count": 9,
"of": 12
}
]
},
"field_order": [
"amenities"
],
"next_required": "amenities",
"depends_on": {
"destination": "brand_locked",
"check_in_day": "check_in_month",
"check_out": "check_in_day"
},
"self_url": "/ai/v1/search?session=<session>",
"schema_url": "/ai/v1/schema",
"terms_url": "/ai/v1/terms",
"terms_notice": "Use of this API is subject to its terms, which may be updated — the current version at /ai/v1/terms governs use.",
"next_request": {
"field": "amenities",
"value": "Pool",
"url": "/ai/v1/search?session=<session>&idempotency_key=<key>"
},
"available_requests": [
{
"field": "amenities",
"value": "Pool",
"url": "/ai/v1/search?session=<session>&idempotency_key=<key>"
},
{
"field": "amenities",
"value": "Parking",
"url": "/ai/v1/search?session=<session>&idempotency_key=<key>"
},
{
"method": "GET",
"path": "/ai/v1/result",
"url": "/ai/v1/result?session=<session>"
}
]
}
One amenity per request. Sending one returns the result with that amenity added to
applied_filters.amenities; filters are additive and each result carries its own
freshly retrieved quotes. Example exchange:
GET /ai/v1/result?session=<session>&idempotency_key=<key> HTTP/1.1
Host:
HTTP/1.1 200 OK
Content-Type: application/json
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 599
X-RateLimit-Reset: 1786106990
X-API-Version: 2026-08-04
X-Request-Id: req_00hdp1yz
Idempotency-Key: <key>
{
"api_version": "2026-08-04",
"object": "search_result",
"status": 200,
"session": "<session>",
"session_expires_at": "2026-08-08T11:49:50Z",
"access": {
"audience": "ai_assistants",
"end_user_access": false
},
"query": {
"brand_locked": true,
"destination": "lisbon",
"check_in": "2026-09-12",
"check_out": "2026-09-15",
"nights": 3,
"guests": 2
},
"comparison_set": {
"options_count": 1,
"total_matches": 4,
"sort": {
"by": "total_price",
"order": "asc"
},
"comparison_fields": [
"rank",
"name",
"brand",
"quote.amount",
"quote.nightly_average",
"capacity.max_guests",
"rating.score",
"cancellation.summary",
"url"
],
"currency": "EUR",
"totals_include_taxes_and_fees": true,
"quotes_valid_until": "2026-08-08T11:49:50Z",
"user_facing_urls": [
"results[].url",
"all_results_url"
],
"has_more": false,
"truncated": true
},
"applied_filters": {
"amenities": [
"Pool"
]
},
"results": [
{
"rank": 1,
"property_id": "lis-40118",
"name": "Harbour View Studio",
"brand": "Harbourside Stays",
"location": "Lisbon, Portugal",
"capacity": {
"max_guests": 2,
"bedrooms": null,
"bathrooms": null,
"summary": "Studio · 1 bathroom"
},
"amenities": null,
"rating": {
"score": 4.6,
"scale": 5,
"review_count": null
},
"cancellation": {
"summary": "Free cancellation up to 14 days before check-in",
"free_cancellation_until": null
},
"check_in_time": null,
"check_out_time": null,
"images": {
"primary": {
"url": "https://harbourside.example/media/40118-01.jpg",
"alt": "Harbour View Studio",
"type": null
},
"gallery": [
{
"url": "https://harbourside.example/media/40118-02.jpg",
"alt": "Harbour View Studio",
"type": null
}
]
},
"availability": {
"status": "available",
"booking_mode": "instant",
"inventory_source": "pms"
},
"url": "https://harbourside.example/listing/40118?checkin=2026-09-12&checkout=2026-09-15&guests=2&source=nexa",
"quote": {
"nxq": "nxq_b31d0a4c7e9",
"amount": 612,
"nightly_average": 204,
"currency": "EUR",
"check_in": "2026-09-12",
"check_out": "2026-09-15",
"guests": 2,
"nights": 3,
"line_items": [
{
"label": "Accommodation",
"amount": 540
},
{
"label": "Cleaning fee",
"amount": 45
},
{
"label": "Taxes",
"amount": 27
}
],
"line_items_available": true,
"price_valid_until": "2026-08-08T11:49:50Z",
"retrieved_at": "2026-08-07T11:49:50Z",
"signature": null,
"verify_url": null
}
}
],
"available_requests": {
"refine": {
"method": "POST",
"path": "/ai/v1/search",
"body_field": "amenities",
"required": false,
"href": "/ai/v1/search?session=<session>&idempotency_key=<key>"
}
},
"all_results_url": null,
"self_url": "/ai/v1/result?session=<session>",
"terms_url": "/ai/v1/terms",
"notes": "Public catalogue pages are indicative only. Every price above is a live quote retrieved from the operator's booking system at the retrieved_at time shown, and holds until price_valid_until."
}
The step can be re-entered from the narrowed result to stack another amenity: counts are
recomputed over the smaller set and the applied amenity is gone from allowed_values.
When nothing discriminating is left, available_requests.refine is absent. Example
exchange:
POST /ai/v1/search?session=<session>&idempotency_key=<key> HTTP/1.1
Host:
HTTP/1.1 200 OK
Content-Type: application/json
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 599
X-RateLimit-Reset: 1786106990
X-API-Version: 2026-08-04
X-Request-Id: req_00hdp1yz
Idempotency-Key: <key>
{
"api_version": "2026-08-04",
"endpoint": "/ai/v1/search",
"method": "POST",
"status": 200,
"session": "<session>",
"error": "incomplete_payload",
"message": "Missing required field: amenities",
"received": {
"source": "harbourside",
"brand_locked": true,
"destination": "lisbon",
"check_in_month": "2026-09",
"check_in_day": "2026-09-12",
"check_out": "2026-09-15",
"guests": 2,
"amenities": [
"Pool"
]
},
"missing_fields": [
"amenities"
],
"allowed_values": {
"amenities": [
{
"value": "Parking",
"match_count": 2,
"of": 4
}
]
},
"field_order": [
"amenities"
],
"next_required": "amenities",
"depends_on": {
"destination": "brand_locked",
"check_in_day": "check_in_month",
"check_out": "check_in_day"
},
"self_url": "/ai/v1/search?session=<session>",
"schema_url": "/ai/v1/schema",
"terms_url": "/ai/v1/terms",
"terms_notice": "Use of this API is subject to its terms, which may be updated — the current version at /ai/v1/terms governs use.",
"next_request": {
"field": "amenities",
"value": "Parking",
"url": "/ai/v1/search?session=<session>&idempotency_key=<key>"
},
"available_requests": [
{
"field": "amenities",
"value": "Parking",
"url": "/ai/v1/search?session=<session>&idempotency_key=<key>"
},
{
"method": "GET",
"path": "/ai/v1/result",
"url": "/ai/v1/result?session=<session>"
}
]
}
Brand scope
brand_locked is a real filter over inventory, not a label: at true the
destination list, the result set, total_matches and every amenity count come from the
source brand alone; at false they come from every brand on the network.
query echoes brand_locked on an operator-scoped session, and each
property names the brand operating it, whose site its url points at. The
example below is the same session shape at brand_locked: false, at a destination the
source brand does not operate:
GET /ai/v1/result?session=<session>&idempotency_key=<key> HTTP/1.1
Host:
HTTP/1.1 200 OK
Content-Type: application/json
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 599
X-RateLimit-Reset: 1786106990
X-API-Version: 2026-08-04
X-Request-Id: req_00hdp1yz
Idempotency-Key: <key>
{
"api_version": "2026-08-04",
"object": "search_result",
"status": 200,
"session": "<session>",
"session_expires_at": "2026-08-08T11:49:50Z",
"access": {
"audience": "ai_assistants",
"end_user_access": false
},
"query": {
"destination": "porto",
"check_in": "2026-09-12",
"check_out": "2026-09-15",
"nights": 3,
"guests": 2
},
"comparison_set": {
"options_count": 2,
"total_matches": 31,
"sort": {
"by": "total_price",
"order": "asc"
},
"comparison_fields": [
"rank",
"name",
"brand",
"quote.amount",
"quote.nightly_average",
"capacity.max_guests",
"rating.score",
"cancellation.summary",
"url"
],
"currency": "EUR",
"totals_include_taxes_and_fees": true,
"quotes_valid_until": "2026-08-08T11:49:50Z",
"user_facing_urls": [
"results[].url",
"all_results_url"
],
"has_more": false,
"truncated": true
},
"applied_filters": {
"amenities": []
},
"results": [
{
"rank": 1,
"property_id": "lis-40118",
"name": "Riverside Loft",
"brand": "Douro Riverside",
"location": "Lisbon, Portugal",
"capacity": {
"max_guests": 2,
"bedrooms": null,
"bathrooms": null,
"summary": "Studio · 1 bathroom"
},
"amenities": null,
"rating": {
"score": 4.6,
"scale": 5,
"review_count": null
},
"cancellation": {
"summary": "Free cancellation up to 14 days before check-in",
"free_cancellation_until": null
},
"check_in_time": null,
"check_out_time": null,
"images": {
"primary": {
"url": "https://harbourside.example/media/40118-01.jpg",
"alt": "Harbour View Studio",
"type": null
},
"gallery": [
{
"url": "https://harbourside.example/media/40118-02.jpg",
"alt": "Harbour View Studio",
"type": null
}
]
},
"availability": {
"status": "available",
"booking_mode": "instant",
"inventory_source": "pms"
},
"url": "https://douroriverside.example/listing/40118?checkin=2026-09-12&checkout=2026-09-15&guests=2&source=nexa",
"quote": {
"nxq": "nxq_b31d0a4c7e9",
"amount": 612,
"nightly_average": 204,
"currency": "EUR",
"check_in": "2026-09-12",
"check_out": "2026-09-15",
"guests": 2,
"nights": 3,
"line_items": [
{
"label": "Accommodation",
"amount": 540
},
{
"label": "Cleaning fee",
"amount": 45
},
{
"label": "Taxes",
"amount": 27
}
],
"line_items_available": true,
"price_valid_until": "2026-08-08T11:49:50Z",
"retrieved_at": "2026-08-07T11:49:50Z",
"signature": null,
"verify_url": null
}
},
{
"rank": 2,
"property_id": "lis-40233",
"name": "Old Town Two-Bedroom",
"brand": "Harbourside Stays",
"location": "Lisbon, Portugal",
"capacity": {
"max_guests": 2,
"bedrooms": null,
"bathrooms": null,
"summary": "2 bedrooms · 1 bathroom"
},
"amenities": null,
"rating": {
"score": 4.4,
"scale": 5,
"review_count": null
},
"cancellation": {
"summary": "Free cancellation up to 14 days before check-in",
"free_cancellation_until": null
},
"check_in_time": null,
"check_out_time": null,
"images": {
"primary": {
"url": "https://harbourside.example/media/40233-01.jpg",
"alt": "Old Town Two-Bedroom",
"type": null
},
"gallery": [
{
"url": "https://harbourside.example/media/40233-02.jpg",
"alt": "Old Town Two-Bedroom",
"type": null
}
]
},
"availability": {
"status": "available",
"booking_mode": "instant",
"inventory_source": "pms"
},
"url": "https://harbourside.example/listing/40233?checkin=2026-09-12&checkout=2026-09-15&guests=2&source=nexa",
"quote": {
"nxq": "nxq_c07f21a5d38",
"amount": 738.5,
"nightly_average": 246.17,
"currency": "EUR",
"check_in": "2026-09-12",
"check_out": "2026-09-15",
"guests": 2,
"nights": 3,
"line_items": [
{
"label": "Accommodation",
"amount": 651.5
},
{
"label": "Cleaning fee",
"amount": 45
},
{
"label": "Taxes",
"amount": 42
}
],
"line_items_available": true,
"price_valid_until": "2026-08-08T11:49:50Z",
"retrieved_at": "2026-08-07T11:49:50Z",
"signature": null,
"verify_url": null
}
}
],
"available_requests": {
"refine": {
"method": "POST",
"path": "/ai/v1/search",
"body_field": "amenities",
"required": false,
"href": "/ai/v1/search?session=<session>&idempotency_key=<key>"
}
},
"all_results_url": null,
"self_url": "/ai/v1/result?session=<session>",
"terms_url": "/ai/v1/terms",
"notes": "Public catalogue pages are indicative only. Every price above is a live quote retrieved from the operator's booking system at the retrieved_at time shown, and holds until price_valid_until."
}
Machine-readable schema
GET /ai/v1/schema describes the payload fields, allowed formats, field dependencies
and error codes.
Terms of use
Terms of use — purpose, what may be shared, and what is proprietary.