Fetching listing descriptions and details

Pull rich listing copy — descriptions, house rules, check-in times, bed counts, Wi-Fi credentials — in the same call you already use for your catalog. No second round-trip, no per-platform scraping.

Why this exists

Most apps that render a property-detail page need more than the name and a thumbnail. They need the public description, the space and neighbourhood blurbs, house rules, and the structural details a guest expects on a listing page — bedrooms, bed count, capacity, check-in times, Wi-Fi network, house manual.

Without these expansions, the only way to assemble that view was to call back into Airbnb (or whichever channel held the canonical text) on every page render. That's a rate-limited round-trip per listing per request, and it ties your front-end to whichever upstream is up.

With ?include=content and ?include=details, Repull serves both blocks straight from the synced data already in your workspace. One call, one bearer token, no upstream coupling.

Already using ?include=amenities?

These two new keys compose with it. ?include=amenities,content,details returns everything in a single response — see Combining includes.

The two new include keys

Both keys work on GET /v1/listings/{id} and GET /v1/listings. They're additive: each one drops a new object onto the listing payload. Neither is loaded by default — the base listing response stays small.

include=content

Adds a content object with the long-form listing copy. English locale by default.

content.summarystringnullable

Short marketing summary shown at the top of the listing.

content.descriptionstringnullable

The main listing description — the long block of copy guests read first.

content.spacestringnullable

‘The space’ section — what's inside the property, how it's laid out.

content.guestAccessstringnullable

What parts of the property the guest has access to.

content.neighborhoodOverviewstringnullable

Description of the surrounding area.

content.gettingAroundstringnullable

Transit, parking, and getting-around notes.

content.transitstringnullable

Public transit specifics, where applicable.

content.houseRulesstringnullable

House rules guests agree to at booking.

content.additionalRulesstringnullable

Extra rules beyond the standard set.

content.notesstringnullable

Free-form host notes shown on the listing.

content.interactionWithGuestsstringnullable

How and when the host interacts with guests during the stay.

content: null vs field absent

If the listing has never had a description row synced, content is returned as null — not omitted. That's deliberate: it tells you the workspace has no copy yet, instead of leaving you guessing whether you forgot the include. If you don't pass ?include=content at all, the field is absent from the response.

include=details

Adds a detailsobject with the structural and operational fields you'd render on a listing page or pass to a guest pre-arrival.

details.propertyTypestringnullable

Specific property type — e.g. ‘Condominium’, ‘Cabin’, ‘Townhouse’.

details.propertyTypeCategorystringnullable

Top-level category — e.g. ‘House’, ‘Apartment’, ‘Unique stay’.

details.roomTypeCategorystringnullable

Room arrangement — ‘Entire place’, ‘Private room’, ‘Shared room’.

details.bedroomsintegernullable

Number of bedrooms.

details.bathroomsnumbernullable

Number of bathrooms (may be fractional, e.g. 1.5).

details.bedsintegernullable

Total bed count across all bedrooms.

details.personCapacityintegernullable

Maximum number of guests.

details.checkInTimeStartstringnullable

Earliest check-in time, 24h format (e.g. ‘15:00’).

details.checkInTimeEndstringnullable

Latest check-in time, 24h format.

details.checkOutTimestringnullable

Check-out time, 24h format.

details.minNightsintegernullable

Minimum stay length in nights.

details.maxNightsintegernullable

Maximum stay length in nights.

details.advanceBookingDaysintegernullable

How far in advance guests can book.

details.turnoverDaysintegernullable

Mandatory gap days between bookings.

details.wifiNetworkstringnullable

Wi-Fi network name — for use in pre-arrival messages.

details.wifiPasswordstringnullable

Wi-Fi password — for use in pre-arrival messages.

details.houseManualstringnullable

Free-form house manual / welcome book content.

details.directionsstringnullable

Directions to the property.

details.propertySizenumbernullable

Property size (square metres or square feet, depending on locale).

details.yearBuiltintegernullable

Year the property was built.

details.numberOfFloorsintegernullable

Total floors in the building.

details.listingFloorintegernullable

Which floor the listing is on.

Wi-Fi credentials are sensitive

wifiNetwork and wifiPasswordare intended for back-end use — pre-arrival messages, lock-box codes, host dashboards. Don't render them on a public listing page.

Fetch a listing with both expansions

