Shipping daily
Nov 1DATAAll 646 projects re-verified: 6,419 developer claims tested against registers, courts and pressNov 1DATAMalaysia TEDUH and Philippines DHSUD licence checks: expired licences and approved delays foundNov 1DATAThailand ONEP register: 2 condos found selling before environmental approvalNov 1DATAMexico PROFECO contract register checks: one sales registration belonged to a laundryNov 1DATAAbu Dhabi ADREC inspections and RAK RERA permit list added: % built straight from the regulatorNov 1SHIPPEDNew stage filter: completed but still sellingNov 1SHIPPEDPublic database page: verification levels, disclosure distribution, every marketNov 1DATA+212 projects in one day, round 3: USA, Caribbean, Gulf, Africa, Japan, AustraliaNov 1SHIPPEDStage on every case: off-plan or under constructionOct 31SHIPPEDVerification level per check on every case card: registry, independent, developer, contradicted
Changelog →

Kolaboro / Docs / Partners

Risk feed for banks and insurers

The same anonymous open data as our open API, shaped for underwriting and lending checks: one record of risk fields per project id, a change stream with a cursor, every answer signed with Ed25519. No developer names, no project names, no addresses: projects are ids like BALI-024, as in the open data.

Calling the sandbox and checking the signature in your browser…

1. Get access

  1. Sandbox, now: send x-api-key: kb_sandbox_public. It is public on purpose: real data, pages of at most 10, 120 calls per hour per IP address. Use it to build and test your integration.
  2. Free key: create a kb_live_... key on the API key page. It is shown once and only its SHA-256 is stored. Pages up to 500, 60 calls per minute per key, and a daily quota shared with the metered open API (1,000 calls a day on the free plan, as listed on that page).
  3. Higher limits: by agreement, write to [email protected]. There is no paid tier yet, so there is no price to show.

2. Endpoints

RequestWhat it returns
GET /api/x/z/feed/projectsRisk fields per project, sorted by id. Parameters: limit (1 to 500, sandbox 10, default 100), cursor (from next_cursor), ids (comma separated, up to 100), market (market or country name).
GET /api/x/z/feed/changes?since=YYYY-MM-DDEvery recorded change to a project field on or after since, oldest first. Parameters: since, limit, cursor. Each change has a stable id for de-duplication.
GET /api/x/z/feed/schema.jsonJSON Schema (2020-12) of both answers. No key.
GET /api/x/z/feed/statusWhich sources are present, the data date, whether signing and live keys are on, and the limits. No key.

Pagination is by cursor: pass next_cursor back as cursor until it is null. Cursors name the last item you saw (an id, or a date, project and position), so they keep working when the data is updated between pages. Changes are recorded by the daily data run with a date and no time: to poll, keep the last cursor, or ask again with since set to the last date you saw and drop ids you already have.

3. Fields per project

FieldMeaning and source
disclosure_scoreHow many of six checks are public (permit number, deposit protection, construction progress, plans, track record, no guaranteed returns), 0 to 6. How we score.
disclosureEach check as published: true, false, null for not stated; progress is a percentage when the source gives one.
verification_levelPer check: registry (an official register confirms it), independent, developer (only the developer says so), contradicted, or none. confirmed_score counts registry and independent.
overdue_months, delay_monthsMonths past the promised handover, and announced delay, when public. null otherwise.
claims, public_problemsDeveloper claims tested (verified, unverified, contradicted) and public problems recorded.
satellite, registerLatest satellite change status and official register check, when there is one.
hazardsFrom /data/hazards.json: height, 100-year river and coastal flood depth, earthquakes M5+ within 100 km since 1900, tsunami run-ups within 50 km, nearest Holocene volcano, distance to the coast. Rounded so it cannot pinpoint a plot. null for projects without coordinates; 168 projects have them today. Sources and method.
on_timeFrom /data/on-time.json: the project's handover status, first and current handover month, months moved, and estimate: the share of closed comparable projects that handed over within 6 months of their first date, with a 95% interval and the number of comparables. Today there are few closed projects, so the estimate is pooled over all markets and is a floor, not a forecast; notes.on_time carries that file's own caveat. If the file is missing, the field is omitted.
page_fingerprint, last_checkedSHA-256 of the developer page we last read and the date; provable against the public log with /api/open/proof/{id}.

