# The OffplanIndex API

> A REST read API over the corpus this site is built from: 2,962 off-plan projects, 231 communities and 681 developers across the UAE, priced from 52,882 unit records. Sources read to 2026-07-30, build 2026-10-02. This is the plain-text mirror of [https://offplanindex.com/api-docs](https://offplanindex.com/api-docs); the endpoint list, error codes and sample payloads below are generated from the same code the live routes run, so nothing here can drift from what the API actually returns.

Derived analytics only: aggregates and analysis computed by OffplanIndex from developer published project facts and publicly observed listings. Not a redistribution of any portal's database, and no image URLs are served. See /data-policy for the licence and /data to buy a key. Full terms: [https://offplanindex.com/data-policy](https://offplanindex.com/data-policy). Buy a key: [https://offplanindex.com/data](https://offplanindex.com/data).

## Endpoints

| Method | Path | Returns |
|---|---|---|
| GET | `/api/v1/projects` | Filterable list of every project |
| GET | `/api/v1/projects/{id}` | One project, with its psf position |
| GET | `/api/v1/projects/{id}/history` | Every dated read of one project: handover, status, price per read |
| GET | `/api/v1/changes` | What has been restated since a date: handover, status and price revisions, newest first |
| GET | `/api/v1/communities` | Every tracked community |
| GET | `/api/v1/communities/{slug}` | One community, full aggregate |
| GET | `/api/v1/developers` | Every tracked developer |
| GET | `/api/v1/developers/{slug}` | One developer, full aggregate |
| GET | `/api/v1/market` | Dubai and UAE aggregates |
| GET | `/api/v1/sync` | Cursor delta feed for downstream consumers, HMAC signed, not x-api-key |
| GET | `/api/v1/meta` | Corpus counts and build date, unkeyed |
| GET | `/api/v1/quality` | Every quality gate between a corpus record and a published number, with counts, unkeyed |
| GET | `/api/v1/usage` | Your key's consumer, tier and remaining quota, keyed, never counted |
| GET | `/api/v1/openapi.json` | OpenAPI 3.1 description of every route above, generated from the same builders, unkeyed |

## What to key on

Every project carries `opi_id` (`opi-` plus ten hex, derived from the developer and the project title) beside `id`. Key your store on `opi_id`: `id` is the site slug, a URL, and it changes when two records turn out to be one building, while `opi_id` does not change when a URL changes. In 2026-10 every id that was a portal's own URL slug became our own slug; the old id still resolves on `/api/v1/projects/{id}` and `opi_id` did not change. Every response's `meta` also carries `dataset_version`, the data edition as YYYY-MM; store it beside your `/sync` cursor, because a record's `updated_at` is the day we read it, so a cursor walk will not redeliver a record whose shape gained a field. When `dataset_version` moves and you want the new fields on records you already hold, walk once from the beginning.

## Auth, errors, rate limit

Every endpoint except `/api/v1/meta`, `/api/v1/quality` and `/api/v1/openapi.json` requires a key sent as the `x-api-key` header. Each key sits on a tier that sets a per minute rate limit and a daily quota, reported on every keyed response as `x-ratelimit-limit`, `x-ratelimit-remaining`, `x-quota-limit-day` and `x-quota-remaining-day`; a 429 adds `x-quota-exceeded` naming which window ran out. Read your own usage at `/api/v1/usage`, which never counts against it. A missing key and an unrecognised key return the same 401 body, so a bad key cannot be told apart from no key at all.

Every keyed response carries `Cache-Control: private, no-store` and `Vary: x-api-key`, because a shared cache in front of this API keys on the URL alone and knows nothing about your key. Cache in your own process instead, keyed on `X-Corpus-Version`, which every response carries and which only changes when the corpus is rebuilt. `/api/v1/meta` needs no key, carries no records, and is cacheable for an hour.

| Status | error.code | Meaning |
|---|---|---|
| 400 | `bad_request` | A query parameter was malformed, for example a non numeric price bound. |
| 401 | `unauthorized` | x-api-key is missing or not on the allowlist (same body either way, on purpose). On /sync specifically: a missing HMAC header, an unknown key id, a timestamp more than 300s from server time, or a signature that does not match. |
| 404 | `not_found` | No project, community or developer exists with that id or slug. |
| 429 | `rate_limited` | This key went over its minute rate limit or its daily quota; x-quota-exceeded names which. Check the Retry-After header. Every keyed response carries x-ratelimit-limit, x-ratelimit-remaining, x-quota-limit-day and x-quota-remaining-day. |

## Derived blocks on group records

A developer with ten or more projects read on two dates carries `handover_revisions`: how many of its stated handover dates read later on the second read, how many earlier, the sample size and the median slip. A community or developer with ten or more projects priced on both reads carries `price_movement`: how many quoted a higher starting price, how many lower, and the median move. Both carry their own `caveat` and `not_measured` text in the same object, because neither is what it superficially resembles: the first is not a delivery rate, the second is not a price index.

## Freshness

The corpus is rebuilt periodically from source rather than streamed continuously. `X-Corpus-Version` on every response is set to the build date, so you can tell when the whole corpus last moved. `/api/v1/sync` walks the full project set and stamps each row with a `content_hash`; keep your own last-synced hash per id, pull the current page, and any id whose hash changed is the delta.

## GET /api/v1/projects

Filterable list of every tracked project. Query params: `city`, `community` (slug), `developer` (slug), `status` (sale status), `handover_year` (integer), `min_price` / `max_price` (AED), `bedrooms`, `q` (substring match on title), `limit` (default 50, max 500), `cursor` (opaque, from `meta.next_cursor`).

```
curl -s "https://offplanindex.com/api/v1/projects?city=Dubai&limit=2" -H "x-api-key: YOUR_KEY"
```

```json
{
  "data": [
    {
      "id": "durar-j-one-tower-a",
      "opi_id": "opi-5ec19f55df",
      "title": "J One Tower A",
      "developer": "Durar",
      "developer_slug": "durar",
      "community": "Business Bay",
      "community_slug": "business-bay",
      "city": "Dubai",
      "starting_price": null,
      "delivery_quarter": "Q4 2021",
      "delivery_year": 2021,
      "sale_status": "out_of_stock",
      "construction_status": "completed",
      "property_types": null,
      "bedrooms": null,
      "n_units": 0,
      "n_plans": 0,
      "lat": 25.186162564692015,
      "lng": 55.28328666778401
    },
    {
      "id": "sol-properties-sol-bay",
      "opi_id": "opi-46a17064c8",
      "title": "SOL Bay",
      "developer": "Sol Properties",
      "developer_slug": "sol-properties",
      "community": "Business Bay",
      "community_slug": "business-bay",
      "city": "Dubai",
      "starting_price": 2040000,
      "delivery_quarter": "Q4 2020",
      "delivery_year": 2020,
      "sale_status": "on_sale",
      "construction_status": "completed",
      "property_types": null,
      "bedrooms": "1",
      "n_units": 9,
      "n_plans": 1,
      "lat": 25.18034209709853,
      "lng": 55.273135884655545
    }
  ],
  "meta": {
    "total": 3234,
    "limit": 2,
    "cursor": "MA",
    "next_cursor": "Mg",
    "license": "Derived analytics only: aggregates and analysis computed by OffplanIndex from developer published project facts and publicly observed listings. Not a redistribution of any portal's database, and no image URLs are served. See /data-policy for the licence and /data to buy a key."
  }
}
```

Live sample, filtered to city=Dubai and limit=2. `meta.total` counts the full filtered set, not just this page.

## GET /api/v1/projects/{id}

One project's full record, resolved from its community's data and, if that is missing, from its developer's data. Includes an estimated price per sqft position against its community and against the Dubai market. Path param: `id`, as returned by `/api/v1/projects`.

```
curl -s "https://offplanindex.com/api/v1/projects/danube-properties-bayz-102" -H "x-api-key: YOUR_KEY"
```

```json
{
  "id": "danube-properties-bayz-102",
  "opi_id": "opi-c8c71f2587",
  "title": "Bayz 102",
  "developer": "Danube",
  "developer_slug": "danube",
  "community": "Business Bay",
  "community_slug": "business-bay",
  "city": "Dubai",
  "starting_price": 2525000,
  "delivery_quarter": "Q4 2029",
  "delivery_year": 2029,
  "sale_status": "on_sale",
  "construction_status": "under_construction",
  "property_types": "apartment,penthouse",
  "bedrooms": "1,2,3,4,5",
  "n_units": 299,
  "n_plans": 9,
  "lat": 25.187670501380495,
  "lng": 55.25988699826104,
  "psf_median": 2944,
  "pre_handover_pct": 50,
  "post_handover_pct": 50,
  "psf_position": {
    "community": {
      "percentile_estimate": 39,
      "comparator_median_psf": 3188,
      "delta_vs_median_pct": -7.7,
      "note": "percentile_estimate is a linear interpolation between the comparator's published quantiles (min, p25, median, p75, max), not a rank over the raw distribution."
    },
    "dubai_market": {
      "percentile_estimate": 75,
      "comparator_median_psf": 2021,
      "delta_vs_median_pct": 45.7,
      "note": "percentile_estimate is a linear interpolation between the comparator's published quantiles (min, p25, median, p75, max), not a rank over the raw distribution."
    }
  }
}
```

`psf_position.percentile_estimate` is interpolated between the comparator's published min, p25, median, p75 and max; it is not computed over the raw unit-level distribution. Either side is null when the project has no priced units or the comparator has no psf statistics.

## GET /api/v1/communities

Every tracked community, one summary row each. Query params: `city`, `q` (substring match on name), `limit` (default 100, max 500), `cursor`.

```json
{
  "data": [
    {
      "name": "Dubai Islands",
      "slug": "dubai-islands",
      "city": "Dubai",
      "n_projects": 187,
      "n_units": 2309,
      "psf_median": 2492,
      "psf_p25": 2139,
      "psf_p75": 2962,
      "price_median": 3407000,
      "starting_price_min": 1100000,
      "lat": 25.29912157311391,
      "lng": 55.30294552,
      "rank_psf_dubai": 26,
      "pre_handover_median": 50,
      "n_developers": 87
    },
    {
      "name": "Jumeirah Village Circle",
      "slug": "jumeirah-village-circle",
      "city": "Dubai",
      "n_projects": 169,
      "n_units": 2808,
      "psf_median": 1654,
      "psf_p25": 1462,
      "psf_p75": 1950,
      "price_median": 1308220,
      "starting_price_min": 407000,
      "lat": 25.057261108587365,
      "lng": 55.20858446715988,
      "rank_psf_dubai": 53,
      "pre_handover_median": 60,
      "n_developers": 141
    }
  ],
  "meta": {
    "total": 231,
    "limit": 2,
    "cursor": "MA",
    "next_cursor": "Mg",
    "license": "Derived analytics only: aggregates and analysis computed by OffplanIndex from developer published project facts and publicly observed listings. Not a redistribution of any portal's database, and no image URLs are served. See /data-policy for the licence and /data to buy a key."
  }
}
```

## GET /api/v1/communities/{slug}

One community's full aggregate: price per sqft distribution, payment plan norms, bedroom and type mix, handover pipeline. Path param: `slug`, as returned by `/api/v1/communities`.

```json
{
  "name": "Business Bay",
  "slug": "business-bay",
  "kind": "community",
  "city": "Dubai",
  "lat": 25.184333054495546,
  "lng": 55.27318136402,
  "n_projects": 68,
  "n_records": 134,
  "n_delivered_projects": 66,
  "n_priced_projects": 62,
  "n_projects_with_units": 94,
  "n_units": 1458,
  "n_units_unit_level": 1287,
  "n_projects_with_plans": 104,
  "psf": {
    "n": 1458,
    "min": 474,
    "p25": 2650,
    "median": 3188,
    "p75": 3664,
    "max": 17223,
    "mean": 3451
  },
  "psf_hist": {
    "lo": 2110,
    "hi": 7557,
    "step": 226.98,
    "counts": [
      17,
      171,
      235,
      164,
      169,
      171
    ]
  },
  "h": [
    0,
    1,
    1,
    4,
    11,
    124,
    954,
    275,
    63,
    21,
    3,
    1
  ],
  "price": {
    "n": 1458,
    "min": 650000,
    "p25": 2618250,
    "median": 3612017,
    "p75": 5672046,
    "max": 175000000,
    "mean": 7351024
  },
  "size": {
    "n": 1458,
    "min": 344,
    "p25": 851,
    "median": 1209,
    "p75": 1866,
    "max": 14632,
    "mean": 1764
  },
  "starting_price": {
    "n": 111,
    "min": 650000,
    "p25": 1255000,
    "median": 2207000,
    "p75": 3713914,
    "max": 85000000,
    "mean": 6707673
  },
  "by_bedroom": [
    {
      "bedrooms": "Studio",
      "n": 63,
      "psf": {
        "n": 63,
        "min": 1890,
        "p25": 3062,
        "median": 3531,
        "p75": 3664,
        "max": 6138,
        "mean": 3477
      },
      "price": {
        "n": 63,
        "min": 650000,
        "p25": 1460777,
        "median": 1713777,
        "p75": 1929277,
        "max": 2750000,
        "mean": 1693562
      },
      "size": {
        "n": 63,
        "min": 344,
        "p25": 399,
        "median": 485,
        "p75": 542,
        "max": 741,
        "mean": 502
      }
    },
    {
      "bedrooms": "1 BR",
      "n": 468,
      "psf": {
        "n": 468,
        "min": 474,
        "p25": 2549,
        "median": 2864,
        "p75": 3454,
        "max": 4697,
        "mean": 2978
      },
      "price": {
        "n": 468,
        "min": 1000000,
        "p25": 2150000,
        "median": 2544999,
        "p75": 2778000,
        "max": 4485634,
        "mean": 2543350
      },
      "size": {
        "n": 468,
        "min": 608,
        "p25": 772,
        "median": 826,
        "p75": 975,
        "max": 2112,
        "mean": 867
      }
    },
    {
      "bedrooms": "2 BR",
      "n": 609,
      "psf": {
        "n": 609,
        "min": 786,
        "p25": 2714,
        "median": 2969,
        "p75": 3308,
        "max": 10231,
        "mean": 3156
      },
      "price": {
        "n": 609,
        "min": 1599999,
        "p25": 3482000,
        "median": 3760000,
        "p75": 4700000,
        "max": 31600000,
        "mean": 4900664
      },
      "size": {
        "n": 609,
        "min": 818,
        "p25": 1199,
        "median": 1321,
        "p75": 1481,
        "max": 4544,
        "mean": 1497
      }
    }
  ],
  "by_type": [
    {
      "type": "apartment",
      "n": 1364,
      "psf_median": 3162,
      "psf": {
        "n": 1364,
        "min": 474,
        "p25": 2639,
        "median": 3162,
        "p75": 3564,
        "max": 17223,
        "mean": 3394
      },
      "price": {
        "n": 1364,
        "min": 650000,
        "p25": 2583250,
        "median": 3515000,
        "p75": 4817170,
        "max": 175000000,
        "mean": 6308579
      },
      "size": {
        "n": 1364,
        "min": 344,
        "p25": 835,
        "median": 1199,
        "p75": 1564,
        "max": 12853,
        "mean": 1573
      }
    },
    {
      "type": "duplex",
      "n": 60,
      "psf_median": 3850,
      "psf": {
        "n": 60,
        "min": 923,
        "p25": 2637,
        "median": 3850,
        "p75": 4271,
        "max": 8399,
        "mean": 3865
      },
      "price": {
        "n": 60,
        "min": 3403000,
        "p25": 6627902,
        "median": 10896031,
        "p75": 23000000,
        "max": 80012051,
        "mean": 17606679
      },
      "size": {
        "n": 60,
        "min": 1713,
        "p25": 2283,
        "median": 3314,
        "p75": 5389,
        "max": 10280,
        "mean": 3947
      }
    },
    {
      "type": "penthouse",
      "n": 32,
      "psf_median": 3680,
      "psf": {
        "n": 32,
        "min": 2731,
        "p25": 3166,
        "median": 3680,
        "p75": 5547,
        "max": 16391,
        "mean": 4850
      },
      "price": {
        "n": 32,
        "min": 3702000,
        "p25": 17387016,
        "median": 19283344,
        "p75": 33782210,
        "max": 165000000,
        "mean": 30676061
      },
      "size": {
        "n": 32,
        "min": 1199,
        "p25": 4090,
        "median": 5286,
        "p75": 6186,
        "max": 14632,
        "mean": 5689
      }
    }
  ],
  "pipeline_year": [
    {
      "year": 2020,
      "projects": 3
    },
    {
      "year": 2021,
      "projects": 2
    },
    {
      "year": 2022,
      "projects": 2
    },
    {
      "year": 2023,
      "projects": 15
    }
  ],
  "pipeline_quarter": [
    {
      "quarter": "Q1 2024",
      "projects": 2
    },
    {
      "quarter": "Q2 2024",
      "projects": 4
    },
    {
      "quarter": "Q3 2024",
      "projects": 1
    },
    {
      "quarter": "Q4 2024",
      "projects": 9
    }
  ],
  "sale_status": {
    "on_sale": 40,
    "out_of_stock": 29,
    "sold_out": 20,
    "unknown": 20,
    "booking_started": 12,
    "available": 9,
    "might_be_sold_out": 3,
    "waiting_for_sales_start": 1
  },
  "construction_status": {
    "completed": 57,
    "under_construction": 55,
    "unknown": 20,
    "not_started": 2
  },
  "property_types": {
    "apartment": 79,
    "penthouse": 22,
    "duplex": 17,
    "villa": 3,
    "townhouse": 2
  },
  "payment_plans": {
    "n": 124,
    "booking": {
      "n": 124,
      "min": 0,
      "p25": 10,
      "median": 20,
      "p75": 32,
      "max": 100,
      "mean": 33
    },
    "construction": {
      "n": 124,
      "min": 0,
      "p25": 0,
      "median": 35,
      "p75": 50,
      "max": 80,
      "mean": 30
    },
    "handover": {
      "n": 124,
      "min": 0,
      "p25": 0,
      "median": 30,
      "p75": 50,
      "max": 80,
      "mean": 27
    },
    "post_handover": {
      "n": 25,
      "min": 10,
      "p25": 32,
      "median": 40,
      "p75": 60,
      "max": 90,
      "mean": 48
    },
    "pre_handover": {
      "n": 124,
      "min": 10,
      "p25": 40,
      "median": 60,
      "p75": 80,
      "max": 100,
      "mean": 63
    },
    "share_post_handover": 20,
    "share_pre_handover_60_plus": 59,
    "share_pre_handover_50_or_less": 40
  },
  "down_payment": {
    "n": 66,
    "min": 5,
    "p25": 10,
    "median": 10,
    "p75": 20,
    "max": 50,
    "mean": 14
  },
  "cities": {
    "Dubai": 134
  },
  "n_developers": 50,
  "top_developers": [
    {
      "name": "DAMAC",
      "slug": "damac",
      "projects": 20
    },
    {
      "name": "Select Group",
      "slug": "select-group",
      "projects": 15
    },
    {
      "name": "Omniyat",
      "slug": "omniyat",
      "projects": 9
    }
  ],
  "projects": [
    {
      "id": "danube-properties-bayz-102",
      "opi_id": "opi-c8c71f2587",
      "title": "Bayz 102",
      "developer": "Danube",
      "developer_slug": "danube",
      "starting_price": 2525000,
      "delivery_quarter": "Q4 2029",
      "delivery_year": 2029,
      "sale_status": "on_sale",
      "construction_status": "under_construction",
      "delivery_status": "off_plan",
      "property_types": "apartment,penthouse",
      "n_units": 299,
      "psf_median": 2944,
      "lat": 25.187670501380495,
      "lng": 55.25988699826104,
      "pre_handover_pct": 50,
      "post_handover_pct": 50
    },
    {
      "id": "danube-properties-bayz-101",
      "opi_id": "opi-e69d691941",
      "title": "Bayz 101",
      "developer": "Danube",
      "developer_slug": "danube",
      "starting_price": 1407000,
      "delivery_quarter": "Q2 2029",
      "delivery_year": 2029,
      "sale_status": "on_sale",
      "construction_status": "under_construction",
      "delivery_status": "off_plan",
      "property_types": "apartment",
      "n_units": 146,
      "psf_median": 3432,
      "lat": 25.191826890074058,
      "lng": 55.26334006786653,
      "pre_handover_pct": 50,
      "post_handover_pct": 50
    }
  ],
  "rank_psf_dubai": 12,
  "price_movement": {
    "slug": "business-bay",
    "name": "Business Bay",
    "n_both_priced": 72,
    "up": 5,
    "down": 10,
    "unchanged": 57,
    "median_move_pct": -8.8,
    "method": "Starting price on the same project in two reads three weeks apart, matched on title plus developer. A move counts above 1 percent either way; smaller differences are rounding or a portal formatting choice.",
    "caveat": "Asking prices from two different portals, never transactions. A starting price is the cheapest unit on offer that day, so a lower figure on the second read usually means a cheaper unit came to market, not that the project was repriced; a higher one usually means the cheapest units sold. Lines need at least ten priced projects.",
    "not_measured": "This is not a price index and not a measure of value. It counts projects whose quoted starting price differed between two reads three weeks apart, with the direction. Use it as a liveness signal (which communities are moving inventory) and quote the count of projects behind every line."
  }
}
```

## GET /api/v1/developers

Every tracked developer, one summary row each. Same query params as `/api/v1/communities`.

```json
{
  "data": [
    {
      "name": "Emaar",
      "slug": "emaar",
      "n_projects": 229,
      "n_units": 1712,
      "psf_median": 2068,
      "psf_p25": 1624,
      "psf_p75": 2485,
      "price_median": 3193388,
      "starting_price_min": 660000,
      "rank_psf": 68,
      "rank_projects": 1,
      "pre_handover_median": 80,
      "n_communities": 28,
      "top_communities": [
        {
          "name": "Dubai Creek Harbour",
          "slug": "dubai-creek-harbour",
          "projects": 53
        },
        {
          "name": "The Valley",
          "slug": "the-valley",
          "projects": 49
        },
        {
          "name": "Dubai Hills Estate",
          "slug": "dubai-hills-estate",
          "projects": 46
        }
      ]
    },
    {
      "name": "DAMAC",
      "slug": "damac",
      "n_projects": 173,
      "n_units": 1812,
      "psf_median": 1580,
      "psf_p25": 1398,
      "psf_p75": 1903,
      "price_median": 2669000,
      "starting_price_min": 535500,
      "rank_psf": 128,
      "rank_projects": 2,
      "pre_handover_median": 75,
      "n_communities": 23,
      "top_communities": [
        {
          "name": "DAMAC Lagoons",
          "slug": "damac-lagoons",
          "projects": 45
        },
        {
          "name": "Dubai Investments Park",
          "slug": "dubai-investments-park",
          "projects": 39
        },
        {
          "name": "DAMAC Hills",
          "slug": "damac-hills",
          "projects": 37
        }
      ]
    }
  ],
  "meta": {
    "total": 681,
    "limit": 2,
    "cursor": "MA",
    "next_cursor": "Mg",
    "license": "Derived analytics only: aggregates and analysis computed by OffplanIndex from developer published project facts and publicly observed listings. Not a redistribution of any portal's database, and no image URLs are served. See /data-policy for the licence and /data to buy a key."
  }
}
```

## GET /api/v1/developers/{slug}

One developer's full aggregate, the same shape as a community aggregate. Path param: `slug`, as returned by `/api/v1/developers`.

```json
{
  "name": "Danube",
  "slug": "danube",
  "kind": "developer",
  "n_projects": 22,
  "n_records": 31,
  "n_delivered_projects": 9,
  "n_priced_projects": 21,
  "n_projects_with_units": 29,
  "n_units": 1370,
  "n_units_unit_level": 1275,
  "n_projects_with_plans": 29,
  "psf": {
    "n": 1370,
    "min": 829,
    "p25": 2175,
    "median": 2549,
    "p75": 3336,
    "max": 5063,
    "mean": 2703
  },
  "psf_hist": {
    "lo": 1415,
    "hi": 4627,
    "step": 133.86,
    "counts": [
      60,
      77,
      61,
      53,
      20,
      88
    ]
  },
  "h": [
    0,
    0,
    0,
    5,
    184,
    462,
    520,
    199,
    0,
    0,
    0,
    0
  ],
  "price": {
    "n": 1370,
    "min": 650000,
    "p25": 1487250,
    "median": 2581500,
    "p75": 3623250,
    "max": 35270000,
    "mean": 2773682
  },
  "size": {
    "n": 1370,
    "min": 351,
    "p25": 601,
    "median": 853,
    "p75": 1218,
    "max": 8643,
    "mean": 1087
  },
  "starting_price": {
    "n": 30,
    "min": 399000,
    "p25": 654131,
    "median": 875000,
    "p75": 1196500,
    "max": 3670000,
    "mean": 1058504
  },
  "by_bedroom": [
    {
      "bedrooms": "Studio",
      "n": 203,
      "psf": {
        "n": 203,
        "min": 829,
        "p25": 2391,
        "median": 2422,
        "p75": 3752,
        "max": 5063,
        "mean": 3072
      },
      "price": {
        "n": 203,
        "min": 650000,
        "p25": 949500,
        "median": 1057000,
        "p75": 1522000,
        "max": 2264000,
        "mean": 1284275
      },
      "size": {
        "n": 203,
        "min": 351,
        "p25": 394,
        "median": 411,
        "p75": 439,
        "max": 784,
        "mean": 420
      }
    },
    {
      "bedrooms": "1 BR",
      "n": 517,
      "psf": {
        "n": 517,
        "min": 1051,
        "p25": 2187,
        "median": 2421,
        "p75": 3436,
        "max": 4666,
        "mean": 2693
      },
      "price": {
        "n": 517,
        "min": 1002857,
        "p25": 1423000,
        "median": 1654000,
        "p75": 2617000,
        "max": 3531000,
        "mean": 1922202
      },
      "size": {
        "n": 517,
        "min": 480,
        "p25": 600,
        "median": 749,
        "p75": 793,
        "max": 1099,
        "mean": 713
      }
    },
    {
      "bedrooms": "2 BR",
      "n": 431,
      "psf": {
        "n": 431,
        "min": 1192,
        "p25": 2577,
        "median": 2858,
        "p75": 3236,
        "max": 4344,
        "mean": 2861
      },
      "price": {
        "n": 431,
        "min": 1297461,
        "p25": 3303000,
        "median": 3514000,
        "p75": 3791500,
        "max": 5368000,
        "mean": 3346934
      },
      "size": {
        "n": 431,
        "min": 848,
        "p25": 1107,
        "median": 1178,
        "p75": 1236,
        "max": 1536,
        "mean": 1167
      }
    }
  ],
  "by_type": [
    {
      "type": "apartment",
      "n": 1265,
      "psf_median": 2635,
      "psf": {
        "n": 1265,
        "min": 829,
        "p25": 2300,
        "median": 2635,
        "p75": 3417,
        "max": 5063,
        "mean": 2784
      },
      "price": {
        "n": 1265,
        "min": 650000,
        "p25": 1465000,
        "median": 2283000,
        "p75": 3467000,
        "max": 10635000,
        "mean": 2505655
      },
      "size": {
        "n": 1265,
        "min": 351,
        "p25": 596,
        "median": 806,
        "p75": 1199,
        "max": 3700,
        "mean": 907
      }
    },
    {
      "type": "townhouse",
      "n": 64,
      "psf_median": 1572,
      "psf": {
        "n": 64,
        "min": 1440,
        "p25": 1537,
        "median": 1572,
        "p75": 1588,
        "max": 1634,
        "mean": 1555
      },
      "price": {
        "n": 64,
        "min": 3670000,
        "p25": 3810000,
        "median": 4447000,
        "p75": 4707000,
        "max": 4923000,
        "mean": 4358781
      },
      "size": {
        "n": 64,
        "min": 2400,
        "p25": 2400,
        "median": 2750,
        "p75": 3100,
        "max": 3100,
        "mean": 2805
      }
    },
    {
      "type": "villa",
      "n": 32,
      "psf_median": 1588,
      "psf": {
        "n": 32,
        "min": 1391,
        "p25": 1522,
        "median": 1588,
        "p75": 1848,
        "max": 1974,
        "mean": 1645
      },
      "price": {
        "n": 32,
        "min": 4900000,
        "p25": 5100000,
        "median": 5614000,
        "p75": 7300000,
        "max": 7800000,
        "mean": 5945594
      },
      "size": {
        "n": 32,
        "min": 3100,
        "p25": 3100,
        "median": 3700,
        "p75": 3950,
        "max": 3950,
        "mean": 3602
      }
    }
  ],
  "pipeline_year": [
    {
      "year": 2023,
      "projects": 2
    },
    {
      "year": 2024,
      "projects": 3
    },
    {
      "year": 2025,
      "projects": 4
    },
    {
      "year": 2026,
      "projects": 5
    }
  ],
  "pipeline_quarter": [
    {
      "quarter": "Q1 2024",
      "projects": 2
    },
    {
      "quarter": "Q4 2024",
      "projects": 1
    },
    {
      "quarter": "Q2 2025",
      "projects": 2
    },
    {
      "quarter": "Q4 2025",
      "projects": 2
    }
  ],
  "sale_status": {
    "on_sale": 15,
    "out_of_stock": 8,
    "sold_out": 6,
    "unknown": 1,
    "available": 1
  },
  "construction_status": {
    "under_construction": 20,
    "completed": 9,
    "unknown": 1,
    "not_started": 1
  },
  "property_types": {
    "apartment": 28,
    "duplex": 5,
    "penthouse": 2,
    "townhouse": 2,
    "villa": 1
  },
  "payment_plans": {
    "n": 86,
    "booking": {
      "n": 86,
      "min": 0,
      "p25": 10,
      "median": 20,
      "p75": 50,
      "max": 100,
      "mean": 34
    },
    "construction": {
      "n": 86,
      "min": 0,
      "p25": 12,
      "median": 36,
      "p75": 50,
      "max": 65,
      "mean": 31
    },
    "handover": {
      "n": 86,
      "min": 0,
      "p25": 0,
      "median": 16,
      "p75": 30,
      "max": 70,
      "mean": 21
    },
    "post_handover": {
      "n": 34,
      "min": 5,
      "p25": 30,
      "median": 37,
      "p75": 50,
      "max": 70,
      "mean": 38
    },
    "pre_handover": {
      "n": 86,
      "min": 30,
      "p25": 52,
      "median": 66,
      "p75": 70,
      "max": 100,
      "mean": 64
    },
    "share_post_handover": 40,
    "share_pre_handover_60_plus": 74,
    "share_pre_handover_50_or_less": 26
  },
  "down_payment": {
    "n": 25,
    "min": 10,
    "p25": 10,
    "median": 10,
    "p75": 10,
    "max": 40,
    "mean": 12
  },
  "cities": {
    "Dubai": 31
  },
  "n_communities": 13,
  "top_communities": [
    {
      "name": "Dubai Maritime City",
      "slug": "dubai-maritime-city",
      "projects": 5
    },
    {
      "name": "Jumeirah Village Circle",
      "slug": "jumeirah-village-circle",
      "projects": 5
    },
    {
      "name": "Dubai Silicon Oasis",
      "slug": "dubai-silicon-oasis",
      "projects": 3
    }
  ],
  "projects": [
    {
      "id": "danube-properties-serenz",
      "opi_id": "opi-ff1ad99641",
      "title": "Serenz",
      "community": "Jumeirah Village Circle",
      "community_slug": "jumeirah-village-circle",
      "city": "Dubai",
      "starting_price": 903000,
      "delivery_quarter": "Q4 2029",
      "delivery_year": 2029,
      "sale_status": "on_sale",
      "construction_status": "under_construction",
      "delivery_status": "off_plan",
      "property_types": "apartment",
      "n_units": 300,
      "psf_median": 2389,
      "pre_handover_pct": 50,
      "post_handover_pct": 50
    },
    {
      "id": "danube-properties-bayz-102",
      "opi_id": "opi-c8c71f2587",
      "title": "Bayz 102",
      "community": "Business Bay",
      "community_slug": "business-bay",
      "city": "Dubai",
      "starting_price": 2525000,
      "delivery_quarter": "Q4 2029",
      "delivery_year": 2029,
      "sale_status": "on_sale",
      "construction_status": "under_construction",
      "delivery_status": "off_plan",
      "property_types": "apartment,penthouse",
      "n_units": 299,
      "psf_median": 2944,
      "pre_handover_pct": 50,
      "post_handover_pct": 50
    }
  ],
  "rank_psf": 34,
  "rank_projects": 21,
  "handover_revisions": {
    "slug": "danube",
    "name": "Danube",
    "n_both_dated": 29,
    "moved_later": 0,
    "moved_earlier": 4,
    "unchanged": 25,
    "later_pct": 0,
    "median_slip_months": null,
    "method": "Projects matched on normalised title plus developer (no shared id exists). A handover counts as moved later only when the stated date moved to a later quarter; the July side often carries a quarter-end date, so a move within the same quarter is rounding, not a revision. Dates that moved earlier are reported but never ranked on, because a date reading earlier on a second portal is far more often disagreement between the two sources than a project pulled forward.",
    "caveat": "Two different portals read three weeks apart, not two reads of one listing: a difference can be a portal disagreement rather than a developer revision. Every further dated read adds a point of comparison.",
    "not_measured": "This is not a delivery rate and not an on-time percentage. It counts stated handover dates that read later on a second portal three weeks after the first. Across the whole set, 362 dates read earlier against 105 that read later. That gap is the noise floor of comparing two portals, and it is why the count matters more than the percentage: read every figure with the number of projects behind it."
  },
  "price_movement": {
    "slug": "danube",
    "name": "Danube",
    "n_both_priced": 29,
    "up": 2,
    "down": 5,
    "unchanged": 22,
    "median_move_pct": -7,
    "method": "Starting price on the same project in two reads three weeks apart, matched on title plus developer. A move counts above 1 percent either way; smaller differences are rounding or a portal formatting choice.",
    "caveat": "Asking prices from two different portals, never transactions. A starting price is the cheapest unit on offer that day, so a lower figure on the second read usually means a cheaper unit came to market, not that the project was repriced; a higher one usually means the cheapest units sold. Lines need at least ten priced projects.",
    "not_measured": "This is not a price index and not a measure of value. It counts projects whose quoted starting price differed between two reads three weeks apart, with the direction. Use it as a liveness signal (which communities are moving inventory) and quote the count of projects behind every line."
  }
}
```

## GET /api/v1/market

Dubai and UAE aggregates: the same medians, quartiles and pipeline the homepage and the emirate pages read.

```json
{
  "data": {
    "dubai": {
      "name": "Dubai",
      "slug": "dubai",
      "kind": "market",
      "n_projects": 2351,
      "n_records": 3234,
      "n_delivered_projects": 883,
      "n_priced_projects": 2101,
      "n_projects_with_units": 2532,
      "n_units": 42490,
      "n_units_unit_level": 34894,
      "n_projects_with_plans": 2539,
      "psf": {
        "n": 42490,
        "min": 272,
        "p25": 1608,
        "median": 2021,
        "p75": 2649,
        "max": 18352,
        "mean": 2257
      },
      "psf_hist": {
        "lo": 1021,
        "hi": 4870,
        "step": 160.36,
        "counts": [
          1314,
          2490,
          3275,
          4105,
          4596,
          3661,
          3004,
          3533,
          2575,
          2277,
          1195,
          1859,
          973,
          1076,
          1019,
          1244,
          671,
          902,
          413,
          229,
          119,
          112,
          106,
          81
        ]
      },
      "h": [
        8,
        16,
        127,
        1749,
        11294,
        16675,
        9502,
        2530,
        461,
        117,
        9,
        2
      ],
      "price": {
        "n": 42490,
        "min": 375888,
        "p25": 1249500,
        "median": 1910000,
        "p75": 3081230,
        "max": 500000000,
        "mean": 3210918
      },
      "size": {
        "n": 42490,
        "min": 260,
        "p25": 686,
        "median": 916,
        "p75": 1420,
        "max": 39004,
        "mean": 1320
      },
      "starting_price": {
        "n": 2824,
        "min": 310000,
        "p25": 843892,
        "median": 1500000,
        "p75": 2750000,
        "max": 330000000,
        "mean": 4548031
      },
      "by_bedroom": [
        {
          "bedrooms": "Studio",
          "n": 7540,
          "psf": {
            "n": 7540,
            "min": 510,
            "p25": 1806,
            "median": 2280,
            "p75": 2554,
            "max": 6138,
            "mean": 2392
          },
          "price": {
            "n": 7540,
            "min": 375888,
            "p25": 752000,
            "median": 828000,
            "p75": 1052000,
            "max": 4025641,
            "mean": 927573
          },
          "size": {
            "n": 7540,
            "min": 260,
            "p25": 340,
            "median": 375,
            "p75": 408,
            "max": 1204,
            "mean": 393
          }
        },
        {
          "bedrooms": "1 BR",
          "n": 15866,
          "psf": {
            "n": 15866,
            "min": 279,
            "p25": 1613,
            "median": 1916,
            "p75": 2690,
            "max": 7420,
            "mean": 2158
          },
          "price": {
            "n": 15866,
            "min": 400000,
            "p25": 1230000,
            "median": 1500000,
            "p75": 2089894,
            "max": 9337000,
            "mean": 1704974
          },
          "size": {
            "n": 15866,
            "min": 313,
            "p25": 697,
            "median": 754,
            "p75": 857,
            "max": 3138,
            "mean": 797
          }
        },
        {
          "bedrooms": "2 BR",
          "n": 11413,
          "psf": {
            "n": 11413,
            "min": 414,
            "p25": 1539,
            "median": 2027,
            "p75": 2630,
            "max": 11869,
            "mean": 2196
          },
          "price": {
            "n": 11413,
            "min": 879323,
            "p25": 1880000,
            "median": 2529000,
            "p75": 3412777,
            "max": 31600000,
            "mean": 2928842
          },
          "size": {
            "n": 11413,
            "min": 639,
            "p25": 1105,
            "median": 1237,
            "p75": 1446,
            "max": 5645,
            "mean": 1317
          }
        },
        {
          "bedrooms": "3 BR",
          "n": 4301,
          "psf": {
            "n": 4301,
            "min": 277,
            "p25": 1503,
            "median": 2004,
            "p75": 3024,
            "max": 12229,
            "mean": 2488
          },
          "price": {
            "n": 4301,
            "min": 1000000,
            "p25": 2877497,
            "median": 4000000,
            "p75": 6000000,
            "max": 72873192,
            "mean": 5792939
          },
          "size": {
            "n": 4301,
            "min": 934,
            "p25": 1644,
            "median": 1912,
            "p75": 2418,
            "max": 9490,
            "mean": 2165
          }
        },
        {
          "bedrooms": "4 BR",
          "n": 1935,
          "psf": {
            "n": 1935,
            "min": 272,
            "p25": 1522,
            "median": 1900,
            "p75": 2862,
            "max": 17223,
            "mean": 2521
          },
          "price": {
            "n": 1935,
            "min": 1210699,
            "p25": 4291500,
            "median": 4950000,
            "p75": 10400000,
            "max": 175000000,
            "mean": 10815593
          },
          "size": {
            "n": 1935,
            "min": 1521,
            "p25": 2279,
            "median": 2974,
            "p75": 4390,
            "max": 15202,
            "mean": 3615
          }
        },
        {
          "bedrooms": "5 BR",
          "n": 1086,
          "psf": {
            "n": 1086,
            "min": 670,
            "p25": 1287,
            "median": 1633,
            "p75": 1990,
            "max": 12050,
            "mean": 1957
          },
          "price": {
            "n": 1086,
            "min": 2880000,
            "p25": 4098000,
            "median": 5430000,
            "p75": 13000000,
            "max": 140433333,
            "mean": 11272253
          },
          "size": {
            "n": 1086,
            "min": 2403,
            "p25": 3158,
            "median": 3407,
            "p75": 6083,
            "max": 19663,
            "mean": 4904
          }
        },
        {
          "bedrooms": "6+ BR",
          "n": 348,
          "psf": {
            "n": 348,
            "min": 637,
            "p25": 1696,
            "median": 2016,
            "p75": 2494,
            "max": 18352,
            "mean": 2473
          },
          "price": {
            "n": 348,
            "min": 4800000,
            "p25": 8230000,
            "median": 19026290,
            "p75": 29607945,
            "max": 500000000,
            "mean": 31192441
          },
          "size": {
            "n": 348,
            "min": 3613,
            "p25": 5749,
            "median": 8614,
            "p75": 13119,
            "max": 39004,
            "mean": 10932
          }
        }
      ],
      "by_type": [
        {
          "type": "apartment",
          "n": 38559,
          "psf_median": 2084,
          "psf": {
            "n": 38559,
            "min": 310,
            "p25": 1644,
            "median": 2084,
            "p75": 2722,
            "max": 17223,
            "mean": 2287
          },
          "price": {
            "n": 38559,
            "min": 375888,
            "p25": 1204988,
            "median": 1725232,
            "p75": 2640000,
            "max": 175000000,
            "mean": 2557130
          },
          "size": {
            "n": 38559,
            "min": 260,
            "p25": 662,
            "median": 835,
            "p75": 1240,
            "max": 25317,
            "mean": 1029
          }
        },
        {
          "type": "townhouse",
          "n": 1642,
          "psf_median": 1351,
          "psf": {
            "n": 1642,
            "min": 272,
            "p25": 1108,
            "median": 1351,
            "p75": 1684,
            "max": 12862,
            "mean": 1481
          },
          "price": {
            "n": 1642,
            "min": 535516,
            "p25": 2799860,
            "median": 3643000,
            "p75": 4340000,
            "max": 68000000,
            "mean": 3885348
          },
          "size": {
            "n": 1642,
            "min": 1250,
            "p25": 2041,
            "median": 2403,
            "p75": 2906,
            "max": 15000,
            "mean": 2612
          }
        },
        {
          "type": "villa",
          "n": 1603,
          "psf_median": 1770,
          "psf": {
            "n": 1603,
            "min": 538,
            "p25": 1515,
            "median": 1770,
            "p75": 1990,
            "max": 18352,
            "mean": 1835
          },
          "price": {
            "n": 1603,
            "min": 2000000,
            "p25": 4571000,
            "median": 7289481,
            "p75": 13435000,
            "max": 500000000,
            "mean": 11779165
          },
          "size": {
            "n": 1603,
            "min": 1679,
            "p25": 3158,
            "median": 4162,
            "p75": 6774,
            "max": 39004,
            "mean": 5723
          }
        },
        {
          "type": "duplex",
          "n": 420,
          "psf_median": 2615,
          "psf": {
            "n": 420,
            "min": 546,
            "p25": 1672,
            "median": 2615,
            "p75": 3836,
            "max": 11425,
            "mean": 3107
          },
          "price": {
            "n": 420,
            "min": 1204777,
            "p25": 3601000,
            "median": 6330000,
            "p75": 13466250,
            "max": 81333000,
            "mean": 12736055
          },
          "size": {
            "n": 420,
            "min": 690,
            "p25": 2074,
            "median": 2669,
            "p75": 4148,
            "max": 12410,
            "mean": 3344
          }
        },
        {
          "type": "penthouse",
          "n": 252,
          "psf_median": 3657,
          "psf": {
            "n": 252,
            "min": 1042,
            "p25": 2612,
            "median": 3657,
            "p75": 5016,
            "max": 16391,
            "mean": 4054
          },
          "price": {
            "n": 252,
            "min": 2529900,
            "p25": 8249029,
            "median": 18618000,
            "p75": 31765000,
            "max": 220000000,
            "mean": 28210995
          },
          "size": {
            "n": 252,
            "min": 1199,
            "p25": 3341,
            "median": 5184,
            "p75": 6850,
            "max": 32000,
            "mean": 5898
          }
        },
        {
          "type": "other",
          "n": 12,
          "psf_median": 2266,
          "psf": {
            "n": 12,
            "min": 1907,
            "p25": 2243,
            "median": 2266,
            "p75": 2340,
            "max": 2823,
            "mean": 2314
          },
          "price": {
            "n": 12,
            "min": 1769828,
            "p25": 2702828,
            "median": 2975828,
            "p75": 3014578,
            "max": 4201888,
            "mean": 2922333
          },
          "size": {
            "n": 12,
            "min": 756,
            "p25": 1163,
            "median": 1334,
            "p75": 1334,
            "max": 2203,
            "mean": 1278
          }
        }
      ],
      "pipeline_year": [
        {
          "year": 2020,
          "projects": 26
        },
        {
          "year": 2021,
          "projects": 19
        },
        {
          "year": 2022,
          "projects": 34
        },
        {
          "year": 2023,
          "projects": 161
        },
        {
          "year": 2024,
          "projects": 233
        },
        {
          "year": 2025,
          "projects": 318
        },
        {
          "year": 2026,
          "projects": 547
        },
        {
          "year": 2027,
          "projects": 750
        },
        {
          "year": 2028,
          "projects": 586
        },
        {
          "year": 2029,
          "projects": 342
        },
        {
          "year": 2030,
          "projects": 97
        },
        {
          "year": 2031,
          "projects": 10
        },
        {
          "year": 2032,
          "projects": 1
        }
      ],
      "pipeline_quarter": [
        {
          "quarter": "Q1 2024",
          "projects": 50
        },
        {
          "quarter": "Q2 2024",
          "projects": 37
        },
        {
          "quarter": "Q3 2024",
          "projects": 39
        },
        {
          "quarter": "Q4 2024",
          "projects": 107
        },
        {
          "quarter": "Q1 2025",
          "projects": 58
        },
        {
          "quarter": "Q2 2025",
          "projects": 57
        },
        {
          "quarter": "Q3 2025",
          "projects": 68
        },
        {
          "quarter": "Q4 2025",
          "projects": 135
        },
        {
          "quarter": "Q1 2026",
          "projects": 83
        },
        {
          "quarter": "Q2 2026",
          "projects": 104
        },
        {
          "quarter": "Q3 2026",
          "projects": 137
        },
        {
          "quarter": "Q4 2026",
          "projects": 223
        },
        {
          "quarter": "Q1 2027",
          "projects": 160
        },
        {
          "quarter": "Q2 2027",
          "projects": 179
        },
        {
          "quarter": "Q3 2027",
          "projects": 151
        },
        {
          "quarter": "Q4 2027",
          "projects": 260
        },
        {
          "quarter": "Q1 2028",
          "projects": 130
        },
        {
          "quarter": "Q2 2028",
          "projects": 146
        },
        {
          "quarter": "Q3 2028",
          "projects": 100
        },
        {
          "quarter": "Q4 2028",
          "projects": 210
        },
        {
          "quarter": "Q1 2029",
          "projects": 107
        },
        {
          "quarter": "Q2 2029",
          "projects": 78
        },
        {
          "quarter": "Q3 2029",
          "projects": 58
        },
        {
          "quarter": "Q4 2029",
          "projects": 99
        },
        {
          "quarter": "Q1 2030",
          "projects": 30
        },
        {
          "quarter": "Q2 2030",
          "projects": 41
        },
        {
          "quarter": "Q3 2030",
          "projects": 9
        },
        {
          "quarter": "Q4 2030",
          "projects": 17
        },
        {
          "quarter": "Q1 2031",
          "projects": 3
        },
        {
          "quarter": "Q2 2031",
          "projects": 3
        },
        {
          "quarter": "Q4 2031",
          "projects": 4
        },
        {
          "quarter": "Q4 2032",
          "projects": 1
        }
      ],
      "sale_status": {
        "on_sale": 805,
        "out_of_stock": 733,
        "sold_out": 546,
        "unknown": 346,
        "booking_started": 344,
        "available": 277,
        "might_be_sold_out": 83,
        "waiting_for_sales_start": 72,
        "presale": 24,
        "start_of_sales": 2,
        "announced": 2
      },
      "construction_status": {
        "under_construction": 2000,
        "completed": 816,
        "unknown": 346,
        "not_started": 72
      },
      "property_types": {
        "apartment": 1841,
        "villa": 367,
        "townhouse": 306,
        "duplex": 233,
        "penthouse": 193,
        "land": 3
      },
      "payment_plans": {
        "n": 3315,
        "booking": {
          "n": 3315,
          "min": 0,
          "p25": 10,
          "median": 20,
          "p75": 20,
          "max": 100,
          "mean": 25
        },
        "construction": {
          "n": 3315,
          "min": 0,
          "p25": 20,
          "median": 40,
          "p75": 50,
          "max": 100,
          "mean": 36
        },
        "handover": {
          "n": 3315,
          "min": 0,
          "p25": 10,
          "median": 30,
          "p75": 50,
          "max": 90,
          "mean": 31
        },
        "post_handover": {
          "n": 561,
          "min": 5,
          "p25": 30,
          "median": 40,
          "p75": 50,
          "max": 90,
          "mean": 42
        },
        "pre_handover": {
          "n": 3315,
          "min": 0,
          "p25": 50,
          "median": 60,
          "p75": 80,
          "max": 100,
          "mean": 61
        },
        "share_post_handover": 17,
        "share_pre_handover_60_plus": 57,
        "share_pre_handover_50_or_less": 41
      },
      "down_payment": {
        "n": 1855,
        "min": 1,
        "p25": 10,
        "median": 10,
        "p75": 20,
        "max": 70,
        "mean": 15
      },
      "cities": {
        "Dubai": 3234
      }
    },
    "uae": {
      "name": "UAE",
      "slug": "uae",
      "kind": "market",
      "n_projects": 2962,
      "n_records": 4045,
      "n_delivered_projects": 1083,
      "n_priced_projects": 2643,
      "n_projects_with_units": 3114,
      "n_units": 52882,
      "n_units_unit_level": 43771,
      "n_projects_with_plans": 3135,
      "psf": {
        "n": 52882,
        "min": 259,
        "p25": 1548,
        "median": 2008,
        "p75": 2675,
        "max": 24165,
        "mean": 2200
      },
      "psf_hist": {
        "lo": 695,
        "hi": 4675,
        "step": 165.83,
        "counts": [
          1152,
          1291,
          1723,
          3491,
          3785,
          4829,
          5258,
          4233,
          3620,
          3531,
          3196,
          2595,
          2623,
          2097,
          944,
          1214,
          1524,
          1006,
          1075,
          552,
          382,
          259,
          226,
          161
        ]
      },
      "h": [
        18,
        408,
        1105,
        3304,
        12941,
        18939,
        12403,
        3125,
        509,
        118,
        9,
        3
      ],
      "price": {
        "n": 52882,
        "min": 163665,
        "p25": 1252999,
        "median": 1915160,
        "p75": 3156000,
        "max": 500000000,
        "mean": 3244700
      },
      "size": {
        "n": 52882,
        "min": 260,
        "p25": 701,
        "median": 992,
        "p75": 1512,
        "max": 39004,
        "mean": 1390
      },
      "starting_price": {
        "n": 3525,
        "min": 163665,
        "p25": 851337,
        "median": 1520999,
        "p75": 2800000,
        "max": 330000000,
        "mean": 4351529
      },
      "by_bedroom": [
        {
          "bedrooms": "Studio",
          "n": 8104,
          "psf": {
            "n": 8104,
            "min": 259,
            "p25": 1812,
            "median": 2290,
            "p75": 2662,
            "max": 6848,
            "mean": 2406
          },
          "price": {
            "n": 8104,
            "min": 163665,
            "p25": 756192,
            "median": 835000,
            "p75": 1283448,
            "max": 8161512,
            "mean": 958200
          },
          "size": {
            "n": 8104,
            "min": 260,
            "p25": 341,
            "median": 380,
            "p75": 422,
            "max": 1290,
            "mean": 402
          }
        },
        {
          "bedrooms": "1 BR",
          "n": 19139,
          "psf": {
            "n": 19139,
            "min": 279,
            "p25": 1549,
            "median": 1888,
            "p75": 2667,
            "max": 7420,
            "mean": 2092
          },
          "price": {
            "n": 19139,
            "min": 313245,
            "p25": 1169999,
            "median": 1459000,
            "p75": 2078278,
            "max": 12369821,
            "mean": 1681919
          },
          "size": {
            "n": 19139,
            "min": 313,
            "p25": 697,
            "median": 771,
            "p75": 899,
            "max": 3138,
            "mean": 824
          }
        },
        {
          "bedrooms": "2 BR",
          "n": 15722,
          "psf": {
            "n": 15722,
            "min": 292,
            "p25": 1459,
            "median": 2014,
            "p75": 2690,
            "max": 11869,
            "mean": 2131
          },
          "price": {
            "n": 15722,
            "min": 551561,
            "p25": 1730000,
            "median": 2437416,
            "p75": 3331000,
            "max": 31600000,
            "mean": 2822493
          },
          "size": {
            "n": 15722,
            "min": 639,
            "p25": 1083,
            "median": 1245,
            "p75": 1490,
            "max": 5903,
            "mean": 1334
          }
        },
        {
          "bedrooms": "3 BR",
          "n": 5648,
          "psf": {
            "n": 5648,
            "min": 277,
            "p25": 1453,
            "median": 2000,
            "p75": 2909,
            "max": 24165,
            "mean": 2402
          },
          "price": {
            "n": 5648,
            "min": 606825,
            "p25": 2877497,
            "median": 4180000,
            "p75": 6003638,
            "max": 119500000,
            "mean": 5720408
          },
          "size": {
            "n": 5648,
            "min": 934,
            "p25": 1690,
            "median": 2022,
            "p75": 2630,
            "max": 9490,
            "mean": 2257
          }
        },
        {
          "bedrooms": "4 BR",
          "n": 2455,
          "psf": {
            "n": 2455,
            "min": 272,
            "p25": 1414,
            "median": 1900,
            "p75": 2535,
            "max": 17223,
            "mean": 2380
          },
          "price": {
            "n": 2455,
            "min": 1210699,
            "p25": 4291500,
            "median": 5223750,
            "p75": 9915574,
            "max": 175000000,
            "mean": 10181138
          },
          "size": {
            "n": 2455,
            "min": 1521,
            "p25": 2438,
            "median": 3139,
            "p75": 4507,
            "max": 15202,
            "mean": 3700
          }
        },
        {
          "bedrooms": "5 BR",
          "n": 1366,
          "psf": {
            "n": 1366,
            "min": 461,
            "p25": 1282,
            "median": 1748,
            "p75": 2162,
            "max": 12050,
            "mean": 2082
          },
          "price": {
            "n": 1366,
            "min": 2304000,
            "p25": 4147250,
            "median": 6242500,
            "p75": 14096365,
            "max": 146722777,
            "mean": 12477935
          },
          "size": {
            "n": 1366,
            "min": 1842,
            "p25": 3158,
            "median": 4107,
            "p75": 6346,
            "max": 24798,
            "mean": 5134
          }
        },
        {
          "bedrooms": "6+ BR",
          "n": 446,
          "psf": {
            "n": 446,
            "min": 637,
            "p25": 1660,
            "median": 1952,
            "p75": 2490,
            "max": 18352,
            "mean": 2357
          },
          "price": {
            "n": 446,
            "min": 4257000,
            "p25": 7869000,
            "median": 18000000,
            "p75": 27561985,
            "max": 500000000,
            "mean": 28880884
          },
          "size": {
            "n": 446,
            "min": 3613,
            "p25": 5749,
            "median": 8186,
            "p75": 12800,
            "max": 39004,
            "mean": 10504
          }
        }
      ],
      "by_type": [
        {
          "type": "apartment",
          "n": 47448,
          "psf_median": 2047,
          "psf": {
            "n": 47448,
            "min": 259,
            "p25": 1580,
            "median": 2047,
            "p75": 2718,
            "max": 17223,
            "mean": 2217
          },
          "price": {
            "n": 47448,
            "min": 163665,
            "p25": 1200000,
            "median": 1721996,
            "p75": 2655000,
            "max": 175000000,
            "mean": 2512676
          },
          "size": {
            "n": 47448,
            "min": 260,
            "p25": 684,
            "median": 894,
            "p75": 1285,
            "max": 25317,
            "mean": 1076
          }
        },
        {
          "type": "villa",
          "n": 2210,
          "psf_median": 1774,
          "psf": {
            "n": 2210,
            "min": 461,
            "p25": 1427,
            "median": 1774,
            "p75": 2099,
            "max": 24165,
            "mean": 1949
          },
          "price": {
            "n": 2210,
            "min": 2000000,
            "p25": 4811250,
            "median": 7807400,
            "p75": 13795710,
            "max": 500000000,
            "mean": 12420362
          },
          "size": {
            "n": 2210,
            "min": 1679,
            "p25": 3275,
            "median": 4406,
            "p75": 6670,
            "max": 39004,
            "mean": 5740
          }
        },
        {
          "type": "townhouse",
          "n": 1936,
          "psf_median": 1320,
          "psf": {
            "n": 1936,
            "min": 272,
            "p25": 1110,
            "median": 1320,
            "p75": 1745,
            "max": 12862,
            "mean": 1553
          },
          "price": {
            "n": 1936,
            "min": 535516,
            "p25": 2800000,
            "median": 3664500,
            "p75": 4387213,
            "max": 68000000,
            "mean": 4123655
          },
          "size": {
            "n": 1936,
            "min": 1250,
            "p25": 2117,
            "median": 2418,
            "p75": 3014,
            "max": 15000,
            "mean": 2657
          }
        },
        {
          "type": "duplex",
          "n": 860,
          "psf_median": 2329,
          "psf": {
            "n": 860,
            "min": 546,
            "p25": 1713,
            "median": 2329,
            "p75": 3118,
            "max": 11425,
            "mean": 2698
          },
          "price": {
            "n": 860,
            "min": 501740,
            "p25": 3761088,
            "median": 5257481,
            "p75": 8002454,
            "max": 81333000,
            "mean": 9228948
          },
          "size": {
            "n": 860,
            "min": 398,
            "p25": 2027,
            "median": 2263,
            "p75": 3239,
            "max": 12410,
            "mean": 2926
          }
        },
        {
          "type": "penthouse",
          "n": 368,
          "psf_median": 3163,
          "psf": {
            "n": 368,
            "min": 346,
            "p25": 2175,
            "median": 3163,
            "p75": 4271,
            "max": 16391,
            "mean": 3553
          },
          "price": {
            "n": 368,
            "min": 1357500,
            "p25": 7600000,
            "median": 13390000,
            "p75": 26273075,
            "max": 220000000,
            "mean": 23587988
          },
          "size": {
            "n": 368,
            "min": 1199,
            "p25": 3111,
            "median": 4695,
            "p75": 6194,
            "max": 32000,
            "mean": 5546
          }
        },
        {
          "type": "hotel apartment",
          "n": 38,
          "psf_median": 6269,
          "psf": {
            "n": 38,
            "min": 1955,
            "p25": 5770,
            "median": 6269,
            "p75": 6600,
            "max": 6848,
            "mean": 5441
          },
          "price": {
            "n": 38,
            "min": 2520000,
            "p25": 3181192,
            "median": 3583701,
            "p75": 5473750,
            "max": 12369821,
            "mean": 4682575
          },
          "size": {
            "n": 38,
            "min": 484,
            "p25": 552,
            "median": 595,
            "p75": 1190,
            "max": 3880,
            "mean": 1025
          }
        },
        {
          "type": "other",
          "n": 20,
          "psf_median": 2230,
          "psf": {
            "n": 20,
            "min": 1019,
            "p25": 2032,
            "median": 2230,
            "p75": 2277,
            "max": 2823,
            "mean": 2084
          },
          "price": {
            "n": 20,
            "min": 1769828,
            "p25": 2175755,
            "median": 2944828,
            "p75": 3304328,
            "max": 5102578,
            "mean": 3039770
          },
          "size": {
            "n": 20,
            "min": 756,
            "p25": 1053,
            "median": 1334,
            "p75": 1571,
            "max": 3371,
            "mean": 1566
          }
        }
      ],
      "pipeline_year": [
        {
          "year": 2020,
          "projects": 27
        },
        {
          "year": 2021,
          "projects": 21
        },
        {
          "year": 2022,
          "projects": 42
        },
        {
          "year": 2023,
          "projects": 185
        },
        {
          "year": 2024,
          "projects": 291
        },
        {
          "year": 2025,
          "projects": 390
        },
        {
          "year": 2026,
          "projects": 655
        },
        {
          "year": 2027,
          "projects": 880
        },
        {
          "year": 2028,
          "projects": 767
        },
        {
          "year": 2029,
          "projects": 479
        },
        {
          "year": 2030,
          "projects": 135
        },
        {
          "year": 2031,
          "projects": 14
        },
        {
          "year": 2032,
          "projects": 1
        }
      ],
      "pipeline_quarter": [
        {
          "quarter": "Q1 2024",
          "projects": 59
        },
        {
          "quarter": "Q2 2024",
          "projects": 48
        },
        {
          "quarter": "Q3 2024",
          "projects": 48
        },
        {
          "quarter": "Q4 2024",
          "projects": 136
        },
        {
          "quarter": "Q1 2025",
          "projects": 73
        },
        {
          "quarter": "Q2 2025",
          "projects": 69
        },
        {
          "quarter": "Q3 2025",
          "projects": 77
        },
        {
          "quarter": "Q4 2025",
          "projects": 171
        },
        {
          "quarter": "Q1 2026",
          "projects": 107
        },
        {
          "quarter": "Q2 2026",
          "projects": 119
        },
        {
          "quarter": "Q3 2026",
          "projects": 157
        },
        {
          "quarter": "Q4 2026",
          "projects": 272
        },
        {
          "quarter": "Q1 2027",
          "projects": 179
        },
        {
          "quarter": "Q2 2027",
          "projects": 204
        },
        {
          "quarter": "Q3 2027",
          "projects": 166
        },
        {
          "quarter": "Q4 2027",
          "projects": 331
        },
        {
          "quarter": "Q1 2028",
          "projects": 168
        },
        {
          "quarter": "Q2 2028",
          "projects": 181
        },
        {
          "quarter": "Q3 2028",
          "projects": 128
        },
        {
          "quarter": "Q4 2028",
          "projects": 290
        },
        {
          "quarter": "Q1 2029",
          "projects": 151
        },
        {
          "quarter": "Q2 2029",
          "projects": 106
        },
        {
          "quarter": "Q3 2029",
          "projects": 74
        },
        {
          "quarter": "Q4 2029",
          "projects": 148
        },
        {
          "quarter": "Q1 2030",
          "projects": 37
        },
        {
          "quarter": "Q2 2030",
          "projects": 55
        },
        {
          "quarter": "Q3 2030",
          "projects": 18
        },
        {
          "quarter": "Q4 2030",
          "projects": 25
        },
        {
          "quarter": "Q1 2031",
          "projects": 3
        },
        {
          "quarter": "Q2 2031",
          "projects": 6
        },
        {
          "quarter": "Q4 2031",
          "projects": 5
        },
        {
          "quarter": "Q4 2032",
          "projects": 1
        }
      ],
      "sale_status": {
        "on_sale": 1014,
        "out_of_stock": 871,
        "sold_out": 628,
        "unknown": 471,
        "available": 414,
        "booking_started": 364,
        "might_be_sold_out": 173,
        "waiting_for_sales_start": 73,
        "presale": 32,
        "announced": 3,
        "start_of_sales": 2
      },
      "construction_status": {
        "under_construction": 2481,
        "completed": 977,
        "unknown": 471,
        "not_started": 116
      },
      "property_types": {
        "apartment": 2234,
        "villa": 504,
        "townhouse": 405,
        "duplex": 306,
        "penthouse": 258,
        "land": 3
      },
      "payment_plans": {
        "n": 4146,
        "booking": {
          "n": 4146,
          "min": 0,
          "p25": 10,
          "median": 20,
          "p75": 20,
          "max": 100,
          "mean": 24
        },
        "construction": {
          "n": 4146,
          "min": 0,
          "p25": 20,
          "median": 40,
          "p75": 50,
          "max": 100,
          "mean": 36
        },
        "handover": {
          "n": 4146,
          "min": 0,
          "p25": 10,
          "median": 35,
          "p75": 50,
          "max": 90,
          "mean": 33
        },
        "post_handover": {
          "n": 674,
          "min": 5,
          "p25": 30,
          "median": 40,
          "p75": 50,
          "max": 95,
          "mean": 43
        },
        "pre_handover": {
          "n": 4146,
          "min": 0,
          "p25": 44,
          "median": 60,
          "p75": 75,
          "max": 100,
          "mean": 60
        },
        "share_post_handover": 16,
        "share_pre_handover_60_plus": 54,
        "share_pre_handover_50_or_less": 44
      },
      "down_payment": {
        "n": 2275,
        "min": 1,
        "p25": 10,
        "median": 10,
        "p75": 20,
        "max": 70,
        "mean": 14
      },
      "cities": {
        "Dubai": 3234,
        "Abu Dhabi": 369,
        "Sharjah": 202,
        "Ras Al Khaimah": 151,
        "Ajman": 55,
        "Umm Al Quwain": 30,
        "Al Ain": 2,
        "Fujairah": 2
      }
    }
  },
  "meta": {
    "note": "Each group's per project list is omitted here, use /api/v1/projects to list individual projects."
  }
}
```

## GET /api/v1/sync

Full corpus walk for downstream diffing. Query params: `limit`, `cursor`. Each row carries a `content_hash`; see Freshness above.

```json
{
  "data": {
    "records": [
      {
        "id": "7th-key-nexara-tower",
        "opi_id": "opi-c6c4a81b87",
        "title": "Nexara Tower",
        "developer": "7th Key",
        "developer_slug": "7th-key",
        "community": "Jumeirah Village Circle",
        "community_slug": "jumeirah-village-circle",
        "city": "Dubai",
        "starting_price": 1100000,
        "delivery_quarter": "Q2 2028",
        "delivery_year": 2028,
        "sale_status": "on_sale",
        "construction_status": "under_construction",
        "property_types": "apartment,duplex",
        "bedrooms": "1,2,3,4,5",
        "n_units": 12,
        "n_plans": 2,
        "lat": 25.05876771605937,
        "lng": 55.211747690389494,
        "psf_median": 1773,
        "pre_handover_pct": 50,
        "post_handover_pct": 0,
        "psf_position": {
          "community": {
            "percentile_estimate": 60,
            "comparator_median_psf": 1654,
            "delta_vs_median_pct": 7.2,
            "note": "percentile_estimate is a linear interpolation between the comparator's published quantiles (min, p25, median, p75, max), not a rank over the raw distribution."
          },
          "dubai_market": {
            "percentile_estimate": 35,
            "comparator_median_psf": 2021,
            "delta_vs_median_pct": -12.3,
            "note": "percentile_estimate is a linear interpolation between the comparator's published quantiles (min, p25, median, p75, max), not a rank over the raw distribution."
          }
        },
        "updated_at": "2026-07-28T00:00:00Z",
        "content_hash": "bb1d257e6621"
      },
      {
        "id": "a-j-g-jaddaf-l-l-c-lumiere-heights",
        "opi_id": "opi-663a003505",
        "title": "Lumiere Heights",
        "developer": "A J G Jaddaf L. L. C",
        "developer_slug": "a-j-g-jaddaf-l-l-c",
        "community": "Culture Village",
        "community_slug": "culture-village",
        "city": "Dubai",
        "starting_price": 700000,
        "delivery_quarter": "Q2 2027",
        "delivery_year": 2027,
        "sale_status": null,
        "construction_status": null,
        "property_types": null,
        "bedrooms": null,
        "n_units": 0,
        "n_plans": 0,
        "lat": null,
        "lng": null,
        "psf_median": null,
        "pre_handover_pct": null,
        "post_handover_pct": null,
        "psf_position": {
          "community": null,
          "dubai_market": null
        },
        "updated_at": "2026-07-28T00:00:00Z",
        "content_hash": "d8234ce95b6d"
      }
    ],
    "tombstones": [],
    "next_cursor": "MjAyNi0wNy0yOFQwMDowMDowMFoAYS1qLWctamFkZGFmLWwtbC1jLWx1bWllcmUtaGVpZ2h0cw",
    "schema_version": 1,
    "dataset_version": "2026-10",
    "build_date": "2026-10-02"
  },
  "meta": {
    "total": 2982,
    "skipped": 0,
    "limit": 2,
    "since_requested": null,
    "corpus_date": "2026-07-30",
    "merge_date": "2026-08-18",
    "cursor_note": "Store data.dataset_version with your cursor. A cursor walk delivers records whose read date is newer than your cursor; it does not redeliver a record whose shape gained a field, because a record's updated_at is the date we read it, not the date we rebuilt. When dataset_version changes and you want new fields on records you already hold, walk once from the beginning. since is an opaque cursor from a previous call's data.next_cursor, not a timestamp you construct yourself. Omit it to walk from the beginning. Records sort by (updated_at, id); a rebuild that touches every row in the same second does not reorder or reissue a cursor, since an unchanged content_hash keeps its existing updated_at. data.next_cursor is present on every response, never a sentinel: keep paging with it and detect the end of a walk by an empty records AND tombstones array, not by next_cursor being null. Once you reach that empty page, save its next_cursor as your resume point; calling /sync again with that exact value returns empty pages, echoing the same cursor back, until something genuinely changes upstream.",
    "license": "Derived analytics only: aggregates and analysis computed by OffplanIndex from developer published project facts and publicly observed listings. Not a redistribution of any portal's database, and no image URLs are served. See /data-policy for the licence and /data to buy a key."
  }
}
```

## GET /api/v1/meta

Corpus counts and build date. No `x-api-key` required, safe for a health check.

```
curl -s "https://offplanindex.com/api/v1/meta"
```

```json
{
  "data": {
    "build_date": "2026-10-02",
    "corpus_date": "2026-07-30",
    "merge_date": "2026-08-18",
    "data_month": "July 2026",
    "corpus_date_long": "30 July 2026",
    "merge_date_long": "18 August 2026",
    "build_date_long": "2 October 2026",
    "n_projects": 2962,
    "n_records": 4045,
    "n_delivered_projects": 1083,
    "n_delayed_projects": 169,
    "n_priced_projects": 2643,
    "n_unpriced_projects": 319,
    "n_units_raw": 68875,
    "n_units_valid": 59806,
    "n_units_benchmark": 52882,
    "n_units_size_flagged": 36,
    "n_units_unit_level": 43771,
    "n_projects_with_units": 2332,
    "n_projects_without_units": 630,
    "n_projects_with_plans": 2270,
    "n_communities": 231,
    "n_developers": 681,
    "n_dubai_projects": 2351,
    "n_with_coords": 2610,
    "n_without_coords": 352,
    "n_with_community": 2951,
    "n_duplicates_merged": 121,
    "n_developer_variants_merged": 92,
    "latest_scrape": "2026-07-30",
    "n_from_watcher": 78,
    "n_offplan_from_watcher": 77,
    "watcher_date": "2026-09-30",
    "watcher_date_long": "30 September 2026",
    "psf_hist_bins": 12,
    "psf_hist_lo": 250,
    "psf_hist_hi": 25000,
    "psf_hist_scale": "log10",
    "psf_hist_edges": [
      250,
      366.9,
      538.6,
      790.6,
      1160.4,
      1703.2,
      2500,
      3669.5,
      5386.1,
      7905.7,
      11604,
      17032.3,
      25000
    ],
    "corpus_age_days": 64,
    "watcher_age_days": 2,
    "newest_data_date": "2026-09-30",
    "data_age_days": 2,
    "freshness_sla_days": 14,
    "stale": true,
    "freshness_checked": "2026-10-02"
  },
  "meta": {
    "note": "Unkeyed health check endpoint. No x-api-key required.",
    "freshness": "Sources read to 2026-07-30, corpus merged 2026-08-18, bundle built 2026-10-02.",
    "counts": "n_projects is the off-plan count. n_delivered_projects have already handed over and are excluded from it and from every price benchmark; n_records is the two together."
  }
}
```

---

OffplanIndex. Not investment advice. Questions: hello@offplanindex.com.
