Properties

Properties represent vacation rental listings. Each property has an address, location, pricing, and custom fields. Only active properties are readable. An inactive property is not billed and keeps syncing, but every endpoint answers `403 listing_inactive` for it (and for its reservations, conversations, reviews and availability) until it is activated with `POST /v1/listings/status`. List endpoints leave inactive properties out unless you pass `?status=inactive` or `?status=all`. See https://repull.dev/docs/manage-listings.

List all properties in the account

GET/v1/properties

Request

curl https://api.repull.dev/v1/properties?limit=50 \
  -H "Authorization: Bearer $REPULL_API_KEY"

Response

{
  "data": [
    {
      "id": "123",
      "name": "Example",
      "address": "Example text",
      "city": "Example text",
      "currency": "USD",
      "status": "active",
      "lifecycleStatus": "live",
      "channels": [],
      "updatedAt": "2026-06-01T12:00:00.000Z"
    }
  ],
  "pagination": {
    "nextCursor": "eyJpZCI6MTIzfQ==",
    "hasMore": true,
    "total": 0
  }
}

Captured from a real response. Regenerated on every release, and CI fails if this shape stops matching the API.

Query Parameters

limitinteger

Page size (max 100). Requests over the cap return 422.

cursorstring

Opaque cursor returned in the previous response's `pagination.nextCursor`. Omit to fetch the first page.

offsetinteger

First-class alias for cursor-based pagination. Mutually exclusive with `cursor` — passing both returns 422. Accepts integers in `[0, 10000]`; deeper walks must use `cursor` (constant per-page cost). The response always includes `pagination.nextCursor` so consumers can switch from offset → cursor mid-walk for deep pagination without re-keying.

qstring

Case-insensitive substring search on name, street, or city.

statusstring

Filter by status. Default returns active only; pass `inactive` to invert or `all` to include both. Inactive properties carry identity fields only — `id`, `name`, `status`, `lifecycleStatus`, `channels` and `updatedAt` — never `address`, `city` or `currency`.

lifecycle_statusstring

Filter by lifecycle status (e.g. `live`, `draft`, `archived`). Pass `all` to disable the filter.

channelstring

Filter to properties with an active link on the given OTA/channel (airbnb, booking, vrbo). Omit to include every channel. Each property also returns a `channels` array listing the OTAs it is published on.

updated_sincedate

Incremental sync: return only records whose `updatedAt` is at or after this instant. This is the only filter on record **mutation** time — every `check_*` filter targets guest **stay** dates. **Accepted formats.** ISO 8601, with `Z` or a numeric offset — both work: - `2026-08-01T00:00:00Z` - `2026-08-01T00:00:00.123Z` - `2026-08-01T00:00:00+00:00` - `2026-08-01T02:30:00-07:00` (offset colon optional: `-0700`) - `2026-08-01T00:00` (seconds optional) - `2026-08-01T00:00:00` — no zone designator, interpreted as **UTC** - `2026-08-01` — date only, means midnight UTC Anything else returns 422 `invalid_params` naming the field; the value is never silently ignored. **Ordering changes when you pass this.** Results are ordered `updatedAt ASC, id ASC` (instead of the endpoint default) and the cursor keys on the same pair. That is required for correctness: under the default ordering a record amended mid-walk can move behind the cursor and never be emitted — which is exactly the event you are polling for. Ascending mutation time is monotonic with the cursor, so anything touched during a walk resurfaces later in it or on the next poll. **Cursors are not interchangeable between the two orderings.** Keep `updated_since` on every page of an incremental walk; replaying a cursor from the other ordering returns 422 rather than a page that silently skips rows. **Watermark.** The bound is inclusive (`updatedAt >= value`), so the last row of the final page is the watermark for the next poll — re-polling with it re-emits that row. Delivery is at-least-once; upsert by `id`.

include_totalboolean

When `true` (default), the response's `pagination.total` carries the count of rows matching the current filter, across all pages. Pass `false` to skip the count for very large workspaces where the per-page COUNT(*) cost matters.

Get a single property by ID

GET/v1/properties/:id

Request

curl https://api.repull.dev/v1/properties/123 \
  -H "Authorization: Bearer $REPULL_API_KEY"

Response

{
  "id": "123",
  "name": "Example",
  "address": "Example text",
  "city": "Example text",
  "latitude": "Example text",
  "longitude": "Example text",
  "currency": "USD",
  "status": "active",
  "lifecycleStatus": "live",
  "createdAt": "2026-06-01T12:00:00.000Z"
}

Captured from a real response. Regenerated on every release, and CI fails if this shape stops matching the API.

AI