Перенесення перевірки e-mail з Hunter або Apollo в Cold Leads
- Для кого
- Команди, які зараз перевіряють e-mail через Hunter або Apollo
- Проблема
- Код перевірки повний параметрів і значень статусу, специфічних для постачальника. Якщо замінити ендпоінт, не зіставивши їх, непомітно зміниться те, на які адреси надсилає ваш конвеєр.
- Рішення
- Зіставте кожен параметр і поле відповіді з відповідником у Cold Leads, залиште на місці те, чого Cold Leads не замінює (дані про людей, пошук за доменом), і скористайтеся невеликим адаптером, щоб наявний код зберіг свою форму.
- Що ви отримаєте
- Зіставлення поле за полем, Python-адаптер, який повертає поля в стилі Hunter із даних Cold Leads, і таблиця вартості Cold Leads для 1 000, 10 000 і 50 000 перевірок на місяць.
Адреси, ідентифікатори та результати в прикладах ілюстративні. Домен example.com зарезервовано для документації, тож реальна перевірка цих адрес поверне invalid.
Приклади коду однакові для всіх мов, коментарі в них — англійською.
Що переноситься, а що лишається
- Переноситься: перевірка окремих адрес (Hunter Email Verifier) — на POST /api/v1/verify, списки — на POST /api/v1/verify/bulk (до 5 000 адрес у завданні), а пошук імовірної адреси за іменем і доменом (Hunter Email Finder) — на POST /api/v1/find.
- Лишається: Domain Search від Hunter і вебджерела, на яких ґрунтуються його результати, а також People Search, People Enrichment і Bulk People Enrichment від Apollo. Cold Leads не має бази людей чи компаній; GET /api/v1/leads шукає лише серед контактів у вашій власній CRM Cold Leads.
- Публічний API Apollo не має окремого ендпоінта перевірки; email_status приходить разом із даними про людину. Якщо ви лишаєте Apollo як джерело даних, адреси, які він повертає, можна перевірити в Cold Leads перед надсиланням.
Заміна Email Verifier на POST /api/v1/verify
| Hunter | Cold Leads | Примітка |
|---|---|---|
| GET /v2/email-verifier?email=… | POST /api/v1/verify з JSON-тілом {email} | Cold Leads приймає POST із JSON-тілом. |
| Параметр запиту api_key, заголовок X-API-KEY або Authorization: Bearer | Заголовок x-api-key або Authorization: Bearer | Ключі в рядку запиту не приймаються. |
| Параметра часу немає; 202, поки перевірка ще триває | timeout_ms, від 1 000 до 30 000 | Cold Leads відповідає, коли перевірку завершено або ліміт часу вичерпано (тоді risky, причина timeout). |
| data.status valid | status valid з останньою причиною ok | valid зі smtp_unreachable означає, що скриньку не перевірено. |
| data.status invalid | status invalid, причина mailbox_missing, no_mx або syntax | |
| data.status accept_all | status risky, причина catch_all, catch_all true | |
| data.status disposable | disposable true (status risky або invalid, якщо домен не має MX) | Вбудований список із 40 одноразових поштових доменів. |
| data.status webmail | Відповідника немає | Адреси вебпошти перевіряються так само, як будь-які інші. |
| data.status unknown | status risky, причина smtp_unknown або timeout | |
| data.score | score (від 0 до 100) | Інша шкала; ухвалюйте рішення за причинами, а не за числом. |
| data.regexp | reasons містить syntax, якщо перевірку синтаксису не пройдено | |
| data.mx_records | mx: перший MX-хост або null, якщо домен не має ні MX-, ні A-запису (причина no_mx) | |
| data.smtp_server | smtp_unreachable, якщо жоден поштовий сервер не був досяжний через SMTP | ok, catch_all і mailbox_missing означають, що поштовий сервер відповів на перевірку одержувача. |
| data.smtp_check | остання причина ok або catch_all | Поштовий сервер прийняв адресу. |
| data.accept_all | catch_all | Має сенс лише тоді, коли поштовий сервер відповів (остання причина ok, catch_all або mailbox_missing). |
| data.gibberish, data.block, data.sources | Відповідника немає |
Заміна Email Finder на POST /api/v1/find
| Hunter | Cold Leads | Примітка |
|---|---|---|
| domain | domain | URL скорочується до хоста (https://www.example.com/about стає example.com). |
| company | Відповідника немає | Домен обов’язковий. |
| first_name, last_name | first, last | Діакритика прибирається, а інші нелатинські символи відкидаються, тож спершу транслітеруйте імена. |
| full_name | name | Ділиться за першим пробілом на first і last. |
| linkedin_handle, max_duration | Відповідника немає | |
| data.email | null, якщо не вдалося побудувати жодного кандидата, наприклад коли домен не має MX. | |
| data.score | confidence (від 0 до 100) | |
| data.verification.status | method | verified — лише коли поштовий сервер підтвердив скриньку; pattern — припущення за поширеними шаблонами адрес; none — коли нічого не знайдено. |
| data.accept_all | catch_all | |
| data.position, twitter, linkedin_url, phone_number, company, sources | Відповідника немає | |
| (немає) | candidates | Адреси, побудовані за поширеними шаблонами, кожна зі статусом своєї перевірки, якщо її перевіряли. |
Адаптер для прямої заміни
Якщо ваш код розгалужується за назвами полів Hunter, ці дві функції повертають ту саму структуру з даних Cold Leads, а також сиру відповідь у полі coldleads. Зіставлення навмисно консервативне: адреса, скриньку якої не перевірено (smtp_unreachable), стає unknown, а не valid.
# pip install httpx
# Drop-in helpers for code written against Hunter's email-verifier and email-finder.
# They call Cold Leads and return Hunter-style fields, plus the raw Cold Leads answer.
import os
import httpx
cold = httpx.Client(
base_url="https://coldleads.app/api/v1",
headers={"x-api-key": os.environ["COLDLEADS_API_KEY"]}, # the key goes in a header, never in the URL
timeout=40.0,
)
# a mail server answered the SMTP probe only when the last reason is one of these
PROBED = {"ok", "catch_all", "mailbox_missing"}
def email_verifier(email: str) -> dict:
r = cold.post("/verify", json={"email": email, "timeout_ms": 20000})
r.raise_for_status()
c = r.json()
last = c["reasons"][-1]
if c["disposable"] and last not in ("syntax", "no_mx"):
status = "disposable"
elif last == "ok":
status = "valid"
elif last == "catch_all":
status = "accept_all"
elif last in ("mailbox_missing", "no_mx", "syntax"):
status = "invalid"
else:
# smtp_unreachable (mailbox not checked), smtp_unknown or timeout: not verified
status = "unknown"
return {
"email": c["email"],
"status": status,
"score": c["score"],
"regexp": last != "syntax",
"disposable": c["disposable"],
"mx_records": c["mx"] is not None,
"smtp_check": last in ("ok", "catch_all"),
"accept_all": c["catch_all"] if last in PROBED else None, # None: not tested
"coldleads": c,
}
def email_finder(domain: str, first_name: str = "", last_name: str = "", full_name: str = "") -> dict:
body = {"domain": domain, "first": first_name, "last": last_name}
if full_name and not (first_name or last_name):
body = {"domain": domain, "name": full_name}
r = cold.post("/find", json=body)
r.raise_for_status()
c = r.json()
return {
"email": c["email"],
"score": c["confidence"],
"accept_all": c["catch_all"],
# Cold Leads says "verified" only when a mail server confirmed the mailbox; a pattern guess is not verified
"verification": {"status": "valid" if c["method"] == "verified" else None},
"coldleads": c,
}
if __name__ == "__main__":
print(email_verifier("anna@example.com"))Відмінності в поведінці, які варто врахувати
- Коди помилок: Cold Leads відповідає 429 у разі перевищення ліміту запитів (rate_limited, з Retry-After: 60) і коли відкрито забагато масових завдань (too_many_jobs), а 402 no_credits — коли закінчилися кредити. Hunter документує 403 для свого ліміту запитів і 429 для ліміту використання, тож обробку помилок доведеться переписати, а не просто перейменувати.
- Ліміт запитів: 120 запитів на хвилину на ключ. Для списків одне масове завдання замінює до 5 000 окремих викликів.
- Кожен виклик коштує 1 кредит, зокрема й для результатів із 30-денного кешу.
- SMTP-перевірка скриньки та accept-all виконується лише тоді, коли Cold Leads може відкрити SMTP-з’єднання з поштовим сервером одержувача. reasons кожного результату показують, чи вона відбулася: smtp_unreachable означає, що скриньку не перевірено.
Ліміти та вартість
| Перевірок на місяць | Кредити | Вартість Cold Leads на місяць |
|---|---|---|
| 1 000 | 1 000 | $99: тариф Business, який містить 10 000 кредитів |
| 10 000 | 10 000 | $99 |
| 50 000 | 50 000 | $299: $99 плюс 40 додаткових пакетів по 1 000 кредитів по $5 |
API є лише в тарифі Business, який можна оплачувати й щороку ($999). Виклики пошуку адреси списують ті самі кредити, 1 за виклик. Місячні кредити рахуються за календарний місяць (UTC) і використовуються раніше за куплені пакети. Ціни вказано в доларах США; може додаватися податок.
Питання
Чи є в Cold Leads аналог Domain Search від Hunter або пошуку людей в Apollo?
Ні. Cold Leads не збирає даних про людей. GET /api/v1/leads шукає лише серед контактів, які ви вже зберігаєте у своїй CRM Cold Leads.
Чи працюватимуть мої наявні пороги оцінки?
Ненадійно, бо шкали різні. Натомість ухвалюйте рішення за статусом і останнім кодом причини: ok — надсилати, catch_all і smtp_unknown — переглянути, mailbox_missing, no_mx і syntax — відкинути, а smtp_unreachable позначає адреси, скриньку яких не перевірено.
Чи можна перевірити експорт з Apollo в Cold Leads?
Так. Надішліть адреси масовими завданнями до 5 000 і опитуйте результати або скористайтеся Python-скриптом для CSV-файлів на цьому сайті. Кожна адреса коштує 1 кредит.
Чи дешевші результати з кешу?
Ні. Результат із 30-денного кешу позначається cached: true і коштує 1 кредит, як і будь-який інший виклик.