API Users
The Markondo API
A read API over our new-development catalogue. Pull listings, pricing and images into your own site or feed; every listing carries a link back to markondo.com, where the enquiry reaches the developer.
It is a syndication API, not a data feed. You get what a visitor sees on a listing page — never the developer's contact details.
Getting a key
Sign in and ask on your API access page , telling us what you plan to build. A person reads it — the API returns the whole catalogue, so we like to know who is pulling it.
Once you are approved, you create the key yourself on that page and it is shown once, in your browser. It is never emailed and nobody here can read it back to you.
A key is shown once, when it is created, and only its hash is stored — we cannot recover it for you. If you lose one, ask for a replacement and the old one is revoked.
Keys are per partner, so one can be switched off without affecting anyone else.
Making a request
Every request carries the key as a bearer token.
curl https://markondo.com/api/v1/projects \
-H "Authorization: Bearer YOUR_KEY"
A missing, malformed or revoked key returns 401
with {"error": "unauthorized"}. The three cases
are deliberately indistinguishable.
Endpoints
/api/v1/projects
A page of the catalogue, newest changes first.
limit
|
integer | How many listings to return. Defaults to 50. |
cursor
|
string | The next_cursor from your previous response. Omit for the first page. |
updated_since
|
ISO 8601 | Only listings changed since this time — how you keep a copy in step without re-crawling. |
city
|
string | Filter by city, exact match. |
country
|
string | Filter by country, exact match. |
status
|
string | planning, under_construction or completed. |
/api/v1/projects/:id
One listing, by slug or by id. Prefer the slug — it is stable and readable.
A listing that is hidden or unpublished returns 404,
exactly as one that never existed. Telling the two apart would say what is in
the catalogue that you cannot see.
Paging through the catalogue
Responses carry a next_cursor. Pass it back as
cursor for the following page, and stop when it is
null — that means you have reached the end, not that
something went wrong.
The cursor is opaque: it encodes a position, not a page number, so listings added mid-crawl cannot make you skip or repeat one. Treat it as a string and do not construct your own. A cursor we cannot read starts you from the beginning rather than erroring.
GET /api/v1/projects?limit=100
GET /api/v1/projects?limit=100&cursor=MjAyNi0wOC0yOFQxMTo0MDowMFp8YWJjMTIz
What a listing looks like
{
"data": [
{
"id": "a30d53a8-f530-…",
"name": "Rivage Bal Harbour",
"slug": "rivage-bal-harbour",
"url": "https://markondo.com/projects/rivage-bal-harbour",
"description": "At Home in the Sky…",
"location": {
"address": "10245 Collins Ave",
"city": "Bal Harbour",
"state": "FL",
"country": "United States",
"neighborhoods": [],
"latitude": 25.8896,
"longitude": -80.1256
},
"building": {
"property_type": "condo",
"status": "under_construction",
"year_built": 2027,
"total_units": 56,
"stories": 25,
"architect": "…",
"interior_designer": "…",
"amenities": ["Pool deck", "Spa"],
"highlights": ["Oceanfront"]
},
"pricing": {
"price_from": 4200000,
"currency": "USD",
"min_ticket": null,
"unit_mix": "3–5 bed",
"payment_plan": "20% on contract…"
},
"developer": { "name": "Related Group" },
"images": ["https://…"],
"updated_at": "2026-08-27T19:12:53Z"
}
],
"next_cursor": "MjAyNi0wOC0yN1QxOToxMjo1M1p8YTMwZDUzYTg"
}
url is included on every listing deliberately.
Linking back is the deal: you get the content, the developer gets the enquiry.
What the API never returns
Worth stating plainly, because it is the question every developer listing with us asks. These fields exist on our side and are not serialized at any endpoint, at any access level:
- Developer contact details — Email, phone, sales team and notification addresses. This is the developer's asset, not ours to distribute.
- Who holds the listing on Markondo — Owner and creator accounts stay internal.
- Internal state — Review status, hidden flags, sample listings, our quality scores and lifecycle stage. How the sausage is made is not part of the catalogue.
- CRM and import identifiers — HubSpot ids and legacy import keys are joins into our own systems.
The serializer allowlists fields rather than excluding them, so a column added to a listing later cannot appear here by accident — someone has to decide to publish it.
Rate limit
600 requests per minute, per key. It is a ceiling on runaway crawls rather than a pace for normal use — syncing hourly, or walking the whole catalogue several times a day, will never reach it.
Over the limit returns 429 with a
Retry-After header in seconds. The body names the
limit and the window, so a client can back off correctly without asking us what
the numbers are.
If you have a use that genuinely needs more, say so — the limit is per key and we can raise yours without affecting anyone else.
Errors
401
|
unauthorized | The key is missing, malformed, revoked, or belongs to a closed account. |
404
|
not found | No listing with that slug or id is publicly visible. |
429
|
rate_limited | Over the per-key limit. Wait the number of seconds in Retry-After. |
Using it fairly
Cache what you pull and use updated_since rather
than re-crawling the whole catalogue — it is the difference between one request
and several hundred, it keeps your copy fresher, and it keeps you far below the
rate limit without thinking about it.
Keep the url on anything you publish, and keep the
developer's name with their listing.
If you need something the API does not return, ask rather than working around it. Some of it we will add; the contact details we will not.
Questions, or something missing from the payload? hello@markondo.com.