Errors and limits
The /v1 error format and every error code, paging, idempotency keys, rate limits and the daily property search allowance.
This page covers what every /v1 request shares: the error format, paging, safe retries of creates, and the limits. Listings and property-source routes keep the error shape they have always had; Authentication shows both.
Read an error
Every error on /v1 has the same envelope:
Branch on code. message is for people and can change. details is present when there is more to say, for example the missing scopes or a list of validation issues.
A request that fails validation answers 400 with VALIDATION_FAILED and the problems in details.issues. Unknown query parameters and body fields are ignored on most routes, so check spelling against the route's page. A body sent to a route that takes none is refused. A JSON body sent without Content-Type: application/json is not read and answers 400:
Error codes
| Status | Code | When |
|---|---|---|
400 | VALIDATION_FAILED | A parameter or body is missing or malformed. See details.issues |
400 | BAD_REQUEST | The request is not valid in another way |
401 | UNAUTHENTICATED | The key is missing, malformed, unknown, revoked or expired, or you sent a session token. The response carries a WWW-Authenticate: Bearer realm="Fondaro API" header |
403 | API_KEY_SCOPE_MISSING | The key lacks a scope. details.missing lists them |
403 | ORG_ADMIN_REQUIRED | Only an organization admin can do this |
403 | SUBSCRIPTION_REQUIRED | This needs an active Fondaro subscription |
403 | ORG_DISABLED | The organization is disabled |
403 | API_KEY_HAS_NO_OWNER | The key has no owner. Create a new key. Rotating an older key does not change this |
403 | API_KEY_LEGACY | The key was made before 4 October 2026. /v1 does not accept it. Create a new key; rotating the older key does not change this |
403 | API_KEY_HAS_WEBSITES | The key lists allowed websites. /v1 is for servers, so make a key without websites for it |
409 | API_KEY_LIMIT_REACHED | Creating a key would pass the limit of 10 active keys per person or 50 per organization. Revoke one you no longer use |
403 | API_KEY_OWNER_NOT_IN_ORGANIZATION | The key's owner left the agency |
403 | API_KEY_ORIGIN_NOT_ALLOWED | A browser request. /v1 is for servers |
403 | API_OPERATION_NOT_DECLARED | A route that does not declare its access is refused. If you see this, tell support |
403 | FORBIDDEN | You do not have access to this |
404 | NOT_FOUND | The record does not exist or is not yours. For a record the message is "<Resource> not found.", whichever it is. An unknown path, or a method a path does not support, answers "No such endpoint." |
409 | CONFLICT | The request conflicts with the current state |
409 | IDEMPOTENCY_KEY_REUSED, IDEMPOTENCY_KEY_IN_PROGRESS | See Retry a create safely |
413 | PAYLOAD_TOO_LARGE | The body is too large |
415 | UNSUPPORTED_MEDIA_TYPE | The body's content type is not supported. Send Content-Type: application/json |
422 | UNPROCESSABLE | The request is understood but cannot be applied |
429 | RATE_LIMITED | Too many requests. Wait Retry-After seconds |
429 | DAILY_LIMIT_REACHED | The daily property site search allowance is used up |
502, 503 | INTERNAL | A service Fondaro depends on is briefly unavailable. Retry with backoff |
500 | INTERNAL | Something went wrong on our side |
503 | AUTH_UNAVAILABLE | Fondaro could not check the key's owner just now. Retry shortly |
A 404 does not tell you whether a record exists: a lead you cannot see answers the same as one that does not exist. A domain error can carry its own code and extra details; the page for that route lists it.
A 5xx never contains a stack trace or a message from a service behind Fondaro.
Request ids
Every /v1 response, success or error, has an X-Request-Id header, and every error carries the same value as requestId. Fondaro generates it; a value you send is ignored. When you contact support, send the requestId, the time, the method and the path. Never send the key.
Page through a list
A list is an object with data and hasMore:
Each route's page names its paging style and its limit range. The styles that exist:
| Style | How | Used by |
|---|---|---|
| Page number | Send page and limit. Leads answer page, limit and an exact total; brochures answer hasMore and, when it is known, total | Leads, brochures |
| Offset | Send offset and limit. While hasMore is true, send nextOffset as the next offset. Calls, viewings and documents also carry total | Lead timeline, calls, viewings, documents |
| Older than | Send before with the time of the last message you have | A lead's conversation |
| Capped | One page, at most a fixed number of rows. hasMore: true means more exist: narrow your filters to reach them | Deals, tasks, emails, members, buyer requests, collections, open houses |
| Whole list | Everything, in one response, with hasMore: false | Notes, tags, teams |
limit is an upper bound, so a page can hold fewer rows than asked for. Offset paging has no snapshot: rows added or removed while you page can shift between pages.
Retry a create safely
Send an Idempotency-Key header on a POST that creates something (for example a lead, a task, a note or a deal; the route's page says when it accepts one). The header is optional: 1 to 255 visible ASCII characters, such as a UUID.
- Retry after a timeout or a
5xx: send the same key with the same request. If the first attempt succeeded, you get its response back withIdempotent-Replayed: true, and nothing is created twice. - Same key, different request:
409withIDEMPOTENCY_KEY_REUSED. A request is the same when the method, the path and the JSON body match exactly, including key order. The query string is not compared. - Same key while the first request still runs:
409withIDEMPOTENCY_KEY_IN_PROGRESS. Wait a moment and retry. The marker lasts up to 5 minutes. - Failed requests are not stored. A
4xxor5xxfrees the key, so you can fix the request and send it again with the same key. - Responses are kept for 24 hours. Keys belong to the API key that sent them.
- Responses that carry a secret, or are larger than 512 KB, are not stored. A retry runs the request again.
Without the header, a retried create creates again. If the idempotency store is briefly unavailable, requests still run, without protection.
Stay within rate limits
Each API key has its own allowance per endpoint:
| Window | Requests |
|---|---|
| A minute | 120 |
| 10 minutes | 600 |
| An hour | 3,000 |
Fondaro can raise one key's per-minute limit for a busy integration; the 10-minute and hourly windows scale with it. Responses carry the remaining allowance in X-RateLimit-Limit-<window> and X-RateLimit-Remaining-<window> headers, one pair per window (short, medium, long).
Over a limit, the API answers 429 with RATE_LIMITED, a Retry-After header in seconds and an X-RateLimit-Reset header with the reset time in epoch seconds. Wait that long, then retry; slow down rather than retry at once.
Two reads cost more to run and share one limit: semantic search of your CRM (POST /v1/search) and listing matches (POST /v1/listing-matches). Together they allow 30 requests a minute per key, and 30 a minute for your whole organization across all its keys, so creating more keys does not raise it. Over it, the API answers 429 with RATE_LIMITED and Retry-After. This budget is separate from the one the n8n integration API uses.
Plan around the daily search allowance
Searches of the property sites you can use with Fondaro share one allowance per organization: 300 upstream requests a day, counted from midnight UTC. It is shared by the dashboard, Ask Fondaro and every API key, so a busy script uses what your team would otherwise have. Cached results are free. When it is used up:
- On
/v1, the API answers429withDAILY_LIMIT_REACHED. - On
POST /property-sources/{source}/searchand the other/property-sourcesroutes, which keep their own error shape, it answers400withPROPERTY_PORTAL_QUOTA_EXHAUSTED.
Either way, wait for the next UTC day.
Handle new versions
The /v1 routes change only by adding: new routes, new response fields, new optional parameters and new error codes. Ignore fields and codes you do not know. A breaking change would be a new version path, /v2, announced in the changelog, with /v1 kept running.