When a request fails, the API returns an HTTP status code and a JSON body with the same shape every time:
{
"error": {
"code": "INSUFFICIENT_CREDITS",
"message": "Not enough credits ...",
"detail": {}
}
}codeis stable: branch on it in your code.messageis written for people: log it or show it.detailholds extra facts when there are any, such as the plan a feature needs.
Status codes
| Status | Code | Meaning | What to do |
|---|---|---|---|
| 400 | VALIDATION_ERROR | The body or parameters are invalid: a missing field, a wrong type, a value out of range | detail.errors lists each problem and where it is. Fix the request; don't retry unchanged |
| 422 | A specific code, for example INVALID_EMAIL_SYNTAX | The request is well formed, but the value can't be processed, such as an address that isn't a valid email | Read code and message; fix the value. Don't retry unchanged |
| 401 | UNAUTHORIZED | Missing, wrong, revoked or expired API key | See Authentication |
| 402 | INSUFFICIENT_CREDITS | Not enough credits for this action | Top up in Credits & billing, then retry, or resume the job |
| 403 | PLAN_REQUIRED | Your plan doesn't include this feature | detail.required_plan names the plan that does |
| 403 | FORBIDDEN | You can't access this resource | Check you're using a key from the right account |
| 404 | NOT_FOUND | No such resource on your account | Check the id; it may have been deleted |
| 409 | CONFLICT | The resource is busy, for example a job already runs on that list | Wait for it to finish, or stop it first |
| 429 | RATE_LIMITED | Too many requests | Wait the number of seconds in the Retry-After header, then retry. See Rate limits |
| 500 | INTERNAL_SERVER_ERROR | Something went wrong on our side | Retry once; if it persists, contact support |
| 502, 503, 504 | SERVICE_UNAVAILABLE | A temporary problem | Retry with backoff |
Which errors to retry
- Retry with backoff:
429(after itsRetry-After),502,503,504, and a single500. - Don't retry unchanged:
400,401,403,404,422. The same request will fail the same way. - Retry after acting:
402after topping up,409after the running job finishes.
Failed requests aren't charged. You pay only for results, so a request that errors costs nothing.
Validation errors
A request whose body or parameters don't match what the endpoint expects returns 400 with the code VALIDATION_ERROR. detail.errors lists each problem, with where it is (loc) and what's wrong (msg):
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed.",
"detail": {
"errors": [
{"loc": ["body", "email"], "msg": "Field required", "type": "missing"}
]
}
}
}Plan required
AI features (ICP scoring, targeting, AI columns and outreach openers) need the Growth plan or higher. Signals monitoring and watchers need Pro or higher (or the 2-day free LinkedIn trial, with up to 3 monitors), and each plan includes a set number of monitors. Credit packs aren't a plan: a pack from $25 unlocks AI columns and outreach openers only, so with just a pack, targeting, ICP scoring and signals answer PLAN_REQUIRED with a message such as Targeting is included in the Growth plan and up (credit packs cover AI columns and openers). When your plan doesn't include something, the response says which one does:
{
"error": {
"code": "PLAN_REQUIRED",
"message": "AI features: available on the Growth plan and up. Your free account doesn't include it; upgrade in Credits & billing.",
"detail": {"required_plan": "Growth"}
}
}Reading, cancelling and clearing things you already created keeps working after a downgrade. See What each plan unlocks.
Insufficient credits in a job
A job that runs out of credits partway doesn't fail. Bulk verification and bulk finder jobs stop with stopped, keeping the rows already done; top up and call their resume endpoint. Captures pause and continue by themselves once your balance covers the next person. See Long-running jobs.
Reporting a problem
If an error doesn't make sense, email support@filtrat.io with the endpoint, the time of the request (with time zone), the status code and the full error body. Never include your API key.