Public API Documentation

January 18, 2026 · View on GitHub

PropertyWebBuilder provides a RESTful JSON API for headless frontend clients (Astro, React, etc.).

Base URL

All API endpoints are relative to the tenant subdomain:

https://{tenant}.propertywebbuilder.com/api_public/v1/

For local development:

http://localhost:3000/api_public/v1/

Locale-Prefixed Paths (Preferred)

For better CDN caching, locale can be placed in the URL path:

https://{tenant}.propertywebbuilder.com/api_public/v1/en/properties

The legacy ?locale= query parameter still works but is deprecated.

OpenAPI Specification

The complete API specification is available at:

  • File: docs/api/openapi.yaml
  • Format: OpenAPI 3.1

You can view the spec interactively using:

Endpoints

Theme Configuration

GET /theme

Returns complete theme data including colors, fonts, and CSS variables.

Query Parameters:

ParameterTypeDescription
localestringLocale code (e.g., "en", "es")
palette_idstringOverride palette for preview

Response:

{
  "theme": {
    "name": "brisbane",
    "palette_id": "ocean_blue",
    "colors": {
      "primary_color": "#3B82F6",
      "secondary_color": "#10B981"
    },
    "fonts": {
      "heading": "Playfair Display",
      "body": "Inter"
    },
    "dark_mode": {
      "enabled": true,
      "setting": "auto"
    },
    "css_variables": ":root { --pwb-primary-color: #3B82F6; ... }"
  }
}

Client Configuration

GET /client-config

Returns configuration for client-rendered (Astro) sites.

Query Parameters:

ParameterTypeDescription
includestringComma-separated includes: links, site_details, translations, homepage, testimonials, featured_properties
localestringLocale for translations (if not using locale-prefixed path)
testimonials_limitintegerMax testimonials when include=testimonials
properties_per_groupintegerMax properties per group when include=featured_properties

Response:

{
  "data": {
    "rendering_mode": "client",
    "theme": {
      "name": "brisbane",
      "friendly_name": "Brisbane"
    },
    "config": { /* theme config */ },
    "css_variables": { /* CSS vars as object */ },
    "website": {
      "id": 1,
      "subdomain": "demo",
      "company_display_name": "Demo Agency",
      "default_locale": "en",
      "supported_locales": ["en", "es"]
    }
  }
}

Error (200):

{
  "data": { ... },
  "error": {
    "message": "Client rendering not enabled for this website",
    "rendering_mode": "rails"
  }
}

Site Details

GET /site-details

Returns website metadata, locales, and analytics configuration.

Response:

{
  "id": 1,
  "company_display_name": "Demo Agency",
  "default_client_locale": "en",
  "supported_locales": ["en", "es"],
  "currency": "EUR",
  "analytics": {
    "ga4_id": "G-XXXXXXXXXX",
    "gtm_id": "GTM-XXXXXXX",
    "posthog_key": "phc_xxxxx"
  }
}

Properties

GET /properties

Search and filter property listings with pagination.

Query Parameters:

ParameterTypeDefaultDescription
localestring"en"Locale for translations
sale_or_rentalstring"sale""sale" or "rental"
property_typestring-Property type filter
bedrooms_frominteger-Minimum bedrooms
bathrooms_frominteger-Minimum bathrooms
for_sale_price_frominteger-Min sale price (cents)
for_sale_price_tillinteger-Max sale price (cents)
highlightedboolean-Featured properties only
featuredboolean-Featured properties only (alias of highlighted)
sort_bystring-"price-asc", "price-desc", "newest"
pageinteger1Page number
per_pageinteger12Results per page
group_bystring-Use sale_or_rental to group results
per_groupinteger3Results per group when using group_by

Response:

{
  "data": [
    {
      "id": 1,
      "slug": "luxury-villa",
      "title": "Luxury Villa with Pool",
      "price_sale_current_cents": 50000000,
      "formatted_price": "€500,000",
      "count_bedrooms": 4,
      "count_bathrooms": 3,
      "primary_image_url": "https://..."
    }
  ],
  "map_markers": [
    {
      "id": 1,
      "slug": "luxury-villa",
      "lat": 36.5,
      "lng": -4.9,
      "title": "Luxury Villa",
      "price": "€500,000"
    }
  ],
  "meta": {
    "total": 50,
    "page": 1,
    "per_page": 12,
    "total_pages": 5
  }
}

Grouped Results

Use group_by=sale_or_rental to fetch sale and rental listings in one request:

GET /api_public/v1/en/properties?group_by=sale_or_rental&per_group=3&featured=true
{
  "sale": { "properties": [...], "meta": { "total": 5, "per_group": 3 } },
  "rental": { "properties": [...], "meta": { "total": 3, "per_group": 3 } }
}

GET /properties/{slug}

Returns full property details by slug or ID.

Query Parameters:

ParameterTypeDescription
localestringLocale for translations
include_imagesstringSet to "variants" for image variants

Response:

{
  "id": 1,
  "slug": "luxury-villa",
  "title": "Luxury Villa with Pool",
  "description": "Beautiful villa...",
  "price_sale_current_cents": 50000000,
  "currency": "EUR",
  "count_bedrooms": 4,
  "count_bathrooms": 3,
  "count_garages": 2,
  "constructed_area": 350,
  "area_unit": "sqm",
  "for_sale": true,
  "for_rent": false,
  "highlighted": true,
  "latitude": 36.5,
  "longitude": -4.9,
  "prop_photos": [
    { "url": "https://...", "image": "https://..." }
  ]
}

Pages

GET /pages/{slug}

Returns CMS page content by slug.

Response:

{
  "id": 1,
  "slug": "about-us",
  "title": "About Us",
  "page_contents": [
    {
      "content_page_part_key": "heroes/hero_centered",
      "content": {
        "title": { "en": "About Our Agency" },
        "subtitle": { "en": "Your trusted partner..." }
      }
    }
  ]
}

Caching

The API uses HTTP caching headers for optimal performance:

EndpointCache DurationHeaders
/theme1 hourCache-Control: max-age=3600, ETag
/site-details1 hourCache-Control: max-age=3600, ETag
/properties2 minutesCache-Control: max-age=120
/properties/:slug5 minutesCache-Control: max-age=300, ETag

Clients should respect ETag headers for conditional requests.

Error Handling

All errors return a consistent format:

{
  "error": "Error message here"
}
Status CodeMeaning
200Success
404Resource not found
500Server error

Authentication

Most endpoints are public and do not require authentication.

For tenant identification, the API uses:

  1. Subdomain: Automatically detected from request host
  2. Header: X-Website-Slug (for proxy/SSR scenarios)