{
  "openapi": "3.1.0",
  "info": {
    "title": "Dubai Wealth Index HTTP API",
    "version": "1.1.0",
    "summary": "Independent property intelligence: registered Dubai prices, rents and returns — apartments by building and by area, villas and townhouses by community.",
    "description": "Registered Dubai apartment prices, rents, gross yields and short-let rates, published as data. Medians of Dubai Land Department sale transactions and Ejari rental contracts — recorded prices, not asking prices. Current data edition: 2026-08-26.\n\nFree, and there is nothing to sign up for: no API key, no account, no quota on these endpoints. Read them straight away.\n\nAgents should prefer the MCP server at https://dubaiwealthindex.com/api/mcp, which exposes the same data as typed tools with the caveats attached to every figure. Its manifest is at https://dubaiwealthindex.com/.well-known/mcp.json. Discovery there is open; calling a tool takes a one-click OAuth authorization with dynamic client registration, so no human has to issue you anything.\n\nQUOTING: yields are gross. Rents are registered contracts. Short-let occupancy is a stated assumption, never a measurement. A null is a suppressed figure, not a zero. Every record carries a prebuilt citation — use it.",
    "contact": {
      "name": "Dubai Wealth Index",
      "email": "hello@dubaiwealthindex.com",
      "url": "https://dubaiwealthindex.com/contact"
    },
    "license": {
      "name": "Source data: Dubai Land Department open data",
      "url": "https://dubailand.gov.ae/en/open-data/"
    },
    "termsOfService": "https://dubaiwealthindex.com/terms"
  },
  "servers": [
    {
      "url": "https://dubaiwealthindex.com",
      "description": "Production"
    }
  ],
  "externalDocs": {
    "description": "The same surfaces, in prose",
    "url": "https://dubaiwealthindex.com/docs/api"
  },
  "x-mcp": {
    "description": "The Model Context Protocol server over the same data. Nine read-only tools, five resources, five prompts. Streamable HTTP. Discovery is unauthenticated; tool calls use OAuth 2.1.",
    "endpoint": "https://dubaiwealthindex.com/api/mcp",
    "manifest": "https://dubaiwealthindex.com/.well-known/mcp.json",
    "documentation": "https://dubaiwealthindex.com/docs/mcp"
  },
  "x-onboarding": {
    "freeTier": true,
    "apiKeyRequired": false,
    "accountRequired": false,
    "selfServe": true,
    "rateLimited": false,
    "notes": "These HTTP endpoints need no credential of any kind. The MCP server needs an OAuth token, obtainable end-to-end by a machine: dynamic client registration is open, so a client registers itself and a human only approves the consent screen."
  },
  "paths": {
    "/{path}": {
      "get": {
        "operationId": "getPageAsMarkdown",
        "summary": "Any page as markdown, at its canonical URL",
        "description": "Returns any page on this site as markdown instead of HTML, at the same canonical URL. Reach for this when you want a whole page — its tables, its caveats, its links — without parsing HTML. Every rendition carries the canonical link, the data date, the sources, and the citation format. A client that accepts neither markdown nor HTML gets 406.",
        "parameters": [
          {
            "name": "path",
            "in": "path",
            "required": true,
            "description": "Any canonical path on the site, without the leading slash. Enumerate them from /sitemap.xml.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Accept",
            "in": "header",
            "required": true,
            "description": "Send `text/markdown` to get the markdown rendition. q-values are honoured per RFC 9110, and an equal-weight tie resolves to HTML so browsers are unaffected.",
            "schema": {
              "type": "string",
              "const": "text/markdown"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Any page as markdown, at its canonical URL",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "No such page or record.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "type": "object",
                  "title": "Problem",
                  "description": "RFC 9457 problem details. Every JSON surface here fails in this shape; branch on `code`, and `hint` says what to do next.",
                  "properties": {
                    "type": {
                      "type": "string",
                      "format": "uri",
                      "const": "https://dubaiwealthindex.com/docs/api#errors"
                    },
                    "title": {
                      "type": "string"
                    },
                    "status": {
                      "type": "integer"
                    },
                    "detail": {
                      "type": "string",
                      "description": "What went wrong with this request."
                    },
                    "code": {
                      "type": "string",
                      "enum": [
                        "not_found",
                        "bad_request",
                        "unauthorized",
                        "rate_limited",
                        "not_acceptable",
                        "server_error"
                      ],
                      "description": "The stable machine token. Closed set — branch on this."
                    },
                    "hint": {
                      "type": "string",
                      "description": "What the caller should do next."
                    }
                  },
                  "required": [
                    "type",
                    "title",
                    "status",
                    "detail",
                    "code",
                    "hint"
                  ]
                }
              }
            }
          },
          "406": {
            "description": "Nothing this endpoint can produce is acceptable.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "type": "object",
                  "title": "Problem",
                  "description": "RFC 9457 problem details. Every JSON surface here fails in this shape; branch on `code`, and `hint` says what to do next.",
                  "properties": {
                    "type": {
                      "type": "string",
                      "format": "uri",
                      "const": "https://dubaiwealthindex.com/docs/api#errors"
                    },
                    "title": {
                      "type": "string"
                    },
                    "status": {
                      "type": "integer"
                    },
                    "detail": {
                      "type": "string",
                      "description": "What went wrong with this request."
                    },
                    "code": {
                      "type": "string",
                      "enum": [
                        "not_found",
                        "bad_request",
                        "unauthorized",
                        "rate_limited",
                        "not_acceptable",
                        "server_error"
                      ],
                      "description": "The stable machine token. Closed set — branch on this."
                    },
                    "hint": {
                      "type": "string",
                      "description": "What the caller should do next."
                    }
                  },
                  "required": [
                    "type",
                    "title",
                    "status",
                    "detail",
                    "code",
                    "hint"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/md/{path}": {
      "get": {
        "operationId": "getMarkdownRendition",
        "summary": "The same markdown at its own URL",
        "description": "The markdown rendition at a URL of its own, for when the format has to be part of the cache key rather than negotiated. Prefer content negotiation on the canonical URL; use this when you cannot control request headers. Marked noindex, and it redirects to the canonical form where one exists.",
        "parameters": [
          {
            "name": "path",
            "in": "path",
            "required": true,
            "description": "The canonical path, without the leading slash.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The same markdown at its own URL",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "No such page or record.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "type": "object",
                  "title": "Problem",
                  "description": "RFC 9457 problem details. Every JSON surface here fails in this shape; branch on `code`, and `hint` says what to do next.",
                  "properties": {
                    "type": {
                      "type": "string",
                      "format": "uri",
                      "const": "https://dubaiwealthindex.com/docs/api#errors"
                    },
                    "title": {
                      "type": "string"
                    },
                    "status": {
                      "type": "integer"
                    },
                    "detail": {
                      "type": "string",
                      "description": "What went wrong with this request."
                    },
                    "code": {
                      "type": "string",
                      "enum": [
                        "not_found",
                        "bad_request",
                        "unauthorized",
                        "rate_limited",
                        "not_acceptable",
                        "server_error"
                      ],
                      "description": "The stable machine token. Closed set — branch on this."
                    },
                    "hint": {
                      "type": "string",
                      "description": "What the caller should do next."
                    }
                  },
                  "required": [
                    "type",
                    "title",
                    "status",
                    "detail",
                    "code",
                    "hint"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/data/buildings/{area}/{building}.json": {
      "get": {
        "operationId": "getBuildingRecord",
        "summary": "One building's numbers as data",
        "description": "The versioned JSON record for one published apartment building: median registered sale prices and rents, gross yield and its basis, growth, turnover upside, off-plan uplift and the forward short-let curve, all by bedroom type. Reach for this rather than the markdown page when you want figures rather than a document. Figures are medians of registered Dubai Land Department sales and Ejari contracts, not asking prices. The record carries its own citation and canonical URL, so a figure taken from it keeps the link back.",
        "parameters": [
          {
            "name": "area",
            "in": "path",
            "required": true,
            "description": "Area slug from a canonical URL, e.g. \"business-bay\".",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{0,79}$"
            }
          },
          {
            "name": "building",
            "in": "path",
            "required": true,
            "description": "Building slug, e.g. \"vera-tower\".",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{0,79}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One building's numbers as data",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "title": "BuildingRecord",
                  "description": "The versioned record for one published apartment building. Identical to what the MCP `get_building` tool returns, so a citation from either resolves the same way.",
                  "properties": {
                    "schemaVersion": {
                      "type": "string",
                      "description": "Version of this record shape. Additive changes bump the minor; a breaking one would bump the major and be announced on /docs."
                    },
                    "type": {
                      "type": "string",
                      "const": "DubaiWealthIndexBuildingRecord"
                    },
                    "id": {
                      "type": "string",
                      "format": "uri",
                      "description": "Stable absolute identifier for the record, of the form <canonicalUrl>#record. It does not change across data refreshes. Grain: one building record. When null: Never null."
                    },
                    "canonicalUrl": {
                      "type": "string",
                      "format": "uri",
                      "description": "The human-readable page this record mirrors. Cite this, not the JSON URL. Grain: one building record. When null: Never null."
                    },
                    "name": {
                      "type": "string",
                      "description": "The building's name as published."
                    },
                    "area": {
                      "type": "object",
                      "description": "The Dubai area this building sits in.",
                      "properties": {
                        "name": {
                          "type": "string"
                        },
                        "slug": {
                          "type": "string"
                        },
                        "locality": {
                          "type": "string"
                        },
                        "country": {
                          "type": "string"
                        }
                      }
                    },
                    "purpose": {
                      "type": "string",
                      "enum": [
                        "all",
                        "sale",
                        "rent"
                      ],
                      "description": "Which market's rows this record carries."
                    },
                    "dataAsOf": {
                      "type": "string",
                      "format": "date",
                      "description": "The edition of the registered data every figure in the record was computed from. Quote it with any figure taken from here. Unit: ISO date. Grain: the whole record. When null: Never null on a published record."
                    },
                    "sources": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "The registries the figures came from. Airbnb appears only where short-let metrics are present. Grain: the whole record. When null: Never null."
                    },
                    "methodologyUrl": {
                      "type": "string",
                      "format": "uri"
                    },
                    "attribution": {
                      "type": "object",
                      "title": "Attribution",
                      "description": "A prebuilt citation for this record in three formats. Copy the one matching your output format rather than composing your own; a figure quoted without its link back is the failure this block exists to prevent.",
                      "properties": {
                        "plain": {
                          "type": "string"
                        },
                        "markdown": {
                          "type": "string"
                        },
                        "html": {
                          "type": "string"
                        }
                      }
                    },
                    "metrics": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "title": "MetricRow",
                        "description": "One building × bedroom × purpose. A null is a suppressed figure, never a zero. Rows carry some fields that are not yet in the published field reference at /docs/data-dictionary; only the documented ones below are stable, and an undocumented field may change or disappear without notice.",
                        "properties": {
                          "bedroom": {
                            "type": "string",
                            "enum": [
                              "all",
                              "studio",
                              "1br",
                              "2br",
                              "3br",
                              "4br_plus"
                            ],
                            "description": "One of all, studio, 1br, 2br, 3br, 4br_plus. \"all\" is the building-wide median across every unit size, not a sum or an average of the rows below it. Grain: one metric row. When null: Never null."
                          },
                          "purpose": {
                            "type": "string",
                            "enum": [
                              "sale",
                              "rent"
                            ],
                            "description": "Which market the row measures: sale, rent, offplan or short_let. A building has a row per bedroom type per purpose. Grain: one metric row. When null: Never null."
                          },
                          "medianPrice": {
                            "type": [
                              "number",
                              "null"
                            ],
                            "description": "Median registered sale price. A median of recorded transactions — not an asking price, not a valuation, and not the price of any particular unit. Unit: AED. Grain: building × bedroom, trailing 24 months. When null: Too few registered sales in the window to publish a median that would hold. It does not mean nothing sold."
                          },
                          "medianPriceSqft": {
                            "type": [
                              "number",
                              "null"
                            ],
                            "description": "Median registered sale price per square foot of measured floor area. Unit: AED per sqft. Grain: building × bedroom, trailing 24 months. When null: No publishable price median, or no measured area on the records."
                          },
                          "fairPsf": {
                            "type": [
                              "number",
                              "null"
                            ],
                            "description": "The recorded price: median price per square foot over the most recent window holding at least five registered sales of READY units — 3 months, else 6, else 12. Ready-only and never wider than a year, unlike medianPriceSqft (24 months, all sales) and yieldPrice (pooled with off-plan, falls back to the last five sales however old). Read it with fairWindowMonths and fairSaleCount; the figure is not publishable without them. Unit: AED per sqft. Grain: building x bedroom, freshest window clearing 5 ready sales. When null: No window in the last 12 months held five ready sales. Not zero and not stale — the building has not traded enough recently for a median to mean anything."
                          },
                          "fairPrice": {
                            "type": [
                              "number",
                              "null"
                            ],
                            "description": "Median total price over the same window as fairPsf. A median across the units that happened to trade, never a valuation of one apartment: floor, view, condition and layout all move a real price and none is in this figure. Unit: AED. Grain: building x bedroom, same window as fairPsf. When null: As fairPsf."
                          },
                          "fairWindowMonths": {
                            "type": [
                              "integer",
                              "null"
                            ],
                            "enum": [
                              3,
                              6,
                              12,
                              null
                            ],
                            "description": "Which rung of the ladder fairPsf landed on: 3, 6 or 12. A price whose window is unstated is not evidence, so every surface prints this beside the figure. Unit: months. Grain: building x bedroom. When null: fairPsf is null."
                          },
                          "fairSaleCount": {
                            "type": [
                              "integer",
                              "null"
                            ],
                            "description": "Registered ready sales inside the window. Never fewer than five: a price computed from two sales moves by a third when one penthouse trades. Unit: sales. Grain: building x bedroom. When null: fairPsf is null."
                          },
                          "fairAsof": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "date",
                            "description": "Date of the most recent sale inside the window. A 12-month window whose newest sale is eleven months old is a different claim from one closing last week, and this is what separates them. Unit: date. Grain: building x bedroom. When null: fairPsf is null."
                          },
                          "growth12mPct": {
                            "type": [
                              "number",
                              "null"
                            ],
                            "description": "Change in the median price per square foot, last 12 months against the 12 before. Positive is a rise. Unit: percent. Grain: building × bedroom. When null: One of the two windows has too few sales to compare. A flat market returns 0, not null."
                          },
                          "medianRent": {
                            "type": [
                              "number",
                              "null"
                            ],
                            "description": "Median annual rent on new Ejari contracts. Registered contracts, not listing asks. Unit: AED per year. Grain: building × bedroom, trailing 24 months. When null: Too few registered contracts in the window to publish a median."
                          },
                          "medianRentSqft": {
                            "type": [
                              "number",
                              "null"
                            ],
                            "description": "Median annual rent per square foot. Annual, not monthly — the most common misreading of this field. Unit: AED per year per sqft. Grain: building × bedroom, trailing 24 months. When null: No publishable rent median, or no measured area."
                          },
                          "medianRentRenew": {
                            "type": [
                              "number",
                              "null"
                            ],
                            "description": "Median annual rent on renewal contracts — what a sitting tenant pays, as against medianRent for a new one. Unit: AED per year. Grain: building × bedroom, trailing 24 months. When null: Too few renewals registered in the window."
                          },
                          "rentSource": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "description": "The grain the rent was measured at: building, project or vicinity. A project- or vicinity-grain rent is pooled from nearby stock and is weaker evidence for this building than a building-grain one. Grain: one metric row. When null: No rent figure on the row."
                          },
                          "grossYieldPct": {
                            "type": [
                              "number",
                              "null"
                            ],
                            "description": "A year of registered rent as a share of the median resale price. Gross — before service charge, agency fees, maintenance and vacant months. Unit: percent. Grain: building × bedroom. When null: Suppressed, not zero: the price base behind the yield was too thin to publish, or the median price falls below the publication floor."
                          },
                          "yieldBasis": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "description": "How the yield's denominator was built: 'current' means the last 3 months of sales, 'last_sales' means the last 5 sales however old they are. Grain: one metric row. When null: No published yield on the row."
                          },
                          "yieldPriceN": {
                            "type": [
                              "integer",
                              "null"
                            ],
                            "description": "How many sales the yield's denominator rests on. Unit: count. Grain: one metric row. When null: No published yield on the row."
                          },
                          "yieldPriceAsof": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "date",
                            "description": "Date of the most recent sale in the yield's denominator. The 'last_sales' fallback is uncapped, so this can be years old — check it before treating a yield as current. Unit: ISO date. Grain: one metric row. When null: No published yield on the row."
                          },
                          "turnoverUpsidePct": {
                            "type": [
                              "number",
                              "null"
                            ],
                            "description": "The gap between what a new tenant pays and what a sitting tenant renews at, in the same building and bedroom type. Unit: percent. Grain: building × bedroom. When null: Below the materiality floor, or too few renewals. Dubai rents cluster on round numbers, so small gaps are noise and are not published."
                          },
                          "offplanUpliftPct": {
                            "type": [
                              "number",
                              "null"
                            ],
                            "description": "What off-plan buyers made when their units later resold: resale price against off-plan purchase price. Longitudinal, not a windowed median. Unit: percent. Grain: building × bedroom. When null: No off-plan units in this building have resold. Units that never resold cannot appear, so the published set skews toward the ones that performed."
                          },
                          "medianAdr": {
                            "type": [
                              "number",
                              "null"
                            ],
                            "description": "Median nightly asking rate from public Airbnb search results, sampled for every month of the next twelve. An asking rate, not a transaction. Unit: AED per night. Grain: building × bedroom. When null: No short-let listings seen for this building and bedroom type."
                          },
                          "distinctListings": {
                            "type": [
                              "integer",
                              "null"
                            ],
                            "description": "How many distinct public listings the nightly rate was taken from. A floor, not a census of the building's short-let stock. Unit: count. Grain: building × bedroom. When null: No short-let listings seen."
                          },
                          "txnCount24m": {
                            "type": "integer",
                            "description": "Registered sales in the trailing 24 months. This is the sample size behind the price figures — quote it with them. Unit: count. Grain: building × bedroom. When null: Never null; zero means no registered sales in the window."
                          },
                          "rentCount24m": {
                            "type": "integer",
                            "description": "Registered new rental contracts in the trailing 24 months. The sample size behind the rent figures. Unit: count. Grain: building × bedroom. When null: Never null; zero means no new contracts registered in the window."
                          },
                          "isPageEligible": {
                            "type": "boolean",
                            "description": "Whether this row cleared the publication gate. False rows exist in the data and are not published as pages. Grain: one metric row. When null: Never null."
                          }
                        },
                        "additionalProperties": true,
                        "required": [
                          "bedroom",
                          "purpose",
                          "isPageEligible"
                        ]
                      }
                    },
                    "yearly": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      },
                      "description": "Per-year medians and transaction counts. Rows exist per bedroom type and per purpose — filter to one of each before plotting a line, or a mix shift reads as a price move. Unit: AED and AED per sqft. Grain: building × bedroom × purpose × year. When null: Empty for a building with no history in the registry."
                    },
                    "monthly": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      },
                      "description": "Monthly medians. Carried at bedroom 'all' only. Future-dated rent contracts exist in the registry, so clip the series at dataAsOf. Unit: AED and AED per sqft. Grain: building × month × purpose. When null: Empty where the monthly series is too thin to publish."
                    },
                    "shortLetMonthly": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      },
                      "description": "The forward nightly-rate curve: one row per month of the next twelve, with the listing count behind each. strSource says whether the rate is the building's own or pooled from within 500m. Unit: AED per night. Grain: building × bedroom × stay month. When null: Empty where no short-let pricing was collected."
                    }
                  },
                  "required": [
                    "schemaVersion",
                    "type",
                    "id",
                    "canonicalUrl",
                    "name",
                    "area",
                    "dataAsOf",
                    "sources",
                    "attribution",
                    "metrics"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "No such page or record.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "type": "object",
                  "title": "Problem",
                  "description": "RFC 9457 problem details. Every JSON surface here fails in this shape; branch on `code`, and `hint` says what to do next.",
                  "properties": {
                    "type": {
                      "type": "string",
                      "format": "uri",
                      "const": "https://dubaiwealthindex.com/docs/api#errors"
                    },
                    "title": {
                      "type": "string"
                    },
                    "status": {
                      "type": "integer"
                    },
                    "detail": {
                      "type": "string",
                      "description": "What went wrong with this request."
                    },
                    "code": {
                      "type": "string",
                      "enum": [
                        "not_found",
                        "bad_request",
                        "unauthorized",
                        "rate_limited",
                        "not_acceptable",
                        "server_error"
                      ],
                      "description": "The stable machine token. Closed set — branch on this."
                    },
                    "hint": {
                      "type": "string",
                      "description": "What the caller should do next."
                    }
                  },
                  "required": [
                    "type",
                    "title",
                    "status",
                    "detail",
                    "code",
                    "hint"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/llms.txt": {
      "get": {
        "operationId": "getAgentIndex",
        "summary": "What this index covers, and when not to use it",
        "description": "Read this first, to decide whether this index can answer your question at all. States what the site covers, what it explicitly does not, the URL grammar for drilling in, and the current data edition.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "What this index covers, and when not to use it",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/skill.md": {
      "get": {
        "operationId": "getSkillDocument",
        "summary": "The instruction document",
        "description": "Goes further than llms.txt: the workflow, the rules for quoting a figure, and what an absent figure means. This is the same text the MCP server returns as its `instructions` at initialize.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "The instruction document",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/openapi.json": {
      "get": {
        "operationId": "getOpenApiDocument",
        "summary": "This document",
        "description": "The machine-readable description of everything above. Self-referential on purpose: an agent that found one surface can find the rest.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "This document",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/sitemap.xml": {
      "get": {
        "operationId": "getSitemapIndex",
        "summary": "Index of the per-family shards",
        "description": "Every URL on the site, sharded by page family so a crawler can take one template at a time. Use it to enumerate entities before reading them.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Index of the per-family shards",
            "content": {
              "application/xml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/sitemaps/{family}.xml": {
      "get": {
        "operationId": "getSitemapShard",
        "summary": "One family's URLs",
        "description": "One shard of the sitemap index. Fetch a single family when you only need buildings, or only areas, rather than the whole site.",
        "parameters": [
          {
            "name": "family",
            "in": "path",
            "required": true,
            "description": "Which page family to enumerate.",
            "schema": {
              "type": "string",
              "enum": [
                "buildings-sale",
                "buildings-rent",
                "areas",
                "categories",
                "projects",
                "projects-offplan",
                "developers",
                "rankings",
                "docs",
                "villas"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One family's URLs",
            "content": {
              "application/xml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "No such page or record.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "type": "object",
                  "title": "Problem",
                  "description": "RFC 9457 problem details. Every JSON surface here fails in this shape; branch on `code`, and `hint` says what to do next.",
                  "properties": {
                    "type": {
                      "type": "string",
                      "format": "uri",
                      "const": "https://dubaiwealthindex.com/docs/api#errors"
                    },
                    "title": {
                      "type": "string"
                    },
                    "status": {
                      "type": "integer"
                    },
                    "detail": {
                      "type": "string",
                      "description": "What went wrong with this request."
                    },
                    "code": {
                      "type": "string",
                      "enum": [
                        "not_found",
                        "bad_request",
                        "unauthorized",
                        "rate_limited",
                        "not_acceptable",
                        "server_error"
                      ],
                      "description": "The stable machine token. Closed set — branch on this."
                    },
                    "hint": {
                      "type": "string",
                      "description": "What the caller should do next."
                    }
                  },
                  "required": [
                    "type",
                    "title",
                    "status",
                    "detail",
                    "code",
                    "hint"
                  ]
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "BuildingRecord": {
        "type": "object",
        "title": "BuildingRecord",
        "description": "The versioned record for one published apartment building. Identical to what the MCP `get_building` tool returns, so a citation from either resolves the same way.",
        "properties": {
          "schemaVersion": {
            "type": "string",
            "description": "Version of this record shape. Additive changes bump the minor; a breaking one would bump the major and be announced on /docs."
          },
          "type": {
            "type": "string",
            "const": "DubaiWealthIndexBuildingRecord"
          },
          "id": {
            "type": "string",
            "format": "uri",
            "description": "Stable absolute identifier for the record, of the form <canonicalUrl>#record. It does not change across data refreshes. Grain: one building record. When null: Never null."
          },
          "canonicalUrl": {
            "type": "string",
            "format": "uri",
            "description": "The human-readable page this record mirrors. Cite this, not the JSON URL. Grain: one building record. When null: Never null."
          },
          "name": {
            "type": "string",
            "description": "The building's name as published."
          },
          "area": {
            "type": "object",
            "description": "The Dubai area this building sits in.",
            "properties": {
              "name": {
                "type": "string"
              },
              "slug": {
                "type": "string"
              },
              "locality": {
                "type": "string"
              },
              "country": {
                "type": "string"
              }
            }
          },
          "purpose": {
            "type": "string",
            "enum": [
              "all",
              "sale",
              "rent"
            ],
            "description": "Which market's rows this record carries."
          },
          "dataAsOf": {
            "type": "string",
            "format": "date",
            "description": "The edition of the registered data every figure in the record was computed from. Quote it with any figure taken from here. Unit: ISO date. Grain: the whole record. When null: Never null on a published record."
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The registries the figures came from. Airbnb appears only where short-let metrics are present. Grain: the whole record. When null: Never null."
          },
          "methodologyUrl": {
            "type": "string",
            "format": "uri"
          },
          "attribution": {
            "type": "object",
            "title": "Attribution",
            "description": "A prebuilt citation for this record in three formats. Copy the one matching your output format rather than composing your own; a figure quoted without its link back is the failure this block exists to prevent.",
            "properties": {
              "plain": {
                "type": "string"
              },
              "markdown": {
                "type": "string"
              },
              "html": {
                "type": "string"
              }
            }
          },
          "metrics": {
            "type": "array",
            "items": {
              "type": "object",
              "title": "MetricRow",
              "description": "One building × bedroom × purpose. A null is a suppressed figure, never a zero. Rows carry some fields that are not yet in the published field reference at /docs/data-dictionary; only the documented ones below are stable, and an undocumented field may change or disappear without notice.",
              "properties": {
                "bedroom": {
                  "type": "string",
                  "enum": [
                    "all",
                    "studio",
                    "1br",
                    "2br",
                    "3br",
                    "4br_plus"
                  ],
                  "description": "One of all, studio, 1br, 2br, 3br, 4br_plus. \"all\" is the building-wide median across every unit size, not a sum or an average of the rows below it. Grain: one metric row. When null: Never null."
                },
                "purpose": {
                  "type": "string",
                  "enum": [
                    "sale",
                    "rent"
                  ],
                  "description": "Which market the row measures: sale, rent, offplan or short_let. A building has a row per bedroom type per purpose. Grain: one metric row. When null: Never null."
                },
                "medianPrice": {
                  "type": [
                    "number",
                    "null"
                  ],
                  "description": "Median registered sale price. A median of recorded transactions — not an asking price, not a valuation, and not the price of any particular unit. Unit: AED. Grain: building × bedroom, trailing 24 months. When null: Too few registered sales in the window to publish a median that would hold. It does not mean nothing sold."
                },
                "medianPriceSqft": {
                  "type": [
                    "number",
                    "null"
                  ],
                  "description": "Median registered sale price per square foot of measured floor area. Unit: AED per sqft. Grain: building × bedroom, trailing 24 months. When null: No publishable price median, or no measured area on the records."
                },
                "fairPsf": {
                  "type": [
                    "number",
                    "null"
                  ],
                  "description": "The recorded price: median price per square foot over the most recent window holding at least five registered sales of READY units — 3 months, else 6, else 12. Ready-only and never wider than a year, unlike medianPriceSqft (24 months, all sales) and yieldPrice (pooled with off-plan, falls back to the last five sales however old). Read it with fairWindowMonths and fairSaleCount; the figure is not publishable without them. Unit: AED per sqft. Grain: building x bedroom, freshest window clearing 5 ready sales. When null: No window in the last 12 months held five ready sales. Not zero and not stale — the building has not traded enough recently for a median to mean anything."
                },
                "fairPrice": {
                  "type": [
                    "number",
                    "null"
                  ],
                  "description": "Median total price over the same window as fairPsf. A median across the units that happened to trade, never a valuation of one apartment: floor, view, condition and layout all move a real price and none is in this figure. Unit: AED. Grain: building x bedroom, same window as fairPsf. When null: As fairPsf."
                },
                "fairWindowMonths": {
                  "type": [
                    "integer",
                    "null"
                  ],
                  "enum": [
                    3,
                    6,
                    12,
                    null
                  ],
                  "description": "Which rung of the ladder fairPsf landed on: 3, 6 or 12. A price whose window is unstated is not evidence, so every surface prints this beside the figure. Unit: months. Grain: building x bedroom. When null: fairPsf is null."
                },
                "fairSaleCount": {
                  "type": [
                    "integer",
                    "null"
                  ],
                  "description": "Registered ready sales inside the window. Never fewer than five: a price computed from two sales moves by a third when one penthouse trades. Unit: sales. Grain: building x bedroom. When null: fairPsf is null."
                },
                "fairAsof": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date",
                  "description": "Date of the most recent sale inside the window. A 12-month window whose newest sale is eleven months old is a different claim from one closing last week, and this is what separates them. Unit: date. Grain: building x bedroom. When null: fairPsf is null."
                },
                "growth12mPct": {
                  "type": [
                    "number",
                    "null"
                  ],
                  "description": "Change in the median price per square foot, last 12 months against the 12 before. Positive is a rise. Unit: percent. Grain: building × bedroom. When null: One of the two windows has too few sales to compare. A flat market returns 0, not null."
                },
                "medianRent": {
                  "type": [
                    "number",
                    "null"
                  ],
                  "description": "Median annual rent on new Ejari contracts. Registered contracts, not listing asks. Unit: AED per year. Grain: building × bedroom, trailing 24 months. When null: Too few registered contracts in the window to publish a median."
                },
                "medianRentSqft": {
                  "type": [
                    "number",
                    "null"
                  ],
                  "description": "Median annual rent per square foot. Annual, not monthly — the most common misreading of this field. Unit: AED per year per sqft. Grain: building × bedroom, trailing 24 months. When null: No publishable rent median, or no measured area."
                },
                "medianRentRenew": {
                  "type": [
                    "number",
                    "null"
                  ],
                  "description": "Median annual rent on renewal contracts — what a sitting tenant pays, as against medianRent for a new one. Unit: AED per year. Grain: building × bedroom, trailing 24 months. When null: Too few renewals registered in the window."
                },
                "rentSource": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "The grain the rent was measured at: building, project or vicinity. A project- or vicinity-grain rent is pooled from nearby stock and is weaker evidence for this building than a building-grain one. Grain: one metric row. When null: No rent figure on the row."
                },
                "grossYieldPct": {
                  "type": [
                    "number",
                    "null"
                  ],
                  "description": "A year of registered rent as a share of the median resale price. Gross — before service charge, agency fees, maintenance and vacant months. Unit: percent. Grain: building × bedroom. When null: Suppressed, not zero: the price base behind the yield was too thin to publish, or the median price falls below the publication floor."
                },
                "yieldBasis": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "How the yield's denominator was built: 'current' means the last 3 months of sales, 'last_sales' means the last 5 sales however old they are. Grain: one metric row. When null: No published yield on the row."
                },
                "yieldPriceN": {
                  "type": [
                    "integer",
                    "null"
                  ],
                  "description": "How many sales the yield's denominator rests on. Unit: count. Grain: one metric row. When null: No published yield on the row."
                },
                "yieldPriceAsof": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date",
                  "description": "Date of the most recent sale in the yield's denominator. The 'last_sales' fallback is uncapped, so this can be years old — check it before treating a yield as current. Unit: ISO date. Grain: one metric row. When null: No published yield on the row."
                },
                "turnoverUpsidePct": {
                  "type": [
                    "number",
                    "null"
                  ],
                  "description": "The gap between what a new tenant pays and what a sitting tenant renews at, in the same building and bedroom type. Unit: percent. Grain: building × bedroom. When null: Below the materiality floor, or too few renewals. Dubai rents cluster on round numbers, so small gaps are noise and are not published."
                },
                "offplanUpliftPct": {
                  "type": [
                    "number",
                    "null"
                  ],
                  "description": "What off-plan buyers made when their units later resold: resale price against off-plan purchase price. Longitudinal, not a windowed median. Unit: percent. Grain: building × bedroom. When null: No off-plan units in this building have resold. Units that never resold cannot appear, so the published set skews toward the ones that performed."
                },
                "medianAdr": {
                  "type": [
                    "number",
                    "null"
                  ],
                  "description": "Median nightly asking rate from public Airbnb search results, sampled for every month of the next twelve. An asking rate, not a transaction. Unit: AED per night. Grain: building × bedroom. When null: No short-let listings seen for this building and bedroom type."
                },
                "distinctListings": {
                  "type": [
                    "integer",
                    "null"
                  ],
                  "description": "How many distinct public listings the nightly rate was taken from. A floor, not a census of the building's short-let stock. Unit: count. Grain: building × bedroom. When null: No short-let listings seen."
                },
                "txnCount24m": {
                  "type": "integer",
                  "description": "Registered sales in the trailing 24 months. This is the sample size behind the price figures — quote it with them. Unit: count. Grain: building × bedroom. When null: Never null; zero means no registered sales in the window."
                },
                "rentCount24m": {
                  "type": "integer",
                  "description": "Registered new rental contracts in the trailing 24 months. The sample size behind the rent figures. Unit: count. Grain: building × bedroom. When null: Never null; zero means no new contracts registered in the window."
                },
                "isPageEligible": {
                  "type": "boolean",
                  "description": "Whether this row cleared the publication gate. False rows exist in the data and are not published as pages. Grain: one metric row. When null: Never null."
                }
              },
              "additionalProperties": true,
              "required": [
                "bedroom",
                "purpose",
                "isPageEligible"
              ]
            }
          },
          "yearly": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Per-year medians and transaction counts. Rows exist per bedroom type and per purpose — filter to one of each before plotting a line, or a mix shift reads as a price move. Unit: AED and AED per sqft. Grain: building × bedroom × purpose × year. When null: Empty for a building with no history in the registry."
          },
          "monthly": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Monthly medians. Carried at bedroom 'all' only. Future-dated rent contracts exist in the registry, so clip the series at dataAsOf. Unit: AED and AED per sqft. Grain: building × month × purpose. When null: Empty where the monthly series is too thin to publish."
          },
          "shortLetMonthly": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "The forward nightly-rate curve: one row per month of the next twelve, with the listing count behind each. strSource says whether the rate is the building's own or pooled from within 500m. Unit: AED per night. Grain: building × bedroom × stay month. When null: Empty where no short-let pricing was collected."
          }
        },
        "required": [
          "schemaVersion",
          "type",
          "id",
          "canonicalUrl",
          "name",
          "area",
          "dataAsOf",
          "sources",
          "attribution",
          "metrics"
        ]
      },
      "MetricRow": {
        "type": "object",
        "title": "MetricRow",
        "description": "One building × bedroom × purpose. A null is a suppressed figure, never a zero. Rows carry some fields that are not yet in the published field reference at /docs/data-dictionary; only the documented ones below are stable, and an undocumented field may change or disappear without notice.",
        "properties": {
          "bedroom": {
            "type": "string",
            "enum": [
              "all",
              "studio",
              "1br",
              "2br",
              "3br",
              "4br_plus"
            ],
            "description": "One of all, studio, 1br, 2br, 3br, 4br_plus. \"all\" is the building-wide median across every unit size, not a sum or an average of the rows below it. Grain: one metric row. When null: Never null."
          },
          "purpose": {
            "type": "string",
            "enum": [
              "sale",
              "rent"
            ],
            "description": "Which market the row measures: sale, rent, offplan or short_let. A building has a row per bedroom type per purpose. Grain: one metric row. When null: Never null."
          },
          "medianPrice": {
            "type": [
              "number",
              "null"
            ],
            "description": "Median registered sale price. A median of recorded transactions — not an asking price, not a valuation, and not the price of any particular unit. Unit: AED. Grain: building × bedroom, trailing 24 months. When null: Too few registered sales in the window to publish a median that would hold. It does not mean nothing sold."
          },
          "medianPriceSqft": {
            "type": [
              "number",
              "null"
            ],
            "description": "Median registered sale price per square foot of measured floor area. Unit: AED per sqft. Grain: building × bedroom, trailing 24 months. When null: No publishable price median, or no measured area on the records."
          },
          "fairPsf": {
            "type": [
              "number",
              "null"
            ],
            "description": "The recorded price: median price per square foot over the most recent window holding at least five registered sales of READY units — 3 months, else 6, else 12. Ready-only and never wider than a year, unlike medianPriceSqft (24 months, all sales) and yieldPrice (pooled with off-plan, falls back to the last five sales however old). Read it with fairWindowMonths and fairSaleCount; the figure is not publishable without them. Unit: AED per sqft. Grain: building x bedroom, freshest window clearing 5 ready sales. When null: No window in the last 12 months held five ready sales. Not zero and not stale — the building has not traded enough recently for a median to mean anything."
          },
          "fairPrice": {
            "type": [
              "number",
              "null"
            ],
            "description": "Median total price over the same window as fairPsf. A median across the units that happened to trade, never a valuation of one apartment: floor, view, condition and layout all move a real price and none is in this figure. Unit: AED. Grain: building x bedroom, same window as fairPsf. When null: As fairPsf."
          },
          "fairWindowMonths": {
            "type": [
              "integer",
              "null"
            ],
            "enum": [
              3,
              6,
              12,
              null
            ],
            "description": "Which rung of the ladder fairPsf landed on: 3, 6 or 12. A price whose window is unstated is not evidence, so every surface prints this beside the figure. Unit: months. Grain: building x bedroom. When null: fairPsf is null."
          },
          "fairSaleCount": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Registered ready sales inside the window. Never fewer than five: a price computed from two sales moves by a third when one penthouse trades. Unit: sales. Grain: building x bedroom. When null: fairPsf is null."
          },
          "fairAsof": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "Date of the most recent sale inside the window. A 12-month window whose newest sale is eleven months old is a different claim from one closing last week, and this is what separates them. Unit: date. Grain: building x bedroom. When null: fairPsf is null."
          },
          "growth12mPct": {
            "type": [
              "number",
              "null"
            ],
            "description": "Change in the median price per square foot, last 12 months against the 12 before. Positive is a rise. Unit: percent. Grain: building × bedroom. When null: One of the two windows has too few sales to compare. A flat market returns 0, not null."
          },
          "medianRent": {
            "type": [
              "number",
              "null"
            ],
            "description": "Median annual rent on new Ejari contracts. Registered contracts, not listing asks. Unit: AED per year. Grain: building × bedroom, trailing 24 months. When null: Too few registered contracts in the window to publish a median."
          },
          "medianRentSqft": {
            "type": [
              "number",
              "null"
            ],
            "description": "Median annual rent per square foot. Annual, not monthly — the most common misreading of this field. Unit: AED per year per sqft. Grain: building × bedroom, trailing 24 months. When null: No publishable rent median, or no measured area."
          },
          "medianRentRenew": {
            "type": [
              "number",
              "null"
            ],
            "description": "Median annual rent on renewal contracts — what a sitting tenant pays, as against medianRent for a new one. Unit: AED per year. Grain: building × bedroom, trailing 24 months. When null: Too few renewals registered in the window."
          },
          "rentSource": {
            "type": [
              "string",
              "null"
            ],
            "description": "The grain the rent was measured at: building, project or vicinity. A project- or vicinity-grain rent is pooled from nearby stock and is weaker evidence for this building than a building-grain one. Grain: one metric row. When null: No rent figure on the row."
          },
          "grossYieldPct": {
            "type": [
              "number",
              "null"
            ],
            "description": "A year of registered rent as a share of the median resale price. Gross — before service charge, agency fees, maintenance and vacant months. Unit: percent. Grain: building × bedroom. When null: Suppressed, not zero: the price base behind the yield was too thin to publish, or the median price falls below the publication floor."
          },
          "yieldBasis": {
            "type": [
              "string",
              "null"
            ],
            "description": "How the yield's denominator was built: 'current' means the last 3 months of sales, 'last_sales' means the last 5 sales however old they are. Grain: one metric row. When null: No published yield on the row."
          },
          "yieldPriceN": {
            "type": [
              "integer",
              "null"
            ],
            "description": "How many sales the yield's denominator rests on. Unit: count. Grain: one metric row. When null: No published yield on the row."
          },
          "yieldPriceAsof": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "Date of the most recent sale in the yield's denominator. The 'last_sales' fallback is uncapped, so this can be years old — check it before treating a yield as current. Unit: ISO date. Grain: one metric row. When null: No published yield on the row."
          },
          "turnoverUpsidePct": {
            "type": [
              "number",
              "null"
            ],
            "description": "The gap between what a new tenant pays and what a sitting tenant renews at, in the same building and bedroom type. Unit: percent. Grain: building × bedroom. When null: Below the materiality floor, or too few renewals. Dubai rents cluster on round numbers, so small gaps are noise and are not published."
          },
          "offplanUpliftPct": {
            "type": [
              "number",
              "null"
            ],
            "description": "What off-plan buyers made when their units later resold: resale price against off-plan purchase price. Longitudinal, not a windowed median. Unit: percent. Grain: building × bedroom. When null: No off-plan units in this building have resold. Units that never resold cannot appear, so the published set skews toward the ones that performed."
          },
          "medianAdr": {
            "type": [
              "number",
              "null"
            ],
            "description": "Median nightly asking rate from public Airbnb search results, sampled for every month of the next twelve. An asking rate, not a transaction. Unit: AED per night. Grain: building × bedroom. When null: No short-let listings seen for this building and bedroom type."
          },
          "distinctListings": {
            "type": [
              "integer",
              "null"
            ],
            "description": "How many distinct public listings the nightly rate was taken from. A floor, not a census of the building's short-let stock. Unit: count. Grain: building × bedroom. When null: No short-let listings seen."
          },
          "txnCount24m": {
            "type": "integer",
            "description": "Registered sales in the trailing 24 months. This is the sample size behind the price figures — quote it with them. Unit: count. Grain: building × bedroom. When null: Never null; zero means no registered sales in the window."
          },
          "rentCount24m": {
            "type": "integer",
            "description": "Registered new rental contracts in the trailing 24 months. The sample size behind the rent figures. Unit: count. Grain: building × bedroom. When null: Never null; zero means no new contracts registered in the window."
          },
          "isPageEligible": {
            "type": "boolean",
            "description": "Whether this row cleared the publication gate. False rows exist in the data and are not published as pages. Grain: one metric row. When null: Never null."
          }
        },
        "additionalProperties": true,
        "required": [
          "bedroom",
          "purpose",
          "isPageEligible"
        ]
      },
      "Attribution": {
        "type": "object",
        "title": "Attribution",
        "description": "A prebuilt citation for this record in three formats. Copy the one matching your output format rather than composing your own; a figure quoted without its link back is the failure this block exists to prevent.",
        "properties": {
          "plain": {
            "type": "string"
          },
          "markdown": {
            "type": "string"
          },
          "html": {
            "type": "string"
          }
        }
      },
      "Problem": {
        "type": "object",
        "title": "Problem",
        "description": "RFC 9457 problem details. Every JSON surface here fails in this shape; branch on `code`, and `hint` says what to do next.",
        "properties": {
          "type": {
            "type": "string",
            "format": "uri",
            "const": "https://dubaiwealthindex.com/docs/api#errors"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "detail": {
            "type": "string",
            "description": "What went wrong with this request."
          },
          "code": {
            "type": "string",
            "enum": [
              "not_found",
              "bad_request",
              "unauthorized",
              "rate_limited",
              "not_acceptable",
              "server_error"
            ],
            "description": "The stable machine token. Closed set — branch on this."
          },
          "hint": {
            "type": "string",
            "description": "What the caller should do next."
          }
        },
        "required": [
          "type",
          "title",
          "status",
          "detail",
          "code",
          "hint"
        ]
      }
    }
  }
}