Skip to content

Compsets

  • GET /api/v1/compsets/ — list the comp sets you can see (paginated summary).
  • GET /api/v1/compsets/<id>/ — get one comp set, fully enriched.
  • Scope: compsets:read

Try it out

Explore schemas and make live requests in the Swagger UI.

What is a compset?

A compset (Competitive Set) is a group of comparable nearby listings used to benchmark a property's pricing and performance. Beyond surfaces the competitive set for a listing — its members are comparable nearby properties drawn from the channels Beyond tracks — together with the headline KPIs (posted rate, occupancy, minimum stay) operators use to position their property in the local market.

Compset kinds

Every comp set carries a kind discriminator so you can tell the types apart — some fields are only populated for some kinds:

kind Description
connected Anchored to a Beyond listing (whether that listing's comp set was user-curated or system-generated).
custom Built against a typed-in custom listing, with no Beyond listing. Beta.

Beta

The custom kind is a beta feature. Its response shape and behavior may change without notice, and it is not yet covered by backward-compatibility guarantees.

For a custom comp set (no Beyond listing) the source.min-stay, source.base-price, source.health-score, and source.market fields are null, the whole performance.main-listing-metrics block is null, and source.currency falls back to the user's billing currency.

List comp sets

GET /api/v1/compsets/

Returns a paginated list of summary entries — cheap fields only (no metric or member enrichment) — enough to choose which comp set to fetch in detail. Each entry has its kind, title, listing-id, total member-count, and timestamps.

  • Pagination: page[number] and page[size] (max 100).
  • Filtering: filter[owner]=<user_id> (owning user), filter[listing]=<id> (Beyond listing id), filter[kind]=custom|connected.
  • Sorting: sort=created-at (default -created-at, newest first) or sort=title; prefix with - for descending.
GET /api/v1/compsets/?filter[kind]=connected&sort=title&page[size]=50

filter[owner]

filter[owner] narrows the list to a single user. It is only meaningful for full-access partner tokens; a user-scoped token already sees just its own user, so the filter has no additional effect.

Response

{
  "data": [
    {
      "type": "compsets",
      "id": "55",
      "attributes": {
        "listing-id": 123,
        "kind": "connected",
        "title": "Sunny 2BR near the beach",
        "member-count": 8,
        "created-at": "2026-06-10T14:22:00Z",
        "updated-at": "2026-08-01T09:00:00Z"
      },
      "relationships": {
        "owner": {"data": {"type": "users", "id": "456"}},
        "listing": {"data": {"type": "listings", "id": "123"}}
      }
    }
  ],
  "meta": {
    "pagination": {"page": 1, "pages": 1, "count": 1}
  }
}

Compound Documents (Sideloading)

Include related resources in a single request using the ?include= parameter:

  • owner -- the user who owns the comp set
  • listing -- the Beyond listing the comp set is anchored to (null for custom comp sets)
GET /api/v1/compsets/?include=owner,listing

This avoids separate API calls to fetch the owner or listing. See the JSON:API guide for details on how compound documents work.

Get a comp set

GET /api/v1/compsets/<id>/

Returns one comp set by its id, with the base listing anchor (source), its headline metrics over the next 30 and 90 days, the curated members[] enriched with per-member attributes and metrics, and a performance comparison against the aggregated compset benchmark — enough to render a full side-by-side comparison in a single round trip.

Returns 404 when no comp set with that id is visible to your application.

Stricter rate limit

This is a heavy endpoint, so it carries a stricter limit of 30 requests/minute per user/application. Exceeding it returns 429; cache responses and avoid tight polling loops.

Performance comparison — start_date / end_date

The response always includes a performance block: the listing's metrics compared to the aggregated compset benchmark for the requested date range (posted rate, occupancy, minimum stay, booked rate, adjusted occupancy). Control the window with the start_date and end_date query parameters (defaults: today → today + 90 days, max 180-day range). This is the heaviest part of the query — clients backing real-time UIs should cache results.

Response

Abbreviated to one member; real responses carry every member and the full metric key set described under Notable Fields:

{
  "data": {
    "type": "compsets",
    "id": "55",
    "attributes": {
      "listing-id": 123,
      "kind": "connected",
      "title": "Sunny 2BR near the beach",
      "matched-airbnb-id": "987654321",
      "created-at": "2026-06-10T14:22:00Z",
      "updated-at": "2026-08-01T09:00:00Z",
      "source": {
        "bedrooms": 2,
        "bathrooms": 1.5,
        "latitude": 37.7749,
        "longitude": -122.4194,
        "address": "123 Main St",
        "city": "San Francisco",
        "state": "CA",
        "country": "US",
        "market": "San Francisco",
        "currency": "USD",
        "base-price": 189.0,
        "health-score": 82,
        "min-stay": 2,
        "metrics": {
          "thirty-day-price": 210.5,
          "ninety-day-price": 195.0,
          "thirty-day-adr": 205.0,
          "ninety-day-adr": 188.0,
          "thirty-day-booked": 71.4,
          "ninety-day-booked": 63.0,
          "thirty-day-availability": 28.6,
          "ninety-day-availability": 37.0,
          "thirty-day-min-stays": 2,
          "ninety-day-min-stays": 2,
          "rating": 4.9,
          "reviews-count": 213,
          "average-service-fee": 31.2
        }
      },
      "members": [
        {
          "channel": "airbnb",
          "channel-listing-id": "13358898",
          "title": "Bright loft downtown",
          "url": "https://www.airbnb.com/rooms/13358898",
          "image": "https://images.example.com/loft.jpg",
          "bedrooms": 2.0,
          "bathrooms": 1.0,
          "room-type": "entire_home",
          "latitude": 37.7712,
          "longitude": -122.4103,
          "distance-km": 1.2,
          "rating": 4.8,
          "reviews-count": 145,
          "health-score": 76.0,
          "metrics": {
            "thirty-day-adr": 198.0,
            "ninety-day-adr": 182.0,
            "thirty-day-booked": 66.7,
            "ninety-day-booked": 58.9,
            "thirty-day-availability": 33.3,
            "ninety-day-availability": 41.1,
            "thirty-day-min-stays": 2,
            "ninety-day-min-stays": 2,
            "thirty-day-adj-occupancy": 70.0,
            "ninety-day-adj-occupancy": 61.0,
            "thirty-day-booked-rate": 198.0,
            "ninety-day-booked-rate": 182.0,
            "base-price": 175.0,
            "currency": "USD",
            "rating": 4.8,
            "reviews-count": 145,
            "average-service-fee": 28.4
          }
        }
      ],
      "performance": {
        "start-date": "2026-08-10",
        "end-date": "2026-11-08",
        "currency": "USD",
        "main-listing-metrics": {
          "average-posted-rate": 212.0,
          "occupancy": 68.5,
          "average-min-stay": 2.0,
          "average-booked-rate": 204.0,
          "adj-occupancy": 71.2
        },
        "aggregated-metrics": {
          "average-posted-rate": 197.0,
          "occupancy": 61.3,
          "average-min-stay": 2.1,
          "average-booked-rate": 189.0,
          "adj-occupancy": 64.8,
          "number-of-nearby-listings": 8
        }
      }
    }
  }
}

Notable Fields

  • kindcustom or connected (see Compset kinds).
  • listing-id — the Beyond listing the comp set is anchored to (null for custom comp sets).
  • title — the comp set's name.
  • matched-airbnb-id — external id Beyond matched to the base listing, when available.
  • source — the base listing's attributes (bedrooms, bathrooms, location, base-price, min-stay, health-score) and its headline metrics.
  • source.metrics — base-listing KPIs over the next 30 and 90 days: thirty-day-price / ninety-day-price (posted rate), *-adr (booked rate), *-booked (occupancy), *-availability, *-min-stays, plus rating and reviews-count.
  • members[] — the curated comparables, each with attributes (channel, channel-listing-id, title, url, image, bedrooms, bathrooms, room-type, distance-km, rating, reviews-count, health-score) and a metrics block.
  • members[].channel-listing-id — the comparable's channel-native listing id (the id in members[].url), not a Beyond listing id.
  • members[].distance-km — distance from the base listing, in kilometers.
  • members[].metrics — per-member KPIs over the next 30 and 90 days: *-adr and *-booked-rate (rates), *-booked (occupancy), *-availability, *-adj-occupancy, *-min-stays, plus base-price.
  • performance.main-listing-metrics — the base listing over the requested range: average-posted-rate, occupancy, average-min-stay, average-booked-rate, adj-occupancy. null for custom comp sets.
  • performance.aggregated-metrics — the same figures aggregated across the comp set, plus number-of-nearby-listings (members contributing to the aggregate).

Metrics a channel does not provide are returned as null.

Use cases

  1. Browse and pick. List comp sets with GET /compsets/, optionally filter[kind] or filter[listing], then fetch the chosen one in detail.
  2. Side-by-side comparison table. Combine source and members[] to render a comparison grid — bedrooms, bathrooms, room-type, rating, reviews-count, distance-km, health-score, and a thumbnail (image) linking out (url) — and highlight where the base listing trails the cluster.
  3. Pricing pacing. Compare the base listing's source.metrics thirty-day-price / ninety-day-price against members' thirty-day-adr / ninety-day-adr to spot where the operator is materially above or below the local market.
  4. Occupancy & availability benchmarking. Track thirty-day-booked / ninety-day-booked (occupancy) and the *-availability metrics across source and members[] to tell apart soft pricing from soft demand.
  5. Minimum-stay strategy. Compare the base listing's min-stay and *-min-stays against members to validate length-of-stay rules for the market.
  6. Reputation & review competitiveness. Compare the base listing's rating and reviews-count (source.metrics) against members' rating / reviews-count to spot where weak social proof — not price — is suppressing conversion, and prioritize review generation where the listing trails the cluster.
  7. Listing-quality benchmarking. Rank the base listing's source.health-score against members' health-score to surface quality headroom (content, photos, listing completeness) to address before adjusting price.
  8. Performance scorecard. Use the performance block to benchmark the listing against the aggregated compset over a chosen date range (average-posted-rate, occupancy, average-booked-rate, adj-occupancy, average-min-stay) and roll the result up to a portfolio scorecard.
  9. Seasonality & demand pacing. Sweep start_date / end_date across consecutive windows (e.g. month by month) and chart performance.aggregated-metrics over time to map the local demand curve and see where the listing's pacing diverges from the market by season. Cache results — this is the heaviest part of the query.

Errors

Status Cause
400 Malformed start_date/end_date, or range > 180 days.
401 Missing or invalid bearer token.
403 Token does not have the compsets:read scope, or the credential lacks privileges on the comp set's listing.
404 No comp set with that id is visible to the application.
429 Rate limit exceeded — the detail endpoint is capped at 30 requests/minute per application.