Taxon Partner API
1.Overview
One request per address, one data bundle for the valuation case: the basic package (comparables, neighbourhood statistics, amenities, parcel and price map) and, as a separate option, an indicative valuation (AVM). For reseller partners who show the data in their own software.
| Base URL | https://taxonapi.be/api/v1/partner/ |
|---|---|
| Format | JSON (UTF-8). Field names and enum values in English; labels, notices and error messages in nl, fr or en via Accept-Language |
| Authentication | Header X-Api-Key |
| Specification | openapi.yaml (OpenAPI 3.1, version 2.5.1) |
| Integration guide | integration-guide.md (quickstart, curl, PHP, Python, test plan); FR guide-fr.md, NL gids-nl.md |
| Endpoints | GET /address GET /usage GET /health |
| Packages | Basic package (sections=basic, default): the sections basic, parcel and price_map together, one price per case. AVM option (sections=basic,avm): priced separately. See section 5. |
| Time zone | Europe/Brussels; timestamps in ISO 8601 with offset |
/adres and /verbruik answer 404 not_found, old parameter names answer 400 invalid_request. Only the header aliases X-Gebruiker-Ref, X-Kantoor-Ref and X-Dossier-Ref keep working (deprecated).
2.Authentication
Every request carries the header X-Api-Key. The key is long-lived, bound to your partner account and stored by Taxon as a hash only. You see it exactly once, in the partner dashboard (see "Managing your keys" below).
| Key | Use | Properties |
|---|---|---|
tx_live_ + 32 hex | Production | Usage is counted and invoiced monthly according to the agreement. |
tx_test_ + 32 hex | Development and pilot tests | Never invoiced, at most 20 requests per day, same endpoints and same response format; every response contains "environment": "test" and a notice test_environment. |
- Secrecy. The key stays in your backend. Never in a browser, a mobile app or a public repository. Call the API server to server.
- Rotation. You rotate a live key yourself in the partner dashboard (see "Managing your keys" below): the new key is shown once, the old one keeps working for 7 days with the notice
key_rotation_pending, then answers403 key_revoked. - IP allowlist. Optionally the key can be restricted to the IP addresses of your servers (
403 ip_not_allowedfrom any other address). - Leak or suspected abuse: rotate or revoke the key yourself at once in the dashboard and report it within 48 hours; Taxon then revokes the old key immediately when needed.
Managing your keys 2.3.0
Since 08-09-2026 you manage your keys yourself in the partner dashboard on taxon.be (taxon.be/api_partner, section "Management"). Every action there is logged (who, when, from which IP address) and reported to Taxon; a key value never appears in a log or an e-mail.
| Action | How it works |
|---|---|
| Show a key once | A new key is not sent by e-mail. The dashboard shows it exactly once ("Show key once"), within 7 days after creation; copy it into your secret store right away. Until that moment Taxon keeps an encrypted copy only: after the first display, or after 7 days, the copy is destroyed and the key cannot be shown again. Keys issued before 08-09-2026 cannot be shown; request a new one or create one yourself. |
| Test keys | Create them yourself ("Create test key", with a label), at most 3 active test keys; revoke them yourself ("Revoke": the key stops working immediately, 401 invalid_api_key). |
| First live key | Issued by Taxon after the signed agreement, ready for you to show once in your dashboard. You cannot create a first live key yourself (live_key_not_allowed); the "Request live key" form in the dashboard mails Taxon. |
| Rotate a live key | "Rotate live key" creates a new live key (show it once, put it in production). The previous live key keeps working for 7 days; during that period every /address and /usage response made with the old key carries the notice key_rotation_pending (section: null, message with the end date). After 7 days the old key is refused (403 key_revoked, afterwards 401 invalid_api_key). A live key is never revoked from the dashboard: rotate it, or ask Taxon to revoke it immediately. |
| Leak or suspected abuse | Rotate (live) or revoke (test) immediately and notify info@taxon.be when the old key must stop at once instead of after 7 days. |
Error codes of the dashboard actions (shown as text in the dashboard, never on /address or /usage): key_already_revealed, reveal_expired, not_revealable, key_limit_reached, live_key_not_allowed, not_self_revocable.
3.Headers
| Header | Status | Meaning |
|---|---|---|
X-Api-Key | required | Your partner key (see 2). |
X-User-Ref | required | Your own identification of the user for whom the request is made. A user is the unit you pass yourself with every request: an office, an employee, a branch or a case handler. That choice determines billing: the same user requesting the same property again within 30 days does not pay again (dedup); another user does. A daily cap applies per user, a global cap per key. Character set ^[A-Za-z0-9._:@-]{1,64}$; normalised (trimmed, lower case: [a-z0-9._:@-]{1,64}); invalid characters give 400 invalid_request with details[].field = "X-User-Ref". billing.user_ref shows the normalised value. Keep it stable per user: the free repetition (dedup), the daily cap per user (20 per day) and the usage report per user all rest on it. When missing: 400 user_ref_required. |
X-Gebruiker-RefX-Kantoor-Ref | deprecated | Aliases of X-User-Ref from contract 1.1/1.2 and 1.0. Same character set and normalisation. When two or more of these headers are sent with different values (after normalisation): 400 user_ref_conflict (the details[].issue names the headers involved). Use only X-User-Ref in new code. |
X-Case-Ref | optional | Your case number, character set ^[A-Za-z0-9._:@/ -]{1,64}$. Comes back in billing.case_ref and in the usage CSV, so that you can link every request to a case (technically optional: without it the request is accepted and billing.case_ref is null; contractually required: every request belongs to a concrete valuation case, so always send it). The alias X-Dossier-Ref (contract 1.x) is still accepted; X-Case-Ref wins when both are sent. |
Idempotency-Key | optional | Unique key per request (for example a UUID), 8 to 128 characters (A-Z a-z 0-9 . _ : @ -). Only this header counts: X-Request-ID is overwritten by nginx and is not an idempotency key. The same key within 24 hours returns exactly the same response, with the response header X-Idempotent-Replay: true, and is not charged or counted again. The same key with another address, another user or other sections: 409 idempotency_conflict. Use it on every retry after a timeout. |
Accept-Language | optional | nl (default), fr or en. Determines the language of labels (*_label, condition of a comparable, confidence.label), notices, error messages, attribution and disclaimer, and of the municipality name (Flanders Dutch; Wallonia French; Brussels Dutch for nl, French for fr and en). JSON keys and enum values are always English. |
Every response (errors included) contains the header X-Request-ID and the field request_id. Quote it in every support question.
4.GET /address
Delivers the requested sections for one address in Flanders, Wallonia or Brussels. Processing: validation, cap check, geocoding, dedup check, sections fetched in parallel, billing, response. Typical response time 1 to 8 seconds (AVM and parcel are the slowest; several parcels 3 to 12 seconds); set your client timeout to at least 60 seconds.
Query parameters
| Parameter | Status | Meaning |
|---|---|---|
address | required | Full address: street, house number, postal code, municipality. 8 to 255 characters. URL-encode it. |
type | required | house, apartment or land (2.5.0). Drives the selection of comparables and the AVM. Aliases huis, appartement, grond, terrain and bouwgrond are accepted; the response always shows the English value. land = building plot: the comparables are land listings for sale (5 km, extended to 10 km with fewer than 10), neighbourhood.land_price_level replaces the housing statistics, no AVM (see Land). |
sections | optional | basic (default) = the basic package: delivers the sections basic, parcel and price_map together, one price per case. basic,avm = basic package plus the AVM option (priced separately). Requesting parcel or price_map separately (or basic,parcel) still works technically but is normalised to the package and charged as the package; there is no cheaper partial route. The Dutch aliases basis and prijskaart are still accepted. A section outside your plan gives 403 section_not_allowed (with section); an unknown name gives 400 invalid_request. Your plan is visible in GET /usage (plan.sections_allowed). sections_delivered always lists the sections actually delivered (basic, parcel, avm, price_map, in that order). |
living_area_m2 | optional | Living area in m² (10 to 5000). Needed for avm: when missing the response stays HTTP 200, but avm is not in sections_delivered, a notice living_area_required with section: "avm" is added and the AVM section is not charged. Not needed for type=land (no AVM for land; see Land). |
year_built | optional | Year of construction (1500 to 2100). Improves the AVM. |
epc | optional | Label A+ to G, as given by the principal or as advertised. Taxon does not look up EPC certificates and does not check the label against a register. |
condition | optional | poor, average, good, very_good, excellent. AVM input (premium between -5 % and +3 %). Aliases te_renoveren, matig, goed, zeer_goed, nieuw accepted. |
bedrooms | optional | Number of bedrooms (0 to 20). Filters the comparables on plus or minus 1 bedroom (when fewer than 8 remain the filter is dropped with notice bedrooms_filter_dropped; see bedrooms_filter on the block) and is passed to the AVM. |
plot_area_m2 | optional | Plot area in m² according to you (1 to 100000). Fallback for the AVM when the cadastre yields nothing, or when you give no capakeys and know the total yourself. With capakeys the cadastre always wins. When your value is effectively used, the notice plot_area_not_cadastral follows and avm.inputs_used.plot_area_source shows partner. |
capakeys | optional | Several parcels per property. Comma-separated CaPaKeys of all parcels that belong to the property (dwelling parcel, garden, garage, meadow), in CadGIS form 33016A0299/00K000 (the hyphen form 33016A0299-00K000 is accepted and normalised). At most 10; the parcel section is part of the basic package, so no extra section is needed (400 invalid_request only when your plan does not allow the parcel section). The parcel section then delivers parcels[] plus totals and the AVM uses the total plot area. Errors: 422 capakey_invalid (form or more than 10), 422 capakey_too_far (a parcel more than 2 km from the address, abuse brake). A non-existing parcel only gives a notice capakey_not_found; the rest is delivered. See the flow. |
Example request
GET /api/v1/partner/address?address=Doorniksestraat%2040%2C%208500%20Kortrijk§ions=basic,avm&type=apartment&living_area_m2=95 HTTP/1.1
Host: taxonapi.be
X-Api-Key: tx_live_<32 hex>
X-User-Ref: office-kortrijk-03
X-Case-Ref: DOS-2026-0452
Idempotency-Key: 2c6a8b1e-3f4d-4a5b-9c7e-1d2f3a4b5c6d
Accept-Language: en
Example response (200)
Live response of 08-09-2026 (live key, user office-kortrijk-03, request cf514c7de2714c11806e2ebf053d0247), shortened to one comparable per block and three amenities; the price_map block (part of the basic package) is omitted here, see section 5. With a live key environment is live, charged is true and free_reason is null; the amounts in billing.price are those of the agreement of the client used for the capture, yours follow your own agreement. With a test key environment is test, charged is false, free_reason is test and every amount is 0.00.
{
"request_id": "cf514c7de2714c11806e2ebf053d0247",
"environment": "live",
"address": {
"input": "Doorniksestraat 40, 8500 Kortrijk",
"normalized": "Doorniksestraat 40, 8500 Kortrijk",
"box": null,
"postal_code": "8500",
"municipality": "Kortrijk",
"lat": 50.82541,
"lon": 3.267149,
"region": "VL",
"nis_code": "34022",
"geocoder": "geo.api.vlaanderen.be",
"geocode_score": 0.95,
"precision": "house_number"
},
"sections_delivered": ["basic", "parcel", "avm", "price_map"],
"basic": {
"comparables": [
{
"type": "apartment",
"type_label": "apartment",
"transaction": "sale",
"transaction_label": "for sale",
"address_mode": "house_number",
"radius_m": 1000,
"count": 25,
"excluded_subject_property": 0,
"max_age_months": 24,
"items": [
{
"ref": "r_1a30b3b90e",
"address": "Schouwburgplein 10, 8500 Kortrijk",
"distance_m": 157,
"type": "apartment",
"transaction": "sale",
"price": 250000,
"price_kind": "asking_price",
"price_kind_label": "asking price",
"price_per_m2": 2427,
"living_area_m2": 103,
"plot_area_m2": null,
"bedrooms": 2,
"epc_label": "B",
"epc_kwh_m2": 108.0,
"epc_source": "as advertised",
"year_built": 1980,
"condition": "good condition",
"building_type": "terraced",
"new_build": false,
"published": "2026-07-16",
"days_online": 12,
"last_seen": "2026-07-28",
"status": "offline",
"status_label": "offline",
"source": "listing",
"source_label": "listing",
"features": {
"garage": true,
"parking_spaces": 1,
"terrace": true,
"terrace_m2": null,
"garden": null,
"garden_m2": null,
"cellar": true,
"attic": null,
"floor": 2,
"floors_count": 5,
"elevator": false,
"kitchen": "semi_equipped",
"bathrooms": 1,
"shower_rooms": null,
"toilets": 1,
"heating": "gas",
"solar_panels": false,
"double_glazing": true,
"orientation_garden": null,
"renovation_year": null,
"inspections": {"electrical_compliant": null, "asbestos_certificate": null, "oil_tank": null},
"flood_zone": "none"
},
"summary": "Apartment on the 2nd floor of 103 m² with garage, terrace and cellar, 2 bedrooms, EPC B, built in 1980.",
"thumbnail": {"url": "https://taxonapi.be/api/v1/marketexplorer/foto/<token>?w=160", "valid_until": "2026-09-08T09:39:16Z"},
"photo_count": 12,
"history": [
{"date": "2026-04-29", "price": 250000, "event": "published", "label": "published"},
{"date": "2026-07-28", "price": null, "event": "offline", "label": "offline"}
],
"photos": [
{"url": "https://taxonapi.be/api/v1/marketexplorer/foto/<token>?w=640", "download_token": "b71c6e8f1c51a562f33d", "valid_until": "2026-09-08T09:39:16Z"},
{"url": "https://taxonapi.be/api/v1/marketexplorer/foto/<token>?w=640", "download_token": "4bdfcfbd9ea0c282e886", "valid_until": "2026-09-08T09:39:16Z"}
]
}
]
},
{
"type": "apartment",
"type_label": "apartment",
"transaction": "rent",
"transaction_label": "for rent",
"address_mode": "house_number",
"radius_m": 1000,
"count": 25,
"excluded_subject_property": 0,
"max_age_months": 24,
"items": ["…"]
}
],
"neighbourhood": {
"sector": {"code": "34022A00-", "name": "KORTRIJK-CENTRUM", "level": "sector", "municipality": "Kortrijk"},
"building_stock": {
"level": "sector",
"reference_date": "2026-01-01",
"total": 2882,
"distribution": [
{"category": "residential", "label": "Residential", "count": 1908, "pct": 66.2},
{"category": "commerce_services", "label": "Commerce & services", "count": 383, "pct": 13.3},
{"category": "industry", "label": "Industry", "count": 7, "pct": 0.2},
{"category": "agriculture", "label": "Agriculture", "count": 0, "pct": 0.0},
{"category": "other", "label": "Other", "count": 584, "pct": 20.3}
],
"source": "FPS Finance - open patrimonial data"
},
"price_level": {
"type": "apartment",
"radius_m": 2000,
"sale_per_m2": {"n": 430, "p25": 2260.25, "median": 2856.5, "p75": 3747.0},
"rent_per_m2_year": {"n": 346, "p25": 89.12, "median": 105.85, "p75": 126.7},
"gross_yield_pct": 3.71,
"gross_yield_p25_p75": [3.12, 4.44],
"price_kind": "asking_price"
},
"epc_prices": {
"type": "apartment",
"radius_m": 5000,
"labels": {
"B": {"median_per_m2": 2578.1, "count": 132},
"C": {"median_per_m2": 2441.6, "count": 56},
"A": {"median_per_m2": 3849.2, "count": 83},
"D": {"median_per_m2": 2208.5, "count": 16},
"F": {"median_per_m2": 1645.3, "count": 10},
"A+": {"median_per_m2": 5432.6, "count": 6},
"E": {"median_per_m2": 2521.7, "count": 4}
},
"price_kind": "asking_price"
},
"safety": {
"level": "municipality",
"municipality": "Kortrijk",
"year": 2025,
"burglaries_per_1000": 2.7,
"crimes_per_1000": 89.0,
"region_burglaries_per_1000": 2.1,
"region_crimes_per_1000": 63.3,
"years": [
{"year": 2025, "burglaries_per_1000": 2.7, "crimes_per_1000": 89.0},
{"year": 2024, "burglaries_per_1000": 3.2, "crimes_per_1000": 100.1},
{"year": 2023, "burglaries_per_1000": 3.3, "crimes_per_1000": 101.9}
],
"source": "Federal Police - Police Crime Statistics (PCS)"
}
},
"amenities": {
"score": 9.3,
"scale": "0-10",
"radius_m": 1000,
"sub_scores": {
"public_transport": {"score": 9.2, "label": "Public transport", "count": 17},
"healthcare": {"score": 9.2, "label": "Healthcare", "count": 7},
"shops": {"score": 9.8, "label": "Shops and catering", "count": 87},
"sport_culture": {"score": 9.3, "label": "Sport and culture", "count": 12},
"education": {"score": 8.9, "label": "Education", "count": 9}
},
"poi_count": 158,
"top_10": [
{"name": "Muskat Pureebar", "type": "shops", "type_label": "Shops and catering", "distance_m": 52},
{"name": "KBC", "type": "shops", "type_label": "Shops and catering", "distance_m": 66},
{"name": "Vork", "type": "shops", "type_label": "Shops and catering", "distance_m": 79}
],
"attribution": "© OpenStreetMap contributors (ODbL)"
}
},
"parcel": {
"capakey": "34022G0494/00E000",
"region": "VL",
"area_m2": 259.14,
"cadastral_area_m2": 259.14,
"width_m": 4.79,
"depth_m": 53.61,
"frontage_m": 7.31,
"built_area_m2": 164.1,
"buildings_count": 2,
"garden_orientation": "SW",
"garden_orientation_deg": 247,
"zoning": {"category": "residential", "label": "woongebieden", "plan": "gewestplan"},
"preemption_right": {"status": "none", "source": "RVV thematic layer, right of pre-emption (Flanders)"},
"source": "CadGIS (FPS Finance) / GRB Gbg - building at ground level (Digitaal Vlaanderen)",
"main_parcel": "34022G0494/00E000",
"parcels": [
{
"capakey": "34022G0494/00E000",
"area_m2": 259.14,
"cadastral_area_m2": 259.14,
"width_m": 4.79,
"depth_m": 53.61,
"built_area_m2": 164.1,
"buildings_count": 2,
"zoning": {"category": "residential", "label": "woongebieden", "plan": "gewestplan"},
"preemption_right": {"status": "none", "source": "RVV thematic layer, right of pre-emption (Flanders)"},
"source": "CadGIS (FPS Finance) / GRB Gbg - building at ground level (Digitaal Vlaanderen)",
"is_main": true,
"distance_to_address_m": 0
}
],
"parcels_count": 1,
"total_area_m2": 259.14,
"cadastral_total_area_m2": 259.14,
"zoning_combined": {"category": "residential", "label": "woongebieden", "plan": "gewestplan"},
"preemption_right_combined": {"status": "none", "parcels": []},
"parcels_not_found": [],
"plot_area_source": "cadastre_main_parcel",
"parcel_candidates": [
{"capakey": "34022G0495/00C000", "area_m2": 370.75, "direction": "S", "built": true, "distance_m": 7},
{"capakey": "34022G0493/00L000", "area_m2": 1224.37, "direction": "W", "built": true, "distance_m": 12},
{"capakey": "34022G0490/00C000", "area_m2": 249.56, "direction": "W", "built": true, "distance_m": 32}
],
"parcel_candidates_source": "CadGIS (FPS Finance), adjacent within 0.5 m; built-up via the regional buildings layer",
"parcel_candidates_remark": null
},
"avm": {
"value": 243995,
"range": [190804, 312014],
"range_90": [162820, 365641],
"rental_value": 885,
"rental_value_range": [859, 954],
"confidence": {"score": 72.7, "label": "low", "fsd_pct": 24.59},
"comparables_count": 19,
"comparables_count_500m": 19,
"comparables_count_1km": 19,
"inputs_used": {
"type": "apartment",
"living_area_m2": 95.0,
"year_built": null,
"epc_label": null,
"condition": null,
"bedrooms": null,
"plot_area_m2": null,
"plot_area_source": null
},
"price_basis": "asking_price_model",
"model": "taxon-avm v3.1"
},
"billing": {
"user_ref": "office-kortrijk-03",
"case_ref": "DOS-2026-0452",
"charged": true,
"pilot": false,
"free_reason": null,
"price": {"package": 3.0, "avm": 2.0, "total": 5.0, "currency": "EUR", "excl_vat": true},
"dedup_of": null,
"dedup_valid_until": "2026-10-08T10:39:24+02:00",
"month": "2026-09",
"month_to_date": {"cases": 3, "amount": 10.0},
"pilot_status": null,
"today": {"client_today": 13, "user_today": 1}
},
"notices": [],
"upstream_errors": {},
"attribution": "References: Taxon (taxon.be)",
"disclaimer": "Asking prices from listings, not notarial sale prices. Data for internal use per case only; building a derived database is prohibited. The valuer decides, the AVM is a support tool.",
"generated_at": "2026-09-08T10:39:24+02:00"
}
sections_delivered, the reason is in upstream_errors (key per section or sub-block: parcel, avm, price_map, basic.comparables, basic.neighbourhood, basic.amenities, neighbourhood.building_stock, neighbourhood.safety, neighbourhood.price_level, neighbourhood.epc_prices; value timeout or upstream_error), a notice section_missing is added. Billing follows the package: the basic package is charged once per case as soon as the basic section is delivered, also when parcel or price_map is missing because of a failure (a repeat within the dedup window is free and rebuilds the missing section); the AVM option is only charged when delivered. For price_map the value no_coverage (with notice price_map_unavailable) means that no neighbourhood with prices lies within 3 km: no failure, the package stays charged. A failing sub-block inside basic becomes null; basic stays delivered. Zero comparables (notice no_comparables): package not charged (free_reason: no_result), unless your plan says otherwise. The response stays HTTP 200 as long as the basic section is delivered. When the basic section itself fails: 502 upstream_unavailable, nothing charged.
5.Sections
Basic package and AVM option (2.4.0). The sections basic, parcel and price_map form one basic package per case: sections=basic delivers the three together at one price (billing.price.package). The AVM is a separate option (sections=basic,avm, billing.price.avm). sections_delivered keeps listing the real sections.
basic (basic package, always)
- Comparables (
comparables): a list of blocks per type and transaction (saleandrent; withtypegiven at most 2 blocks), each with at most 25 comparable properties, published within 24 months, in an adaptive radius (1,000 m for apartments, 1,500 m for houses, doubled up to 15 km as long as fewer than 5 are found). Per property: asking price, price per m², areas, bedrooms, year built, condition, building type (detached,semi_detached,terraced,apartment), new build, EPC label and figure as advertised (epc_source), publication date, days online, last seen, status online/offline and the full price history (published,price_drop,price_increase,republished,offline). The fieldaddressis street + house number without box number, or street and municipality only, according to your agreement (fieldaddress_modeper block:house_numberorstreet); comparables never contain coordinates, onlydistance_m. No portal name, no link, no listing text. Withtype=landthere is one block (saleonly, no rent block), see Land. - Photos (
photos): per property at most 5 objects{url, download_token, valid_until}, main photo (facade) first.urlis a signed capability URL on taxonapi.be (server thumbnail of at most 640 px:w160, 320 or 640, other values rounded up and capped at 640, withoutwalso 640 px; the original is never served), valid 1 hour (on a repeated request, dedup or Idempotency-Key, the photo links are refreshed: newurlandvalid_until, samedownload_token), works directly in an<img>without a key: do not parse, do not compose, fetch within the hour. After expiry or manipulation it answers404(invalid_or_expired_token).download_tokenis a stable opaque identifier for your own bookkeeping, not to fetch anything. Embedding in the report of the case: see 11. - Features, summary and thumbnail (
features,summary,thumbnail,photo_count, since 2.2.0): each comparable carries an objectfeatureswith the structured features of the listing (garage,parking_spaces,terraceandterrace_m2,gardenandgarden_m2,cellar,attic,floor(apartments),floors_count,elevator,kitchen: not_equipped|semi_equipped|equipped|fully_equipped,bathrooms,shower_rooms,toilets,heating: gas|oil|electric|heat_pump|wood|district|other,solar_panels,double_glazing,orientation_garden: N|NE|E|SE|S|SW|W|NW,renovation_year,inspections {electrical_compliant, asbestos_certificate, oil_tank},flood_zone: none|possible|effective). Every key is always present;nullmeans unknown, so coverage depends on the listing.summaryis one sentence in Taxon's own words built from those fields only, in the language ofAccept-Language; listing texts are never delivered.thumbnail {url, valid_until}is a signed link to the primary photo (front facade) atw=160, valid 1 hour and refreshed like the other photo links;photos[0]is the same photo at 640 px;nullwithout photos.photo_countis the total number of photos of the listing (photosstays capped at 5). - Neighbourhood statistics (
neighbourhood): statistical sector, building stock per category (residential,commerce_services,industry,agriculture,other), asking-price level sale and rent per m² (quartiles, indexed) with gross yield, median price per advertised EPC label, and safety as real police figures per 1,000 inhabitants (burglaries and total offences, 3 years, with regional comparison) at municipality level. There is no "safety score". A sub-block that fails becomesnulland is listed inupstream_errors. Withtype=land:price_levelandepc_pricesarenull(noticehousing_stats_not_available_for_land) andland_price_level{radius_m,count,price_per_m2_plot {p25, median, p75},price_kind: asking_price,max_age_months: 24} gives the asking-price level per m² of plot from the land listings (2.5.0). - Amenities (
amenities): total score 0-10, sub-scores with label and count (public_transport,healthcare,shops,sport_culture,education), number of amenities within the radius and the 10 nearest (top_10: name, type, distance). OpenStreetMap attribution required.
parcel (basic package)
- CaPaKey of the main parcel, area from the geometry and cadastral area, width and depth, frontage, built area, number of buildings, garden orientation (abbreviation in the language of
Accept-Language, plus degrees;nullfor apartments). - Zoning (
category,label,plan) from the plan of the region (see table) and the pre-emption right (statusyes/none/unknown,source, possibly coverage and details). The zoninglabelis the text of the regional service (Dutch for VL, French for WAL); there is no English translation of that text. - No geometry, no map images or tiles: you render maps with your own map licence from the coordinates.
- When the point lies on public domain,
parcelis missing with noticeparcel_not_found(no failure; the price of the basic package does not change). - Several parcels: see below.
- No flood data (see table per region).
avm (separate option)
- Estimated market value with range (68 %) and wide range (
range_90), estimated monthly rent with range, confidence (score 0-100, label high/medium/low, dispersionfsd_pct), number of comparables (total, within 500 m and 1 km), the inputs used and the model version. Alwaysprice_basis: "asking_price_model". - Needs
living_area_m2: without it the section is skipped with noticeliving_area_required.year_built,epc,conditionandbedroomsimprove the estimate. Too little data: noticeavm_insufficient_data, section not delivered, the AVM option is not charged. Not delivered fortype=land(noticeavm_not_available_for_land, not charged; 2.5.0). - An indicative model output, not an appraisal: the valuer decides. In Wallonia and Brussels always accompanied by the notice
avm_indicative_not_regionally_calibrated.
price_map (basic package) 2.0
- Asking-price level per neighbourhood around the address, with the outlines as GeoJSON so that you draw the map yourself. See below.
Source of the parcel data (2.4.2). The parcel section reads the current cadastral parcel plan of the FPS Finance (CadGIS PlanParcellaire, running fiscal situation, today 01-01-2027). The yearly INSPIRE snapshot (01-01-2026) is only the fallback on an outage or an empty answer. The field fiscal_situation (on parcel, on every parcels[] item and on every parcel_candidates[] item) gives the ISO date of the fiscal situation the parcel comes from; source names the layer. The date is the fiscal situation since which the current version of this parcel applies: a parcel that has not changed for years keeps an older date (for example 2019-01-01), while the plan it is read from is always the running fiscal situation. An older date therefore does not mean outdated data; only 2026-01-01 combined with an INSPIRE source marks the yearly snapshot fallback. A parcel that was split or merged after the last snapshot (for example 45043C0460/00R000 in Kluisbergen: one parcel of 921 m² in the snapshot, 460R 392 m² + 460X 529 m² in the current plan) is therefore delivered in its current form.
Several parcels per property: first candidates, then capakeys
A terraced house with a separate garage or garden parcel, a farm with meadows, a villa on two cadastral parcels: the point parcel at the address is then only part of the property. The API solves this in two steps.
- Step 1: request without
capakeys. The parcel section contains the point parcel (main_parcel,parcels[]with 1 item) andparcel_candidates[]: the adjacent parcels (at most 12), each withcapakey, cadastralarea_m2,directionrelative to the main parcel (alwaysN,E,SorW, language-independent),built(regional buildings layer;nullwhen that layer was not available) anddistance_mbetween the centroids. Cadastral basics only, no owner data. Show that list to the user (for example "33011I0172/00G000, 40 m², south, built" = the garage) and let them tick what belongs to the property. - Step 2: request again with
capakeys(the main parcel plus the ticked parcels, comma-separated, at most 10). The section then deliversparcels[](per parcel capakey, area from geometry and cadastre, width/depth, built area, number of buildings, zoning, pre-emption right,distance_to_address_m,is_main) and the totalstotal_area_m2,cadastral_total_area_m2,zoning_combined(one object when all parcels are equal, otherwise a list withparcels[]per zoning),preemption_right_combined(yesas soon as one parcel falls in a perimeter, with the capakeys concerned),main_parcel(the parcel on which the address falls, otherwise the first one, with noticemain_parcel_not_in_capakeys) andparcels_not_found[]. The section-level fields stay those of the main parcel. The AVM (type house) usestotal_area_m2;avm.inputs_used.plot_area_sourcesayscadastre_main_parcel,cadastre_<n>_parcels(n = number of delivered parcels, 2 to 10) orpartner; the section fieldparcel.plot_area_sourceonly carries the two cadastre values. For an apartment no plot area goes to the model (not even withcapakeys). - Dedup. Same user, same address: the basic package delivered in step 1 is not charged again (notice
dedupwithsection: basic;billing.free_reasonisdeduponly when nothing new is delivered, otherwisenull(charged),testorpilot), even though the parcel section is rebuilt with the new parcels. When you add the AVM option in step 2, you only pay that option. The samecapakeysagain within 30 days = answer from the ledger (from_cache: true). The sameIdempotency-Keywith othercapakeys=409 idempotency_conflict.parcel_candidates,parcel_candidates_sourceandparcel_candidates_remarkare always present andnullwhencapakeyswas given. A cold parcel analysis can take up to 21 s; onupstream_errors.parcel: timeout(noticesection_missing; the basic package is charged once, the repeat is free within the dedup window) request again after a few seconds with a newIdempotency-Key. - Error paths.
422 capakey_invalid(wrong form or more than 10 keys;details[]names the wrong parts);422 capakey_too_far(a parcel more than 2 km from the address; nothing delivered or charged). The parcel section is part of the basic package, socapakeysneeds no extra section. A capakey that does not exist in the parcel plan: noticecapakey_not_found(section parcel) andparcels_not_found[]; the other parcels are delivered. When the parcel analysis fails for one parcel, only its cadastral area counts (noticeparcel_analysis_incomplete). - Load. Keep to the parcels of the case. Every request with
capakeysdoes one cadastre query plus one parcel analysis per parcel (up to 10); a farm with 3 parcels takes 3 to 12 s.
Worked example with real responses (Rijselstraat 62, Ieper): integration guide, section 3.
Land (type=land) 2.5.0
type=land (aliases grond, terrain, bouwgrond) requests the bundle for a building plot. What changes compared to a house or apartment:
- Comparables: one block (
type: land,transaction: sale, no rent block) with land listings for sale (building plots, project land and land without a more precise subtype; agricultural land, meadows, woodland, orchards, industrial and business land, parking spaces, garages and recreational land are excluded on the basis of the advertised subtype) of all publication years (2.5.1: no age limit, so that you can adjust the asking prices of older listings for the market evolution within the case (price indexation); the licence terms remain: no merging, indexing or storing across cases), within 5 km of the address; with fewer than 10 in total the radius is extended once to 10 km (radius_m). Ranking: the listings of the last 24 months first (sorted by distance), then the older ones (sorted by distance), at most 25 per block; every item carriesage_months(whole calendar months betweenpublishedand today) and the block fieldmax_age_monthsisnullfor land. Still fewer than 10: block fieldlow_sample: trueand noticelow_sample(section basic). Each item carries the usual fields where they make sense (address,distance_m,price,price_kind,source,published,status,history,photos,thumbnail,photo_count) plusplot_area_m2andprice_per_m2_plot(asking price per m² of plot); the housing fields (living_area_m2,price_per_m2,bedrooms,epc_label,year_built,condition,building_type,new_build) arenull.featuresis limited to{plot_area_m2, zoning, flood_zone, land_type}(zoningis currently alwaysnull: the zoning of a listing is not looked up; the zoning of the subject parcel is inparcel.zoning;land_typeisbuilding_plot,project_landorotherand drives the first word ofsummary: "Building plot", "Development land" or "Land").summaryreads for example "Building plot of 694 m² in Kluisbergen, listed since 10-09-2026." - Neighbourhood:
land_price_level(see above) from the same land listings (same exclusions) of the last 24 months; a price level must be current, so with fewer than 10 usable listings within 24 months the window is widened to 60 months andmax_age_monthssays which window was used (24 or 60; 2.5.1); only listings with a plot area and an asking price between 15 and 5,000 EUR per m² of plot enter the quartiles (count), so that agricultural land advertised as buildable and placeholder areas do not distort the level (items outside that band stay in the list with their ownprice_per_m2_plot);price_levelandepc_pricesarenullwith noticehousing_stats_not_available_for_land;building_stock,safetyandamenitiesas usual. - No AVM: Taxon delivers no automated valuation and no indicative land value for land.
sections=basic,avmstays HTTP 200:avmisnulland not insections_delivered, notice{code: avm_not_available_for_land, section: avm}, the AVM option is not charged.living_area_m2may be omitted. - Parcel and price map: unchanged. The parcel section (zoning, pre-emption right, several parcels via
capakeys) is the core for land;price_mapstays the housing map (asking prices per m² of houses and apartments per neighbourhood), not a land price map. - Billing: the basic package as usual (one price per case, dedup 30 days, pilot and test key as for houses); no AVM option.
Coverage (15-09-2026, listings of the last 24 months with a price; on 16-09-2026 across all publication years about 73,000 land listings, of which about 38,200 within 24 months): Flanders about 30,800 land listings, Wallonia about 8,300, Brussels about 330; about 95 % carry a plot area. Some rural Walloon municipalities have few land listings (then low_sample).
Example: live response of 15-09-2026 with a test key (Accept-Language: en, request f0c372798b3f285b66ab4b584819be57; the 2.5.1 fields age_months and max_age_months: null added), shortened to one comparable with one photo, the main parcel fields and the neighbourhood blocks that change for land; building_stock, safety, amenities, parcels, parcel_candidates and price_map are replaced by "...":
{
"request_id": "f0c372798b3f285b66ab4b584819be57",
"environment": "test",
"address": {
"input": "Buissestraat 17, 9690 Kluisbergen",
"normalized": "Buissestraat 17, 9690 Kluisbergen",
"box": null,
"postal_code": "9690",
"municipality": "Kluisbergen",
"lat": 50.76716,
"lon": 3.484313,
"region": "VL",
"nis_code": "45060",
"geocoder": "geo.api.vlaanderen.be",
"geocode_score": 0.95,
"precision": "house_number"
},
"sections_delivered": [
"basic",
"parcel",
"price_map"
],
"basic": {
"comparables": [
{
"type": "land",
"type_label": "land",
"transaction": "sale",
"transaction_label": "for sale",
"address_mode": "house_number",
"radius_m": 5000,
"count": 25,
"excluded_subject_property": 0,
"max_age_months": null,
"low_sample": false,
"items": [
{
"ref": "r_9965f5493e",
"address": "Buissestraat 19, 9690 Kluisbergen",
"distance_m": 25,
"type": "land",
"transaction": "sale",
"price": 80000,
"price_kind": "asking_price",
"price_kind_label": "asking price",
"price_per_m2": null,
"price_per_m2_plot": 115,
"living_area_m2": null,
"plot_area_m2": 694,
"bedrooms": null,
"epc_label": null,
"epc_kwh_m2": null,
"epc_source": null,
"year_built": null,
"condition": null,
"building_type": null,
"new_build": null,
"published": "2026-09-10",
"days_online": 4,
"last_seen": "2026-09-14",
"age_months": 0,
"status": "online",
"status_label": "online",
"source": "listing",
"source_label": "listing",
"features": {
"plot_area_m2": 694,
"zoning": null,
"flood_zone": null,
"land_type": "building_plot"
},
"summary": "Building plot of 694 m² in Kluisbergen, listed since 10-09-2026.",
"thumbnail": {
"url": "https://taxonapi.be/api/v1/marketexplorer/foto/fp_WzI3NDc1OTEsIjAxYTA0NzM2LTE5OGUtN2Q3ZC1iZjNjLWYyZDQ2NzliOTI3ZSIsMTc4OTQ2MzYyNiwicCJd.QB9ILrzzIwQxlyOV?w=160",
"valid_until": "2026-09-15T09:13:46Z"
},
"photo_count": 13,
"history": [
{
"date": "2026-08-28",
"price": 80000,
"event": "published",
"label": "published"
}
],
"photos": [
{
"url": "https://taxonapi.be/api/v1/marketexplorer/foto/fp_WzI3NDc1OTEsIjAxYTA0NzM2LTE5OGUtN2Q3ZC1iZjNjLWYyZDQ2NzliOTI3ZSIsMTc4OTQ2MzYyNiwicCJd.QB9ILrzzIwQxlyOV?w=640",
"download_token": "b17f02c8feb98b013e60",
"valid_until": "2026-09-15T09:13:46Z"
}
]
}
]
}
],
"neighbourhood": {
"sector": {
"code": "45060A010",
"name": "BUISESTRAAT",
"level": "sector",
"municipality": "Kluisbergen"
},
"building_stock": "...",
"price_level": null,
"epc_prices": null,
"safety": "...",
"land_price_level": {
"radius_m": 5000,
"count": 116,
"price_per_m2_plot": {
"p25": 126,
"median": 160,
"p75": 224
},
"price_kind": "asking_price",
"max_age_months": 24
}
},
"amenities": "..."
},
"parcel": {
"capakey": "45043C0460/00X000",
"region": "VL",
"area_m2": 528.79,
"cadastral_area_m2": 528.78,
"fiscal_situation": "2027-01-01",
"zoning": {
"category": "residential",
"label": "woongebieden",
"plan": "gewestplan"
},
"preemption_right": {
"status": "none",
"source": "RVV thematic layer, right of pre-emption (Flanders)"
},
"source": "CadGIS PlanParcellaire (FPS Finance) / GRB Gbg - building at ground level (Digitaal Vlaanderen)",
"main_parcel": "45043C0460/00X000",
"parcels_count": 1,
"total_area_m2": 528.79,
"plot_area_source": "cadastre_main_parcel",
"parcels": "...",
"parcel_candidates": "..."
},
"avm": null,
"price_map": "...",
"billing": {
"user_ref": "office-oudenaarde-02",
"case_ref": "DOS-2026-0518",
"charged": false,
"pilot": false,
"free_reason": "test",
"price": {
"package": 0.0,
"avm": 0.0,
"total": 0.0,
"currency": "EUR",
"excl_vat": true
},
"dedup_of": null,
"dedup_valid_until": "2026-10-15T10:13:47+02:00",
"month": "2026-09",
"month_to_date": {
"cases": 0,
"amount": 0.0
},
"pilot_status": {
"active": true,
"quota": 200,
"used": 0,
"remaining": 200,
"ends": "2026-11-03",
"expired": false
},
"today": {
"client_today": 10,
"user_today": 1
}
},
"notices": [
{
"code": "avm_not_available_for_land",
"section": "avm",
"message": "AVM not delivered: Taxon does not provide an automated valuation for land (type land); not charged."
},
{
"code": "housing_stats_not_available_for_land",
"section": "basic",
"message": "price_level and epc_prices are housing statistics and are not delivered for land; see neighbourhood.land_price_level."
},
{
"code": "price_map_sparse",
"section": "price_map",
"message": "price map: fewer than 30 listings in the subject neighbourhood (small sample)."
},
{
"code": "test_environment",
"section": null,
"message": "test key: not invoiced"
}
],
"upstream_errors": {},
"attribution": "References: Taxon (taxon.be)",
"disclaimer": "Asking prices from listings, not notarial sale prices. Data for internal use per case only; building a derived database is prohibited. The valuer decides, the AVM is a support tool.",
"generated_at": "2026-09-15T10:13:47+02:00"
}
Price map (section price_map) 2.0
Asking-price levels per neighbourhood within radius_m (3,000 m) of the address, sorted by distance, at most 40 neighbourhoods (the subject neighbourhood always included), delivered as data plus GeoJSON geometry. Taxon delivers no map images or tiles: you draw the polygons yourself with your own map library and licence (for example Leaflet, MapLibre or Mapbox) and colour them by price_per_m2_house or price_per_m2_apartment. Part of the basic package since 2.4.0: no separate price (the package price is in billing.price.package); dedup follows the package. Block size 20 to 50 KB; warm response under 0.5 s, the first request after a service restart can take up to 20 s. Measured live on 07-09-2026: Braine-l'Alleud 40 neighbourhoods, Ieper 38, Schaerbeek 40.
| Field | Meaning |
|---|---|
radius_m | Radius around the address, 3000. |
price_kind | Always asking_price. |
reference_period, updated_at | Reference date of the price layer the figures are based on ("2026-03-30") and the moment that layer was last rebuilt (ISO 8601). |
subject_neighbourhood | id (string) of the neighbourhood that contains the address: the item with is_subject: true and distance_m: 0. |
neighbourhoods[] | Per neighbourhood: id, name, municipality (in the language of Accept-Language), postal_code, distance_m, is_subject, price_per_m2_house, price_per_m2_apartment (asking-price level per m² from the Taxon price layer of reference_period, null when the layer has no figure), listings_count_house, listings_count_apartment (number of Taxon listings of the last 24 months in that neighbourhood: a measure of how much current supply supports the figure, not the sample behind it; 0 = a reference-layer figure without recent Taxon listings), low_sample (fewer than 30 such listings: show as indicative) and geometry (GeoJSON Polygon or MultiPolygon, WGS84 lon/lat). |
source | Source line, to be shown next to the map (translated; always says that these are asking prices, not notarial sale prices). |
Notice price_map_sparse (section price_map) when the subject neighbourhood itself has fewer than 30 listings; the section is still delivered. When no neighbourhood with prices lies within 3 km the section is null, the notice price_map_unavailable is added, upstream_errors.price_map is no_coverage; this is no failure and the basic package stays charged. Do not store the geometry beyond the case (usage rules, section 11). Real example: live response of 08-09-2026 (live key, sections=basic, Grote Markt 34, 8900 Ieper, request f5569dd2c231469abaa2536b1bc4dd61), 2 of the 38 neighbourhoods, geometry of the subject neighbourhood complete:
"price_map": {
"radius_m": 3000,
"price_kind": "asking_price",
"reference_period": "2026-03-30",
"updated_at": "2026-07-09T10:26:08+00:00",
"subject_neighbourhood": "7220",
"source": "Taxon asking prices (advertised prices, not notarial sale prices)",
"neighbourhoods": [
{
"id": "7220",
"name": "Ieper-Centrum",
"municipality": "Ieper",
"postal_code": "8900",
"distance_m": 0,
"is_subject": true,
"price_per_m2_house": 1937.91,
"price_per_m2_apartment": 2693.77,
"listings_count_house": 76,
"listings_count_apartment": 59,
"low_sample": false,
"geometry": {
"type": "Polygon",
"coordinates": [[[2.89239, 50.84819], [2.89082, 50.84804], [2.88999, 50.84756], [2.88856, 50.84716], [2.88727, 50.84885], [2.88559, 50.84838], [2.88386, 50.84823], [2.88343, 50.84899], [2.88207, 50.8487], [2.88188, 50.84888], [2.88017, 50.8484], [2.87996, 50.84875], [2.87954, 50.84865], [2.87908, 50.84969], [2.87869, 50.84955], [2.87772, 50.85146], [2.88352, 50.85238], [2.88367, 50.85225], [2.88646, 50.85268], [2.88857, 50.85268], [2.89005, 50.85297], [2.89134, 50.853], [2.89214, 50.85059], [2.89239, 50.84819]]]
}
},
{
"id": "7226",
"name": "Diksmuidse Poort",
"municipality": "Ieper",
"postal_code": "8900",
"distance_m": 121,
"is_subject": false,
"price_per_m2_house": 1991.52,
"price_per_m2_apartment": 2374.19,
"listings_count_house": 41,
"listings_count_apartment": 22,
"low_sample": false,
"geometry": {"type": "Polygon", "coordinates": ["…"]}
}
]
}
Per region
| Flanders (VL) | Wallonia (WAL) | Brussels (BXL) | |
|---|---|---|---|
| Geocoding | geo.api.vlaanderen.be, fallback BeSt (fedservices.be) | BeSt (fedservices.be) | BeSt (fedservices.be) |
| Zoning (parcel) | Gewestplan or RUP (plan: gewestplan | RUP) | Plan de secteur (plan: "plan de secteur") | GBP / PRAS (plan: "GBP/PRAS") |
| Parcel source | FPS Finance (CadGIS) + GRB | FPS Finance (CadGIS) + SPW (PICC) | FPS Finance (CadGIS) + UrbIS |
Municipality name (one rule for address, comparables, neighbourhood.sector, neighbourhood.safety and price_map) | Dutch in every language | French in every language, also for nl (no exonyms: Liège, Braine-l'Alleud) | Dutch for nl, French for fr and en |
| AVM | Fully calibrated | Notice avm_indicative_not_regionally_calibrated: model not yet regionally calibrated, wider range, explicit check by the valuer | Same notice, idem |
| Flood data, NOT included | No information duty on flood sensitivity (fluvial/pluvial, 2023 rules), no P-score/G-score | No aléa d'inondation (SPW maps) | No flood maps of Brussels Environment |
Flood data comes in a later version as an extra field in the parcel section. Until then the valuer consults the flood data at the region; the API makes no statement about it.
6.Dedup (free repetition) and Idempotency-Key
Billing happens per user + property. The property is determined by the geocode (rounded to about 1 m), not by the literal spelling of the address. A charged request stays valid for 30 days for the same user. A property is the geocoded point plus the box number; another type, other AVM parameters, other capakeys or another language do not make another property: such a repeat is rebuilt with the new input and stays free for the sections already delivered (dedup freezes nothing).
| Situation | Result |
|---|---|
User A requests basic; again the same day; a third time two days later ("clicking three times") | One charge. The second and third request give charged: false, dedup_of = request_id of the first, price.total = 0, free_reason: "dedup", from_cache: true (the answer comes from the ledger of the first request) and a notice dedup with section: null. |
User A requested basic; a week later he requests basic,avm for the same property | Only the new AVM option is charged (price.avm); the basic package is 0 (price.package) with a notice dedup for section: basic (message: basic package already delivered). The difference, nothing more. |
User A requested basic,avm; a week later basic,avm again | Fully free until dedup_valid_until (30 days after the first charge). free_reason is dedup whenever every delivered section had already been delivered for this user and property (ledger hit or rebuild, also with a test key); otherwise test, pilot or null (charged) with notices dedup per section. |
| User B requests the same property one hour after user A | New charge: user A and user B are two cases (dedup_of: null). |
| User A requests the same property after 31 days | New charge; a new period of 30 days starts. |
Address not found, zero comparables (free_reason: no_result), section failed, error at request level | Not charged, and the dedup period does not start: the next request for the same property by the same user is rebuilt and charged normally when comparables are found (an AVM option delivered with the empty result does keep its own window). |
Idempotency-Key is a technical safeguard for retries: the same key within 24 hours returns byte for byte the same response (same request_id) without counting, also against the daily cap, recognisable by the response header X-Idempotent-Replay: true. Use a new UUID per request and reuse it only on a retry after a timeout or a 5xx. Two concurrent identical requests without a key are also caught: only one is charged, the other gets dedup_of and price 0.
Mind the watchdog: when one X-User-Ref carries an unnaturally large share of the traffic, or a user shows a grid-scan pattern (consecutive house numbers or postal codes in a short time), that user is suspended (403 user_suspended); the other users keep working. The suspension hits the user, not the client (your key); only with three or more suspended users within 24 hours 403 client_suspended follows. So always use the real user id.
7.Caps and error codes
Caps
| Level | Value | When exceeded |
|---|---|---|
| Per key, per minute | 60 requests (burst 20: at most 20 may arrive at once; above that 429 follows even below 60 per minute) | 429 rate_limited + header Retry-After (also retry_after in the body), or the nginx variant 429 rate_limit_exceeded without header |
| Per key, per day | 300 requests (global cap per key) | 403 quota_exceeded with scope: "day" and limit + header Retry-After (seconds until midnight Europe/Brussels) |
| Per user, per day | 20 requests | 403 quota_exceeded with scope: "user_day", limit and user_ref + header Retry-After; other users keep working |
| Per key, per month | According to the agreement | Notification to both parties; blocking only after consultation |
| Pilot | 200 cases within 60 days after signature | 403 pilot_exhausted (body pilot{active, quota, used, remaining, ends, expired}) until the annual contract is active |
| Grid scan per user | Watchdog on consecutive house numbers/postal codes, night peaks, skewed user distribution | 403 user_suspended with user_ref, reason (raster_scan, handmatig (manual) or free text) and since, without Retry-After; only that user is suspended, the others keep working; dedup and usage unchanged; no automatic expiry; lifting only by Taxon administration (info@taxon.be, quoting user_ref). Three or more suspended users within 24 hours: the client itself gets 403 client_suspended |
| Test key | 20 requests per day (counted, for every daily cap: every request that reaches the address lookup, dedup hits and 404/422 address errors included; not counted: 400, 401, 403, 405, 409, 422 capakey_invalid, 429, 503 and Idempotency-Key replays) | 403 quota_exceeded, scope: "day" |
| Overload guard | Too many bundles in progress on the service | 503 overloaded + Retry-After, nothing charged or counted |
| Bundle timeout | 40 seconds | 504 timeout, nothing charged |
Every 200 response shows the daily count in billing.today (client_today, user_today), so that you can see a cap coming. Dedup repetitions count as traffic too.
Retry advice. On 429: wait for Retry-After (or retry_after from the body when the header is missing, the nginx variant) and retry with the same Idempotency-Key. On 502, 503, 504 or 500: at most 3 attempts with exponential back-off (2, 4, 8 s; for 503 the Retry-After value), same Idempotency-Key. On 403 quota_exceeded: wait for Retry-After (until midnight). On other 4xx: do not retry, the request itself is wrong.
Error codes
Uniform error body: {"error": "...", "message": "...", "request_id": "...", "charged": false}, possibly completed with details[] ({field, issue}), retry_after, scope and limit, section, user_ref, reason, since or pilot. message follows Accept-Language.
| HTTP | error | When | What the client does |
|---|---|---|---|
| 400 | invalid_request | Parameter or header missing or invalid (also an X-User-Ref with forbidden characters, an unknown section, a 1.x parameter name, a parameter sent twice); details[] gives field and issue. | Fix the request; do not retry as is. |
| 400 | user_ref_required | Header X-User-Ref missing (and no alias). | Add the user header. |
| 400 | user_ref_conflict | Two or more of X-User-Ref, X-Gebruiker-Ref, X-Kantoor-Ref sent with different values (after normalisation). | Send one header, preferably X-User-Ref. |
| 401 | missing_api_key | Header X-Api-Key missing. | Configuration error; alert your operations. |
| 401 | invalid_api_key | Key unknown. | Idem; do not retry. |
| 403 | key_revoked | Key revoked (after rotation or incident). | Switch to the new key. |
| 403 | ip_not_allowed | Request from outside the IP allowlist. | Pass the new server IP to Taxon. |
| 403 | client_suspended | Access suspended (abuse pattern, non-payment). | Contact Taxon; do not retry. |
| 403 | section_not_allowed | Requested section not in your plan (section in the body). | Remove the section or extend the plan. |
| 403 | quota_exceeded | Daily cap of the partner (scope: "day") or of the user (scope: "user_day", with user_ref); limit in the body. Header Retry-After and retry_after (seconds until midnight). | Queue until Retry-After; other users keep working. |
| 403 | pilot_exhausted | Pilot quota of 200 cases reached or 60 days passed; pilot{} in the body. | Contact Taxon (annual contract). |
| 403 | user_suspended | User suspended (grid scan or manual); user_ref, reason and since in the body. Lifting only via Taxon administration (info@taxon.be). | Block that user in your UI; do not retry. |
| 404 | address_not_found | No geocoder finds the address. Check postal code and house number. | Let the user correct the address. |
| 404 | not_found | Unknown path under /api/v1/partner/ (also the 1.x paths /adres and /verbruik). | Fix the path. |
| 405 | method_not_allowed | Method other than GET. | Use GET. |
| 409 | idempotency_conflict | Same Idempotency-Key within 24 hours with another address, another user, other sections or other parameters. | Use a new key. |
| 422 | address_imprecise | Address found, but not down to the house number (street or municipality only), or no postal code and no municipality given. | Let the user add the house number. |
| 422 | type_unsupported | The address found or the geocoder does not support the given type. | Change the type. |
| 422 | capakey_invalid | capakeys in the wrong form or more than 10 (details[]). | Fix the capakeys. |
| 422 | capakey_too_far | A parcel lies more than 2 km from the address; nothing delivered. | Remove that parcel. |
| 429 | rate_limited | Per-minute cap in the application. Header Retry-After and retry_after in the body. | Wait Retry-After, retry with the same Idempotency-Key. |
| 429 | rate_limit_exceeded | Per-minute cap in nginx: body {"error": "rate_limit_exceeded", "retry_after": 60}, without Retry-After header and without request_id. | Wait retry_after from the body, then retry. |
| 502 | upstream_unavailable | Basic section could not be delivered (database or geocoder unreachable); details[] names the block and the reason. | Retry up to 3 times with back-off, same Idempotency-Key. |
| 503 | overloaded | Too many bundles in progress; Retry-After and retry_after. | Wait Retry-After, then retry. |
| 504 | timeout | The bundle took longer than 40 seconds. | Retry with the same Idempotency-Key. |
| 500 | internal_error | Unexpected error. | Retry once; then report the request_id. |
No error is ever charged (charged: false).
avm (living_area_required), a failed section (section_missing) or zero comparables (no_comparables) give HTTP 200 with a line in notices[] (code, section, message); the section concerned is then missing from sections_delivered. Billing follows the package (see 4): a missing AVM option is not charged, a missing parcel or price_map leaves the basic package charged, zero comparables leave the package uncharged (free_reason: no_result). Other codes: dedup (section: basic = basic package already delivered, section: avm = AVM option already delivered, section: null = full ledger hit), avm_indicative_not_regionally_calibrated (WAL/BXL), avm_type_unsupported, avm_insufficient_data, parcel_not_found, address_street_level, region_conflict, geocoder_partially_unavailable, subject_property_excluded (listings of the subject property itself were removed from the comparables: same street and house number within 300 m, with the same box when you gave a box; the count is in excluded_subject_property on the block), capakey_not_found, main_parcel_not_in_capakeys, parcel_analysis_incomplete, plot_area_not_cadastral, bedrooms_filter_dropped, price_map_sparse (section delivered), price_map_unavailable (no coverage within 3 km, section not delivered, package unchanged), pilot_exhausted (only with a plan whose pilot overflow charges), key_rotation_pending (the key used has been rotated and keeps working until the date in the message; also in /usage), test_environment, info; with section_missing the message is timeout or upstream_error. Ignore unknown codes.8.GET /usage
Your own usage per calendar month: total, per user, per API key (2.4.1), per day, per section and a quality block (error percentage, latency p50/p95, number of requests with upstream failures). requests counts all requests, cases the requests with HTTP 200, charged those with a price; sections counts charged units with the keys package (basic package) and avm (AVM option); sections_delivered (since 2.4.0 also in total) counts the four real sections basic, parcel, avm, price_map; per_section uses package and avm. Meant to check yourself what Taxon invoices and to re-invoice your users. X-User-Ref is optional here and works as a filter. The block plan shows your plan without prices: sections_allowed, max_per_minute, max_per_day, max_per_user_per_day, test_max_per_day, dedup_days, idempotency_hours, address_mode. requests counts the same requests as the daily caps (see 7).
| Parameter | Meaning |
|---|---|
month | YYYY-MM, default the current month (Europe/Brussels). |
format | json (default) or csv. |
user_ref | Optional filter on one user (equivalent to the header X-User-Ref, which wins when both are given), case-insensitive. |
Unknown parameters (including the 1.x names maand, formaat, gebruiker_ref) give 400 invalid_request with details[].field. Live response of 08-09-2026 (live key, two users, two keys, four requests; the client code is replaced by a placeholder):
GET /api/v1/partner/usage?month=2026-09 HTTP/1.1
X-Api-Key: tx_live_<32 hex>
{
"client": "<client code>",
"month": "2026-09",
"environment": "live",
"user_ref": null,
"total": {
"requests": 4,
"cases": 4,
"charged": 4,
"free_dedup": 0,
"free_pilot": 0,
"free_error": 0,
"sections": {"package": 3, "avm": 2},
"sections_delivered": {"basic": 4, "parcel": 4, "avm": 2, "price_map": 4},
"amount": 13.0,
"currency": "EUR",
"excl_vat": true,
"duration_ms_avg": 3531
},
"per_user": [
{
"user_ref": "office-kortrijk-03",
"requests": 2,
"cases": 2,
"charged": 2,
"free_dedup": 0,
"sections": {"package": 2, "avm": 1},
"sections_delivered": {"basic": 2, "parcel": 2, "avm": 1, "price_map": 2},
"amount": 8.0,
"last_activity": "2026-09-08T10:39:27+02:00"
},
{
"user_ref": "office-ieper-01",
"requests": 2,
"cases": 2,
"charged": 2,
"free_dedup": 0,
"sections": {"package": 1, "avm": 1},
"sections_delivered": {"basic": 2, "parcel": 2, "avm": 1, "price_map": 2},
"amount": 5.0,
"last_activity": "2026-09-08T10:39:16+02:00"
}
],
"per_day": [{"day": "2026-09-08", "requests": 4, "cases": 4, "charged": 4, "amount": 13.0}],
"per_section": {"package": {"delivered": 4, "charged": 3, "amount": 9.0}, "avm": {"delivered": 2, "charged": 2, "amount": 4.0}},
"quality": {"error_pct": 0.0, "latency_p50_ms": 3065, "latency_p95_ms": 7531, "upstream_errors": 0},
"pilot": null,
"generated_at": "2026-09-08T10:39:27+02:00",
"plan": {
"sections_allowed": ["basic", "parcel", "avm", "price_map"],
"max_per_minute": 30,
"max_per_day": 300,
"max_per_user_per_day": 80,
"test_max_per_day": 80,
"dedup_days": 30,
"idempotency_hours": 24,
"address_mode": null
},
"request_id": "c535faa3301644c1af51f3901fea7e2c"
}
The block pilot (and billing.pilot_status) is only the pilot counter. The client status from the ledger (pilot, active, suspended, ended) is not delivered in the response; you notice a suspension by 403 client_suspended or 403 user_suspended. With a live key sections and charged carry the charged counts and amount the amounts according to your agreement.
Per API key 2.4.1
Since 2.4.1 the response also carries the block per_key (between per_user and per_day): the usage per API key, with the same filters as the rest of the response (month, the environment of the calling key, user_ref). Per key: prefix (the first 12 characters, as in your key list), label (null when empty), environment, active, revoked_at (null while active), requests, cases, charged, sections, sections_delivered, amount and last_activity; sorted by amount, then requests, descending. Keys without requests in the month are not listed; a revoked key with requests in the month is (active: false, revoked_at filled in). The sums over per_key equal total. For the example above (three requests with the current live key, one with a previous live key that was revoked since):
"per_key": [
{
"prefix": "tx_live_3f9a",
"label": "production",
"environment": "live",
"active": true,
"revoked_at": null,
"requests": 3,
"cases": 3,
"charged": 3,
"sections": {"package": 2, "avm": 2},
"sections_delivered": {"basic": 3, "parcel": 3, "avm": 2, "price_map": 3},
"amount": 10.0,
"last_activity": "2026-09-08T10:39:27+02:00"
},
{
"prefix": "tx_live_b71c",
"label": "old key",
"environment": "live",
"active": false,
"revoked_at": "2026-09-08T10:39:20+02:00",
"requests": 1,
"cases": 1,
"charged": 1,
"sections": {"package": 1, "avm": 0},
"sections_delivered": {"basic": 1, "parcel": 1, "avm": 0, "price_map": 1},
"amount": 3.0,
"last_activity": "2026-09-08T10:39:15+02:00"
}
]
CSV
?format=csv delivers one row per request, UTF-8 with BOM, separator ;, every field in double quotes, decimal , (opens directly in Excel nl-BE/fr-BE), CRLF line ends. File name taxon-usage-<client>-<month>.csv (Content-Disposition). This is the same list as the usage attachment of the monthly invoice. Sections are separated with |; charged is 0 or 1. Real rows of 08-09-2026 (the four requests of the usage example above, 16 columns since 2.4.1): row 1 is the basic package for Grote Markt 34 Ieper, row 2 the AVM option added one second later for the same case (dedup_of = row 1, package 0), row 3 the main example of section 4, row 4 a request with sections=parcel normalised to the package:
"request_id";"timestamp";"user_ref";"case_ref";"address";"region";"sections_requested";"sections_delivered";"charged";"free_reason";"dedup_of";"price_package";"price_avm";"price_total";"status_code";"key_prefix"
"f5569dd2c231469abaa2536b1bc4dd61";"2026-09-08T10:39:15+02:00";"office-ieper-01";"DOS-2026-0451";"Grote Markt 34, 8900 Ieper";"VL";"basic|parcel|price_map";"basic|parcel|price_map";"1";"";"";"3,00";"0,00";"3,00";"200";"tx_live_b71c"
"76cddf8bb6fc4a299283f85aa2be799e";"2026-09-08T10:39:16+02:00";"office-ieper-01";"DOS-2026-0451";"Grote Markt 34, 8900 Ieper";"VL";"basic|parcel|avm|price_map";"basic|parcel|avm|price_map";"1";"";"f5569dd2c231469abaa2536b1bc4dd61";"0,00";"2,00";"2,00";"200";"tx_live_3f9a"
"cf514c7de2714c11806e2ebf053d0247";"2026-09-08T10:39:24+02:00";"office-kortrijk-03";"DOS-2026-0452";"Doorniksestraat 40, 8500 Kortrijk";"VL";"basic|parcel|avm|price_map";"basic|parcel|avm|price_map";"1";"";"";"3,00";"2,00";"5,00";"200";"tx_live_3f9a"
"3aa7ba378561487fad54fd5fbbe83f1e";"2026-09-08T10:39:27+02:00";"office-kortrijk-03";"";"Rijselstraat 20, 8900 Ieper";"VL";"basic|parcel|price_map";"basic|parcel|price_map";"1";"";"";"3,00";"0,00";"3,00";"200";"tx_live_3f9a"
free_reason is empty (charged) or dedup, pilot, pilot_exhausted (row with status 403: the request got pilot_exhausted), test, no_result (zero comparables, or an address error: rows with status 404 or 422), error, timeout. price_package is the price of the basic package (basic + parcel + price_map), price_avm that of the AVM option (columns since 2.4.0; the three former per-section price columns have disappeared). sections_requested is the normalised list: the package written out as basic|parcel|price_map. key_prefix (last column, 2.4.1) is the prefix of the API key that made the request, as in per_key[].prefix.
9.GET /health
Public, without key, without data. Suitable for your monitoring. The keys of this endpoint are deliberately kept from 1.x (Dutch names): versie = version, tijd = time, bundel = bundle mode, db = database.
{"status": "ok", "service": "taxon-partner-api", "versie": "2.5.1", "tijd": "2026-09-16T10:47:16+02:00", "bundel": "live", "db": "ok"}
versie is the version of the service (2.4.1 = contract 2.0 plus the price_map section, the comparable features, the fixes of the first external integration test, key management by the partner, the basic package usage per key and, since 2.4.2, the current cadastral parcel plan with fiscal_situation), not the version of this contract (2.0). bundel is live (real sections) or stub (test set-up); db is ok or fout (error). On a database error the endpoint answers HTTP 503 with "status": "degraded". There is no separate maintenance flag; planned maintenance is announced by e-mail.
10.Attribution and disclaimer (required)
Every screen, report or document showing API data carries visibly and inseparably the attribution and the disclaimer. The API delivers both literally in the fields attribution and disclaimer, in the language of Accept-Language. They may not be shortened, moved to an annex or removed.
Attribution
Disclaimer
Source lines per data layer
Next to the general attribution the API delivers per layer a source line (source, attribution) that you also copy next to that data, also in white-label:
- Parcel and building stock: Source: FPS Finance, cadastral parcel plan (CadGIS) plus the regional buildings layer named in
parcel.source - Statistical sectors and indexes: Source: Statbel, CC BY 4.0
- Wallonia (zoning, geocoding): Source: Service public de Wallonie
- Flanders: Source: Flemish government, Free Open Data Licence v1.2
- Brussels: Source: perspective.brussels / Brussels Environment, CC BY
- Safety: Source: Federal Police, police crime statistics (PCS)
- Amenities: © OpenStreetMap contributors (ODbL)
- Price map: the
sourceline of the section
Additional qualifications that you copy literally into your report templates: the comparables are asking prices from public supply, not transaction prices; the AVM value is an indicative model output with a range, not an appraisal; parcel, plan and government data are delivered "as is" and for information only with their reference date; the data contains no energy performance data (EPC/PEB).
11.Usage rules
Summary of the contractual rules that the API also enforces technically. The agreement prevails.
- Per case. Show the data only to the valuer and include it only in the valuation report of the case for which the request was made. Every request belongs to a concrete case and an identified user (
X-User-Ref, preferably alsoX-Case-Ref). - Retention at most 30 days. Raw API responses disappear from operational systems, logs and caches at the latest 30 days after the request. Only the finished report (PDF) stays in the legal case archive.
- No derived database. Do not merge, index or store data from several requests into your own database, map layer, index or price model; this includes the price-map geometry.
- No model feeding. Do not use the data to train, calibrate, validate or improve an algorithm, model or AI system.
- No resale or bulk. Do not resell, publish, export or hand over to third parties outside the report; do not query the API automatically or systematically outside a case.
- Photos (2.5.0). Fetch photos within the validity of the link (1 hour) and use them only for the case for which the request was made. Embedding them in the valuation report (PDF) of that case is allowed, always with a visible source line next to every photo (attribution, see 10), subject to the partner agreement (the partner indemnifies Taxon against claims of portals or agents concerning the photos). No republication, no storage outside that report, no bulk download; the resolution stays at most 640 px.
- Attribution visible in your UI and in every report (see 10). White-label only through a separate addendum.
- EPC "as advertised". Never show EPC labels of comparables as a looked-up or verified value.
- No notarial prices. Never communicate that the data comes from notaries, VLABEL or transactions; they are asking prices.
- Human decision. No decision with legal effect (credit, tax, insurance) solely on the basis of the API or the AVM without the final responsibility of an expert.
- Key secret, report a leak within 48 hours; on request (at most twice a year) provide an extract of case numbers against requests.
- Documentation confidential. This documentation is meant for your technical staff; do not publish it.
12.Changelog and 1.x mapping
| Version | Date | Changes |
|---|---|---|
| 2.5.1 | 16-09-2026 | Land without age limit. With type=land the comparables are no longer limited to listings published within 24 months: all land listings for sale (same subtype exclusions) within 5 km (extended once to 10 km with fewer than 10 in total) are delivered, the listings of the last 24 months first (sorted by distance) and the older ones after them (sorted by distance), at most 25 per block, so that you can adjust older asking prices for the market evolution within the case (price indexation); the licence terms remain: no merging, indexing or storing across cases. New item field age_months (whole calendar months since published, only with type: land); block field max_age_months is null for land (stays 24 for house and apartment). neighbourhood.land_price_level keeps the last 24 months and widens to 60 months with fewer than 10 usable listings within 24 months; its max_age_months says which window was used (24 or 60). Notice no_comparables gets a land-specific message. House and apartment unchanged. Additive. Service version 2.5.1. Patch of 16-09-2026 (same version 2.5.1): days_online is documented as nullable (null when the source recorded no online duration, mostly older offline listings; last_seen then equals published); the purpose of the older listings is worded as price indexation within the case (licence unchanged); test scenario T24d uses an example address that actually returns older listings; land responses stored under 2.5.0 are no longer replayed from the input cache. |
| 2.5.0 | 15-09-2026 | Land and photo rule. New value type=land (aliases grond, terrain, bouwgrond) for building plots: one comparables block with land listings for sale within 5 km (extended once to 10 km with fewer than 10; block field low_sample and notice low_sample), items with plot_area_m2 and price_per_m2_plot (housing fields null, features limited to plot_area_m2, zoning, flood_zone), neighbourhood.land_price_level {radius_m, count, price_per_m2_plot {p25, median, p75}, price_kind, max_age_months} while price_level and epc_prices are null (notice housing_stats_not_available_for_land); no AVM for land (notice avm_not_available_for_land, section not delivered, not charged); living_area_m2 optional; parcel and price_map unchanged; basic package price. Photo rule relaxed: photos may be embedded in the report of the case with a visible source line per photo, subject to the partner agreement (no republication, no storage outside that report, no bulk download, at most 640 px). Patch of 15-09-2026 (same version): land comparables and land_price_level are limited to building plots, project land and land without a more precise subtype (agricultural land, meadows, woodland, industrial land and parking spaces are excluded by subtype); land_price_level counts only listings between 15 and 5,000 EUR per m² of plot; new LandFeatures key land_type (building_plot | project_land | other), the first word of summary follows it (additive). Additive: no changes to paths or existing fields. Service version 2.5.0. |
| 2.4.2 | 14-09-2026 | Current cadastral parcel plan. The parcel section now reads the FPS Finance PlanParcellaire layer (running fiscal situation, today 01-01-2027) instead of the yearly INSPIRE snapshot (01-01-2026); the INSPIRE layer remains the fallback on an outage or an empty answer. New field fiscal_situation (ISO date of the fiscal situation of the parcel plan, for example 2027-01-01; 2026-01-01 when the INSPIRE fallback answered; null when unknown) on parcel, on every parcels[] item and on every parcel_candidates[] item. source names the layer (CadGIS PlanParcellaire (FPS Finance) or CadGIS INSPIRE (FPS Finance)). Additive: no changes to paths, parameters or existing fields. Service version 2.4.2. |
| 2.4.1 | 08-09-2026 | Usage per API key. /usage gets the block per_key[] (per key: prefix, label, environment, active, revoked_at, requests, cases, charged, sections, sections_delivered, amount, last_activity; same filters as the rest of the response; keys without requests in the month are not listed, revoked keys with requests are). CSV: new last column key_prefix (16 columns). No changes to paths, parameters or existing fields. Service version 2.4.1. |
| 2.4.0 | 08-09-2026 | Basic package. basic + parcel + price_map are now one package per case; the AVM stays a separate option. billing.price is now {package, avm, total, currency, excl_vat}; /usage total.sections, per_user[].sections and per_section use package/avm, total.sections_delivered added; CSV columns price_package, price_avm, price_total. Requesting parcel or price_map separately is normalised to the package. Service version 2.4.0. |
| 2.3.0 | 08-09-2026 | Key management by the partner. Keys are shown exactly once in the partner dashboard on taxon.be (within 7 days after creation, then the encrypted copy is destroyed); partners create and revoke their own test keys (at most 3 active) and rotate their live key themselves: the old live key keeps working for 7 days and every /address and /usage response made with it carries the new notice key_rotation_pending (section: null, message with the end date); afterwards 403 key_revoked. The first live key is still issued by Taxon after the signed agreement. No changes to paths, parameters or fields. Service version 2.3.0. |
| 2.2.2 | 07-09-2026 | Fixes after the first external integration test (Propteo): photo links always deliver a server thumbnail (640 px by default, never the original); rental_value always inside rental_value_range; parcel_candidates, parcel_candidates_source and parcel_candidates_remark always present (null with capakeys); parcel_candidates[].direction and garden_orientation language-independent (N/E/S/W); zoning.category is an English enum; source lines translated for fr/en; one municipality-name rule for the whole bundle (no exonyms); new_build only when year_built does not contradict it; French summaries agree in gender; header and parameter errors in one 400; free_reason: dedup also with a test key when every delivered section was already delivered; pilot_status.expired everywhere; plan block in /usage; CSV Content-Type with one charset; Cache-Control and X-Content-Type-Options once. Service version 2.2.2. |
| 2.2.1 | 07-09-2026 | Adversarial QA round (no key or path changes): box numbers box 3, b3, app 3 recognised; an address without postal code and without municipality gives 422 address_imprecise; epc=A+ with a raw plus gives 400 (send A%2B); the input cache and the Idempotency-Key comparison cover all parameters and the language (other input = fresh bundle, still free for delivered sections; same key with other parameters = 409); section_missing for a sub-block of basic carries section: basic with the block name in the message; /usage rejects a parameter sent twice; price_map geometries always valid; concurrent identical requests in pilot/test count once. Service version 2.2.1. |
| 2.2.0 | 07-09-2026 | Features per comparable. Every comparable (sale and rent) carries features (structured listing features, unknown = null), summary (one sentence in Taxon's own words, nl/fr/en), thumbnail {url, valid_until} (primary photo at 160 px, same photo as photos[0]) and photo_count. No listing text, no price change (part of basic). Service version 2.2.0 in /health. |
| 2.0.0 | 07-09-2026 | English contract. Paths /address and /usage (old paths 404). Header X-User-Ref required (aliases X-Gebruiker-Ref, X-Kantoor-Ref deprecated), X-Case-Ref (alias X-Dossier-Ref), Accept-Language: en added. All query parameters, JSON keys, enum values and notice codes English (mapping below); old parameter names give 400 invalid_request. Error bodies with charged, details[] {field, issue}, user_ref, reason, since, limit, scope user_day; error codes user_ref_required and user_ref_conflict. New section price_map (service 2.1.0): notices price_map_sparse and price_map_unavailable, upstream_errors.price_map: no_coverage, a price key, a usage key and a CSV column for the price map (replaced by the package keys in 2.4.0). Behaviour change: condition now reaches the AVM. /health keeps its keys; versie shows the service version (2.1.0 at that time). |
| 1.2.0 | 07-09-2026 | Several parcels per property. Query parameter capakeys (comma-separated, at most 10, CadGIS form, also with hyphen): the parcel section delivers parcels[] plus totals; section-level fields stay those of the main parcel. Without capakeys: parcel_candidates[] (adjacent parcels, at most 12, with direction and built). New errors 422 capakey_invalid and 422 capakey_too_far (more than 2 km); notices for parcel not found, main parcel not in capakeys, incomplete parcel analysis, non-cadastral plot area, dropped bedrooms filter. Parameters plot area (fallback for the AVM) and bedrooms (filter plus or minus 1 with fallback below 8, and AVM input). Dedup: the same property with other capakeys within the window = no new charge. |
| 1.1.0 | 04-09-2026 | The notion "office" became "user" (Taxon decision). The user header became required; the office header stayed as a deprecated alias (same normalisation; both with a different value = 400 conflict). Definition of user in section 3. Caps fixed: 20 per user per day, 300 per key per day, 60 per minute. The watchdog suspends the user, not the client. |
| 1.0.0 | 03-09-2026 | First version of the integration contract: address bundle (basic, parcel, avm), usage (JSON + CSV), health. Dedup per user + property for 30 days, Idempotency-Key 24 hours, caps per minute/day/month and per user, pilot 200 cases. Flood data not included. AVM without living area = HTTP 200 with a notice; daily caps as 403 quota_exceeded; 504 timeout; response header X-Idempotent-Replay; user reference normalised to lower case. Response shape according to the built bundle: comparables as a list of blocks per type and transaction, photo objects (404 after expiry), safety as figures per 1,000 inhabitants, upstream errors, wide range and rental range. |
| planned | later | Flood data per region as an extra field in parcel; POST /address with JSON body for richer AVM input; asynchronous processing for slow layers. |
Changes to existing fields are announced at least 30 days in advance; new fields can be added without notice (your parser must ignore unknown fields). A new major version gets a new path (/api/v2/partner/).
Mapping 1.x (Dutch) to 2.0 (English)
For teams that read the 1.x documentation. The old names no longer work, except the three header aliases. Keys not listed (lat, lon, capakey, epc_label, score, label, plan, model, n, p25, p75, pct, ref, url, download_token, quota, code, status, type, request_id, pilot, details, top_10, comparables, parcel, avm, capakeys, epc, format) are unchanged.
| Where | 1.x | 2.0 |
|---|---|---|
| Paths | /adres, /verbruik | /address, /usage |
| Headers | X-Gebruiker-Ref (1.1), X-Kantoor-Ref (1.0), X-Dossier-Ref; Accept-Language: nl|fr | X-User-Ref, X-Case-Ref (old names stay as aliases); Accept-Language: nl|fr|en |
| Query /address | adres, secties (basis), type=huis|appartement, opp, bouwjaar, staat (nieuw|zeer_goed|goed|matig|te_renoveren), slaapkamers, opp_grond | address, sections (basic), type=house|apartment|land, living_area_m2, year_built, condition (excellent|very_good|good|average|poor), bedrooms, plot_area_m2 |
| Query /usage | maand, formaat, gebruiker_ref | month, format, user_ref |
| Top level | omgeving, uit_cache, adres, secties_geleverd, basis, facturatie, meldingen, fouten_upstream, bronvermelding, gegenereerd_op | environment, from_cache, address, sections_delivered, basic, billing, notices, upstream_errors, attribution, generated_at |
address | invoer, genormaliseerd, bus, postcode, gemeente, gewest, niscode, geocode_bron, precisie: huisnummer | input, normalized, box, postal_code, municipality, region, nis_code, geocoder, precision: house_number |
basic.comparables[] block | transactie: koop|huur, transactie_label, adres_modus: huisnummer|straat, straal_m, aantal, max_leeftijd_maanden, uitgesloten_eigen_pand, slaapkamers_filter {gevraagd, bereik, toegepast, aantal_binnen_filter} | transaction: sale|rent, transaction_label, address_mode: house_number|street, radius_m, count, max_age_months, excluded_subject_property, bedrooms_filter {requested, range, applied, count_within_filter}; new in 2.5.0 (no 1.x equivalent): low_sample (land) |
items[] (comparable) | adres, afstand_m, prijs, prijs_soort: vraagprijs, prijs_soort_label, prijs_per_m2, opp_wonen_m2, opp_grond_m2, slaapkamers, epc_kengetal_kwh_m2, epc_bron, bouwjaar, staat, bebouwing: open|halfopen|gesloten|appartement, nieuwbouw, publicatie, dagen_online, laatst_gezien, bron: advertentie, bron_label, historiek[] {datum, prijs, gebeurtenis: publicatie|prijsdaling|prijsstijging|herpublicatie|offline}, fotos[] {geldig_tot} | address, distance_m, price, price_kind: asking_price, price_kind_label, price_per_m2, living_area_m2, plot_area_m2, bedrooms, epc_kwh_m2, epc_source, year_built, condition, building_type: detached|semi_detached|terraced|apartment, new_build, published, days_online (null when the source recorded no online duration, mostly older offline listings; last_seen then equals published), last_seen, source: listing, source_label, history[] {date, price, event: published|price_drop|price_increase|republished|offline}, photos[] {valid_until}; new in 2.2.0 (no 1.x equivalent): features {...}, summary, thumbnail {url, valid_until}, photo_count; new in 2.5.0: price_per_m2_plot (land) |
basic.neighbourhood (was buurtstats) | sector {naam, niveau, gemeente}, gebouwenpark {niveau, peildatum, totaal, verdeling[] {cat: residentieel|handel_diensten|industrie|landbouw|andere, aantal}, bron}, prijsniveau {koop_m2, huur_m2_jaar, brutorendement_pct, brutorendement_p25_p75, straal_m, prijs_soort} with mediaan, epc_prijzen {straal_m, labels {mediaan_m2, aantal}}, veiligheid {niveau, gemeente, jaar, woninginbraak_per_1000, misdrijven_per_1000, gewest_woninginbraak_per_1000, gewest_misdrijven_per_1000, jaren[], bron} | sector {name, level, municipality}, building_stock {level, reference_date, total, distribution[] {category: residential|commerce_services|industry|agriculture|other, count}, source}, price_level {sale_per_m2, rent_per_m2_year, gross_yield_pct, gross_yield_p25_p75, radius_m, price_kind} with median, epc_prices {radius_m, labels {median_per_m2, count}}, safety {level, municipality, year, burglaries_per_1000, crimes_per_1000, region_burglaries_per_1000, region_crimes_per_1000, years[], source}; new in 2.5.0: land_price_level {radius_m, count, price_per_m2_plot {p25, median, p75}, price_kind, max_age_months} (land) |
basic.amenities (was voorzieningen) | schaal, straal_m, aantal_pois, deelscores {openbaar_vervoer, zorg, winkels, sport_cultuur, onderwijs, groen} {aantal}, top_10[] {naam, afstand_m}, attributie | scale, radius_m, poi_count, sub_scores {public_transport, healthcare, shops, sport_culture, education, green_space} {count}, top_10[] {name, distance_m}, attribution |
parcel | gewest, oppervlakte_m2, oppervlakte_kadastraal_m2, perceel_breedte_m, perceel_diepte_m, gevel_breedte_m, bebouwde_opp_m2, gebouwen_aantal, orientatie_tuin, orientatie_tuin_graden, bestemming {categorie}, voorkooprecht {status: ja|geen|onbekend, bron, dekking_pct, percelen, percelen_onbekend}, opmerking, bron, hoofdperceel, percelen[] {hoofdperceel, afstand_tot_adres_m}, percelen_aantal, oppervlakte_totaal_m2, oppervlakte_kadastraal_totaal_m2, bestemming_gezamenlijk, voorkooprecht_gezamenlijk, percelen_niet_gevonden, opp_grond_bron: "kadaster hoofdperceel" | "kadaster n percelen" | partner, perceel_kandidaten[] {oppervlakte_m2, richting, bebouwd, afstand_m}, perceel_kandidaten_bron, perceel_kandidaten_opmerking | region, area_m2, cadastral_area_m2, fiscal_situation (2.4.2, also in parcels[] en parcel_candidates[]; no 1.x equivalent), width_m, depth_m, frontage_m, built_area_m2, buildings_count, garden_orientation, garden_orientation_deg, zoning {category}, preemption_right {status: yes|none|unknown, source, coverage_pct, parcels, parcels_unknown}, remark, source, main_parcel, parcels[] {is_main, distance_to_address_m}, parcels_count, total_area_m2, cadastral_total_area_m2, zoning_combined, preemption_right_combined, parcels_not_found, plot_area_source: cadastre_main_parcel | cadastre_<n>_parcels (n = 2 to 10; partner only in avm.inputs_used), parcel_candidates[] {area_m2, direction, built, distance_m}, parcel_candidates_source, parcel_candidates_remark |
avm | waarde, vork, vork_90, huurwaarde, huurwaarde_vork, betrouwbaarheid, n_comparables, n_comparables_500m, n_comparables_1km, invoer_gebruikt {opp_wonen_m2, bouwjaar, staat, slaapkamers, opp_grond_m2, opp_grond_bron}, prijs_soort: vraagprijs | value, range, range_90, rental_value, rental_value_range, confidence, comparables_count, comparables_count_500m, comparables_count_1km, inputs_used {living_area_m2, year_built, condition, bedrooms, plot_area_m2, plot_area_source}, price_basis: asking_price_model |
billing (was facturatie) | gebruiker_ref (1.0: kantoor_ref), dossier_ref, aangerekend, reden_gratis: geen_resultaat|fout|pilot_uitgeput, prijs {basis, totaal, munt, excl_btw}, dedup_van, dedup_geldig_tot, maand, verbruik_maand_tot_nu {dossiers, bedrag}, pilot_stand {actief, gebruikt, resterend, einde, verlopen}, dag {klant_vandaag, gebruiker_vandaag} | user_ref, case_ref, charged, free_reason: no_result|error|pilot_exhausted, price {package, avm, total, currency, excl_vat} (2.4.0), dedup_of, dedup_valid_until, month, month_to_date {cases, amount}, pilot_status {active, used, remaining, ends, expired}, today {client_today, user_today} |
notices[] (was meldingen) | {code, sectie, bericht}; codes opp_required, avm_indicatief, geen_comparables, adres_straatniveau, geocoder_deels_onbereikbaar, gewest_conflict, eigen_pand_uitgesloten, hoofdperceel_niet_in_capakeys, perceel_analyse_onvolledig, opp_grond_niet_kadastraal_bevestigd, slaapkamers_filter_losgelaten, sectie_ontbreekt (upstream_fout), test_omgeving, pilot_uitgeput | {code, section, message}; codes living_area_required, avm_indicative_not_regionally_calibrated, no_comparables, address_street_level, geocoder_partially_unavailable, region_conflict, subject_property_excluded, main_parcel_not_in_capakeys, parcel_analysis_incomplete, plot_area_not_cadastral, bedrooms_filter_dropped, section_missing (upstream_error), test_environment, pilot_exhausted; new in 2.3.0 (no 1.x equivalent): key_rotation_pending |
upstream_errors keys | basis, basis.comparables, basis.buurtstats, basis.voorzieningen, buurtstats.gebouwenpark, buurtstats.veiligheid, buurtstats.prijsniveau, buurtstats.epc_prijzen; value upstream_fout | basic, basic.comparables, basic.neighbourhood, basic.amenities, neighbourhood.building_stock, neighbourhood.safety, neighbourhood.price_level, neighbourhood.epc_prices; value upstream_error |
| Error bodies | aangerekend, details[] {veld, fout}, sectie, gebruiker_ref, reden, sinds, limiet, scope: gebruiker_day; codes gebruiker_ref_required, gebruiker_ref_conflict | charged, details[] {field, issue}, section, user_ref, reason, since, limit, scope: user_day; codes user_ref_required, user_ref_conflict |
/usage | klant, maand, omgeving, gebruiker_ref, totaal {opvragingen, dossiers, aangerekend, gratis_dedup, gratis_pilot, gratis_storing, secties, bedrag, munt, excl_btw, duur_ms_gem}, per_gebruiker[] {secties_geleverd, laatste_activiteit}, per_dag[] {dag}, per_sectie {geleverd, aangerekend, bedrag}, kwaliteit {fout_pct, latentie_p50_ms, latentie_p95_ms, upstream_fouten}, gegenereerd_op | client, month, environment, user_ref, total {requests, cases, charged, free_dedup, free_pilot, free_error, sections, amount, currency, excl_vat, duration_ms_avg}, per_user[] {sections_delivered, last_activity}, per_key[] {prefix, label, environment, active, revoked_at, requests, cases, charged, sections, sections_delivered, amount, last_activity} (2.4.1, internal name per_sleutel, no 1.x equivalent), per_day[] {day}, per_section {delivered, charged, amount} (2.4.0: keys package, avm; total.sections {package, avm}, total.sections_delivered), quality {error_pct, latency_p50_ms, latency_p95_ms, upstream_errors}, generated_at |
| CSV header | request_id;tijdstip;gebruiker;dossier_ref;adres;gewest;secties_gevraagd;secties_geleverd;aangerekend;reden_gratis;dedup_van;prijs_basis;prijs_parcel;prijs_avm;prijs_totaal;status_code; file taxon-verbruik-... | request_id;timestamp;user_ref;case_ref;address;region;sections_requested;sections_delivered;charged;free_reason;dedup_of;price_package;price_avm;price_total;status_code;key_prefix (2.4.0; key_prefix 2.4.1, internal name sleutel_prefix); file taxon-usage-<client>-<month>.csv |
13.Contact
| Technical and commercial questions | info@taxon.be, quoting the request_id of a concrete request |
|---|---|
| Data protection | privacy@taxon.be (requests of data subjects to be forwarded within 5 working days) |
| Availability | GET /health; planned maintenance is announced by e-mail in advance |
| Publisher | Taxon, Schat mijn huis BV, Ieper (Belgium), taxon.be |