A dirty email list means a bounce rate above 5%, an ESP suspension, and money wasted on addresses that never deliver. Manual checking does not scale. You need validation built into your pipeline.
This guide covers the full uChecker API integration flow: authentication, single and bulk validation, task polling, and result retrieval. All examples are tested and ready to run.
Base URL
https://api.uchecker.netAll endpoints below are relative to this URL. Requests and responses use JSON.
Authentication
The API supports two authentication methods:
API Key recommended
Pass the key in the x-api-key header. Simple and safe for server-side integrations.
Bearer JWT
JWT token from /auth/login. Access tokens expire after 1 hour; refresh tokens last 7 days.
Getting an API key: POST /auth/login
Register at app.uchecker.net, then retrieve your key via the login endpoint:
curl -X POST https://api.uchecker.net/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "you@example.com",
"password": "your_password"
}'{
"user": {
"id": 1,
"email": "you@example.com",
"telegram_code": 123456
},
"api_key": "uk_xxxxxxxxxxxxx",
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"refresh_token": "eyJhbGciOiJIUzI1NiIs..."
}Store the api_key. It stays the same until you explicitly rotate it via POST /auth/reset-api-key.
Never hardcode your API key in source code. Store it in an environment variable: UCHECKER_API_KEY.
Single email validation
The POST /api/v1/validate/single endpoint checks one address in real time. Use it for registration form validation.
Request body
{
"email": "user@example.com"
}Optional: webhook_url to receive a notification when the job finishes.
curl -X POST https://api.uchecker.net/api/v1/validate/single \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com"}'import requests
import os
API_KEY = os.environ["UCHECKER_API_KEY"]
BASE = "https://api.uchecker.net"
resp = requests.post(
f"{BASE}/api/v1/validate/single",
headers={"x-api-key": API_KEY},
json={"email": "user@example.com"},
)
data = resp.json()
print(data["task_id"], data["credits_remaining"])const API_KEY = process.env.UCHECKER_API_KEY;
const BASE = "https://api.uchecker.net";
const res = await fetch(`${BASE}/api/v1/validate/single`, {
method: "POST",
headers: {
"x-api-key": API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({ email: "user@example.com" }),
});
const data = await res.json();
console.log(data.task_id, data.credits_remaining);Response 200
{
"success": true,
"task_id": 123,
"email": "user@example.com",
"status": "queued",
"credits_used": 1,
"credits_remaining": 999,
"estimated_completion": "2024-01-01T12:00:30.000Z"
}The request queues the email and returns a task_id. Use it to poll status and fetch the result.
Bulk validation
POST /api/v1/validate/bulk accepts an array of addresses. Any address with invalid syntax is filtered out automatically and not billed. You can also pass a webhook_url for completion notification.
curl -X POST https://api.uchecker.net/api/v1/validate/bulk \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"emails": [
"alice@company.com",
"bob@example.org",
"invalid-email",
"carol@domain.net"
]
}'emails = ["alice@company.com", "bob@example.org", "carol@domain.net"]
resp = requests.post(
f"{BASE}/api/v1/validate/bulk",
headers={"x-api-key": API_KEY},
json={"emails": emails},
)
data = resp.json()
print(f"Task {data['task_id']}: {data['valid_emails']} queued, "
f"{data['invalid_emails']} skipped")Response 200
{
"success": true,
"task_id": 124,
"status": "queued",
"total_emails": 4,
"valid_emails": 3,
"invalid_emails": 1,
"invalid_details": [
{ "email": "invalid-email", "reason": "Invalid email syntax" }
],
"credits_used": 3,
"credits_remaining": 996,
"estimated_completion": "2024-01-01T12:01:35.000Z",
"note": "1 invalid addresses were skipped"
}The invalid_details field shows which addresses were filtered and why. Credits are charged only for addresses that passed syntax validation.
Try the uChecker API
Free credits on signup. Get your API key in 30 seconds and send your first request.
Get API keyTask status polling
Validation is asynchronous. After submitting a request, poll GET /api/v1/tasks/{taskId} until the status reaches completed or failed.
| Status | Code | Description |
|---|---|---|
| pending | 0 | Task created, waiting in queue |
| processing | 1 | Addresses are being checked |
| completed | 2 | Done, results are ready |
| failed | -2 | Processing error |
curl https://api.uchecker.net/api/v1/tasks/124 \
-H "x-api-key: YOUR_API_KEY"{
"success": true,
"task_id": 124,
"status": "processing",
"total_emails": 3,
"processed_emails": 1,
"progress_percent": 33,
"created_at": "2024-01-01T12:00:00.000Z",
"finished_at": null
}Polling loop in Python
import time
def wait_for_task(task_id, timeout=300, interval=5):
"""Poll task until completed or failed."""
elapsed = 0
while elapsed < timeout:
resp = requests.get(
f"{BASE}/api/v1/tasks/{task_id}",
headers={"x-api-key": API_KEY},
)
status = resp.json()["status"]
progress = resp.json()["progress_percent"]
print(f"Status: {status} ({progress}%)")
if status == "completed":
return True
if status == "failed":
raise Exception(f"Task {task_id} failed")
time.sleep(interval)
elapsed += interval
raise TimeoutError(f"Task {task_id} timed out")
wait_for_task(124)Polling loop in Node.js
async function waitForTask(taskId, timeout = 300_000, interval = 5_000) {
const start = Date.now();
while (Date.now() - start < timeout) {
const res = await fetch(`${BASE}/api/v1/tasks/${taskId}`, {
headers: { "x-api-key": API_KEY },
});
const { status, progress_percent } = await res.json();
console.log(`Status: ${status} (${progress_percent}%)`);
if (status === "completed") return true;
if (status === "failed") throw new Error(`Task ${taskId} failed`);
await new Promise((r) => setTimeout(r, interval));
}
throw new Error(`Task ${taskId} timed out`);
}
await waitForTask(124);Retrieving and processing results
Once a task reaches completed status, you can fetch results in two ways.
JSON results: GET /api/v1/tasks/{taskId}/results
curl "https://api.uchecker.net/api/v1/tasks/124/results?format=json" \
-H "x-api-key: YOUR_API_KEY"{
"success": true,
"format": "json",
"data": [
{
"email": "alice@company.com",
"validation_result": "good"
},
{
"email": "bob@example.org",
"validation_result": "bad",
"result": "mailbox_not_found"
},
{
"email": "carol@domain.net",
"validation_result": "good"
}
]
}The format parameter accepts json or csv. Use JSON for programmatic processing, CSV for spreadsheet imports.
resp = requests.get(
f"{BASE}/api/v1/tasks/124/results",
headers={"x-api-key": API_KEY},
params={"format": "json"},
)
results = resp.json()["data"]
good = [r["email"] for r in results if r["validation_result"] == "good"]
bad = [r["email"] for r in results if r["validation_result"] == "bad"]
print(f"Valid: {len(good)}, Invalid: {len(bad)}")ZIP archive: GET /api/v1/tasks/{taskId}/download
Returns a ZIP with two files: good.txt and bad.txt, one email per line.
curl "https://api.uchecker.net/api/v1/tasks/124/download" \
-H "x-api-key: YOUR_API_KEY" \
-o results.zipimport zipfile, io
resp = requests.get(
f"{BASE}/api/v1/tasks/124/download",
headers={"x-api-key": API_KEY},
)
with zipfile.ZipFile(io.BytesIO(resp.content)) as zf:
good = zf.read("good.txt").decode().splitlines()
bad = zf.read("bad.txt").decode().splitlines()
print(f"Good: {len(good)}, Bad: {len(bad)}")Tasks and balance
Check balance: GET /api/v1/account/balance
Always check your balance before a bulk request to avoid a 403 (Insufficient credits) error mid-job.
curl https://api.uchecker.net/api/v1/account/balance \
-H "x-api-key: YOUR_API_KEY"{
"success": true,
"account_id": 123456,
"credits_remaining": 1000,
"api_key": "uk_xxxxx..."
}List tasks: GET /api/v1/tasks
Paginated list of all your tasks, sorted by creation date (newest first).
curl "https://api.uchecker.net/api/v1/tasks?page=1&limit=10" \
-H "x-api-key: YOUR_API_KEY"{
"success": true,
"tasks": [
{
"task_id": 124,
"fileName": "bulk_100_emails",
"status": "completed",
"created_at": "2024-01-01T12:00:00.000Z",
"finished_at": "2024-01-01T12:01:35.000Z"
}
],
"total": 50,
"page": 1,
"limit": 10
}Payment history: GET /api/v1/billing/history
Paginated list of all payments, useful for reconciliation and reporting.
curl "https://api.uchecker.net/api/v1/billing/history?page=1&limit=10" \
-H "x-api-key: YOUR_API_KEY"{
"data": [
{
"id": 1,
"amount": 1000,
"status": "completed",
"product_details": "5000 addresses (5k) via freekassa from web",
"creation_date": "2024-01-01T12:00:00.000Z",
"payment_id": "uuid-xxx"
}
],
"pagination": {
"page": 1,
"limit": 10,
"total": 5,
"totalPages": 1
}
}Error handling and best practices
HTTP error codes
| Code | Cause | Fix |
|---|---|---|
| 401 | Missing or invalid API key | Check the x-api-key header |
| 403 | Insufficient credits | Top up your balance or reduce the batch size |
| 404 | Task not found | Verify the task_id and ownership |
Recommendations
Check balance before bulk requests
GET /api/v1/account/balance and compare credits_remaining against your batch size before submitting.
Use environment variables for keys
Never commit API keys to a repository. Store them in .env files, Vault, or CI/CD secrets.
Implement retry with backoff for failed tasks
If a task fails, wait and create a new one. Exponential backoff works well: 5s, 10s, 20s.
Split large lists into batches
For 100K+ addresses, send several bulk requests. Smaller batches are easier to track and recover if one fails.
Cache validation results
Store results locally. Re-checking the same address a day later wastes credits with no new information.
Summary
The full uChecker API integration flow in five steps:
Plain REST with standard JSON. No SDKs or extra dependencies — any HTTP client on any language works.
Register at app.uchecker.net, get your API key, and send your first request in under five minutes.
Start for free