Getting Started
Overview
Sovolink is a real-time email verification API. Send an email address, get back a clear verdict: valid, invalid, or risky — with full diagnostic context so you can make an informed sending decision.
The API supports single-address verification and bulk jobs. All endpoints return JSON. Authentication uses Bearer tokens.
Auth
Authentication
All authenticated endpoints require a Bearer token in the Authorization header. Retrieve your API key from the API Keys section of your dashboard.
curl https://api.sovolink.com/api/verifications \
-H "Authorization: Bearer sk_your_api_key_here" \
-H "Content-Type: application/json"Base URL & Headers
| Property | Value |
|---|---|
| Base URL | https://api.sovolink.com |
| Content-Type | application/json |
| Authorization | Bearer <your-api-key> |
| API version | v1 (current) |
Endpoints
Single Email Verification
Verify a single email address. The verifier performs a syntax check, MX record lookup, SMTP handshake, and provider heuristics in one pass.
This endpoint requires authentication. For a public (rate-limited) check, use POST /api/public/verify without a Bearer token.
/api/verificationsRequest
{
"email": "user@example.com"
}Response
{
"_id": "64f1a2b3c4d5e6f7a8b9c0d1",
"email": "user@example.com",
"status": "completed",
"result": {
"status": "valid",
"reason": "smtp_ok",
"provider": "google",
"deliverable": true,
"catchAll": false,
"confidence": "high",
"confidenceScore": 95,
"smtpReachable": true,
"mxHost": "aspmx.l.google.com"
},
"createdAt": "2024-01-15T10:00:00.000Z"
}| Field | Type | Description |
|---|---|---|
| status | string | Verification job status: pending | processing | completed | failed |
| result.status | string | Email verdict: valid | invalid | risky | catch-all | disposable |
| result.deliverable | boolean | Whether the address is safe to send to |
| result.catchAll | boolean | Domain accepts all addresses regardless of existence |
| result.confidence | string | high | medium | low |
| result.confidenceScore | number | 0–100 confidence score |
| result.mxHost | string | Primary MX record host for the domain |
Endpoints
Bulk Verification
Bulk verification runs as a multi-step job. Create a job, upload rows in batches of up to 500, then poll until complete and download results as CSV.
Step 1 — Create a job
/api/bulk-jobs{
"fileName": "leads_march_2024.csv",
"headers": ["email", "first_name", "company"],
"emailColumn": "email",
"statusColumn": "verification_status",
"totalRows": 1000
}Returns { id, status, total, createdAt }. Save the id — you need it for all subsequent calls.
Step 2 — Upload rows (max 500 per batch)
/api/verifications/bulk{
"jobId": "a1b2c3d4-...",
"emails": ["alice@company.com", "bob@agency.io"],
"rows": [
["alice@company.com", "Alice", "Company"],
["bob@agency.io", "Bob", "Agency"]
]
}Step 3 — Poll status
/api/bulk-jobs/:jobId/status{
"id": "a1b2c3d4-...",
"status": "processing",
"total": 1000,
"completed": 340,
"failed": 12,
"pending": 648,
"readyForDownload": false
}Poll until readyForDownload is true. Recommended interval: 2–5 seconds.
Step 4 — Download CSV
/api/bulk-jobs/:jobId/downloadReturns a CSV file with the original columns plus a status column appended (or overwritten if it already existed). The filename in Content-Disposition matches the job's original filename.
List Verifications
/api/verifications?limit=25&cursor=ISO_DATETIMEReturns cursor-paginated verification history for the authenticated user. Use nextCursor from the response as the cursor param in the next request to page through results.
| Param | Type | Default | Max |
|---|---|---|---|
| limit | integer | 25 | 100 |
| cursor | ISO datetime | — | — |
Reference
Result Statuses
| Status | Deliverable | Meaning |
|---|---|---|
| valid | true | MX records found, SMTP reachable, mailbox exists. Safe to send. |
| invalid | false | Mailbox does not exist, domain has no MX, or syntax fails. Do not send. |
| risky | false | Catch-all domain or uncertain SMTP signal. Send with caution or skip. |
| catch-all | false | Domain accepts all addresses. Cannot confirm individual mailbox exists. |
| disposable | false | Temporary or throwaway email service. High bounce risk. |
| unknown | false | Could not reach the mail server to confirm. Treat as risky. |
Error Codes
| HTTP Status | Meaning | How to handle |
|---|---|---|
| 400 | Bad request — invalid input | Check request body against the schema |
| 401 | Unauthorized — missing or invalid token | Re-authenticate and retry with a fresh token |
| 403 | Forbidden — insufficient permissions | Check account plan and email verification status |
| 404 | Resource not found | Verify the ID or slug exists |
| 409 | Conflict — duplicate resource | Slug or email already exists |
| 422 | Validation error | Fix the field highlighted in the error message |
| 429 | Rate limited | Respect Retry-After header and back off |
| 500 | Server error | Retry with exponential backoff; contact support if persistent |
| 503 | Service unavailable | Feature not configured on this instance (e.g. billing) |
Error response shape
{
"error": "Human-readable error message"
}Rate Limits
Monthly verification quotas are enforced per account. In addition, auth endpoints are rate-limited to prevent abuse.
| Plan | Monthly quota | Price |
|---|---|---|
| Free | 200 verifications | $0 |
| Starter | 2,000 verifications | $29/mo |
| Growth | 25,000 verifications | $149/mo |
| Scale | 100,000 verifications | $499/mo |
When you exceed your quota, the API returns a 403 error. Upgrade your plan in the billing dashboard to continue.
SDKs
Node.js
const API_BASE = "https://api.sovolink.com";
const API_KEY = process.env.SOVOLINK_API_KEY;
async function verifyEmail(email) {
const res = await fetch(`${API_BASE}/api/verifications`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${API_KEY}`,
},
body: JSON.stringify({ email }),
});
if (!res.ok) {
const err = await res.json();
throw new Error(err.error || `HTTP ${res.status}`);
}
return res.json();
}
// Usage
const result = await verifyEmail("user@example.com");
console.log(result.result?.status); // "valid" | "invalid" | "risky"Python
import os
import requests
API_BASE = "https://api.sovolink.com"
API_KEY = os.environ["SOVOLINK_API_KEY"]
def verify_email(email: str) -> dict:
response = requests.post(
f"{API_BASE}/api/verifications",
json={"email": email},
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
},
)
response.raise_for_status()
return response.json()
# Usage
result = verify_email("user@example.com")
print(result["result"]["status"]) # valid | invalid | riskyPHP
<?php
$apiBase = "https://api.sovolink.com";
$apiKey = getenv("SOVOLINK_API_KEY");
function verifyEmail(string $email): array {
global $apiBase, $apiKey;
$ch = curl_init("{$apiBase}/api/verifications");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"Authorization: Bearer {$apiKey}",
],
CURLOPT_POSTFIELDS => json_encode(["email" => $email]),
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status >= 400) {
throw new RuntimeException("API error {$status}: {$body}");
}
return json_decode($body, true);
}
// Usage
$result = verifyEmail("user@example.com");
echo $result["result"]["status"]; // valid | invalid | riskyMore
FAQ
What is the difference between valid and catch-all?+
Does Sovolink send emails during verification?+
What happens to my uploaded CSV data?+
Can I verify role-based addresses like info@ or support@?+
How do I handle rate limit errors in bulk jobs?+
Ready to start verifying?
200 free verifications every month. No credit card.