Skip to content
SimpleCold

API docs

Results

Common scenarios

On this page

The situations most integrations meet sooner or later, what the API returns in each, and what to do. Each one links to the page with the full details.

A mail server is slow (processing)

Some mail servers answer slowly on purpose (greylisting), so a check can take up to a minute. The API doesn't keep your request open that long:

  • A single check answers within about 12 seconds, a batch within about 25 seconds.
  • An address whose server hasn't answered by then comes back with "status": "processing" and "category": "pending". The rest of a batch has final results.
  • The check keeps running on our side.
payload
{
  "email": "[email protected]",
  "status": "processing",
  "statusMessage": "Still checking this mailbox. Ask again in about a minute; you will not be charged again.",
  "category": "pending",
  "failType": "none"
}

What to do:

  1. Store the address as pending. Never suppress it based on a pending result.
  2. Collect the final result with GET /api-validation/single/result?email=..., every 10 to 20 seconds for up to 3 minutes. It answers processing until the result is ready, then the final result once, then 404 No check in progress for this email. So save it the first time.
  3. Or subscribe a webhook to result.ready and get the final result pushed.

Collecting a result never uses credits, and asking GET /api-validation/single again for the same address within 30 minutes doesn't charge again either. Each address is charged once, when its final result is ready. Don't resend a whole batch because one item is pending.

Details: Slow mail servers.

The domain accepts every address (accept_all)

Some domains accept mail for any address, so the server can't tell us whether this mailbox exists. The result is accept_all, category risky, failType none:

payload
{
  "email": "[email protected]",
  "status": "accept_all",
  "statusMessage": "The domain accepts mail for any address, so this mailbox can't be confirmed.",
  "category": "risky",
  "failType": "none"
}

A re-check won't change it. Send carefully: keep accept-all contacts in a separate, smaller send and watch bounces. Test it with [email protected], or catchall@… with a test key.

Details: Statuses and categories.

The address has a typo (possible_typo)

When the domain looks like a misspelling of a common provider (gmial.com, hotmial.com, gmail.con), the address is reported as possible_typo, category undeliverable, with the likely correction in suggestion:

payload
{
  "email": "[email protected]",
  "status": "possible_typo",
  "statusMessage": "Possible typo. Did you mean [email protected]?",
  "category": "undeliverable",
  "failType": "permanent",
  "suggestion": "[email protected]"
}

We never change the address for you. On a sign-up form, ask the person to confirm the suggestion. In a list, fix the address at the source and check the suggested address separately before you use it. Bulk result files show the correction in the Suggestion column.

Details: Typos.

A result says "retry later"

blocked_by_server and no_response (category risky) and server_error (category undeliverable) have failType temporary: the mail server, not the address, got in the way. Bulk files show Yes in the Retry later column.

Don't send to them now. Check them again later; the answer can change. If it stays the same, treat blocked_by_server and no_response as risky and server_error as undeliverable.

Details: Fail type.

A bulk job was canceled

Jobs can be canceled from the dashboard. The status goes CANCELING, then CANCELED:

  • Rows that were finished keep their results.
  • Rows that never got a result show the reason code no_response (category risky), and their credits are released.
  • You can still download the file, and bulk.finished is sent with "status": "canceled". Its creditsUsed can be lower than totalEmails.

Stop polling on CANCELED the same way you do on COMPLETED. If you want the rest checked, upload those rows again.

Details: Job statuses and Download the results.

A bulk job failed

A job with status FAILED stopped. Its reserved credits are released, downloads answer 400 Results are not available for this job., and bulk.finished is sent with "status": "failed" and no download links. Retry the job from the dashboard.

An upload timed out on your side

The job may still have been created. Retrying starts a second job and reserves credits twice. Check the dashboard for the job before uploading again, and store the job id as soon as you get it.

You run out of credits

Credits are reserved when a request arrives (for bulk, when the file is uploaded) and charged once each address has a final result. If your balance can't cover a request:

400 · Insufficient credits
{
  "code": "FAILURE",
  "message": "Insufficient credits.",
  "data": null
}

The message is Please buy credits. when the balance is empty. Nothing is checked and no bulk job is created. Retrying won't help: top up in the dashboard, then send the request again.

To see it coming, subscribe a webhook to credits.low, which fires when your available credits drop below the low-credit threshold.

Test emails don't use credits on single and batch requests and work with a balance of zero; any other address in the request still needs credits. Test keys work with a balance of zero.

Details: Credits.

The key is expired, inactive or regenerated

Every endpoint checks the key first. A problem with it answers 401 with one of these messages:

MessageCauseFix
API key has expiredThe key passed its expiry date.Update its expiry; the same key works again. Or create a new key.
API key is inactiveThe key is disabled in the dashboard.Enable it. Its value doesn't change.
Invalid API keyNo such key: often a copy-paste error, or the key was regenerated and the old value stopped working.Deploy the current value, or regenerate the key if you lost it.

None of these fix themselves, so don't retry a 401: fail fast and alert someone. Repeated requests with a bad key from one IP address also run into the invalid-key limit (30 per 15 minutes) and then get 429s even with a valid key.

To swap keys without failed requests, create a new key, deploy it, then disable the old one.

Details: Using your API key.

You hit a rate limit

A 429 with code RATE_LIMITED means you went over a per-key or per-organisation limit. Wait the number of seconds in Retry-After, then send again. For many addresses, use batch or bulk instead of many single checks.

Details: Rate limits.

You're building against the sandbox

Create a test key (sc_test_…) and use the sandbox addresses: every result, processing and polling, simulated 429 and 500 errors, bulk jobs with progress, downloads and webhooks, with no credits and a zero balance.

  • Sandbox results carry "sandbox": true; live results never have the field.
  • Webhook events caused by a test key have "livemode": false. Ignore them in production.
  • A test key only sees sandbox jobs, and a live key only live jobs.

When you go live, swap the key through configuration, make sure you have credits, and keep sandbox addresses out of production: to a live key they are ordinary addresses and are checked for real.

Details: Sandbox (test mode).

Common scenarios | SimpleCold API docs