Dubai property data looks easy until you try to compare two listings. One portal shows
"2BR, Dubai Marina, AED 2,100,000"; the next shows "2 Beds, 1,180 sqft, Marina, 2.1M".
Same building, different fields, no shared key. Multiply that by a few thousand listings
and any price-per-sqm analysis becomes a manual spreadsheet exercise.
This post documents the schema I settled on for Dubai listings, the parts that actually
matter for analysis, and a free 40-row sample you can inspect.
Why listing data is harder than it looks
- The listing id is not the property id. The same unit can be re-listed by two brokers with different ids. Dedupe on the portal id, then cluster on coordinates + size + bedrooms if you need property-level counts.
-
Price is not comparable across transaction types. A sale price and a yearly rent
both look like integers. Without
transactionTypeandpricePeriod, averages are meaningless. - Size is published in both units, inconsistently. Some records carry sqft, some sqm, some both rounded differently. Keep both and store the conversion you used.
- Community metrics are snapshots. "Average community price" is computed by the portal at collection time. It is useful context, not a time series.
- Broker contact data is personal data. If you publish a sample, strip names, phones, emails, and full descriptions. The listing facts are enough for analysis.
The schema
Each record is one listing:
| Field | Why it matters |
|---|---|
listingId |
Stable dedupe key across runs |
url |
Canonical source for verification |
price, currency, pricePeriod
|
Comparable only when read together |
transactionType, propertyType
|
Sale vs rent; apartment vs villa |
bedrooms, bathrooms
|
Studio = bedrooms: 0, not null |
sizeSqft, sizeSqm
|
Both units, no silent conversion |
pricePerArea |
Portal value when present; null otherwise |
city, community, subCommunity
|
Location hierarchy for grouping |
completionStatus |
Ready vs off-plan changes the price logic |
isVerified, isExclusive, isNewConstruction
|
Source flags, not guarantees |
latitude, longitude
|
Approximate; useful for cluster checks |
listedDate, scrapedAt
|
Snapshot integrity |
communityAvgPrice, communityPriceTrend12mPct, communityActiveListings
|
Context at collection time |
rera |
Permit string when the portal publishes it |
Two design choices I would defend:
-
Never coerce a missing price to zero. A zero in a price column silently destroys
every average. Use
nulland filter explicitly. -
Keep portal-computed fields separate from your own.
pricePerAreafrom the portal andprice / sizeSqftfrom your pipeline will disagree at the edges; label which is which.
The sample
A real 40-row snapshot collected on 6 November 2026 from public PropertyFinder search
results (sale and rent). Broker names, emails, phones, and descriptions are removed.
- JSON: https://fauzimatakupan.github.io/indonesian-marketplace-data/downloads/dubai-sample-2026-11-06.json
- CSV: https://fauzimatakupan.github.io/indonesian-marketplace-data/downloads/dubai-sample-2026-11-06.csv
- Schema: https://fauzimatakupan.github.io/indonesian-marketplace-data/downloads/dubai-property-listing.schema.json
- Repo: https://github.com/FauziMatakupan/indonesian-marketplace-data
The sample is small on purpose. Forty rows validate a schema; they cannot estimate the
Dubai market.
Limitations and ethics
- Public listings only. No login, no private data, no broker contact details.
- Asking prices, not transaction prices.
- Listings change daily; a snapshot is not a feed.
- Respect the portal's terms and applicable data-protection rules. Do not republish personal data.
What I packaged
I turned the collection into an Apify Actor so pagination, normalization, and dedupe are
reusable:
https://apify.com/matakupanlabs/dubai-property-listings-scraper
Disclosure: this is my Actor and it is pay-per-listing. The sample and schema are free.
If you are building something similar, I am happy to compare notes on deduplication or
price-per-sqm normalization.
Top comments (0)