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

PropertyValue
Base URLhttps://api.sovolink.com
Content-Typeapplication/json
AuthorizationBearer <your-api-key>
API versionv1 (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.

POST/api/verifications

Request

{
  "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"
}
FieldTypeDescription
statusstringVerification job status: pending | processing | completed | failed
result.statusstringEmail verdict: valid | invalid | risky | catch-all | disposable
result.deliverablebooleanWhether the address is safe to send to
result.catchAllbooleanDomain accepts all addresses regardless of existence
result.confidencestringhigh | medium | low
result.confidenceScorenumber0–100 confidence score
result.mxHoststringPrimary 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

POST/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)

POST/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

GET/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

GET/api/bulk-jobs/:jobId/download

Returns 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

GET/api/verifications?limit=25&cursor=ISO_DATETIME

Returns 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.

ParamTypeDefaultMax
limitinteger25100
cursorISO datetime

Reference

Result Statuses

StatusDeliverableMeaning
validtrueMX records found, SMTP reachable, mailbox exists. Safe to send.
invalidfalseMailbox does not exist, domain has no MX, or syntax fails. Do not send.
riskyfalseCatch-all domain or uncertain SMTP signal. Send with caution or skip.
catch-allfalseDomain accepts all addresses. Cannot confirm individual mailbox exists.
disposablefalseTemporary or throwaway email service. High bounce risk.
unknownfalseCould not reach the mail server to confirm. Treat as risky.

Error Codes

HTTP StatusMeaningHow to handle
400Bad request — invalid inputCheck request body against the schema
401Unauthorized — missing or invalid tokenRe-authenticate and retry with a fresh token
403Forbidden — insufficient permissionsCheck account plan and email verification status
404Resource not foundVerify the ID or slug exists
409Conflict — duplicate resourceSlug or email already exists
422Validation errorFix the field highlighted in the error message
429Rate limitedRespect Retry-After header and back off
500Server errorRetry with exponential backoff; contact support if persistent
503Service unavailableFeature 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.

PlanMonthly quotaPrice
Free200 verifications$0
Starter2,000 verifications$29/mo
Growth25,000 verifications$149/mo
Scale100,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 | risky

PHP

<?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 | risky

More

FAQ

What is the difference between valid and catch-all?+
A valid email confirmed the mailbox exists via SMTP. A catch-all domain accepts any address regardless of whether the mailbox exists — Sovolink labels these risky because you cannot confirm delivery.
Does Sovolink send emails during verification?+
No. The SMTP handshake opens a connection to the mail server and performs a RCPT TO check, but no email is ever sent or delivered.
What happens to my uploaded CSV data?+
Uploaded files are stored for processing and can be deleted from the dashboard after download. Email addresses are stored hashed. We do not enrich or resell your data.
Can I verify role-based addresses like info@ or support@?+
Yes. They appear in results like any address. Role addresses often belong to distribution lists and may be catch-all. The result will reflect what the SMTP handshake returned.
How do I handle rate limit errors in bulk jobs?+
Bulk jobs are processed by the Sovolink worker asynchronously — you do not need to manage SMTP rate limits yourself. If the API returns 429, wait and retry the upload batch after the Retry-After window.

Ready to start verifying?

200 free verifications every month. No credit card.

Create free account