Docs
Sites API

Inventory & pricing

Floor plans, units, the availability feed, price matrices, unit types and search facets — and how pricing display settings shape every one of them.

Inventory endpoints run across the website's properties. Add property={slug} to narrow to one. All are tagged inventory:{property_id} for each property on the page, with a TTL of 300 (60 for /availability).

Floor plans

GET /floor-plans · GET /floor-plans/{floorPlan}

Default order: order, then name.

Parameter
propertyA property slug. Unknown is a 404, not an empty list
groupA group id; includes descendants
qName contains
beds, bathsCounts, comma-separated
sqft[gte] …Range: gte, lte, gt, lt
min_rent, max_rentStarting rent at or above / at or below
featuredBoolean
availableHas (true) or lacks (false) an available unit
available_byYYYY-MM-DD; has a unit available on or before this date
includeunits to embed each plan's units
sortorder, name, beds, rent, sqft
page, per_pageDefault 50, ceiling 200
{
  "id": "01000000-0000-4000-8000-000000000005",
  "slug": "the-contract",
  "path": "/floor-plan/the-contract",
  "name": "The Contract",
  "marketing_name": null,
  "code": "A1",
  "description": "One bedroom.",
  "unit_type": { "slug": "1-bedroom", "name": "1 Bedroom" },
  "bedrooms": 1,
  "bathrooms": 1,
  "bedrooms_max": 1,
  "bathrooms_max": 1,
  "sqft": { "min": 700, "max": 750 },
  "rent": { "min": 1500, "max": 1800, "base_min": 1500, "base_max": 1800 },
  "fees": { "lines": [], "monthly_total": 0, "monthly_total_max": 0, "upfront_total": 0 },
  "deposit": { "min": null, "max": null },
  "available_on": "2026-02-01",
  "availability": { "available_now": 0, "coming_soon": 1 },
  "has_specials": false,
  "is_featured": false,
  "sort_order": 0,
  "tags": [],
  "image_2d": null, "image_3d": null, "pdf_url": null, "pdfs": [],
  "images": [], "videos": [], "virtual_tours": [],
  "buildings": [{ "id": "0100…0004", "number": "1", "code": null, "name": "North Building" }],
  "groups": [],
  "inherited_groups": [],
  "property": { "id": "0100…0002", "slug": "contract-property", "name": "Contract Property" },
  "units": null
}

Grouped floor plans. When Resi groups several floor plans as one, only the primary is listed, and it speaks for the whole group: its availability, rent bounds and embedded units cover every sibling. A sibling's own slug is a 404.

units is null unless include=units; meta.includes echoes what was embedded. At most 100 units per plan are embedded, by unit number; a plan with more is paged through GET /units?floor_plan={slug}.

Units

GET /units · GET /units/{unit}

Enabled, unhidden units, ordered by unit number unless sorted. A unit Resi marks hidden is never served, by list or by slug.

Parameter
property, groupAs above
qUnit number contains
floor_planA floor plan slug. A primary matches its whole group's units
buildingBuilding ids, comma-separated
amenityAmenity ids, comma-separated. A unit must have all of them
beds, bathsComma-separated. Falls back to the floor plan's counts when the unit holds none
sqft[gte] …Range
min_rent, max_rent
featured, availableBoolean
available_byYYYY-MM-DD
sortnumber, rent, sqft, beds, available
page, per_pageDefault 50, ceiling 200
{
  "id": "01000000-0000-4000-8000-00000000000a",
  "slug": "101",
  "path": "/unit/101",
  "number": "101",
  "floor": "1",
  "bedrooms": 1,
  "bathrooms": 1,
  "sqft": { "interior": 720, "exterior": null },
  "description": null,
  "view_direction": null,
  "is_available": true,
  "available_on": "2026-02-01",
  "rent": { "min": 1500, "max": 1500, "base_min": 1500, "base_max": 1500 },
  "fees": { "lines": [], "monthly_total": 0, "monthly_total_max": 0, "upfront_total": 0 },
  "deposit": null,
  "specials": null,
  "flags": { "featured": false, "accessible": false, "furnished": false, "affordable": false, "model": false, "guest_suite": false, "penthouse": false },
  "links": { "tour": null, "quote": null, "application": null },
  "sightmap_unit_id": "258702",
  "tags": [], "pdf_url": null, "pdfs": [],
  "images": [], "videos": [], "virtual_tours": [], "amenities": [],
  "building": { "id": "0100…0004", "number": "1", "code": null, "name": "North Building" },
  "floor_plan": { "id": "0100…0005", "slug": "the-contract", "name": "The Contract", "path": "/floor-plan/the-contract", "unit_type": { "slug": "1-bedroom", "name": "1 Bedroom" }, "image_2d": null, "image_3d": null },
  "groups": [],
  "inherited_groups": [
    { "id": "0100…0018", "label": "North Tower", "slug": "north-tower", "via": "building", "set": { "id": "0100…0017", "key": "towers", "name": "Towers", "object_type": "building" } }
  ],
  "property": { "id": "0100…0002", "slug": "contract-property", "name": "Contract Property" }
}

