Як працює виявлення accept-all і як читати результати Cold Leads
- Для кого
- Розробники й ops-команди, які вирішують, що робити з результатами risky
- Проблема
- Деякі поштові сервери приймають пошту для будь-якої адреси на своєму домені, тож перевірка скриньки не відрізнить справжню людину від вигаданого імені. Інші сервери дають лише тимчасові відповіді або взагалі недосяжні, а сама мітка valid може приховати те, що скриньку так і не перевірили.
- Рішення
- Розберіться, який SMTP-діалог веде сервіс перевірки і що означає кожна відповідь, а потім читайте статус, оцінку й коди причин, які повертає Cold Leads, зокрема у випадках, коли перевірка скриньки не виконувалася.
- Що ви отримаєте
- Таблиця рішень, що зіставляє кожен код причини Cold Leads із дією, і скрипт, який застосовує її до однієї адреси.
Адреси, ідентифікатори та результати в прикладах ілюстративні. Домен example.com зарезервовано для документації, тож реальна перевірка цих адрес поверне invalid.
Приклади коду однакові для всіх мов, коментарі в них — англійською.
SMTP-діалог, що стоїть за перевіркою скриньки
Сервіс перевірки знаходить MX-хости домену й відкриває SMTP-з’єднання з одним із них на порту 25. Він вітається із сервером, називає відправника й одержувача, читає відповідь щодо одержувача і прощається, перш ніж було б надіслано будь-який лист. Друга сесія запитує той самий сервер про адресу, яка не може існувати.
# Session 1: is the address accepted?
S: 220 mx1.example.com ESMTP
C: EHLO verifier.example.net
S: 250-mx1.example.com
S: 250 SIZE 52428800
C: MAIL FROM:<check@verifier.example.net>
S: 250 2.1.0 Sender OK
C: RCPT TO:<anna@example.com>
S: 250 2.1.5 Recipient OK <- accepted (a 550 here would mean: rejected)
C: QUIT <- no DATA command: nothing is delivered
S: 221 2.0.0 Bye
# Session 2: would the same server also accept an address that cannot exist?
S: 220 mx1.example.com ESMTP
C: EHLO verifier.example.net
S: 250 mx1.example.com
C: MAIL FROM:<check@verifier.example.net>
S: 250 2.1.0 Sender OK
C: RCPT TO:<zq7c41e09b2a6f5d@example.com>
S: 250 2.1.5 Recipient OK <- also accepted: the server accepts all addresses
C: QUIT
S: 221 2.0.0 ByeКоди відповідей і висновки, які з них можна зробити
Коди відповідей визначено в RFC 5321. Зміст несе перша цифра: 2 — успіх, 4 — тимчасова помилка, 5 — постійна помилка.
| Відповідь | Значення в RFC 5321 | Який висновок може зробити сервіс перевірки |
|---|---|---|
| 220 | Сервіс готовий (вітальний банер) | Сервер розмовляє SMTP; перевірку можна продовжувати. |
| 250, 251, 252 | Дію виконано, лист буде переслано або перевірити неможливо, але лист буде прийнято | Сервер зараз прийме пошту для цього одержувача. На accept-all сервері це нічого не каже про скриньку. |
| 421 | Сервіс недоступний, канал закривається | Висновку немає; сервер зайнятий або відмовляє сервісу перевірки. |
| 450, 451, 452 | Скринька недоступна, локальна помилка, недостатньо місця (тимчасово) | Висновку немає. Сервери з грейлістингом так відповідають невідомим відправникам і чекають повторної спроби через кілька хвилин. |
| 550 | Скринька недоступна, наприклад не знайдена, або відхилено політикою | Зазвичай скриньки не існує, але блокування сервера перевірки за політикою виглядає так само. |
| 551, 552, 553, 554 | Користувач не локальний, перевищено обсяг сховища, недопустиме ім’я скриньки, транзакція не вдалася | Остаточна відмова цьому одержувачеві в цій сесії. |
Перевірка accept-all, грейлістинг та інші обмеження
- Accept-all: після того як справжню адресу прийнято, сервіс перевірки запитує той самий сервер про випадкову локальну частину на тому самому домені. Якщо її теж прийнято, перше прийняття нічого не означає, і результат — risky з причиною catch_all.
- Грейлістинг (RFC 6647): сервер тимчасово відхиляє невідомі комбінації відправника й одержувача відповіддю 4xx і приймає повторну спробу пізніше. Один прохід бачить лише 4xx, а Cold Leads у межах однієї перевірки не чекає й не повторює спробу, тож результат — risky зі smtp_unknown.
- Пізні відмови: деякі сервери під час діалогу приймають кожного одержувача, а невідомих відхиляють лише після того, як прийняли лист, — відмовою (bounce). Перевірка цього не бачить.
- Блокування за політикою: сервер може відмовити самому хосту перевірки. Відмови на етапі привітання, EHLO або MAIL FROM Cold Leads трактує як smtp_unknown, а будь-яку відповідь 500–559 на RCPT TO — як mailbox_missing.
- Порт 25: для перевірки потрібне вихідне SMTP-з’єднання. Багато хмарних і serverless-платформ блокують вихідний порт 25, і тоді діалогу взагалі не відбувається.
Що повертає Cold Leads і коли перевірка не виконувалася
Перевірка виконується в такому порядку: синтаксис (помилка завершує перевірку), вбудований список із 40 одноразових поштових доменів, список із 25 рольових імен (info, sales, support, admin та інші) і пошук MX, який за відсутності MX-записів переходить до A-запису домену. SMTP-перевірка скриньки та accept-all виконується лише тоді, коли Cold Leads може відкрити SMTP-з’єднання з одним із перших двох MX-хостів. Масив reasons завжди показує, який у вас випадок.
{
"email": "anna@example.com",
"status": "valid",
"score": 75,
"reasons": ["smtp_unreachable"],
"mx": "mx1.example.com",
"catch_all": false,
"disposable": false,
"role": false,
"checked_at": "2026-09-30T08:15:02.114Z",
"cached": false
}| Остання причина | status | score | Значення |
|---|---|---|---|
| ok | valid | 97 (80 для рольових адрес) | Сервер прийняв адресу й не прийняв випадкову адресу. |
| catch_all | risky | 60 | Сервер прийняв і адресу, і випадкову адресу. |
| mailbox_missing | invalid | 2 | Сервер відхилив одержувача відповіддю від 500 до 559. |
| smtp_unknown | risky | 50 | Немає однозначної відповіді: тимчасова відповідь 4xx, відмова сервісу перевірки, MX-хост у приватній мережі або поєднання недосяжних і неоднозначних хостів. |
| smtp_unreachable | valid | 75 (65 для рольових адрес) | Жоден MX-хост не відповів на порту 25 на з’єднання з хоста перевірки. Домен приймає пошту, але скриньку й accept-all не перевірено. |
| timeout | risky | 50 або 60, якщо адресу прийнято, але перевірка accept-all не завершилася | Вичерпано необов’язковий ліміт часу timeout_ms. Результат не кешується. |
| no_mx | invalid | 0 | Домен не має ні MX-, ні A-запису. |
| syntax | invalid | 0 | Адреса не відповідає правилам синтаксису; більше нічого не перевіряється. |
- disposable і role, якщо вони стосуються адреси, стоять перед останньою причиною. Одноразовий домен робить статус risky з оцінкою 40 (ok), 30 (smtp_unreachable) або 20 (smtp_unknown).
- catch_all дорівнює true лише тоді, коли перевірка accept-all виконалася і випадкову адресу прийнято. Значення false інформативне лише тоді, коли остання причина — ok або mailbox_missing; після smtp_unreachable, smtp_unknown чи timeout accept-all не перевірявся. MCP-інструмент verify_email, хостований чи локальний, у цих випадках повертає catch_all як null; REST API повертає false.
- cached дорівнює true, коли результат узято з 30-денного кешу; тоді checked_at показує час початкової перевірки.
- Результати масових завдань містять лише останній код причини — у полі reason.
Таблиця рішень і скрипт, який її застосовує
| Остання причина | Рекомендована дія |
|---|---|
| ok | Надсилати. |
| smtp_unreachable | Надсилати обережно: невеликими порціями, одразу прибираючи жорсткі відмови. Скриньку не підтверджено. |
| catch_all | Переглянути. Залишати лише тоді, коли є інша ознака того, що людина існує, наприклад відповідь або відомий шаблон адрес у цій компанії. |
| smtp_unknown | Переглянути. Нова перевірка протягом 30 днів поверне результат із кешу й однаково коштуватиме кредит. |
| timeout | Перевірити ще раз із більшим timeout_ms, до 30 000; тайм-аути не кешуються. |
| mailbox_missing, no_mx, syntax | Відкинути. |
| є disposable | Відкинути для B2B-розсилок. |
// node decide.mjs anna@example.com (Node 18+, COLDLEADS_API_KEY=sk_... in the environment)
const email = process.argv[2];
const res = await fetch("https://coldleads.app/api/v1/verify", {
method: "POST",
headers: { "x-api-key": process.env.COLDLEADS_API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ email, timeout_ms: 10000 }),
});
const r = await res.json();
if (!res.ok) throw new Error(`HTTP ${res.status}: ${r.error}`);
const final = r.reasons[r.reasons.length - 1];
// catch_all only carries information when a mail server answered the probe
const smtpAnswered = ["ok", "catch_all", "mailbox_missing"].includes(final);
const ACTION = {
ok: "send",
smtp_unreachable: "send carefully: domain accepts mail, mailbox not checked",
catch_all: "review: the server accepts every address",
smtp_unknown: "review: no definite answer from the server",
timeout: "check again with a larger timeout_ms",
mailbox_missing: "drop",
no_mx: "drop",
syntax: "drop",
};
console.log({
email: r.email,
status: r.status,
score: r.score,
reasons: r.reasons,
mailbox_checked: smtpAnswered,
catch_all: smtpAnswered ? r.catch_all : "not tested",
cached: r.cached,
action: r.disposable ? "drop: disposable domain" : ACTION[final] ?? "review",
});Ліміти та вартість
- 1 кредит за перевірку, зокрема за помилки синтаксису й результати з 30-денного кешу.
- timeout_ms необов’язковий і приймає від 1 000 до 30 000 мілісекунд; на будь-яке інше значення API відповідає 400 bad_timeout.
- API входить у тариф Business: $99 на місяць із 10 000 кредитів на місяць; додаткові пакети по 1 000 кредитів коштують $5. 120 запитів на хвилину на ключ.
Питання
Чому адреса valid, якщо її скриньку не перевірено?
Бо те, що з хоста перевірки не вдалося достукатися до поштового сервера, не свідчить проти адреси. Статус відображає ті перевірки, які відбулися: синтаксис правильний, і домен приймає пошту. Нижча оцінка (75 замість 97) і причина smtp_unreachable показують, що саму скриньку не перевіряли.
Чи означає catch_all false, що домен не accept-all?
Лише якщо перевірка виконалася, — це видно з останньої причини ok або mailbox_missing. Після smtp_unreachable, smtp_unknown чи timeout accept-all взагалі не перевірявся.
Чи зміниться результат, якщо ще раз перевірити risky-адресу?
Протягом 30 днів нова перевірка повертає результат із кешу (cached: true) і однаково коштує 1 кредит. Результати з тайм-аутом не кешуються, тож їх варто перевірити ще раз із більшим timeout_ms.
Чи надсилає перевірка лист людині?
Ні. Діалог завершується командою QUIT одразу після кроку з одержувачем, до команди DATA, тож жоден лист не доставляється.