API Reference

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.

Endpoint
POST /sproutsdata
Account & contact search over the Sprouts company and people database.
Endpoint
POST /enrich-contact-email
Reveal a contact's work email and/or phone number.
Base URL   https://ml.sprouts.ai/utility/
Format   JSON · detail field on failure
Rate limit   60 requests per 60 seconds
sprouts.ai
Version 1.0

1Overview

These responses apply to both public endpoints below. Both share the same base URL and header-based authentication.

POST
https://ml.sprouts.ai/utility/sproutsdata
POST
https://ml.sprouts.ai/utility/enrich-contact-email

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:

JSON · status 422
{ "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:

JSON · status 422
{
  "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 }
  ]
}
Reading 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.
2xx Success 4xx You can fix the request 5xx Retry / contact Sprouts
In this document
  1. Overview 1.1 – 1.2 · how errors are returned
  2. Success 2.1 · 200, including empty results
  3. Errors 3.1 – 3.5 · 401, 403, 422, 429, 5xx
  4. 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.

JSON · status 200
{
  "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.

Statusdetail
401Invalid API secret. Please check your X-API-SECRET header.
401Invalid or missing API secret. Please check your X-API-SECRET header.
401Customer could not be resolved. Please check your X-Customer-ID header.
401Your account is not authenticated for data search. Please verify your credentials or contact support.
403Your 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.

Conditiondetail message
Empty required headerX-API-SECRET header cannot be empty
Unknown filter nameUnknown filter '<filter_name>'
Filter not valid for the request typeFilter '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 rangepage_size must be between 1 and 100.
Value outside a filter's taxonomyInvalid value 'Information Technology' for 'department'. Valid values are: …
Parameters rejected upstreamUpstream data service rejected the request parameters.
Unknown filters are rejected, not ignored. A misspelled filter name returns 422 rather than silently matching every record, so a typo can't quietly return the wrong data. Check filter names against the API reference.

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 conditiondetail[].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 valueInput 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.

Conditiondetail[].typedetail[].msg
contact_type sent on an Account search ("request_type": "Account")value_errorValue error, contact_type is not valid for an Account search — use account_type, and leave contact_type null.
account_type sent on a Contact searchvalue_errorValue 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 reportedvalue_errorValue 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_errorInput should be 'NET_NEW' or 'SAVED'
Unrecognised account_type valueliteral_errorInput should be 'NET_NEW' or 'SAVED'
Non-string account_type / contact_type (boolean, number, object)literal_errorInput should be 'NET_NEW' or 'SAVED'
request_type in the wrong case or an unknown value ("account", "ACCOUNT", "contacts")literal_errorInput should be 'Account' or 'Contact'
request_type omittedmissingField 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:

JSON · status 422
// 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:

JSON · status 422
// 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:

JSON · status 422
// 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" }
    }
  ]
}
What is accepted. Both fields are optional, and “not set” is never an error:
  • "account_type": null or "contact_type": null on 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 null or "" — e.g. "request_type": "Account" with "contact_type": null.
Values are case-insensitive; 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.

FilterAcceptsRejected example
department22 top-level departments and 225 sub-departments"Information Technology" — renamed to "IT & Infrastructure"
seniorityC-Suite, VP, Director / Head, Manager, IC, Entry, In Training"C Suite" — the hyphen is required
governance_levelFounder, Co Founder, Owner, Board Member, Advisor, Investor, Partner, None"Cofounder" — use "Co Founder"
employment_typeConsultant (External), Contractor, Freelancer, Part Time, Full Time"Full-Time" — no hyphen
has_csuite_accessa boolean — true or false["true"] — not a list, not a string
JSON · status 422
// 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" }
Changed in taxonomy v2: an invalid value is an error, not an empty result. A value outside the list used to return 200 with zero records, so a stale or misspelled value looked like “no matches”. It now returns 422 naming the valid values. If a previously working request starts failing, the value was almost certainly renamed — Information TechnologyIT & Infrastructure, C SuiteC-Suite, SeniorIC, InternIn Training.
Founder, Owner and Partner moved off 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.

JSON · status 422
{ "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." }
page × page_size ≤ 10,000. 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_sizeDepthResult
100 × 10010,000200 — at the limit
1000 × 1010,000200
101 × 10010,100422 — over the limit
1001 × 1010,010422

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.

JSON · status 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.

JSON · status 429
{ "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.

StatusMeaningdetail
502Upstream errorUpstream data service error
504TimeoutRequest timed out
500Server errorInternal server error
404Unknown routeNot Found
405Wrong methodMethod Not Allowed — both endpoints accept POST
Handling 5xx. 502, 504 and 500 are transient or server-side. Retry with backoff; if the failure continues, contact Sprouts support — there is nothing to change in the request itself.

4Status code summary

The complete set of status codes you can receive, and how to handle each.

StatusMeaningWhat to do
200SuccessUse the payload. An empty list is still success.
401AuthenticationCheck your secret and account headers.
403Access deniedNot permitted for this operation. Contact Sprouts about access.
422Request needs a changeRead detail, fix the request, retry.
429Rate limitedBack off and retry after the interval in detail.
404 / 405Route / methodConfirm the URL and use POST.
500 / 502 / 504Server / upstreamRetry with backoff; contact Sprouts if it persists.