Groups: direct and inherited

groups lists the groups the unit itself was put in. inherited_groups lists the groups it belongs to through what contains it, each with via — property, building or floor_plan. The group filter matches on both, so every unit a group filter returns carries that group in one list or the other. Floor plans carry inherited_groups too, from their property.

pdfs[] is every PDF attached (id, url, title); pdf_url is the first one's URL. fees.upfront_total is the one-time mandatory fees due up front, hidden with the other fee totals. On a floor plan, bedrooms_max and bathrooms_max equal bedrooms and bathrooms unless the plan spans a range, and marketing_name is the name the management system markets the plan under, when it has one.

sightmap_unit_id is the unit's id in SightMap, for deep-linking the SightMap embed to a unit. It is null when Resi holds no mapping.

Pricing, and what is hidden

Every rent block has the same four keys:

KeyMeans
min, maxThe total monthly price: base rent plus mandatory monthly fees
base_min, base_maxBase rent alone

rent.min always equals base_min + fees.monthly_total. Values are stored when pricing is synced, not computed per request, so the identity holds in every payload.

Each property chooses how its prices may be shown, reported as pricing_display.mode on the property card:

Modemin / maxbase_min / base_max
BASE_AND_TOTALshownshown
TOTAL_ONLYshownnull
BASE_ONLYnullshown

A property can also hide all pricing, or hide it when nothing is available. In every case:

  • a hidden value is null, never absent. Render a label from pricing_display in its place: hidden_price_label when all pricing is hidden, no_availability_price_label when the price is hidden because nothing is available, and no_price_label for any other null price, such as one the PMS never sent;
  • fees.lines is [] when the fee breakdown is off, and the totals are null when fee totals are hidden;
  • the API enforces this. There is no parameter that returns a hidden price, and a rent filter leaves hidden-price records out of its results.

Sorting by rent still orders records whose price is hidden by their stored value. Do not present a rent-sorted list as a price ranking on a site that mixes properties with hidden pricing.

The availability feed

GET /availability — only the part of a unit that moves.

{
  "id": "01000000-0000-4000-8000-00000000000a",
  "slug": "101",
  "property_id": "01000000-0000-4000-8000-000000000002",
  "floor_plan_id": "01000000-0000-4000-8000-000000000005",
  "is_available": true,
  "available_on": "2026-02-01",
  "rent": { "min": 1500, "max": 1500, "base_min": 1500, "base_max": 1500 },
  "updated_at": "2026-01-15T12:00:00+00:00"
}

Parameters: property, available, page, per_page (default 500, ceiling 2000). meta.cache.ttl is 60.

The intended pattern: cache the heavy /units and /floor-plans payloads for as long as their tags allow, re-read /availability often, and join on id or slug.

Price matrix

GET /units/{unit}/price-matrix?from=2026-02-01&to=2026-04-30 — rent by move-in date and lease term.

{
  "data": [
    { "start_date": "2026-02-01", "best_term": { "months": 12, "rent": 1500 }, "terms": [{ "months": 6, "rent": 1650 }, { "months": 12, "rent": 1500 }, { "months": 15, "rent": 1525 }] }
  ],
  "meta": { "unit": { "id": "0100…000a", "slug": "101", "number": "101" }, "cache": { "tags": ["inventory:0100…0002"], "ttl": 300 } }
}
  • from defaults to today. At most 120 rows.
  • Served from stored data only. The endpoint never calls the property's management system, so it is always fast; a unit with no stored matrix returns [].
  • [] too when the property's pricing display settings hide the price.
  • Counted against the tighter write rate limit (60 / minute). Fetch it when a visitor opens a unit, not for every unit in a list.

Unit types

GET /unit-types (optionally ?property=) — studio, one bedroom and so on, for "browse by size" navigation.

{ "slug": "1-bedroom", "name": "1 Bedroom", "bedrooms": 1, "units_count": 1, "available_units_count": 1, "rent": { "min": 1500, "max": 1500, "base_min": 1500, "base_max": 1500 }, "sqft": { "min": 720, "max": 720 }, "fees": { "monthly_total": 0, "monthly_total_max": 0 } }

fees is the range of mandatory monthly fees across the type's available units; both values are null when fee totals are hidden.

Search facets

GET /facets (optionally ?property=) — what a search UI's filters can offer, with counts. Tags: site, inventory:{property_id}.

{
  "properties_count": 1,
  "units_count": 1,
  "available_units_count": 1,
  "rent": { "min": 1500, "max": 1500, "base_min": 1500, "base_max": 1500 },
  "sqft": { "min": 720, "max": 720 },
  "bedrooms": [{ "value": 1, "units_count": 1, "available_units_count": 1 }],
  "bathrooms": [{ "value": 1, "units_count": 1, "available_units_count": 1 }],
  "cities": [{ "value": "Austin", "state": "TX", "properties_count": 1 }],
  "states": [{ "value": "TX", "properties_count": 1 }]
}

Each value is exactly what the matching filter accepts (beds, baths, city, state), so a facet can be turned into a query string without translation. Use rent and sqft as slider bounds.

Last updated on

On this page