---
title: "Get canonical property record"
method: GET
path: "/properties/{property_id}"
---

# Get canonical property record

`GET /properties/{property_id}`

## Request

- Base URL: `https://api.b2a.bluepillow.com/v1`
- URL: `https://api.b2a.bluepillow.com/v1/properties/{property_id}`
- Auth: HTTP bearer

## Path parameters

- `property_id` string, required

## Query parameters

- `include` string: Comma-separated enrichments: reviews_aggregate (score + total count + per-OTA distribution), reviews_sample (up to 5 recent review texts), reviews_extended (up to 20 recent review texts; supersedes reviews_sample). Review texts carry a reviews_meta block declaring the deliberate cap, and are returned only when no dates are provided.
- `user_country` string
- `language` string
- `currency` string

## Headers

- `B2A-Version` string, date

## Response `200`

Property found

- Property
  - `id` string, required
  - `name` string, required
  - `brand` string, nullable
  - `chain` string, nullable
  - `property_type` 'hotel' | 'aparthotel' | 'apartment' | 'villa' | 'bnb' | 'hostel' | 'resort', required
  - `rating` number, required
  - `stars` integer, nullable
  - `location` LocationObject, required
    - `lat` number, required
    - `lon` number, required
    - `address` string, required
    - `city` string, required
    - `country` string, required
    - `neighborhood` string, nullable
  - `amenities` string[], required
  - `images` ImageObject[]
    - `url` string, uri, required
    - `caption` string, nullable
  - `web_url` string, nullable: BluePillow property-page URL with the caller's search context (check-in/out, language, currency, party size) baked into query params. Links directly to the BluePillow listing page, not an OTA. None when the search context is too sparse to build a meaningful URL (e.g. single-property lookup without dates).
  - `description` string, nullable: Free-text property description from the canonical AccomodationIndex doc. Populated by the single-property endpoint; null when not available or on the search path.
  - `features` FeatureItem[]: Feature/facility tags from BP's AccomodationFeature list (e.g. non-smoking, wifi, pool). Populated by the single-property endpoint; empty on the search path (search-result payloads do not carry this field).
    - `code` string, required
    - `type` string, nullable
    - `name` string, nullable
  - `reviews_aggregate` ReviewsAggregate: rating_count and comment_count are two DISTINCT magnitudes — do not conflate. rating_count = reviews backing the score (OTA-published aggregate, e.g. 156). comment_count = readable review TEXTS available (the crawled subset, e.g. 53), fetchable via the reviews_sample / reviews_extended includes.
    - `score_0_5` number, required
    - `rating_count` integer, required
    - `comment_count` integer, nullable
    - `distribution_by_ota` object
  - `reviews_sample` ReviewSampleItem[], nullable: Recent review texts. On search, populated by include=reviews_sample (cap 5). On property detail, populated by include=reviews_sample (cap 5) or include=reviews_extended (cap 20). null when not requested; empty when requested but no review texts are available. See reviews_meta for the cap.
    - `score` number, required
    - `description` string, required
    - `language` string, nullable
    - `date` string, date-time, required
    - `connector_name` string, nullable
  - `reviews_meta` ReviewsMeta: Provenance for the reviews_sample list on property detail. Makes explicit that the list is deliberately capped (token efficiency), not a data limit: the aggregate rating already reflects ALL reviews, and the omitted texts are older.
    - `returned` integer, required
    - `total_available` integer, required
    - `capped` boolean, required
    - `cap` integer, required
    - `note` string, required
  - `ota_coverage` OTA[], required
  - `dedup_meta` DedupMeta
    - `source_listings` integer, required
    - `confidence` number, required
    - `last_reconciled` string, date-time, required
  - `offers` Offer[]: Live per-OTA offer list from BluePillow GET /search, sorted best-first. Populated only when the BP bridge is active and dates/guests were provided. Empty when no live data is available (Mongo fallback path).
    - `ota` string, required: Connector name (e.g. 'Booking', 'Agoda') as reported by BluePillow.
    - `amount` number, required
    - `amount_per_night` number, nullable
    - `currency` string, required
    - `breakfast_included` boolean
    - `refundable` boolean
    - `is_discounted` boolean
    - `discount_percentage` number, nullable
    - `deeplink_url` string, nullable: BluePillow skippy redirect URL (tracked redirect to the OTA booking page). Opens the OTA landing page for this property with dates/guests pre-filled, routing through BluePillow's tracking layer. Not a direct OTA link. None when BP did not return a URL for this offer.
    - `rooms_left` integer, nullable: Number of rooms remaining at this rate per BluePillow's live-availability fan-out. A low value (1-3) is a scarcity signal. Populated from /search when BP reports it; None when not available.
  - `price` Price
    - `amount` number, required
    - `amount_per_night` number, nullable
    - `currency` string, required
    - `breakfast_included` boolean
    - `refundable` boolean
    - `is_discounted` boolean
    - `discount_percentage` number, nullable
    - `ota` string, nullable
  - `availability_status` 'available' | 'unavailable' | 'unknown': Live availability state: available = BP confirmed bookable rooms; unavailable = BP reported no rooms for the requested dates; unknown = dates not provided or live fan-out was skipped.

## Other responses

- `401`: Auth missing or invalid
- `404`: Property not found
- `429`: Rate limited

---

[API](https://skmtc.dev/bluepillow/apis/b2a-api.md) · [All operations](https://skmtc.dev/bluepillow/apis/b2a-api/llms.txt) · [OpenAPI document](https://skmtc.dev/bluepillow/apis/b2a-api/revisions/1dfd82be92b6?raw)
