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.

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:

FileWhat it is
housebook-feed-v1.schema.jsonJSON Schema, draft 2020-12. Printed from our validator, so it cannot drift away from what we actually accept.
housebook-feed-v1.xsdThe same feed in XML. Same fields, same rules.
housebook-feed-v1.example.jsonA valid example, two buildings and three units.
housebook-feed-v1.example.xmlThe 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

ItemRule
TransportHTTPS. Plain HTTP is accepted only for a feed with no access key.
AddressOne permanent URL. It is expected to stay the same between runs.
MethodGET, no parameters required from us.
FormatJSON or XML. They are equal: same fields, same rules, same report.
EncodingUTF-8. If your XML declaration names another encoding, the file is refused rather than read as garbage.
Content-Typeapplication/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 keyOptional. 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 limit12 MB per response, about fifty thousand units in JSON. A larger response is refused outright, not truncated.
PaginationOptional. 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.
RedirectsUp to three, and each hop is checked. The access key is dropped if a redirect leaves your host.
Timeout60 seconds for the whole response.
Update frequencyRegenerate at least once a week, before we read. Generating hourly is fine. We read once a week.
CompressionContent-Encoding: gzip is welcome.
DOCTYPENot allowed in XML. We do not resolve external entities.
NestingXML 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.

FieldTypeRequiredExampleNotes
versionstringyes"1"Always "1" for this document. A string, not a number: the version is the name of a contract.
providerobjectyessee belowWho is sending the file.
provider.namestringyes"Example Developer LLC"Legal or trading name.
provider.countrystringyes"AE"ISO 3166-1 alpha-2, from the list in section 6. The country of the provider, not of every building.
provider.contactstringyes"feeds@example.com"A live address or phone we can use when numbers look wrong. Not published anywhere.
generated_atstringyes"2026-09-14T06:00:00+04:00"When this file was produced. ISO 8601. If it carries a time, it carries a time zone.
buildingsarrayyessee section 4May be empty, but then no unit can be linked.
unitsarrayyessee section 5May be empty, for example while a project is still in planning.
next_urlstringno"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

FieldTypeRequiredExampleNotes
idstringyes"BLD-100"Your id for the building. Stable forever. See section 8.
namestringyes"Marina Heights"The name buyers see. Not a code.
countrystringyes"AE"ISO 3166-1 alpha-2, from the list in section 6.
citystringyes"Dubai"City or municipality. Latin or local script, one value.
districtstringno"Dubai Marina"Area, neighborhood, or island.
addressstringno"Al Marsa Street 12"Street address without the city and country.
latnumberno25.0805Decimal degrees, -90 to 90. Use a dot, never a comma.
lngnumberno55.1403Decimal degrees, -180 to 180.
developerobjectyessee belowWho builds it.
developer.namestringyes"Example Developer LLC"Spell it the same way in every file. We match developers by this name.
developer.idstringno"DEV-1"Your id for the developer, if you have one.
completionobjectnosee belowDelivery state.
completion.statusstringyes inside completion"under_construction"One of planned, under_construction, completed.
completion.datestringno"2027-12"YYYY-MM-DD or YYYY-MM. Handover is rarely known to the day.
ownershipstringno"freehold"One of freehold, leasehold, other.
descriptionobjectno{"en": "...", "ru": "..."}Text by language. Keys are two lowercase letters. See the content rules in section 8.
photosarraynosee section 7Images of the building.
amenitiesarray of stringsno["pool", "gym"]Free text, one item per amenity. We map them on our side.
updated_atstringyes"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

FieldTypeRequiredExampleNotes
idstringyes"U-100-1201"Your id for the unit. Stable forever. See section 8.
building_idstringyes"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.
typestringyes"2br"One of the ten values in section 6.
bedroomsintegerno2Leave it out when the unit has none. A zero here reads as a studio, which is a fact, not a blank.
bathroomsintegerno2Same rule.
areaobjectyessee belowSize of the unit.
area.valuenumberyes1195Greater than zero. A dot for decimals.
area.unitstringyes"sqft"sqm or sqft. Always stated. We never infer it from the country.
floorintegerno12Negative values are allowed for basement levels.
priceobjectyessee belowAsking price.
price.amountnumberyes2450000A number, greater than zero. Not "1 200 000 THB", not "from 1.2M". No spaces, no currency sign, no thousands separator.
price.currencystringyes"AED"ISO 4217, three uppercase letters. Symbols are not accepted.
price.perstringyes"total"What the amount is for: total, sqm, or sqft.
statusstringyes"available"available, reserved, or sold. See the 30 day rule in section 8.
floor_plan_urlstringno"https://cdn.example.com/plan-2br.pdf"Direct link to the plan of this unit.
updated_atstringyes"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.

CodeCountryCodeCountry
AEUnited Arab EmiratesPAPanama
AZAzerbaijanSASaudi Arabia
CLChileTHThailand
CYCyprusTRTurkey
GEGeorgiaUSUnited States
IDIndonesiaUZUzbekistan
OMOman

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

ValueMeaning
sqmSquare meters
sqftSquare feet

Price basis, price.per

ValueMeaning
totalThe amount is the price of the whole unit
sqmThe amount is the price of one square meter
sqftThe amount is the price of one square foot

Unit status

ValueMeaning
availableOn the market
reservedHeld, not yet sold
soldSold or withdrawn

Completion status

ValueMeaning
plannedAnnounced, construction not started
under_constructionBuilding
completedHanded over

Ownership

ValueMeaning
freeholdFull ownership
leaseholdLong lease
otherAnything else. Explain it in the description.

Unit types

ValueMeaning
studioOne room with a kitchenette
1brOne bedroom
2brTwo bedrooms
3brThree bedrooms
4br+Four bedrooms or more
villaDetached house
townhouseAttached house
penthouseTop floor unit sold as a penthouse
landPlot
commercialOffice, retail, or other non residential space

Photo kinds

ValueMeaning
photoPhotograph or render of the building
planFloor plan or layout
masterplanSite plan of the whole project
facilityPool, gym, lobby, and other shared spaces

7. Photos and plans

ItemRule
LinkA direct URL to the image file, over http or https. Not a gallery page, not a data: blob, not an FTP path.
PermanenceOne 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.
FormatJPEG, PNG, or WebP for photos. PDF is accepted for floor_plan_url.
Minimum size1200 pixels on the long side. Smaller images are accepted but look poor on a full width page, so we may leave them out.
WatermarksNone. Phone numbers, logos of other sites, and agency stamps on the image make it unusable for us.
OrderSignificant. The first photo of a building is its cover.
KindOptional. If you do not set kind, we read the image as photo.
CountNo hard limit. Twenty to forty images per building is normal.
AvailabilityThe 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.

OutcomeWhat it meansWho fixes it
Read successfullyThe file matches the schema and nothing in the content looks wrong.Nobody.
Read with warningsThe shape is correct, the content is not. Duplicate ids, units pointing at a missing building.You, before publication.
Read but does not match the schemaThe file parsed, but fields are missing, misspelled, or out of range.You.
Could not be fetchedNo 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:

PartExampleWhat it is
Pathunits[12].price.amountWhere to look in your file. Array positions are zero based.
FieldamountThe name to search for.
Reasonexpected 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

VersionDateChange
1September 14, 2026First 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.