The Sieve API

Three JSON endpoints: validate one address, validate up to 100 in a batch, or get a send/caution/suppress decision that also weighs your own sending domain. Every live verdict ships with its receipt: the MX host that answered and the SMTP reply code. AI agents get the same verdicts over an official MCP server.

Not a developer? You never need this page: the app cleans lists with no code at all. This reference is for teams wiring Sieve into their own software.

https://sievemails.com/apiBearer auth120 req/min per keyBatch ≤ 100, synchronous

Authentication

Create a key under API Access in the app. API access comes with Pro or any credit pack. Send it on every request as Authorization: Bearer sv_… (an x-api-key header also works). Keys are shown once at creation; treat them like passwords.

Each key is limited to 120 requests per minute. Past the limit you get 429 with a Retry-After header.

POST/v1/validate

Validate a single address through the full 8-layer pipeline. Consumes one live verification only when the mailbox layer actually runs.

curl
curl -X POST https://sievemails.com/api/v1/validate \
  -H "Authorization: Bearer sv_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"email":"[email protected]"}'
fetch (Node 18+)
const res = await fetch("https://sievemails.com/api/v1/validate", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SIEVE_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ email: "[email protected]" }),
});
const result = await res.json();
if (result.category !== "clean") suppress(result.email);
200 response
{
  "email": "[email protected]",
  "normalized": "[email protected]",
  "category": "clean",
  "status": "valid",
  "statusLabel": "Valid mailbox",
  "score": 98,
  "reason": "Mailbox exists and accepts mail",
  "typoSuggestion": null,
  "suggestedDomain": null,
  "mode": "full",
  "checks": [
    { "layer": "syntax", "label": "Syntax", "status": "pass", "detail": "Well-formed address" },
    { "layer": "typo", "label": "Typo detection", "status": "pass", "detail": "No common typo detected" },
    { "layer": "disposable", "label": "Disposable check", "status": "pass", "detail": "Not a disposable domain" },
    { "layer": "role", "label": "Role-based check", "status": "pass", "detail": "Personal mailbox" },
    { "layer": "mx", "label": "Mail server (MX)", "status": "pass", "detail": "Accepts mail via mx1.example-corp.com" },
    { "layer": "auth", "label": "Domain authentication", "status": "pass", "detail": "SPF present, DMARC present" },
    { "layer": "catch_all", "label": "Catch-all probe", "status": "pass", "detail": "Not a catch-all domain" },
    { "layer": "smtp", "label": "Mailbox probe (SMTP)", "status": "pass", "detail": "Accepted (SMTP 250)" }
  ],
  "evidence": {
    "mxHost": "mx1.example-corp.com",
    "smtpCode": 250,
    "catchAll": false,
    "latencyMs": 412,
    "probedAt": "2026-06-10T08:21:04.512Z",
    "confidence": 98,
    "recommendation": "send",
    "intelObservations": 14
  },
  "elapsedMs": 731
}
POST/v1/validate/batch

Validate 1 to 100 addresses in one synchronous call. The response is a JSON array of the same result objects, in input order.

curl
curl -X POST https://sievemails.com/api/v1/validate/batch \
  -H "Authorization: Bearer sv_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"emails":["[email protected]","[email protected]"]}'

Probing runs at deliberately modest concurrency to stay gentle on receiving mail servers, so a full batch of 100 can take a couple of minutes when many domains need a live probe. One malformed address never fails the batch. Its slot comes back as category: "rejected" with status: "error". For lists beyond 100, use bulk jobs in the app: CSV upload, progress tracking, export, and a webhook when the job completes.

POST/v1/decision

The send-readiness gate: validates one recipient, optionally grades YOUR sending domain (senderDomain), and fuses both into a single send / caution / suppress decision. Put it in front of every send.

curl
curl -X POST https://sievemails.com/api/v1/decision \
  -H "Authorization: Bearer sv_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"email":"[email protected]","senderDomain":"yourstartup.com"}'
200 response
{
  "email": "[email protected]",
  "decision": "caution",
  "confidence": 98,
  "status": "valid",
  "reasons": [
    "Mailbox exists and accepts mail",
    "Sending domain has deliverability gaps (grade C)."
  ],
  "senderDomain": "yourstartup.com",
  "senderGrade": "C"
}

A clean recipient is downgraded to caution or suppress when your own domain's SPF/DKIM/DMARC posture would land the message in spam anyway. The reasons array says why.