Every answer also carries dataset_sha256: the SHA-256 of the open dataset file it was computed from. That fingerprint is written to the public log each day and timestamped in Bitcoin, so a figure you relied on can be shown later to be what we published that day. Scores describe what is public, not whether a developer is honest, and they are not a valuation or a credit rating.

4. Example answers

Real answers from the sandbox on 4 November 2026 (pretty-printed exactly as served).

GET /api/x/z/feed/projects?ids=BALI-024 x-api-key: kb_sandbox_public { "feed": "kolaboro-risk-feed", "version": "1.1.0", "kind": "projects", "generated_at": "2026-11-04T09:06:46.774Z", "timestamp": 1793783206, "dataset_sha256": "b43b17f35cef73dd0c6740a22dfd01570891b622a69ce9c2ea64c3991b34dfec", "data_updated": "2026-11-04", "license": "CC BY 4.0 (credit: Kolaboro, kolaboro.com)", "sandbox": true, "total": 1, "count": 1, "next_cursor": null, "notes": { "meaning": "Scores describe what is public about a project and how much of it an official register or independent source confirms. They are not a judgement of the developer, a valuation or a credit rating.", "hazards": "From /data/hazards.json (updated 2026-11-04, 168 projects with coordinates). Figures are rounded so they cannot pinpoint a plot. null means the project has no coordinates there. Sources and method: https://kolaboro.com/hazards/", "on_time": "on_time comes from /data/on-time.json (generated 2026-11-04). Question: Will this project hand over within 6 months of the first handover date we have on record? estimate is the share of closed comparable projects that handed over on time, with a 95% interval. The tracked projects are the ones being marketed now. Projects that handed over on time and stopped marketing are less likely to be in the data, so the on-time rate among closed projects is very likely lower than the true rate. Treat it as a floor for what this data set shows, not as a forecast. Method: https://kolaboro.com/data/on-time.json" }, "projects": [ { "id": "BALI-024", "market": "Bali", "country": "Indonesia", "region": "Ubud", "stage": "under construction", "units": 22, "tenure": "leasehold", "lease_years": 30, "promised_completion": 2026, "disclosure_score": 2, "disclosure_max": 6, "disclosure": { "permit_public": false, "permit_claimed": false, "deposit_protection": null, "progress": 95, "plans": false, "track_record": true, "no_guaranteed_returns": false }, "confirmed_score": 0, "verification_level": { "permit_public": "none", "deposit_protection": "none", "progress": "contradicted", "plans": "none", "track_record": "developer", "no_guaranteed_returns": "none" }, "tier": "mixed", "claims": { "verified": 0, "unverified": 6, "contradicted": 5 }, "public_problems": 0, "overdue_months": null, "delay_months": null, "satellite": null, "register": null, "hazards": { "elevation_m": 280, "flood_river_100y_m": 0, "flood_coast_100y_m": 0, "quakes_m5_within_100km_since_1900": 44, "quakes_m6_within_100km_since_1900": 5, "quakes_m7_within_100km_since_1900": 0, "strongest_quake": { "magnitude": 6.54, "year": 1917, "distance_km": 10 }, "tsunami_runups_within_50km": 21, "tsunami_max_runup_m": 4.95, "tsunami_latest_year": 2018, "holocene_volcano_within_100km_km": 25, "coast_distance": "over 10 km" }, "last_checked": "2026-11-04", "page_fingerprint": { "sha256": "22dc088f84b5aae7794dbf593a7eddffc406fad78f379a2a962e94ff3a36a8ee", "date": "2026-11-04" }, "url": "https://kolaboro.com/p/?id=BALI-024", "on_time": { "status": "open", "first_handover": "2026-12", "current_handover": "2026-12", "months_moved": 0, "months_overdue": 0, "months_to_first_handover": 1, "estimate": { "on_time_pct": 12.8, "ci95_low_pct": 6, "ci95_high_pct": 25.2, "comparables": 47, "comparables_on_time": 6, "basis": "all markets", "level": "all" } } } ] }
GET /api/x/z/feed/changes?since=2026-11-03&limit=2 x-api-key: kb_sandbox_public { "feed": "kolaboro-risk-feed", "version": "1.1.0", "kind": "changes", "generated_at": "2026-11-04T09:06:46.782Z", "timestamp": 1793783206, "dataset_sha256": "b43b17f35cef73dd0c6740a22dfd01570891b622a69ce9c2ea64c3991b34dfec", "data_updated": "2026-11-04", "license": "CC BY 4.0 (credit: Kolaboro, kolaboro.com)", "sandbox": true, "since": "2026-11-03", "total": 103, "count": 2, "next_cursor": "YzoyMDI2LTExLTAzfEFKTS0wMDR8MA", "notes": { "granularity": "Changes are recorded by the daily data run with a date, not a time. Keep the last next_cursor, or re-read since=<last date you saw> and drop ids you already have: ids are stable." }, "changes": [ { "id": "chg_e34fb31edde2c5a39e6c", "date": "2026-11-03", "project": "AJM-002", "market": "Ajman", "field": "plans published", "from": false, "to": true, "url": "https://kolaboro.com/p/?id=AJM-002" }, { "id": "chg_91a07cbf9323ad2c4c3f", "date": "2026-11-03", "project": "AJM-004", "market": "Ajman", "field": "plans published", "from": false, "to": true, "url": "https://kolaboro.com/p/?id=AJM-004" } ] }

