Feed specification, version 1
This page describes one file that you publish and Housebook reads. You need no account with us to start and you send us nothing by email: one permanent HTTPS address is enough, and we fetch the file from it on our own schedule.
JSON and XML are equal: the same fields, the same rules, the same validation report. Buildings come first and units live inside them, because the catalogue page we build is a building page.
The machine-readable schemas and two working examples are below as downloads. The full text of the contract follows them.
Download
Four files: two machine-readable descriptions of the shape, and two valid examples to copy from.
- JSON Schema (the field-by-field contract)housebook-feed-v1.schema.json
- XML schema, XSD (the same contract for an XML export)housebook-feed-v1.xsd
- Example feed in JSON (a valid file to copy from)housebook-feed-v1.example.json
- Example feed in XML (the same example, field for field)housebook-feed-v1.example.xml
The specification
Housebook Feed Specification, version 1
For developers and partners in any country Housebook works in.
This document describes one file that you publish and we read. It is the whole contract. The machine-readable form of the same contract lives next to this file:
| File | What it is |
|---|---|
housebook-feed-v1.schema.json | JSON Schema, draft 2020-12. Printed from our validator, so it cannot drift away from what we actually accept. |
housebook-feed-v1.xsd | The same feed in XML. Same fields, same rules. |
housebook-feed-v1.example.json | A valid example, two buildings and three units. |
housebook-feed-v1.example.xml | The same example in XML, field for field. |
If this text and the schema ever disagree, the schema wins. It is generated from the code that accepts or rejects your file.
1. Purpose and how Housebook uses your feed
Housebook is a real estate platform. Your feed is how your inventory reaches our catalog and our brokers without anyone retyping it.
The model is buildings first, units inside. A building is a project, a tower,
a villa compound, a phase. A unit is an apartment, a villa, a townhouse, a plot,
a commercial space. Both arrays live in the same file. Every unit names its
building through building_id. A flat list of listings cannot be accepted: with
no building we have nothing to group the listings by, and the catalog page we
build is a building page.
We pull, you do not push. You publish one permanent HTTPS address. We fetch it on our schedule. You need no account in our system to start, and you send us nothing by email.
Price refresh is weekly. We read the feed once a week and update prices and availability from it (owner decision 190, September 14, 2026). Between reads the catalog shows what your last file said. If your prices move faster than that, tell us and we will discuss a shorter interval for your source.
Publication comes after validation. The first run is a check: we download, parse, and validate your file and send you the report described in section 9. Nothing enters the catalog until that report is clean. After intake starts, a file that fails validation is refused as a whole and the previous good data stays in place. We never publish half a file.
Photos are copied, not hotlinked. We download every image and serve it from our own storage. Your server does not carry our traffic, and a public page of ours never points at your host.
2. Delivery
| Item | Rule |
|---|---|
| Transport | HTTPS. Plain HTTP is accepted only for a feed with no access key. |
| Address | One permanent URL. It is expected to stay the same between runs. |
| Method | GET, no parameters required from us. |
| Format | JSON or XML. They are equal: same fields, same rules, same report. |
| Encoding | UTF-8. If your XML declaration names another encoding, the file is refused rather than read as garbage. |
| Content-Type | application/json or application/xml preferred. If your host sends text/plain, we fall back to the first character of the body. A body that disagrees with the format configured for your source is refused, not guessed. |
| Access key | Optional. If your export is closed, send us a key and we send it back on every request as Authorization: Bearer <key>. We store it encrypted, and it is never shown on a screen again. A feed with a key must be served over HTTPS. |
| Size limit | 12 MB per response, about fifty thousand units in JSON. A larger response is refused outright, not truncated. |
| Pagination | Optional. If your export does not fit, put the address of the next page in next_url and repeat the same envelope on each page. Omit next_url on the last page. |
| Redirects | Up to three, and each hop is checked. The access key is dropped if a redirect leaves your host. |
| Timeout | 60 seconds for the whole response. |
| Update frequency | Regenerate at least once a week, before we read. Generating hourly is fine. We read once a week. |
| Compression | Content-Encoding: gzip is welcome. |
| DOCTYPE | Not allowed in XML. We do not resolve external entities. |
| Nesting | XML is limited to 100 levels of depth and 300,000 nodes per document. A feed of this shape never comes close. |
Unknown fields do not break anything. If your export carries a field we do not know, the file still passes and the field is ignored. You can extend your export without asking us first.
3. Feed envelope
The top level of the file.
| Field | Type | Required | Example | Notes |
|---|---|---|---|---|
version | string | yes | "1" | Always "1" for this document. A string, not a number: the version is the name of a contract. |
provider | object | yes | see below | Who is sending the file. |
provider.name | string | yes | "Example Developer LLC" | Legal or trading name. |
provider.country | string | yes | "AE" | ISO 3166-1 alpha-2, from the list in section 6. The country of the provider, not of every building. |
provider.contact | string | yes | "feeds@example.com" | A live address or phone we can use when numbers look wrong. Not published anywhere. |
generated_at | string | yes | "2026-09-14T06:00:00+04:00" | When this file was produced. ISO 8601. If it carries a time, it carries a time zone. |
buildings | array | yes | see section 4 | May be empty, but then no unit can be linked. |
units | array | yes | see section 5 | May be empty, for example while a project is still in planning. |
next_url | string | no | "https://example.com/feed?page=2" | Address of the next page. Only for exports too large for one response. |
XML uses the same names. The root element is <feed>, buildings holds
<building> elements, units holds <unit> elements, amenities holds
<amenity> elements, and photos holds <photo> elements.
4. Building fields
| Field | Type | Required | Example | Notes |
|---|---|---|---|---|
id | string | yes | "BLD-100" | Your id for the building. Stable forever. See section 8. |
name | string | yes | "Marina Heights" | The name buyers see. Not a code. |
country | string | yes | "AE" | ISO 3166-1 alpha-2, from the list in section 6. |
city | string | yes | "Dubai" | City or municipality. Latin or local script, one value. |
district | string | no | "Dubai Marina" | Area, neighborhood, or island. |
address | string | no | "Al Marsa Street 12" | Street address without the city and country. |
lat | number | no | 25.0805 | Decimal degrees, -90 to 90. Use a dot, never a comma. |
lng | number | no | 55.1403 | Decimal degrees, -180 to 180. |
developer | object | yes | see below | Who builds it. |
developer.name | string | yes | "Example Developer LLC" | Spell it the same way in every file. We match developers by this name. |
developer.id | string | no | "DEV-1" | Your id for the developer, if you have one. |
completion | object | no | see below | Delivery state. |
completion.status | string | yes inside completion | "under_construction" | One of planned, under_construction, completed. |
completion.date | string | no | "2027-12" | YYYY-MM-DD or YYYY-MM. Handover is rarely known to the day. |
ownership | string | no | "freehold" | One of freehold, leasehold, other. |
description | object | no | {"en": "...", "ru": "..."} | Text by language. Keys are two lowercase letters. See the content rules in section 8. |
photos | array | no | see section 7 | Images of the building. |
amenities | array of strings | no | ["pool", "gym"] | Free text, one item per amenity. We map them on our side. |
updated_at | string | yes | "2026-09-13T18:20:00+04:00" | When this building record last changed. ISO 8601. |
A building with no units is accepted. It appears in the check report as a warning so that a missing unit block is noticed, not silently lost.
5. Unit fields
| Field | Type | Required | Example | Notes |
|---|---|---|---|---|
id | string | yes | "U-100-1201" | Your id for the unit. Stable forever. See section 8. |
building_id | string | yes | "BLD-100" | Must match the id of a building in the same file. A unit pointing at a missing building is reported and cannot be published. |
type | string | yes | "2br" | One of the ten values in section 6. |
bedrooms | integer | no | 2 | Leave it out when the unit has none. A zero here reads as a studio, which is a fact, not a blank. |
bathrooms | integer | no | 2 | Same rule. |
area | object | yes | see below | Size of the unit. |
area.value | number | yes | 1195 | Greater than zero. A dot for decimals. |
area.unit | string | yes | "sqft" | sqm or sqft. Always stated. We never infer it from the country. |
floor | integer | no | 12 | Negative values are allowed for basement levels. |
price | object | yes | see below | Asking price. |
price.amount | number | yes | 2450000 | A number, greater than zero. Not "1 200 000 THB", not "from 1.2M". No spaces, no currency sign, no thousands separator. |
price.currency | string | yes | "AED" | ISO 4217, three uppercase letters. Symbols are not accepted. |
price.per | string | yes | "total" | What the amount is for: total, sqm, or sqft. |
status | string | yes | "available" | available, reserved, or sold. See the 30 day rule in section 8. |
floor_plan_url | string | no | "https://cdn.example.com/plan-2br.pdf" | Direct link to the plan of this unit. |
updated_at | string | yes | "2026-09-13T18:20:00+04:00" | When this unit record last changed. ISO 8601. |
A unit has no photo array of its own in version 1. Unit level images belong in
the building photos array with kind set to plan, and the plan of a specific
unit goes in floor_plan_url.
6. Enumerations
Every list below is closed. A value outside it fails validation with the path of the record, so nothing enters the catalog under a meaning we guessed.
Countries, ISO 3166-1 alpha-2
Thirteen codes, one per market Housebook operates in.
| Code | Country | Code | Country |
|---|---|---|---|
AE | United Arab Emirates | PA | Panama |
AZ | Azerbaijan | SA | Saudi Arabia |
CL | Chile | TH | Thailand |
CY | Cyprus | TR | Turkey |
GE | Georgia | US | United States |
ID | Indonesia | UZ | Uzbekistan |
OM | Oman |
Cyprus is one code. We run two market lines on the island, and we tell them apart by the city, not by the country code.
Currencies, ISO 4217
Three uppercase letters, for example AED, THB, USD, EUR, TRY, GEL,
IDR, SAR, OMR, AZN, UZS, CLP, PAB. Any valid ISO 4217 code is
accepted. Currency symbols and names are not.
Area units
| Value | Meaning |
|---|---|
sqm | Square meters |
sqft | Square feet |
Price basis, price.per
| Value | Meaning |
|---|---|
total | The amount is the price of the whole unit |
sqm | The amount is the price of one square meter |
sqft | The amount is the price of one square foot |
Unit status
| Value | Meaning |
|---|---|
available | On the market |
reserved | Held, not yet sold |
sold | Sold or withdrawn |
Completion status
| Value | Meaning |
|---|---|
planned | Announced, construction not started |
under_construction | Building |
completed | Handed over |
Ownership
| Value | Meaning |
|---|---|
freehold | Full ownership |
leasehold | Long lease |
other | Anything else. Explain it in the description. |
Unit types
| Value | Meaning |
|---|---|
studio | One room with a kitchenette |
1br | One bedroom |
2br | Two bedrooms |
3br | Three bedrooms |
4br+ | Four bedrooms or more |
villa | Detached house |
townhouse | Attached house |
penthouse | Top floor unit sold as a penthouse |
land | Plot |
commercial | Office, retail, or other non residential space |
Photo kinds
| Value | Meaning |
|---|---|
photo | Photograph or render of the building |
plan | Floor plan or layout |
masterplan | Site plan of the whole project |
facility | Pool, gym, lobby, and other shared spaces |
7. Photos and plans
| Item | Rule |
|---|---|
| Link | A direct URL to the image file, over http or https. Not a gallery page, not a data: blob, not an FTP path. |
| Permanence | One URL per image, forever. When the picture changes, the URL changes. A URL that quietly returns a different image leaves stale pictures in our catalog. |
| Format | JPEG, PNG, or WebP for photos. PDF is accepted for floor_plan_url. |
| Minimum size | 1200 pixels on the long side. Smaller images are accepted but look poor on a full width page, so we may leave them out. |
| Watermarks | None. Phone numbers, logos of other sites, and agency stamps on the image make it unusable for us. |
| Order | Significant. The first photo of a building is its cover. |
| Kind | Optional. If you do not set kind, we read the image as photo. |
| Count | No hard limit. Twenty to forty images per building is normal. |
| Availability | The files must be reachable without a key for the duration of the run. We copy every file to our own storage on intake. |
kind is a hint, not a promise. Setting it makes the gallery better. Setting it
at random makes it worse than leaving it out.
8. Data quality rules
These rules are what turns a valid file into usable inventory. The validator checks the ones marked as checked. The rest are agreements, and we come back to you when the data says otherwise.
1. Ids are stable. Checked in part. The same building and the same unit keep
the same id in every export, forever. Ids are never reused for a different
object. A unit that comes back on the market comes back with its old id. A
changed id means a new object to us and the loss of its price history. Within one
file, duplicate ids are reported.
2. updated_at on every record. Checked. ISO 8601. If it carries a time, it
carries a time zone: 2026-09-14 10:00 with no offset is a different moment in
Panama and in Bangkok, and our price freshness rules stand on this value. Move
the value only when the record actually changed. A file where every record is
touched every night tells us nothing about what changed.
3. No contacts and no third party links in descriptions. Not checked, and it matters most. Descriptions go on a public page of ours. Phone numbers, messenger handles, email addresses, and links to other sites turn that page into a way around the platform. A feed that carries them is either cleaned by you or not published by us.
4. No duplicates. Checked. One object, one record. Two buildings with the same id: the second overwrites the first. The same physical building exported under two ids: two cards in the catalog and two prices for the same apartment. If you merge two internal systems, decide which id survives before the first export, not after.
5. Prices are numbers. Checked. 2450000, not "2,450,000", not
"2.45M AED", not "price on request". A unit whose price you cannot state is
better left out of the file than sent with a zero or a placeholder. The currency
is a separate ISO 4217 code, and per says what the amount is for.
6. Area always states its unit. Checked. sqm or sqft on every unit. We
never infer it from the country. That mistake is worth a factor of 10.76 in both
the area and the price per meter.
7. Sold units stay in the feed for 30 days. Not checked. When a unit is sold
or withdrawn, keep it in the export with status: "sold" for at least 30 days,
then drop it. A unit that simply disappears is indistinguishable from an export
that broke halfway, so we hold it instead of removing it, and the catalog keeps
showing something you no longer sell.
8. Every unit points at a building in the same file. Checked. If your export is paginated, keep a building and its units on the same page.
9. A field you do not have is better left out than filled in approximately.
An absent bedrooms on a plot of land is honest. A zero there reads as a studio.
10. The file is complete on every run. Each response is the full current state of the inventory it covers, not a list of changes since last time. We compare it with what we hold and work out the difference ourselves.
9. Validation and the error report
Once you give us the address, we add the source on our side and press the check button. That run does four things: downloads the file, works out the format, validates it against the schema in this folder, and summarizes what is inside.
The check run writes nothing into the catalog. You can ask for it as often as you like while you build your export.
The report comes back with one of four outcomes.
| Outcome | What it means | Who fixes it |
|---|---|---|
| Read successfully | The file matches the schema and nothing in the content looks wrong. | Nobody. |
| Read with warnings | The shape is correct, the content is not. Duplicate ids, units pointing at a missing building. | You, before publication. |
| Read but does not match the schema | The file parsed, but fields are missing, misspelled, or out of range. | You. |
| Could not be fetched | No response, an HTTP error, a redirect loop, a body over the size limit, or a timeout. | Your hosting, or us if the address changed. |
When the file matches the schema, the report also states what arrived:
- number of buildings and number of units;
- countries present, by code;
- currencies present, and the price range per currency, counted only over
per: "total"units, because a price per meter and a price for a whole unit cannot be compared; - how many units are priced per area instead of in total;
- number of photos and plans we would have to copy;
- duplicate building ids and duplicate unit ids, with the total count and the first twenty listed by name;
- units whose building is not in the file, with the same treatment;
- buildings that have no units at all.
When the file does not match the schema, the report lists the first 50 errors and the total count. Every error has three parts:
| Part | Example | What it is |
|---|---|---|
| Path | units[12].price.amount | Where to look in your file. Array positions are zero based. |
| Field | amount | The name to search for. |
| Reason | expected a number, received the string "2,450,000" | Why it was refused. |
Fifty is a deliberate cut. One systematic mistake, such as the wrong area unit in every record, produces one error per unit, and fifty thousand lines help nobody. The fifty shown cover every distinct kind of error a real file contains, and the total next to them answers the question of scale.
10. Full examples
One building with two units, in both formats. Both parse into the same object.
JSON
{
"version": "1",
"provider": {
"name": "Riverstone Development Ltd",
"country": "TH",
"contact": "feeds@riverstone.example"
},
"generated_at": "2026-09-14T09:00:00+07:00",
"buildings": [
{
"id": "RS-TOWER-1",
"name": "Riverstone Tower One",
"country": "TH",
"city": "Bangkok",
"district": "Khlong Toei",
"address": "Rama IV Road 155",
"lat": 13.7215,
"lng": 100.5604,
"developer": { "name": "Riverstone Development Ltd", "id": "RSD" },
"completion": { "status": "under_construction", "date": "2028-06" },
"ownership": "freehold",
"description": {
"en": "A residential tower by the river with a rooftop pool and a shuttle to the MRT station.",
"ru": "Жилая башня у реки с бассейном на крыше и шаттлом до станции метро."
},
"photos": [
{ "url": "https://cdn.riverstone.example/rs-tower-1/exterior-01.jpg", "kind": "photo" },
{ "url": "https://cdn.riverstone.example/rs-tower-1/lobby.jpg", "kind": "facility" },
{ "url": "https://cdn.riverstone.example/rs-tower-1/site-plan.png", "kind": "masterplan" }
],
"amenities": ["rooftop pool", "gym", "co-working", "parking"],
"updated_at": "2026-09-13T21:40:00+07:00"
}
],
"units": [
{
"id": "RS-T1-0904",
"building_id": "RS-TOWER-1",
"type": "1br",
"bedrooms": 1,
"bathrooms": 1,
"area": { "value": 42.5, "unit": "sqm" },
"floor": 9,
"price": { "amount": 6450000, "currency": "THB", "per": "total" },
"status": "available",
"floor_plan_url": "https://cdn.riverstone.example/rs-tower-1/plan-1br-a.pdf",
"updated_at": "2026-09-13T21:40:00+07:00"
},
{
"id": "RS-T1-2101",
"building_id": "RS-TOWER-1",
"type": "penthouse",
"bedrooms": 3,
"bathrooms": 3,
"area": { "value": 154, "unit": "sqm" },
"floor": 21,
"price": { "amount": 28900000, "currency": "THB", "per": "total" },
"status": "reserved",
"updated_at": "2026-09-13T21:40:00+07:00"
}
]
}
XML
<?xml version="1.0" encoding="UTF-8"?>
<feed>
<version>1</version>
<provider>
<name>Riverstone Development Ltd</name>
<country>TH</country>
<contact>feeds@riverstone.example</contact>
</provider>
<generated_at>2026-09-14T09:00:00+07:00</generated_at>
<buildings>
<building>
<id>RS-TOWER-1</id>
<name>Riverstone Tower One</name>
<country>TH</country>
<city>Bangkok</city>
<district>Khlong Toei</district>
<address>Rama IV Road 155</address>
<lat>13.7215</lat>
<lng>100.5604</lng>
<developer>
<name>Riverstone Development Ltd</name>
<id>RSD</id>
</developer>
<completion>
<status>under_construction</status>
<date>2028-06</date>
</completion>
<ownership>freehold</ownership>
<description>
<en>A residential tower by the river with a rooftop pool and a shuttle to the MRT station.</en>
<ru>Жилая башня у реки с бассейном на крыше и шаттлом до станции метро.</ru>
</description>
<photos>
<photo>
<url>https://cdn.riverstone.example/rs-tower-1/exterior-01.jpg</url>
<kind>photo</kind>
</photo>
<photo>
<url>https://cdn.riverstone.example/rs-tower-1/lobby.jpg</url>
<kind>facility</kind>
</photo>
<photo>
<url>https://cdn.riverstone.example/rs-tower-1/site-plan.png</url>
<kind>masterplan</kind>
</photo>
</photos>
<amenities>
<amenity>rooftop pool</amenity>
<amenity>gym</amenity>
<amenity>co-working</amenity>
<amenity>parking</amenity>
</amenities>
<updated_at>2026-09-13T21:40:00+07:00</updated_at>
</building>
</buildings>
<units>
<unit>
<id>RS-T1-0904</id>
<building_id>RS-TOWER-1</building_id>
<type>1br</type>
<bedrooms>1</bedrooms>
<bathrooms>1</bathrooms>
<area>
<value>42.5</value>
<unit>sqm</unit>
</area>
<floor>9</floor>
<price>
<amount>6450000</amount>
<currency>THB</currency>
<per>total</per>
</price>
<status>available</status>
<floor_plan_url>https://cdn.riverstone.example/rs-tower-1/plan-1br-a.pdf</floor_plan_url>
<updated_at>2026-09-13T21:40:00+07:00</updated_at>
</unit>
<unit>
<id>RS-T1-2101</id>
<building_id>RS-TOWER-1</building_id>
<type>penthouse</type>
<bedrooms>3</bedrooms>
<bathrooms>3</bathrooms>
<area>
<value>154</value>
<unit>sqm</unit>
</area>
<floor>21</floor>
<price>
<amount>28900000</amount>
<currency>THB</currency>
<per>total</per>
</price>
<status>reserved</status>
<updated_at>2026-09-13T21:40:00+07:00</updated_at>
</unit>
</units>
</feed>
Both examples above are parsed and validated by our own test suite on every change to this repository. A document that promises something the validator refuses would turn that test red before it reached you.
11. Changelog and contact
| Version | Date | Change |
|---|---|---|
| 1 | September 14, 2026 | First public version. Buildings and units in one file, JSON and XML equal, weekly price refresh, check before publication. |
Version 1 will grow by adding optional fields. A field that is optional today will not become required without a new version number and notice to every partner already sending a feed. Fields we add will not change the meaning of fields you already send.
Planned for a later version, listed so you can prepare, not so you can send it now: an optional layout level between building and unit, payment plans as a list of steps, service charges, and rental yield figures.
Questions, a feed address to register, a key to hand over, or a report that does not make sense: partner@housebook.deals.