MCP for AI agents

Sieve ships an official Model Context Protocol server, so coding assistants and autonomous outreach agents can call the same verdicts the REST API serves: validate an address, gate a send, grade a domain, read domain intel. It speaks stateless Streamable HTTP at POST https://sievemails.com/api/mcp and uses the same API keys, the same metering (a live verification is charged only when a probe actually runs), and the same per-key rate limit. There is no anonymous access, and Gmail, Yahoo, Outlook and iCloud are never SMTP-probed: verdicts there are DNS-layer and say so.

Claude Code
claude mcp add --transport http sieve https://sievemails.com/api/mcp \
  --header "Authorization: Bearer sv_your_api_key"
Claude Desktop (claude_desktop_config.json)
{
  "mcpServers": {
    "sieve": {
      "command": "npx",
      "args": [
        "-y", "mcp-remote", "https://sievemails.com/api/mcp",
        "--header", "Authorization: Bearer sv_your_api_key"
      ]
    }
  }
}
Cursor (.cursor/mcp.json)
{
  "mcpServers": {
    "sieve": {
      "url": "https://sievemails.com/api/mcp",
      "headers": {
        "Authorization": "Bearer sv_your_api_key"
      }
    }
  }
}
validate_emailValidate one address through the full pipeline. Returns status, score, category and the verdict receipt: MX host, SMTP reply code, catch-all confidence.
pre_send_decisionUp to 100 recipients in, one verdict out: go, caution or stop, with the projected bounce band, a recommended first send size and the factors behind the call.
grade_domainGrade a domain's sending readiness from public DNS: MX, SPF, DKIM, DMARC and blacklists, scored 0 to 100. Costs no verification units.
domain_intelRead Sieve's accumulated domain-level intel: MX host, catch-all determination with its observation counts, SPF/DMARC, last grade, freshness. Read-only.

Form Shield

A drop-in guard for your signup forms. Create a public site key under API Access in the app, add two lines of HTML, and every email field gets typo rescue plus disposable and undeliverable warnings, inline and accessible. It runs the same DNS-layer checks as the embeddable widget but is scoped to your site key, so usage rolls up to your account. It never opens an SMTP connection and is free.

index.html
<script src="https://sievemails.com/api/shield/embed.js"
  data-sieve-shield-key="shld_your_site_key" defer></script>

<input type="email" name="email" data-sieve-shield />
AttributePut it onWhat it does
data-sieve-shield-keyscript tagYour public site key (shld_...). Required. Safe to put in page source.
data-sieve-shieldinput or formMark an email input to protect it, or a form to protect every email field inside it.
data-sieve-shield-blockscript tag, form, or inputBlock native submit while a disposable or undeliverable address is in the field. Off by default (warn only).
data-endpointscript tagPoint the script at a different Sieve host. Defaults to the host the script was loaded from.

Under the hood the script POSTs to https://sievemails.com/api/shield/check with { key, email }. You can call it directly. The verdict is one of ok, risky or undeliverable; reasons is a short list of plain-language notes and suggestion is a corrected address when a typo is likely.

200 response
{
  "ok": true,
  "verdict": "undeliverable",
  "reasons": ["This is a disposable or throwaway email address."],
  "suggestion": null
}

The hint warns by default and never blocks your form. Add data-sieve-shield-block to stop native submission on disposable or undeliverable addresses. Addresses are checked in memory and never stored. Per-site and per-visitor rate limits keep the endpoint fast; past a limit it returns 429 and the hint simply does not render, so signups are never interrupted.

Validation result fields

FieldTypeNotes
emailstringThe address exactly as you sent it.
normalizedstringTrimmed, lowercased form that was actually validated.
category"clean" | "risky" | "rejected"The three-bucket verdict most integrations key on.
statusstringGranular verdict: valid, likely_valid, valid_unverifiable, role_based, risky, soft_bounce, mailbox_full, typo_suspect, disposable, no_mx, hard_bounce, mailbox_disabled, syntax_invalid, missing.
statusLabelstringHuman-readable label, e.g. "Valid mailbox".
scorenumber0 to 100 confidence in deliverability.
reasonstring | nullOne-line explanation of the verdict.
typoSuggestionstring | nullCorrected address when a typo is suspected (gmial.com to gmail.com).
suggestedDomainstring | nullCorrected provider domain when a non-deliverable domain looks like a major provider (gmail.com). Null otherwise.
mode"full" | "dns_only"full = live SMTP probing ran. dns_only = the probe path was unavailable and only DNS layers ran. The response never pretends otherwise.
checks[]arrayOne entry per pipeline layer (syntax, typo, disposable, role, mx, auth, catch_all, smtp), each with status pass | fail | warn | skip and a detail string.
evidenceobjectThe Verdict Receipt: mxHost, smtpCode, catchAll, latencyMs, probedAt (ISO), confidence (0 to 100), recommendation (send | caution | suppress), intelObservations (prior probes of the domain that informed this verdict).
elapsedMsnumberWall-clock time for the validation (single-call endpoints).

