Working recipes for developers and automation builders. Each post has a concrete config, workflow or script for the Cold Leads API and states the limits and credit costs that apply. The API needs a secret key from the Business plan.
Addresses, IDs and results in the examples are illustrative. example.com is reserved for documentation, so a real check of these addresses returns invalid.
Developers who use AI assistants in Cursor, Claude Desktop or Windsurf7 min read
Copying addresses between an assistant, a CRM and a verification tool is slow and error-prone, and an assistant asked for contacts will happily make some up.
Connect the Cold Leads MCP server once. An assistant can work with contacts already in your workspace, import structured rows from a user-provided file, read synced conversations, maintain templates and campaign drafts, and set up website lead forms. Sending remains behind explicit user confirmation and Cold Leads' consent, unsubscribe, DNC, content and account safeguards.
What you get: An assistant that works with your own contact data, inbox and outreach drafts through MCP or the Node SDK, while keeping each website enquiry and outreach consent status distinct.
Calling a verification API once per row is slow and runs into rate limits, while a bulk API needs a job, a polling loop and a reliable way to map results back to the right rows.
One workflow takes up to 5,000 rows that have no result yet, sends their unique addresses as a single Cold Leads bulk job, polls the job every 5 seconds, fetches the results, routes each row by status and writes status, score, reason and a next step into the sheet. Run it again for the next 5,000.
What you get: Four filled columns per row (verify_status, verify_score, verify_reason, next_step) and a workflow you can run again until the whole sheet is done.
Addresses that were never checked go straight into a sending campaign. The invalid ones bounce, and bounces count against the mailboxes that send the campaign.
Put a Cold Leads check between the trigger and Instantly: one HTTP request per lead, a filter that lets only valid results through, and a second HTTP request that adds the lead to your campaign with the Instantly API v2.
What you get: A running Make scenario: new lead, Cold Leads verification, filter, lead added to the Instantly campaign. Leads that fail the filter stop there or go to a review sheet.
Cold Leads module, Body content
{
"email": "{{map the e-mail field of the trigger here}}",
"timeout_ms": 10000
}
Checking one address per request is slow and runs into the rate limit. A naive bulk loop can open more jobs than the account allows, pay twice after a crash, or quietly drop rows whose results never came back.
Normalise and de-duplicate the column, send bulk jobs of up to 5,000 addresses, keep at most five open, poll every job to the end, wait out rate limits, stop cleanly when credits run out, and save the job ids so that a second run resumes instead of paying again.
What you get: Your CSV with three new columns (verify_status, verify_score, verify_reason) and a small .jobs.json file that makes the run resumable.
verify_csv.py
"""Verify a large CSV with the Cold Leads bulk API (Python 3.9+, httpx).
pip install httpx
export COLDLEADS_API_KEY=sk_...
python verify_csv.py leads.csv leads_verified.csv --column email
Addresses are normalised and de-duplicated, sent in jobs of up to 5,000 with at most
5 jobs open at a time, and every job is polled until it is done. The output is the
input CSV plus verify_status, verify_score and verify_reason. Job ids are saved next
to the output file, so a second run resumes the same jobs instead of paying twice.
Verification code is full of vendor-specific parameters and status values. Swapping the endpoint without mapping them quietly changes which addresses a pipeline sends to.
Map every parameter and response field to its Cold Leads counterpart, keep what Cold Leads does not replace (people data, domain search) where it is, and use a small adapter so existing code keeps its shape.
What you get: A field-by-field mapping, a Python adapter that returns Hunter-style fields from Cold Leads, and a Cold Leads cost table for 1,000, 10,000 and 50,000 verifications a month.
hunter_adapter.py
# 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
An agent that needs e-mail verification should never hold a card or subscribe on its own. Sending the human off to sign up, pick a plan and copy a key breaks the task, and pasting keys into chats leaks them.
The agent calls one endpoint without a key and receives a Stripe Checkout link for its owner plus a secret claim token. The owner reviews the plan and decides. After payment, the agent collects the API key exactly once with the claim token, by polling or after a signed callback.
What you get: A working secret API key for the owner's new Business account, delivered to the agent once, and an e-mail to the owner with a link to open the Cold Leads web app.
Some mail servers accept mail for any address at their domain, so a mailbox check cannot tell a real person from a made-up name. Other servers answer only temporarily, or cannot be reached at all, and a bare valid label can hide the fact that the mailbox was never checked.
Understand the SMTP dialogue a verifier runs and what each reply means, then read the status, score and reason codes Cold Leads returns, including the cases in which the mailbox probe did not run.
What you get: A decision table that maps every Cold Leads reason code to an action, and a script that applies it to one address.
Two SMTP sessions (illustration)
# 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
In this recipe
The SMTP dialogue behind a mailbox check
Reply codes and what they allow you to conclude
The accept-all probe, greylisting and other limits
What Cold Leads returns, and when the probe did not run
Contacts arrive from forms, imports and manual entry with typos, dead domains or no address at all, and nobody checks them until a campaign bounces.
Check each contact when it is created: a custom code action in a HubSpot workflow, or a small webhook receiver for Pipedrive. A contact with an e-mail is verified; a contact without one but with a name and a company website gets a suggested address that is clearly marked as a guess.
What you get: Contact properties in HubSpot or person fields in Pipedrive that show the Cold Leads status, score and reason, or a suggested address with its method and confidence.
Custom code (Node.js)
// HubSpot workflow, Custom code action (Node.js)
// Secret: COLDLEADS_API_KEY (your Cold Leads secret key, sk_...)
// Properties to include in code: email, firstname, lastname, website
// Data outputs (add them in the action): verify_status, verify_reason, suggested_email, find_method (String);
// verify_score, find_confidence (Number); mailbox_checked (Boolean)
const axios = require("axios");
const coldleads = axios.create({
baseURL: "https://coldleads.app/api/v1",
headers: { "x-api-key": process.env.COLDLEADS_API_KEY },
In this recipe
How it fits together
HubSpot: a custom code action in a contact workflow
An agent asked for contact details will guess, and a guessed address looks exactly like a real one. Without a verification tool, neither the agent nor the person reading its output can tell the difference.
Give the agent three narrow tools backed by the Cold Leads API: search the user's own CRM, verify an address, and guess an address from a name and a domain with an honest method label. Errors come back as data, so the agent can react instead of crashing.
What you get: A shared API client, tool classes for CrewAI and for LangChain, and a minimal crew and agent that use them.
coldleads_tools.py
# coldleads_tools.py - shared by the CrewAI and LangChain examples (pip install httpx)
import json
import os
import httpx
VERIFY_DESC = (
"Verify one e-mail address with Cold Leads (costs 1 credit). Returns status valid, risky or invalid, "
"a score from 0 to 100 and reason codes. The last reason says what was checked: ok means a mail server "
"accepted the mailbox, smtp_unreachable means the mailbox itself was not checked, catch_all means the "
Exporting a sheet to a verification tool and pasting the results back is tedious. A naive custom function calls the API again, and spends a credit again, every time Sheets runs the formula again.
Two custom functions call the Cold Leads API with UrlFetchApp, read the key from the script properties and keep each answer in the script cache for up to six hours, so running the same formula again does not spend another credit for the same input.
What you get: Three result cells per row: status, score and reason from =VERIFY_EMAIL, or address, method and confidence from =FIND_EMAIL.
Code.gs
// 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.