Getting started
The WoningMe Developer API gives you programmatic access to rental listings across the Netherlands, scoped to searches you define, with optional webhook notifications when new listings match.
- Create a developer account. The same account works for the WoningMe app.
- Open your dashboard and click "Generate API key". Copy it immediately, it's shown once.
- Create a saved search (below) and start querying matching properties, or add a
webhook_urlto get notified automatically.
Authentication
Every request must include your API key in the X-Api-Key header:
curl https://woning.me/api/developer/v1/searches \ -H "X-Api-Key: YOUR_API_KEY"
Requests are rate limited to 60 per minute per API key. Exceeding this returns 429.
Searches
A saved search defines a city and radius. You can't query properties directly: every property lookup and webhook is scoped to a search you've created first.
POST /api/developer/v1/searches
Creates a new saved search. webhook_url is optional. Omit it if you only want to query properties.
| Field | Type | Required |
|---|---|---|
label | string | yes |
city | string | yes |
radius_km | number | yes |
webhook_url | string | no |
curl -X POST https://woning.me/api/developer/v1/searches \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"label": "Amsterdam centrum", "city": "Amsterdam", "radius_km": 5.0, "webhook_url": "https://your-app.com/hooks/amsterdam"}'
201 Created, with a Location header pointing at the new search:
{
"id": 42,
"label": "Amsterdam centrum",
"city": "Amsterdam",
"radiusKm": 5.0,
"createdAt": "2026-07-27T20:00:00+00:00",
"propertiesUrl": "/api/developer/v1/searches/42/properties",
"webhookUrl": "https://your-app.com/hooks/amsterdam",
"webhookSecret": "a3f9c2e1..."
}
webhookSecret is generated the first time you set a webhook_url and stays the same across later changes to the URL. See Webhooks for how to use it.
GET /api/developer/v1/searches
Lists all of your saved searches.
curl https://woning.me/api/developer/v1/searches \ -H "X-Api-Key: YOUR_API_KEY"
{
"searches": [
{ "id": 42, "label": "Amsterdam centrum", "city": "Amsterdam", "radiusKm": 5.0, ... }
]
}
GET /api/developer/v1/searches/{id}
Fetches a single search by ID. Returns 404 if it doesn't exist or belongs to another developer.
curl https://woning.me/api/developer/v1/searches/42 \ -H "X-Api-Key: YOUR_API_KEY"
PATCH /api/developer/v1/searches/{id}
Updates a search. Only fields you include are changed. Omit a field to leave it as-is. Send an empty string for webhook_url to remove the webhook (this also clears the secret).
curl -X PATCH https://woning.me/api/developer/v1/searches/42 \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"radius_km": 7.5}'
DELETE /api/developer/v1/searches/{id}
Deletes a search. Returns 204 No Content.
curl -X DELETE https://woning.me/api/developer/v1/searches/42 \ -H "X-Api-Key: YOUR_API_KEY"
Properties
GET /api/developer/v1/searches/{id}/properties
Returns properties matching that search's city and radius. Supports pagination and sorting. The geographic scope itself always comes from the search and can't be overridden per request.
| Query param | Default | Notes |
|---|---|---|
sort | price | price, surface, rooms, available, city, scraped_at, first_seen_at |
order | asc | asc or desc |
skip | 0 | for pagination |
limit | 200 | max 1000 |
curl "https://woning.me/api/developer/v1/searches/42/properties?limit=2" \ -H "X-Api-Key: YOUR_API_KEY"
{
"total": 274,
"skip": 0,
"limit": 2,
"properties": [
{
"id": 19022,
"source": "pararius",
"sourceLabel": "Pararius",
"url": "https://...",
"imageUrl": "https://...",
"street": "Kerkstraat 12",
"postcode": "1234AB",
"city": "Amsterdam",
"propertyType": "Apartment",
"constructionType": null,
"surfaceM2": 75,
"rooms": 3,
"availableFrom": null,
"priceEurCents": 145000,
"priceText": "β¬ 1.450",
"agent": "Some Agency",
"agentWebsite": "https://...",
"agentPhone": null,
"socialHousing": false,
"isMiddenhuur": false,
"neighbourhoodScore": { "score": 6.8, "class": "ruim voldoende", "year": 2024, "dimensions": {...} },
"lat": 52.37,
"lon": 4.90,
"firstSeenAt": "2026-07-27T20:20:00+00:00",
"scrapedAt": "2026-07-27T20:20:00+00:00"
}
]
}
Webhooks
If a search has a webhook_url, we send an HTTP POST to it whenever a new listing matches. No polling required.
Payload
POST https://your-app.com/hooks/amsterdam
Content-Type: application/json
X-WoningMe-Signature: sha256=<hex digest>
{
"event": "listing.new",
"searchId": 42,
"property": { /* same shape as the Properties endpoint above */ }
}
Verifying the signature
The X-WoningMe-Signature header is an HMAC-SHA256 digest of the raw request body, using your search's webhookSecret (returned when you create or fetch the search) as the key. Always verify it before trusting a payload.
# Python
import hashlib, hmac
def verify(request_body: bytes, signature_header: str, webhook_secret: str) -> bool:
expected = "sha256=" + hmac.new(
webhook_secret.encode(), request_body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature_header)
// Node.js
const crypto = require("crypto");
function verify(rawBody, signatureHeader, webhookSecret) {
const expected = "sha256=" + crypto
.createHmac("sha256", webhookSecret)
.update(rawBody)
.digest("hex");
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signatureHeader));
}
Retries
If your endpoint doesn't respond with a 2xx status (or times out), we retry once after about 30 seconds, then give up. Check delivery history to see what happened.
Deliveries
GET /api/developer/v1/searches/{id}/deliveries
Returns the recent webhook delivery attempts for a search. Useful for debugging your endpoint.
curl https://woning.me/api/developer/v1/searches/42/deliveries \ -H "X-Api-Key: YOUR_API_KEY"
{
"deliveries": [
{
"id": 501,
"propertyId": 19022,
"status": "success",
"httpStatus": 200,
"error": null,
"attemptCount": 1,
"attemptedAt": "2026-07-27T20:20:49+00:00"
}
]
}
status is one of success, pending_retry, or failed.
Errors
| Status | Meaning |
|---|---|
401 | Missing or invalid API key. |
402 | Free developer access has expired; a subscription is required to continue. |
404 | The search doesn't exist, or belongs to a different developer. |
422 | Request body failed validation (missing/invalid field). |
429 | Rate limit exceeded. 60 requests per minute per API key. |
Errors follow the shape {"detail": "..."}.