5. Verify every answer

Each answer has x-kolaboro-signature: base64 Ed25519 over "kolaboro-sig-v1\n" + SHA-256 hex of the exact body, and x-kolaboro-key-id. Public keys: /.well-known/kolaboro-keys.json. It is the scheme of the open API and of our signed webhooks. The body holds timestamp (Unix seconds) so a replayed old answer can be refused.

#!/bin/sh # Fetch one page of the feed and verify its Ed25519 signature with curl and OpenSSL 3. URL=${URL:-"https://kolaboro.com/api/x/z/feed/projects?limit=10"} KEY=${KOLABORO_KEY:-kb_sandbox_public} curl -s -D headers.txt -H "x-api-key: $KEY" "$URL" -o body.json grep -i '^x-kolaboro-signature:' headers.txt | cut -d' ' -f2 | tr -d '\r' | base64 -d > sig.bin # public key (SPKI) from https://kolaboro.com/.well-known/kolaboro-keys.json curl -s https://kolaboro.com/.well-known/kolaboro-keys.json | python3 -c "import json,sys; k=[x for x in json.load(sys.stdin)['keys'] if x['status']=='active'][0]; print('-----BEGIN PUBLIC KEY-----\n'+k['public_key_spki_b64']+'\n-----END PUBLIC KEY-----')" > kolaboro.pem # signed message = "kolaboro-sig-v1\n" + SHA-256 hex of the exact body printf 'kolaboro-sig-v1\n%s' "$(sha256sum body.json | cut -d' ' -f1)" > msg.txt openssl pkeyutl -verify -pubin -inkey kolaboro.pem -rawin -in msg.txt -sigfile sig.bin

6. Limits and errors

KeyPer minutePer hourPer dayPage
Sandbox kb_sandbox_public-120 per IP address-10
Free kb_live_...60 per key-daily quota of the key (free plan 1,000), shared with /api/x/k/m/*500

Headers x-ratelimit-limit and x-ratelimit-remaining (daily quota, or the sandbox hour), and x-ratelimit-burst-limit / x-ratelimit-burst-remaining for the minute. The per-minute counter is kept at each Cloudflare location, so it is approximate across locations; the daily quota is exact. Errors are JSON: 401 missing_key, 429 rate or quota, 400 bad_cursor or bad_since, 503 keys_unavailable (the sandbox keeps working).

7. Service level

There is no contractual SLA and no service credits. What we publish instead is measured, not promised: the service level objectives on /status, computed from outside probes when the page loads. The feed runs on the same worker and data as the API those probes check, but the probes do not call the feed itself.

Loading the current objectives…

The data is refreshed by a daily run; the freshness objective (90% of projects re-read from their source within 7 days) is reported at /api/open/freshness. If the hazards file is unavailable, hazards is null and notes.hazards says so; we never fill a gap with an estimate.

8. Changelog

  • 1.1.0, 4 November 2026. on_time filled from /data/on-time.json, now published: status, handover months, months moved and the on-time estimate with its interval.
  • 1.0.0, 4 November 2026. First version: /feed/projects and /feed/changes, cursor pagination, Ed25519 signatures, JSON Schema, sandbox key. on_time reserved until /data/on-time.json is published.

Changes that remove or rename a field get a new major version and are announced here and on the changelog before they ship. New fields can appear in a minor version; ignore fields you do not know. The version is in every answer (version) and in the header x-kolaboro-feed-version.

Protect my purchase