Errors

Errors are JSON with an error message field.

400Invalid request body. The error field carries the validation message.
401Missing, invalid, or revoked API key.
403No API entitlement on the key’s account (code: "upgrade_required"). Needs Pro or a credit pack.
429Per-key rate limit exceeded. Honor the Retry-After header (seconds).

Webhooks

Register an endpoint under Webhooks in the app and Sieve POSTs four event types: job.completed, job.failed and job.updated (all carrying the same job object shown below), plus domain.regressed when a monitored domain's deliverability grade drops. The x-sieve-event header tells them apart, and every delivery carries two signature headers signed with your endpoint's secret (whsec_…, shown once at creation):

  • x-sieve-signature (legacy): hex HMAC-SHA256 of the raw body.
  • x-sieve-signature-v1 carries t=<unix>,v1=<hex>, where v1 is the hex HMAC-SHA256 of `${t}.${body}`. Prefer this one: the timestamp lets you reject replays.
job event payload
{
  "event": "job.completed",
  "sentAt": "2026-06-10T08:30:00.000Z",
  "job": {
    "id": "9f4c1c2e-…",
    "fileName": "leads-june.csv",
    "status": "completed",
    "total": 4980,
    "processed": 4980,
    "inputCount": 5000,
    "duplicatesRemoved": 15,
    "blanksRemoved": 5,
    "cleanCount": 4117,
    "riskyCount": 512,
    "rejectedCount": 351,
    "mode": "full",
    "createdAt": "2026-06-10T08:12:41.000Z",
    "completedAt": "2026-06-10T08:29:58.000Z"
  }
}
domain.regressed payload
{
  "event": "domain.regressed",
  "sentAt": "2026-06-10T09:00:00.000Z",
  "domain": "yourdomain.com",
  "domainId": "7b2a9d10-…",
  "kind": "regression",
  "severity": "critical",
  "previous": { "score": 97, "grade": "A" },
  "current": { "score": 72, "grade": "C" },
  "scoreDelta": -25,
  "listedZonesAdded": ["bl.spamcop.net"],
  "listedZonesRemoved": []
}
verify-signature.mjs
import { createHmac, timingSafeEqual } from "node:crypto";

// rawBody must be the exact bytes Sieve sent; capture it before any
// JSON middleware re-serializes the request.
export function verifySieveWebhook(headers, rawBody, secret) {
  const v1 = headers["x-sieve-signature-v1"]; // "t=1760000000,v1=<hex>"
  if (v1) {
    const parts = Object.fromEntries(v1.split(",").map((p) => p.split("=")));
    if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) {
      return false; // reject replays older than 5 minutes
    }
    const expected = createHmac("sha256", secret)
      .update(`${parts.t}.${rawBody}`)
      .digest("hex");
    return safeEqual(expected, parts.v1);
  }
  // Legacy header: hex HMAC-SHA256 of the raw body alone.
  const legacy = headers["x-sieve-signature"];
  const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
  return Boolean(legacy) && safeEqual(expected, legacy);
}

function safeEqual(a, b) {
  const ba = Buffer.from(a);
  const bb = Buffer.from(b);
  return ba.length === bb.length && timingSafeEqual(ba, bb);
}

Honest limits

Free-mail providers (Gmail, Yahoo, Outlook, iCloud) block mailbox probing for every vendor. Sieve never SMTP-probes them and returns valid_unverifiable instead of pretending the mailbox was confirmed.

Catch-all domains (Microsoft 365 included) come back risky with an evidence-based evidence.confidence score: never clean, never a binary "unknown".

If the live probe path is unavailable, responses say "mode": "dns_only" and you are never charged a live verification that didn't run. Check the field; don't assume.

Ready to make your first call?

Create an account, grab a key, and validate your first 100 addresses live, free.

Get an API key