Errors
We aim for actionable errors. Validation failures always include an example request that would have worked. Rate-limit responses always carry Retry-After. Per-source partial failures show up inline in warnings and errors arrays, not as a hard 5xx.
HTTP status reference
| Status | Meaning | When you see it |
|---|---|---|
| 200 | OK | Request succeeded. Inspect warnings array for partial-data conditions. |
| 400 | Bad Request | Invalid input - missing type, unknown filter, query without narrowing filter, malformed JSON. Body always includes a worked example. |
| 401 | Unauthorized | Missing or unrecognized bearer key. |
| 403 | Forbidden | Key lacks scope for this endpoint. External keys cannot hit /admin/*. |
| 413 | Payload Too Large | Request body over 1 MB. Trim the request and retry. |
| 429 | Too Many Requests | Burst, daily, or concurrency cap exceeded. Inspect Retry-After. |
| 500 | Internal Server Error | Gateway crashed. Reach out with the X-Request-Id header from the response. |
| 502 | Bad Gateway | A downstream component (lake coordinator, validator backend) was unreachable. Often transient - retry with backoff. |
Validation error
400 responses always include the original failing condition plus a worked example. We try to never leave you guessing what shape would have worked.
HTTP 400
{
"error": "Free-text 'query' requires at least one narrowing filter.",
"detail": "Presence filters (has_email, has_phone, has_linkedin) don't count - they filter AFTER the scan, not before it.",
"example": {
"type": "contacts",
"query": "investment officer",
"filters": {"country": ["US"]}
},
"tip": "Use GET /v1/sources to discover which filters apply to which datasets."
}Rate limit error
HTTP 429 Too Many Requests
Retry-After: 37
{
"error": "rate_limited",
"detail": "30 requests/minute exceeded. Retry in 37 seconds.",
"retry_after": 37
}Three different things produce a 429: minute-window burst, daily unit budget, and umbrella concurrency cap. The detail string tells you which one tripped and how to back off. See Rate limits for the full picture.
Partial source failure (HTTP 200)
When one shard times out or one source is unreachable, the gateway returns the rows it did get with a warning. You don't lose the whole call to a single flaky dataset.
HTTP 200
{
"results": [ ... ],
"total": 42,
"warnings": [
"source 'leadleap_contacts' returned partial data - one or more lake nodes failed to respond. Results may be incomplete."
],
"errors": [
"leadleap_intent_signals: data-node timeout"
],
...
}Validate-endpoint error envelope
The validate endpoint wraps every response in {data, meta, error} and uses a structured error object on failure.
HTTP 400
{
"data": null,
"meta": {},
"error": {
"code": "INVALID_JSON",
"message": "Request body must be valid JSON. Send {\"email\": \"[email protected]\"} for single validation or {\"emails\": [...]} for bulk.",
"status": 400
}
}Always grab X-Request-Id off the response headers when you log a failed call. That one identifier lets us pull the exact gateway log entry for your request.
Errors are sanitized. We strip internal node hostnames, file paths, and raw provider names from error strings before they leave the gateway. You won't see internal data-node URLs in your logs.