Error Codes & Responses
Reference Guide
The HTTP status codes and messages the Sprouts Data and Contact Enrichment APIs return, and
the conditions that trigger them. Every error is a JSON object with a single
detail field, so failures are simple to read and handle.
Format JSON ·
detail field on failureRate limit 60 requests per 60 seconds
Version 1.0
1Overview
These responses apply to both public endpoints below. Both share the same base URL and header-based authentication.
1.1How errors are returned
Every error carries an HTTP status code and a JSON body with a detail field that
explains what went wrong. As a rule, a 4xx means the request can be fixed on your end and
detail tells you how; a 5xx means the failure is on the Sprouts or upstream
side, where there's nothing to change in the request. Handle 4xx by correcting and retrying;
handle 5xx by retrying, then contacting Sprouts if it persists.
1.2Error response shape
Every failed request returns a JSON object with a detail field. For most errors detail is a plain message string:
{ "detail": "Unknown filter '<filter_name>'" }
For request-shape validation (missing headers, wrong field types), detail is instead an array, one entry per rejected field, giving the location and reason:
{ "detail": [ { "type": "missing", "loc": ["header", "X-API-SECRET"], "msg": "Field required", "input": null }, { "type": "missing", "loc": ["header", "X-Login-Email"], "msg": "Field required", "input": null } ] }
detail. When it's a string, show or log it directly. When it's an array,
each entry's loc points at the offending field (["header", "…"] or
["body", "…"]) and msg gives the reason.
- Overview 1.1 – 1.2 · how errors are returned
- Success 2.1 · 200, including empty results
- Errors 3.1 – 3.5 · 401, 403, 422, 429, 5xx
- Status code summary Every status you can receive
2Success
A completed request is a 200 for both endpoints — including a query that matches nothing.
2.1Success responses
A completed request returns 200 with the result payload. A query matching zero records is also a 200 — an empty list, not an error.
{ "list": [ { "id": "<RECORD_ID>", "database": "sprouts", "name": "Example Company", "website_url": "https://www.example.com/", "industry": "Software & Technology", "city": "Lisbon", "country": "Portugal", "founded_year": 2019 } ], "page": 1, "size": 1, "total_elements": 7900, "extra_params": {} } // zero matches — still 200 { "list": [], "page": 1, "size": 25, "total_elements": 0, "extra_params": {} }
3Errors
Everything the APIs reject, grouped by what has to change. Credentials come first, then the request itself, then paging and rate limits, and finally faults on the Sprouts or upstream side.
3.1Authentication & permission
401 means a credential is missing, invalid, or the account can't be authenticated. 403 means you're authenticated but not permitted for the operation.
| Status | detail |
|---|---|
| 401 | Invalid API secret. Please check your X-API-SECRET header. |
| 401 | Invalid or missing API secret. Please check your X-API-SECRET header. |
| 401 | Customer could not be resolved. Please check your X-Customer-ID header. |
| 401 | Your account is not authenticated for data search. Please verify your credentials or contact support. |
| 403 | Your account is not permitted to perform this search. |
3.2Validation errors
3.2.1Request validation errors
Returned as 422 when something in the request needs to change. The detail message names the exact problem so you can correct it and retry.
| Condition | detail message |
|---|---|
| Empty required header | X-API-SECRET header cannot be empty |
| Unknown filter name | Unknown filter '<filter_name>' |
| Filter not valid for the request type | Filter 'company' is not valid for a Contact search |
Wrong shape for tag | 'tag' must be an object with an 'excludes' list |
| Wrong shape for a range filter | 'employee_count' must be an object with 'min' and/or 'max' |
Incomplete funding filter | 'funding' needs at least one of stages/total_funding/last_funding_date |
Wrong scope key for keywords | 'keywords' needs exactly one of 'includes', 'includeAll' or 'excludes' |
page_size out of range | page_size must be between 1 and 100. |
| Value outside a filter's taxonomy | Invalid value 'Information Technology' for 'department'. Valid values are: … |
| Parameters rejected upstream | Upstream data service rejected the request parameters. |
3.2.2Request-shape validation (array form)
Missing headers or wrong field types are reported as a 422 whose detail is an array — one entry per field, with its loc and msg.
| Example condition | detail[].msg |
|---|---|
Required header absent (loc: ["header", …]) | Field required |
page_size above 100 (loc: ["body", "page_size"]) | Input should be less than or equal to 100 |
Invalid request_type value | Input should be 'Account' or 'Contact' |
3.2.3Search-type errors (account_type & contact_type)
account_type belongs to an Account search and contact_type to a Contact search; each accepts "NET_NEW" or "SAVED". Sending the field that doesn’t match request_type, or a value outside that pair, returns 422 with detail in the array form.
| Condition | detail[].type | detail[].msg |
|---|---|---|
contact_type sent on an Account search ("request_type": "Account") | value_error | Value error, contact_type is not valid for an Account search — use account_type, and leave contact_type null. |
account_type sent on a Contact search | value_error | Value error, account_type is not valid for a Contact search — use contact_type, and leave account_type null. |
Both fields sent with values — the one that doesn’t match request_type is reported | value_error | Value error, contact_type is not valid for an Account search — use account_type, and leave contact_type null. |
Unrecognised contact_type value ("NETNEW", "NEW_NEW", "ALL", "true") | literal_error | Input should be 'NET_NEW' or 'SAVED' |
Unrecognised account_type value | literal_error | Input should be 'NET_NEW' or 'SAVED' |
Non-string account_type / contact_type (boolean, number, object) | literal_error | Input should be 'NET_NEW' or 'SAVED' |
request_type in the wrong case or an unknown value ("account", "ACCOUNT", "contacts") | literal_error | Input should be 'Account' or 'Contact' |
request_type omitted | missing | Field required |
Wrong field for the search type — loc is ["body"], because the rejection is about the combination of fields rather than one field on its own:
// contact_type sent on an Account search { "detail": [ { "type": "value_error", "loc": ["body"], "msg": "Value error, contact_type is not valid for an Account search — use account_type, and leave contact_type null.", "input": { "request_type": "Account", "filters": {}, "contact_type": "NET_NEW" }, "ctx": { "error": {} } } ] } // both fields sent — the one that doesn't match request_type is reported { "detail": [ { "type": "value_error", "loc": ["body"], "msg": "Value error, contact_type is not valid for an Account search — use account_type, and leave contact_type null.", "input": { "request_type": "Account", "filters": {}, "account_type": "SAVED", "contact_type": "NET_NEW" }, "ctx": { "error": {} } } ] }
Unrecognised or wrongly-typed value — loc names the offending field, and ctx.expected lists the accepted values:
// unrecognised value for contact_type { "detail": [ { "type": "literal_error", "loc": ["body", "contact_type"], "msg": "Input should be 'NET_NEW' or 'SAVED'", "input": "NETNEW", "ctx": { "expected": "'NET_NEW' or 'SAVED'" } } ] } // non-string value for account_type { "detail": [ { "type": "literal_error", "loc": ["body", "account_type"], "msg": "Input should be 'NET_NEW' or 'SAVED'", "input": true, "ctx": { "expected": "'NET_NEW' or 'SAVED'" } } ] }
request_type itself is validated the same way — a wrong case is a literal_error, an absent field is missing:
// request_type in the wrong case { "detail": [ { "type": "literal_error", "loc": ["body", "request_type"], "msg": "Input should be 'Account' or 'Contact'", "input": "account", "ctx": { "expected": "'Account' or 'Contact'" } } ] } // request_type omitted { "detail": [ { "type": "missing", "loc": ["body", "request_type"], "msg": "Field required", "input": { "filters": {}, "account_type": "SAVED" } } ] }
"account_type": nullor"contact_type": nullon either search type — 200."account_type": ""or"contact_type": ""— an empty string is treated as not set.- Omitting both fields — a normal search over everything matching the filters.
- The non-matching field present but
nullor""— e.g."request_type": "Account"with"contact_type": null.
request_type is not. "net_new",
"Saved" and " saved " are all normalised to NET_NEW /
SAVED. Only the value is case-insensitive — request_type must be
exactly "Account" or "Contact".
3.2.4Taxonomy value errors (department, seniority & friends)
Filters backed by a fixed list of values are validated against that list. A value outside it returns 422 whose detail names the offending value and the accepted ones. Browse every accepted value in the filter taxonomy.
| Filter | Accepts | Rejected example |
|---|---|---|
department | 22 top-level departments and 225 sub-departments | "Information Technology" — renamed to "IT & Infrastructure" |
seniority | C-Suite, VP, Director / Head, Manager, IC, Entry, In Training | "C Suite" — the hyphen is required |
governance_level | Founder, Co Founder, Owner, Board Member, Advisor, Investor, Partner, None | "Cofounder" — use "Co Founder" |
employment_type | Consultant (External), Contractor, Freelancer, Part Time, Full Time | "Full-Time" — no hyphen |
has_csuite_access | a boolean — true or false | ["true"] — not a list, not a string |
// a department that no longer exists { "detail": "Invalid value 'Information Technology' for 'department'. Valid values are: General Management, Sales, Marketing, ..." } // a seniority value dropped in taxonomy v2 { "detail": "Invalid value 'Founder' for 'seniority'. Valid values are: C-Suite, VP, Director / Head, Manager, IC, Entry, In Training" }
Information Technology → IT & Infrastructure,
C Suite → C-Suite, Senior → IC,
Intern → In Training.
seniority. They are now
governance_level values. Filters are ANDed, so sending
seniority: ["C-Suite"] together with governance_level: ["Founder"] matches
only people who are both — send the governance filter on its own to match every founder.
3.3Pagination
3.3.1Pagination depth limit
The search can return at most the first 10,000 records. A request is rejected with 422 when page × page_size exceeds 10,000.
{ "detail": "Cannot read beyond the first 10000 records: page x page_size must not exceed 10000. Narrow your filters so fewer records match, or request an earlier page." }
total_elements often exceeds this (values in
the millions are normal), but you cannot page beyond a depth of 10,000. Incrementing
page until you've retrieved total_elements will fail once past the cap —
narrow the query with filters instead. The limit is identical for account and contact search.
page × page_size | Depth | Result |
|---|---|---|
| 100 × 100 | 10,000 | 200 — at the limit |
| 1000 × 10 | 10,000 | 200 |
| 101 × 100 | 10,100 | 422 — over the limit |
| 1001 × 10 | 10,010 | 422 |
3.3.2Per-account grouping & pagination
On contact search, contacts_per_account can only be used on the first page. Combining it with page > 1 returns 422.
{ "detail": "contacts_per_account is not supported with page > 1. Omit it (or send 0) to paginate without per-account grouping, request page 1, or increase page_size (max 100) to retrieve more contacts." }
3.4Rate limiting
Exceeding the request rate returns 429. The detail message states the limit and how long to wait before retrying.
{ "detail": "Rate limit exceeded: 60 requests per 60 seconds. Retry in 43s." }
Back off and retry after the interval given in the message.
3.5Server & transport
These indicate a problem on the Sprouts or upstream side, or a malformed request line — not something in your search parameters. Retry the 5xx cases; if they persist, contact Sprouts.
| Status | Meaning | detail |
|---|---|---|
| 502 | Upstream error | Upstream data service error |
| 504 | Timeout | Request timed out |
| 500 | Server error | Internal server error |
| 404 | Unknown route | Not Found |
| 405 | Wrong method | Method Not Allowed — both endpoints accept POST |
4Status code summary
The complete set of status codes you can receive, and how to handle each.
| Status | Meaning | What to do |
|---|---|---|
| 200 | Success | Use the payload. An empty list is still success. |
| 401 | Authentication | Check your secret and account headers. |
| 403 | Access denied | Not permitted for this operation. Contact Sprouts about access. |
| 422 | Request needs a change | Read detail, fix the request, retry. |
| 429 | Rate limited | Back off and retry after the interval in detail. |
| 404 / 405 | Route / method | Confirm the URL and use POST. |
| 500 / 502 / 504 | Server / upstream | Retry with backoff; contact Sprouts if it persists. |