uChecker API documentation
uChecker checks whether a mailbox actually exists, working down through DNS/MX records, an SMTP connection and provider-specific behaviour. The answer comes back as a decision, not a probability — the address either accepts mail or it does not.
Every request goes to https://api.uchecker.net. Send one address at a time, or millions in a single task.
Quick start
1. Get your API key
The key is waiting in your dashboard the moment you register — nothing to request or wait for.
2. Send an address for validation
curl -X POST https://api.uchecker.net/api/v1/validate/single \ -H "x-api-key: uk_YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{"email": "user@example.com"}'3. Fetch the result using task_id from the response
curl https://api.uchecker.net/api/v1/tasks/123/results \ -H "x-api-key: uk_YOUR_KEY"
Authentication
Two methods, equal in power: both unlock every endpoint. Which one you want depends on where the request comes from.
API key
Pass the key in the x-api-key header. It never expires and stays valid until you reset it by hand, which makes it the sane choice for server-side integrations — nothing to refresh, nothing to schedule.
x-api-key: uk_xxxxxxxxxxxxxBearer token
Call POST /auth/login for a JWT and pass it in the Authorization header. Use this from front-end code, where shipping a permanent key to the browser is not an option.
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...| Token | Lifetime | Purpose |
|---|---|---|
access_token | 1 hour | Authenticates requests |
refresh_token | 7 days | Renews access_token via POST /auth/refresh |
Your key is visible in the dashboard, and that is also where you reset it if it leaks. The old key stops working immediately. app.uchecker.net
Credits and limits
Billing runs on credits: verifying one address costs 1 credit. The credit is taken when the address enters the queue, not when the SMTP server answers.
- There are no rate limits — send requests as fast as you need to.
- Addresses that fail the syntax check in a batch are never charged: they are dropped before the queue and returned in
invalid_details. GET /api/v1/account/balancereturns what is left.
Reading the result
Every address comes back with one of two values in validation_result. There is deliberately no third option — handing back a maybe would just move the decision to you.
| Value | What it means |
|---|---|
good | The mailbox exists and accepts mail. Safe to keep on your list. |
bad | The mailbox does not exist, is disabled, or the domain refuses mail. The result field carries the reason: mailbox_not_found, domain_not_found, smtp_rejected and others. |
Task lifecycle
Every validation request creates a task. Results only become available once it reaches completed.
pending → processing → completed
↘ failed| State | What is happening |
|---|---|
pending | Task created, waiting for the queue. Code 0. |
processing | Addresses are being checked; progress_percent tracks it. Code 1. |
completed | Every address is checked and results are ready. Code 3. |
failed | The task broke — send us the task_id and we will look. Code −1. |
Polling every 5–10 seconds is plenty. Better still, pass webhook_url when you create the task and we will POST the results to you the moment it finishes, so there is nothing to poll.
Errors
Standard HTTP codes. Error bodies always carry success: false and a readable error field.
| Code | When you see it |
|---|---|
400 | Invalid parameters or a malformed request body. |
401 | The key or token is missing, expired or wrong. |
403 | Not enough credits on the balance. |
404 | No such task, or it belongs to a different account. |
500 | Our side broke — retry the request. |
Endpoint reference
Every public method with its parameters and examples. Paths are shown without the base URL.
https://api.uchecker.netValidation and tasks
Queue addresses, follow progress, collect results.
Verify a single address
/api/v1/validate/singleKey requiredThe address is syntax-checked, queued, then verified through DNS/MX, SMTP and provider-specific methods.
If the format is plainly wrong the response comes back instantly with status: "invalid" and no credit is spent. Otherwise 1 credit is taken and you get a task_id. Verification takes a few seconds to two minutes — domains with aggressive anti-spam policies are the slow ones.
Parameters
emailstring· body· requiredThe address to verify, in RFC 5322 format.
webhook_urlstring· body· optionalWhere to POST the result once verification finishes. Must be reachable from outside and answer 200.
client_typestring· body· optionalweb | api
Tags the source of the request. Only affects the format of internal notifications.
Request
{
"email": "user@example.com",
"webhook_url": "https://your-site.com/webhook/validation-complete"
}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"
}Verify a list of addresses
/api/v1/validate/bulkKey requiredThis is how you work with real lists. Every address is syntax-checked before queueing: malformed ones are dropped, never charged, and returned in invalid_details with a reason. You pay only for what actually got verified.
Pass an idempotency_key when a request might be retried after a network timeout — calling again with the same key returns the existing task instead of creating a second identical one.
Parameters
emailsstring[]· body· requiredArray of addresses. Malformed ones are excluded.
webhook_urlstring· body· optionalWhere to POST all results once the task finishes.
idempotency_keystring· body· optionalAny unique string — an import id works well. Protects against duplicate tasks on retries.
client_typestring· body· optionalweb | api
Tags the source of the request.
Request
{
"emails": ["user1@example.com", "user2@example.com", "info@company.ru"],
"idempotency_key": "import-2024-01-15-batch-3"
}Response 200
{
"success": true,
"task_id": 124,
"status": "queued",
"total_emails": 100,
"valid_emails": 95,
"invalid_emails": 5,
"invalid_details": [
{ "email": "bad-email", "reason": "Invalid email syntax" }
],
"credits_used": 95,
"credits_remaining": 904
}Task status and progress
/api/v1/tasks/{taskId}Key requiredTells you where the task is and how many addresses are done. Results are only served for tasks in completed, so this is the endpoint you poll between queueing and downloading.
A task is only visible to the account that created it.
Parameters
taskIdnumber· path· requiredThe task id returned by the validation request.
Response 200
{
"success": true,
"task_id": 123,
"status": "processing",
"total_emails": 100,
"processed_emails": 45,
"progress_percent": 45,
"created_at": "2024-01-01T12:00:00.000Z",
"finished_at": null
}Validation results
/api/v1/tasks/{taskId}/resultsKey requiredReturns the verdict for every address in the task. Addresses marked bad also carry a result field explaining why.
Parameters
taskIdnumber· path· requiredThe task id.
formatstring· query· optional· default:jsonjson | csv
jsonreturns a structure,csvreturns the same data as a string with headers.
Response 200
{
"success": true,
"format": "json",
"data": [
{ "email": "user1@example.com", "validation_result": "good" },
{
"email": "user2@example.com",
"validation_result": "bad",
"result": "mailbox_not_found"
}
]
}Download results as CSV
/api/v1/tasks/{taskId}/results/csvKey requiredSame data as format=csv above, but delivered as a file with a Content-Disposition: attachment header. Handy when the export goes straight to a user's browser.
Parameters
taskIdnumber· path· requiredThe task id.
Response 200
Content-Type: text/csv
Content-Disposition: attachment; filename="results_123.csv"
email,validation_result,result
user1@example.com,good,
user2@example.com,bad,mailbox_not_foundDownload good and bad as lists
/api/v1/tasks/{taskId}/downloadKey requiredA ZIP holding good.txt and bad.txt, one address per line. Almost every ESP imports this format directly, so there is no CSV to parse first.
Parameters
taskIdnumber· path· requiredThe task id.
Response 200
Content-Type: application/zip
Content-Disposition: attachment; filename="task_123.zip"
task_123.zip
├── good.txt
└── bad.txtTask analytics
/api/v1/tasks/{taskId}/analyticsKey requiredA summary of a finished task: how many addresses are live, how many were filtered out, the resulting deliverability rate, and why addresses failed.
The reasons breakdown is the useful part — it tells you what is actually wrong with the list. A wall of domain_not_found points at typos during collection, while smtp_reject usually means the list has simply aged.
Parameters
taskIdnumber· path· requiredThe task id.
Response 200
{
"total": 100,
"good": 80,
"bad": 15,
"unknown": 5,
"deliverability": 80,
"reasons": [
{ "key": "smtp_reject", "count": 12 },
{ "key": "domain_not_found", "count": 3 }
]
}List tasks
/api/v1/tasksKey requiredPaginated history of the account's tasks, newest first.
Parameters
pagenumber· query· optional· default:1Page number, starting at 1.
limitnumber· query· optional· default:10Tasks per page: 1 to 100.
Response 200
{
"success": true,
"tasks": [
{
"task_id": 123,
"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
}Account and billing
Credit balance, lifetime statistics and payment history.
Account balance
/api/v1/account/balanceKey requiredRemaining credits, account id and a masked key. Worth calling before a large batch so you do not hit a 403 halfway through.
Parameters
No parameters.
Response 200
{
"success": true,
"account_id": 123456,
"credits_remaining": 1000,
"api_key": "uk_xxxxx..."
}Account statistics
/api/v1/account/statsKey requiredTasks created, addresses verified all-time, and average deliverability. last_list breaks down the most recent task.
Parameters
No parameters.
Response 200
{
"tasks_count": 42,
"emails_checked": 12500,
"avg_deliverability": 78,
"last_list": { "total": 100, "good": 80, "bad": 15, "unknown": 5 }
}Payment history
/api/v1/billing/historyKey requiredPaginated transactions, newest first: amount, status, what was bought and the payment id.
Parameters
pagenumber· query· optional· default:1Page number, starting at 1.
limitnumber· query· optional· default:10Records per page.
Response 200
{
"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": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
],
"pagination": { "page": 1, "limit": 10, "total": 5, "totalPages": 1 }
}ESP providers
For partners who create accounts and resell credits to their own customers. Authenticated with a separate token rather than an account key.
Calculate credit pricing
/api/v1/esp/priceESP tokenReturns the price for a given volume under the partner's current rate card. No need to compute it yourself — the per-address price depends on volume.
Parameters
x-esp-tokenstring· header· requiredPartner token, issued when you are onboarded.
countnumber· query· requiredNumber of credits, minimum 1.
Response 200
{
"success": true,
"credits": 10000,
"price": 2000,
"price_per_email": 0.2,
"currency": "RUB"
}Create an account or top up credits
/api/v1/esp/provisionESP tokenOne method for both cases: if no account exists for that address it is created and issued a key, and if one does exist the credits are simply added. The is_new_account flag tells you which happened.
Send an external_id — your own order number. We use it to reject a repeated payment and return is_duplicate: true instead of crediting twice.
Parameters
x-esp-tokenstring· header· requiredPartner token, issued when you are onboarded.
emailstring· body· requiredAddress of the new or existing account.
creditsnumber· body· requiredHow many credits to add.
external_idstring· body· optionalOrder identifier on the partner's side. Guards against double crediting.
Request
{
"email": "user@example.com",
"credits": 10000,
"external_id": "order_12345"
}Response 200
{
"success": true,
"account_id": 123,
"email": "user@example.com",
"api_key": "uk_xxxxxxxxxxxxx",
"credits_added": 10000,
"total_credits": 10000,
"is_new_account": true,
"is_duplicate": false
}Try it live
This page is built for reading and searching. When you want to fire a real request from the browser and look at a real response, open the interactive sandbox — it takes your key and gives every endpoint a send button.
Open the API sandboxFrequently asked questions
What does it cost to verify one address through the API?
One verification costs 1 credit, taken when the address enters the queue. Addresses that fail the syntax check in a batch are not charged at all — they are dropped beforehand and returned in invalid_details.
Are there rate limits?
No. Nothing throttles how fast you send. The only ceiling is your credit balance, and hitting it returns a 403.
How many addresses fit in one request?
The bulk endpoint accepts millions of addresses in a single task, so there is no need to slice a list up just to get around limits.
How do I know a task finished without polling?
Pass webhook_url when you create the task and we POST the results to that URL once every address is checked. If webhooks do not fit, poll GET /api/v1/tasks/:taskId every 5–10 seconds.
API key or Bearer token — what is the difference?
They grant the same access; the difference is lifetime. The x-api-key header never expires, which suits server-side integrations. A JWT lasts an hour and is renewed with a refresh token, which is what you want when a permanent key must not reach the browser.
What should I do about a task that failed?
Email support@uchecker.net with the task_id — it shows exactly where the task broke. Credits for work that never ran are returned.
Can I test the API before paying?
Yes. Registering puts free credits on the balance, and every endpoint can be called straight from the browser in the interactive sandbox.
Support
Integration questions go to support@uchecker.net. If a task ends in failed, include the task_id — it points straight at what went wrong.
