=VERIFY_EMAIL() і =FIND_EMAIL() у Google Sheets
- Для кого
- Ті, хто веде списки лідів у Google Sheets
- Проблема
- Експортувати аркуш в інструмент перевірки й вставляти результати назад — клопітно. Наївна користувацька функція знову викликає API і знову витрачає кредит щоразу, коли Sheets повторно виконує формулу.
- Рішення
- Дві користувацькі функції викликають API Cold Leads через UrlFetchApp, читають ключ із властивостей скрипту й зберігають кожну відповідь у кеші скрипту до шести годин, тож повторне виконання тієї самої формули не витрачає ще один кредит на ті самі вхідні дані.
- Що ви отримаєте
- Три клітинки з результатом у кожному рядку: статус, оцінка й причина від =VERIFY_EMAIL або адреса, метод і впевненість від =FIND_EMAIL.
Адреси, ідентифікатори та результати в прикладах ілюстративні. Домен example.com зарезервовано для документації, тож реальна перевірка цих адрес поверне invalid.
Приклади коду однакові для всіх мов, коментарі в них — англійською.
Налаштування
- У таблиці відкрийте Extensions → Apps Script і замініть вміст Code.gs скриптом нижче. Збережіть.
- Натисніть Project Settings, потім у розділі Script Properties натисніть Add script property. Property: COLDLEADS_API_KEY, значення (Value): ваш секретний ключ Cold Leads (sk_…). Натисніть Save script properties.
- Поверніться в аркуш і введіть =VERIFY_EMAIL(A2) у B2. Результат заповнить B2, C2 і D2 (статус, оцінка, причина), тож залиште ці дві клітинки праворуч порожніми. Протягніть формулу вниз.
- Для адрес, яких у вас ще немає, введіть =FIND_EMAIL(B2, C2, D2), де B2, C2 і D2 містять ім’я, прізвище та домен або сайт компанії. Функція заповнить адресу, метод і впевненість.
Скрипт
// Cold Leads custom functions for Google Sheets (Extensions → Apps Script, paste into Code.gs).
// Key: Project Settings → Script Properties → Add script property COLDLEADS_API_KEY = sk_...
const COLDLEADS_API = 'https://coldleads.app/api/v1';
const CACHE_SECONDS = 21600; // 6 hours, the longest CacheService keeps an entry
/**
* Verifies an e-mail address with Cold Leads. Fills three cells: status, score, last reason code.
* Costs 1 Cold Leads credit unless the same address was answered from this script's cache.
*
* @param {string} email The address, for example A2.
* @return Status, score and reason.
* @customfunction
*/
function VERIFY_EMAIL(email) {
const address = String(email || '').trim().toLowerCase();
if (!address) return '';
const r = cachedCall_('verify:' + address, '/verify', { email: address, timeout_ms: 20000 }, function (res) {
return res.reasons[res.reasons.length - 1] !== 'timeout'; // timeouts are worth checking again
});
return [[r.status, r.score, r.reasons[r.reasons.length - 1]]];
}
/**
* Finds the most likely e-mail address for a person at a company domain. Fills three cells:
* address, method ("verified" only when a mail server confirmed the mailbox, otherwise "pattern"), confidence.
* Costs 1 Cold Leads credit unless answered from this script's cache.
*
* @param {string} first First name, for example B2.
* @param {string} last Last name, for example C2.
* @param {string} domain Company domain or website, for example D2.
* @return Address, method and confidence.
* @customfunction
*/
function FIND_EMAIL(first, last, domain) {
const body = { first: String(first || '').trim(), last: String(last || '').trim(), domain: String(domain || '').trim().toLowerCase() };
if (!body.domain || !(body.first || body.last)) return '';
const key = 'find:' + [body.first, body.last, body.domain].join('|').toLowerCase();
const r = cachedCall_(key, '/find', body, function () { return true; });
return [[r.email || '', r.method, r.confidence]];
}
function cachedCall_(cacheKey, path, payload, cacheable) {
const cache = CacheService.getScriptCache();
// cache keys are limited to 250 characters, so store a hash of the key
const id = Utilities.base64Encode(Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_256, cacheKey, Utilities.Charset.UTF_8));
const hit = cache.get(id);
if (hit) return JSON.parse(hit);
const apiKey = PropertiesService.getScriptProperties().getProperty('COLDLEADS_API_KEY');
if (!apiKey) throw new Error('Add the script property COLDLEADS_API_KEY (Project Settings → Script Properties).');
const res = UrlFetchApp.fetch(COLDLEADS_API + path, {
method: 'post',
contentType: 'application/json',
headers: { 'x-api-key': apiKey },
payload: JSON.stringify(payload),
muteHttpExceptions: true,
});
const data = JSON.parse(res.getContentText() || '{}');
if (res.getResponseCode() !== 200) throw new Error('Cold Leads: ' + (data.error || 'HTTP ' + res.getResponseCode()));
if (cacheable(data)) cache.put(id, JSON.stringify(data), CACHE_SECONDS);
return data;
}Як це працює
- Sheets повторно виконує користувацьку функцію, коли ви редагуєте формулу, копіюєте її в іншу клітинку або змінюєте клітинку, на яку вона посилається. Без кешу кожне виконання — це новий виклик API і новий кредит.
- Кеш скрипту зберігає кожну відповідь до 6 годин і спільний для всіх, хто користується скриптом. Google може видаляти записи раніше, коли кеш заповнений, і зазначає, що Cache service працює в користувацьких функціях, але там «не надто корисний»; тут він лише запобігає подвійній оплаті за ті самі вхідні дані.
- Відповіді з причиною timeout не кешуються, тож наступне виконання перевірить адресу знову.
- Користувацька функція має завершитися за 30 секунд. =VERIFY_EMAIL просить у Cold Leads 20 секунд. =FIND_EMAIL не має ліміту часу й може перевищити 30 секунд на домені, де перевіряється кілька адрес-кандидатів; тоді клітинка показує помилку, а кредит уже витрачено.
- Кожен, хто може редагувати таблицю, може відкрити її проєкт Apps Script, а з ним і ключ. Надавайте доступ на редагування лише тим, кому можна користуватися ключем, і перевипустіть ключ у Cold Leads (Налаштування → API-ключі), якщо це зміниться.
Як читати результати
| Функція | Результат | Значення |
|---|---|---|
| VERIFY_EMAIL | valid, 97 або 80, ok | Поштовий сервер прийняв скриньку (80 для рольових адрес на кшталт info@). |
| VERIFY_EMAIL | valid, 75 або 65, smtp_unreachable | Домен приймає пошту, але саму скриньку не перевірено. |
| VERIFY_EMAIL | risky, catch_all або smtp_unknown | Сервер приймає будь-яку адресу або не дав однозначної відповіді. |
| VERIFY_EMAIL | risky, timeout | Перевірці забракло часу; наступного разу вона виконається знову. |
| VERIFY_EMAIL | invalid, mailbox_missing, no_mx або syntax | Не використовуйте цю адресу. |
| FIND_EMAIL | адреса, verified | Поштовий сервер підтвердив скриньку. |
| FIND_EMAIL | адреса, pattern | Припущення за поширеними форматами адрес; підтвердьте його перед використанням. |
| FIND_EMAIL | порожньо, none | Кандидата немає: домен не має поштового сервера або ім’я не вдалося використати. |
Ліміти та вартість
- 1 кредит Cold Leads за кожен виклик, на який не відповів кеш скрипту. Cold Leads бере оплату й за результати зі свого 30-денного кешу, тож кредити заощаджує саме кеш скрипту.
- Apps Script дозволяє 20 000 викликів URL Fetch на день для особистих акаунтів і 100 000 для Google Workspace, а також 30 секунд на один виклик користувацької функції. Заповнення формулою тисяч рядків одночасно може завершитися помилкою Script invoked too many times per second; заповнюйте меншими блоками або використовуйте масове завдання для великих списків.
- Cold Leads дозволяє 120 запитів на хвилину на ключ. API входить у тариф Business: $99 на місяць із 10 000 кредитів на місяць; додаткові пакети по 1 000 кредитів коштують $5.
Питання
Клітинка показує #ERROR!. Що сталося?
Наведіть курсор на клітинку, щоб прочитати повідомлення: відсутня властивість COLDLEADS_API_KEY, помилка Cold Leads на кшталт no_credits чи rate_limited або ліміт у 30 секунд для користувацьких функцій.
Чи витрачаються кредити, коли таблицю відкривають знову?
Sheets повторно виконує користувацьку функцію переважно тоді, коли змінюються її формула або вхідні клітинки. Якщо ж вона все-таки виконається знову з тими самими вхідними даними протягом шести годин, відповість кеш скрипту без звернення до Cold Leads.
Як перевірити тисячі адрес?
Використовуйте масове завдання замість користувацької функції: до 5 000 адрес за запит, з опитуванням до завершення. Сторінки про n8n і Python на цьому сайті показують повні версії.