Sigilpost verify APItwo endpoints, same verdict shape.
Paste a curl, get a yes or no. The single endpoint is anonymous for visitors (no signup, three free checks per session). The bulk endpoint expects a signed-in caller with an API key.
Three bands. Pick one per address.
Every check lands in one of the three bands below. The single endpoint and the bulk endpoint use the same shape, so you can mix and match.
- deliverable
Send — the address should accept mail. Includes real addresses that look fine on the surface.
- risky
Be careful — couldn’t fully confirm. Often a server that accepts anything it gets.
- undeliverable
Skip — the address is bad, blocked, or a throwaway inbox you don’t want to hit.
/v1/verifyOne email at a time. Anonymous calls work — signed-in callers get their checks logged against their account.
| Field | Type | Required | Example |
|---|---|---|---|
| string | Yes | jordan@stripe.com |
| Field | Type | Meaning |
|---|---|---|
| string | The email you sent — echoed back. | |
| status | 'valid' | 'risky' | 'invalid' | The verdict band above. |
| score | integer (0–100) | How confident we are — higher is safer to send. |
| is_disposable | boolean | True for throwaway inbox domains (mailinator, guerrillamail, …). |
| mx_found | boolean | True when we located a mail server that should accept mail for the address. |
Authorization: Bearer $SIGILPOST_KEY — log this caller against your account. The endpoint still works without it.curl -X POST https://api.sigilpost.dev/v1/verify \
-H "Content-Type: application/json" \
-d '{"email":"jordan@stripe.com"}'// Node 18+ — built-in fetch, no SDK needed.
const res = await fetch("https://api.sigilpost.dev/v1/verify", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email: "jordan@stripe.com" }),
});
const verdict = await res.json();
console.log(verdict);import json
import urllib.request
req = urllib.request.Request(
"https://api.sigilpost.dev/v1/verify",
method="POST",
headers={"Content-Type": "application/json"},
data=json.dumps({"email": "jordan@stripe.com"}).encode(),
)
with urllib.request.urlopen(req) as resp:
verdict = json.loads(resp.read())
print(verdict)| Code | When | Body |
|---|---|---|
| 200 | Verdict returned. | VerifyOutput (above) |
| 400 | Body failed validation. | { "errors": { "email": "Email is too short" } } |
| 500 | Internal server error. | { "error": "Internal Server Error" } |
/v1/verify/bulkOne to fifty emails in a single call. The response carries a per-row verdict array plus a four-bucket summary so dashboards have something to render without a second round trip.
401 Unauthorized. Sign in once, mint a key, then add it as Authorization: Bearer ….["jordan@stripe.com", "ceo@some-startup.io"]webhook_url and webhook_secret must come together or not at all.{
"emails": [
"jordan@stripe.com",
"ceo@some-startup.io",
"throwaway@mailinator.com"
],
"webhook_url": "https://your-app.com/hooks/sigilpost",
"webhook_secret": "at-least-8-chars-long"
}| Field | Type | Meaning |
|---|---|---|
| results | array (1–50 rows) | Same per-row shape as the single endpoint. |
| summary.deliverable | integer | Count where status = valid. |
| summary.risky | integer | Count where status = risky. |
| summary.undeliverable | integer | Count where status = invalid AND not disposable. |
| summary.disposable | integer | Count where is_disposable = true. |
| webhook | object | omitted | Present when you sent a webhook_url. Reports batch_id + delivery status. |
Summary mapping — every per-row verdict lands in exactly one bucket. The row counter adds up to emails.length.
curl -X POST https://api.sigilpost.dev/v1/verify/bulk \
-H "Authorization: Bearer $SIGILPOST_KEY" \
-H "Content-Type: application/json" \
-d '{
"emails": [
"jordan@stripe.com",
"ceo@some-startup.io",
"throwaway@mailinator.com"
]
}'// Node 18+ — built-in fetch, no SDK needed.
const res = await fetch("https://api.sigilpost.dev/v1/verify/bulk", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${process.env.SIGILPOST_KEY}`,
},
body: JSON.stringify({
emails: [
"jordan@stripe.com",
"ceo@some-startup.io",
"throwaway@mailinator.com",
],
}),
});
const batch = await res.json();
console.log(batch.summary);import json
import os
import urllib.request
req = urllib.request.Request(
"https://api.sigilpost.dev/v1/verify/bulk",
method="POST",
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {os.environ['SIGILPOST_KEY']}",
},
data=json.dumps({
"emails": [
"jordan@stripe.com",
"ceo@some-startup.io",
"throwaway@mailinator.com",
],
}).encode(),
)
with urllib.request.urlopen(req) as resp:
batch = json.loads(resp.read())
print(batch["summary"])| Code | When | Body |
|---|---|---|
| 200 | Batch verdicts returned. | VerifyBulkResponse (above) |
| 400 | Body failed validation (empty list, > 50 emails, bad URL, …). | { "errors": { ... } } |
| 401 | No signed-in session. | { "error": "Unauthorized" } |
| 500 | Internal server error. | { "error": "Internal Server Error" } |
Webhook deliveries signed with X-Sigilpost-Signature over the raw body in a stable key order — validate the header server-side before trusting the payload.
How many checks per month?
Every plan includes both endpoints. Higher tiers raise the cap and unlock bulk + webhooks for every caller.
| Plan | Checks / mo | What you get |
|---|---|---|
| Starter | 5,000 | For side projects. One key, one mailbox at a time. |
| Growth | 25,000 | For small teams. A few keys, more checks, EU servers optional. |
| Scale | 100,000 | For senders who live in the API. Higher caps, dedicated support. |
Tried the curl, hit 401 on bulk?
Email us and we'll set you up with a key, walkthrough, and credits to seed your first batch.