Verify up to 40,000 email addresses in a single request. Submit a list, poll its progress, then download the results as CSV — four simple endpoints.
key parameter
Up to 40,000 emails per list
Unlisted — shared by invitation
Unlisted documentation. This page was shared with you directly — please don't publish or link to it. For single verifications, see the public API reference.
POST the emails and get a bulkId back.
Check progress every minute or so until it completes.
Fetch a CSV with the verdict for every address.
Accepts application/json, application/x-www-form-urlencoded, or
multipart/form-data. Emails can be a comma-separated string or a JSON array.
Duplicate addresses are detected automatically and excluded from the processed totals.
| Field | Required | Description |
|---|---|---|
key | Yes | Your API key. |
emails | Yes | Comma-separated string or JSON array of addresses (max 40,000). |
listName | Yes | A friendly name to identify the list in your dashboard. |
{
"key": "YOUR_API_KEY",
"listName": "October signups",
"emails": [
"user1@example.com",
"user2@example.com"
]
}
{
"message": "Bulk request accepted",
"bulkId": "0b7f9d47-3f8e-4a41-9be2-6c1e0f2d9a11"
}
Save the bulkId — you need it to check status and download results.
{
"message": "Your batch is being processed.",
"processed": 25000,
"total": 40000
}
{
"message": "Your batch has been processed successfully.",
"processed": 40000,
"total": 40000
}
Poll every 30–60 seconds. Large lists with slow mail servers can take a while —
the batch is complete when processed equals total.
Returns a CSV file (named like bulk_<bulkId>.csv) with the verdict for every
address. If the batch is still running, the endpoint responds with
400 and a message asking you to try again later.
Each verified address consumes one credit, so check your balance before submitting a large list. This call is free and never consumes a credit.
{
"balance": 12450
}
import time
import requests
BASE_URL = "https://api.validemail.net"
API_KEY = "YOUR_API_KEY"
emails = ["user1@example.com", "user2@example.com", "user3@example.com"]
# 0. Make sure we have enough credits
balance = requests.get(f"{BASE_URL}/v1/bulk/balance", params={"key": API_KEY}).json()["balance"]
assert balance >= len(emails), f"Not enough credits: {balance} < {len(emails)}"
# 1. Submit the list
submit = requests.post(
f"{BASE_URL}/v1/bulk",
json={"key": API_KEY, "listName": "My list", "emails": emails},
)
submit.raise_for_status()
bulk_id = submit.json()["bulkId"]
# 2. Poll until finished
while True:
status = requests.get(
f"{BASE_URL}/v1/bulk/status", params={"id": bulk_id, "key": API_KEY}
).json()
print(f"{status['processed']}/{status['total']} processed")
if status["total"] > 0 and status["processed"] >= status["total"]:
break
time.sleep(30)
# 3. Download the CSV
csv_bytes = requests.get(
f"{BASE_URL}/v1/bulk/downloadcsv", params={"id": bulk_id, "key": API_KEY}
).content
with open(f"bulk_{bulk_id}.csv", "wb") as file:
file.write(csv_bytes)
print("Results saved.")
| HTTP Status | Meaning | What to do |
|---|---|---|
| 200 OK | The request was processed. Check State — a result with State = "Unknown" and a RetryAfterSeconds value is an interim answer, not a final verdict. |
Use the result. If State is Unknown, retry the same request after RetryAfterSeconds. |
| 400 Bad Request | Missing email/token parameter, no credits remaining, or a transient processing failure. |
Check the response text. If you are out of credits, top up your balance before retrying. |
| 401 Unauthorized | The API key is invalid or unknown. | Verify the token value against the API key shown in your dashboard. |
| 403 Forbidden | The account attached to this API key is inactive. | Contact info@validemail.net to reactivate the account. |
| 429 Too Many Requests | You exceeded your per-second request limit. The response body includes the configured limit. | Slow down and retry with backoff, or contact us to raise your rate limit. |
Learn what each field in the results means, or verify one address at a time.