GET/v1/listings/{id}?include=content,details
curl 'https://api.repull.dev/v1/listings/6248?include=content,details' \
  -H "Authorization: Bearer sk_test_YOUR_KEY"

Response shape

{
  "data": {
    "id": "6248",
    "name": "Oceanview Villa",
    "publicName": "Stunning Oceanview Villa — Private Pool & Beach Access",
    "status": "ACTIVE",
    "content": {
      "summary": "Modern 3-bed villa with ocean views and a private pool.",
      "description": "Wake up to the sound of waves...",
      "space": "Open-plan living/dining/kitchen on the main floor...",
      "guestAccess": "You have full access to the entire villa, the pool deck, and the private beach path.",
      "neighborhoodOverview": "Quiet residential street, 5 minutes' walk from the beach...",
      "gettingAround": "A car is recommended. Free off-street parking for two vehicles.",
      "transit": null,
      "houseRules": "No parties. Quiet hours 22:00–08:00.",
      "additionalRules": "No smoking anywhere on the property.",
      "notes": null,
      "interactionWithGuests": "I'm reachable any time via the Repull app and respond within an hour."
    },
    "details": {
      "propertyType": "Villa",
      "propertyTypeCategory": "House",
      "roomTypeCategory": "Entire place",
      "bedrooms": 3,
      "bathrooms": 2.5,
      "beds": 4,
      "personCapacity": 8,
      "checkInTimeStart": "15:00",
      "checkInTimeEnd": "22:00",
      "checkOutTime": "11:00",
      "minNights": 2,
      "maxNights": 30,
      "advanceBookingDays": 365,
      "turnoverDays": 1,
      "wifiNetwork": "VillaGuest",
      "wifiPassword": "beach2026",
      "houseManual": "Welcome! Trash collection is Tuesday morning...",
      "directions": "From PCH, turn left onto Beach Rd...",
      "propertySize": 220,
      "yearBuilt": 2014,
      "numberOfFloors": 2,
      "listingFloor": 1
    }
  }
}

SDK examples

// @repull/sdk@^0.2.5
import Repull from '@repull/sdk';

const repull = new Repull({ apiKey: 'sk_test_YOUR_KEY' });

const listing = await repull.listings.get('6248', {
  include: ['content', 'details'],
});

console.log(listing.content?.description);
console.log(`${listing.details?.bedrooms} bed / ${listing.details?.bathrooms} bath`);
console.log(`Check-in ${listing.details?.checkInTimeStart}–${listing.details?.checkInTimeEnd}`);

// Or list all listings in one go
const { data } = await repull.listings.list({
  include: ['content', 'details'],
  limit: 50,
});
for (const item of data) {
  console.log(item.publicName, '—', item.details?.personCapacity, 'guests');
}

Combining with ?include=amenities

include is multi-value — pass any combination of amenities, content, and details as a comma-separated list. The response merges all requested expansions onto each listing.

curl 'https://api.repull.dev/v1/listings/6248?include=amenities,content,details' \
  -H "Authorization: Bearer sk_test_YOUR_KEY"

Pass exactly the keys your view needs — if a particular page only shows the description and bed count, ask for content,details and skip amenities. Unused expansions still cost a few extra fields per listing on the wire.

null vs missing field

The two states mean different things and your code should treat them differently:

StateMeans
content: {...}You asked for ?include=content and the listing has copy synced. Individual sub-fields can still be null if that section was never filled in.
content: nullYou asked for ?include=content, but no description row exists for this listing yet (e.g. the listing was just created and hasn't been pushed to a channel).
content not presentYou did not pass ?include=content. The expansion is opt-in.

The same rule applies to details. Treat nullas “asked, no data” and missing as “didn't ask”.

Unknown include values return 422

Anything other than amenities, content, or details is rejected at the edge with a structured error envelope. The response tells you exactly what to change — copy the fix message into a log line and your callers will know what to do.

curl 'https://api.repull.dev/v1/listings/6248?include=photos' \
  -H "Authorization: Bearer sk_test_YOUR_KEY"

# HTTP/1.1 422 Unprocessable Entity
{
  "code": "invalid_params",
  "fix": "Supported values: amenities, content, details. Pass multiple comma-separated, e.g. `?include=amenities`.",
  "valid_values": ["amenities", "content", "details"]
}

See errors/invalid_params for the general error contract. The fixfield is human-readable and stable — safe to surface verbatim to a developer or LLM agent.

Related

AI