Systems Lab

Agent skill

cold-email-list

Takes target domains or an ICP and produces a send-ready CSV for a cold email sequencer.

activeNeeds a keyActs undeclared3,088 words

Filed under Outbound email.

From scrapercity/gtm-skills · 31 skill entries · 0 · pushed 2026-11-02

What it does when it runs

Takes target domains or an ICP and produces a send-ready CSV for a cold email sequencer. Finds the right people (Lead Database by domain or ICP filters, Website Finder for empty accounts), fills missing emails with Email Finder, dedupes, suppresses, validates every address, caps contacts per company, adds personalization columns, sizes the list to mailbox capacity, and formats headers for Instantly, Smartlead, Lemlist or EmailBison. Use when the user says "build a cold email list", "get this ready for Instantly", "turn these domains into a campaign", or "I need 1,000 verified leads to send next week".

Automated analysis of the skill and the 12 files bundled beside it. A skill’s own description is written to be selected by an agent, so it describes the job and not the dependencies.

Keys and connectors you must supply
  • SCRAPERCITY_API_KEY
Hosts it reaches
  • alexberman.com
  • app.scrapercity.com
  • aws.amazon.com
  • blitzmetrics.com
  • blog.google
  • datatracker.ietf.org
  • devrims.com
  • docs.emailbison.com
  • emt.gartnerweb.com
  • glockapps.com
  • hackernoon.com
  • help.instantly.ai
  • help.lemlist.com
  • helpcenter.smartlead.ai
  • hunter.io
  • knowledge.apollo.io
  • knowledge.workspace.google.com
  • leanb2bbook.com
  • learn.microsoft.com
  • m3aawg.org
  • publicsuffix.org
  • scrapercity.com
  • senders.yahooinc.com
  • sendersupport.olc.protection.outlook.com
  • sidehustlelab.com
  • substrate.office.com
  • support.google.com
  • support.microsoft.com
  • techcommunity.microsoft.com
  • u.yourdomain.com
  • uribl.com
  • www.apple.com
  • www.bls.gov
  • www.bmj.com
  • www.crexi.com
  • www.entrepreneur.com
  • www.linkedin.com
  • www.m3aawg.org
  • www.rfc-editor.org
  • www.singlegrain.com
Tool permissions it declares
No allowed-tools in the frontmatter. It does act, so it runs under whatever permissions your session already grants.
Actions present in the files
writes files

Ask about cold-email-list

Opens your assistant with this page's recorded context. Check the linked sources before relying on it.

Is this safe to install?ClaudeChatGPT
Adapt it to my stackClaudeChatGPT
What else do I need for it to workClaudeChatGPT
Rather ask a human? Talk to Cheetah
git clone --depth 1 --filter=blob:none --sparse https://github.com/scrapercity/gtm-skills.git /tmp/gtm-skills
git -C /tmp/gtm-skills sparse-checkout set "skills/list-building/cold-email-list"
mkdir -p ~/.claude/skills/cold-email-list
cp -R "/tmp/gtm-skills/skills/list-building/cold-email-list/." ~/.claude/skills/cold-email-list/

Picked up without a restart. A project skill of the same name is shadowed by your personal one. For one repository only, swap ~/.claude/skills for .claude/skills. Claude Code docs ↗

Or take the whole library

This repo ships a .claude-plugin manifest, so Claude Code can install all 31 skills at once. Plugin skills are invoked as /<plugin>:<skill>, so they never collide with your own.

/plugin marketplace add scrapercity/gtm-skills
/plugin

The folder is the same in every client that implements the format — 46 of them — so if yours is not above, only the destination changes.

Before you install: this skill will not complete its job on a bare agent. It needs SCRAPERCITY_API_KEY, which you have to obtain separately.

Reproduced in full from scrapercity/gtm-skills/blob/46d446143690c8048b17817aecf36990ea6880e5/skills/list-building/cold-email-list/SKILL.md, which is licensed MIT (skill frontmatter). 3,088 words, 26 headings.

Cold Email List

Overview

This skill delivers the file a sequencer imports on launch day: one row per prospect, every email valid and validated within four weeks of its send date, duplicates and suppressed contacts removed, no more people per company than the user agreed, personalization columns filled or deliberately blank, headers that the chosen platform accepts, and a row count that the user's mailboxes can send on schedule. A separate catch-all file and a short report come with it.

The mistake it prevents is the half-finished list: a database export pasted into Instantly with unvalidated emails, five people from one company in the same campaign, a {{first_name}} column that is blank on some rows, and 4,000 prospects for three mailboxes. Each of those costs bounces or a week. Alex Berman splits cold email results into 30% targeting, 30% offer and 50% deliverability; this skill covers the targeting share and protects the deliverability share before the first send.

When to Use

  • "Build me a cold email list for this campaign."
  • "Here are 400 domains, get the right people and make it ready for Smartlead."
  • "I need 1,500 verified CFOs at construction companies for Instantly."
  • "Take this ICP list and make it send-ready."
  • A campaign plan from cold-email-strategy exists and needs its file.

Use a different skill when:

The user wantsUse
Only the ICP definition and a validated contact pull, no sequencer prepicp-list-builder
Buying committees at named accounts, with mobilesdecision-maker-finder
An existing list cleaned, no new sourcingemail-list-cleaner
Local businesses by category and citylocal-business-list, then this skill for import prep
The campaign plan, offer or sequencecold-email-strategy
Sequencer setup, warmup or sending limitssending-platforms, email-deliverability

Prerequisites

InputWhyDefault if unknown
Domains, an ICP, or a people-search URLPicks the sourcing pathAsk
Offer and segment in one line eachDecides titles and personalization columnsAsk; see cold-email-strategy
Sequencer (Instantly, Smartlead, Lemlist, EmailBison)Header rules differInstantly
Mailboxes, sends per mailbox per day, sequence stepsSizes the listSends per mailbox: 30 (Instantly's recommendation for campaign emails); mailboxes and steps: ask
Launch dateRows validated more than four weeks before their send date get re-validatedAsk
Suppression: customers, open deals, past sends, unsubscribes, bouncesMust run before validationAsk for CRM and sequencer exports
Re-contact windowPeople contacted inside it are suppressedAsk; Berman's cycle model restarts every 30 to 60 days
Recipient countriesTime zone of the send windowUnited States

Authoritative Foundations

SourceWhat it contributes
Alex Berman (alexberman.com, Lean B2B interview)30/30/50 targeting, offer, deliverability. Build the list after the offer. Test about 100 emails per segment before scaling. Verify every email and keep bounces under 2%. Under 50 emails per inbox per day. Do not email several people at one company at once.
Google, Yahoo, Microsoft sender requirements (Feb 2024, May 2025)Remove invalid recipients, keep complaints under 0.3% (Google targets under 0.1%), authenticate every sending domain. The list is the input those numbers depend on.
Hunter, Email verification guide (4 Nov 2025)Verify shortly before the campaign; in Hunter's test, over 2% of a list went invalid within four weeks and 5.5% within eight. Sets the four-week re-validation rule against Berman's 2% bounce line.
Instantly, Smartlead and Lemlist docsEmail is the required column; Instantly asks for unique UTF-8 headers of 20 characters or fewer, caps an upload at 50 variables, recommends 30 campaign emails per account per day, and pauses a campaign at 5% bounces once it has sent 200; Smartlead allows up to 40 custom variables per lead; Lemlist auto-maps firstname, lastname, companyName and similar names.
M3AAWG Sender Best Common PracticesSuppress repeat bouncers and treat lists not mailed for a year as dirty.
references/list-quality-standard.md and references/deliverability-primer.mdColumns, dedupe keys, validation buckets.

Details: references/framework-notes.md.

Workflow

1. Write the campaign brief

One table: offer, segment, sequencer, mailboxes, sends per mailbox per day, steps, launch date, personalization variables the copy will use, countries. Every later number comes from it.

2. Size the list from capacity

daily_sends     = mailboxes x sends_per_mailbox_per_day          (Instantly recommends 30; Berman: under 50)
total_sends     = send_ready_rows x steps
sending_days    = ceil(total_sends / daily_sends)
rows_for_N_days = daily_sends x N / steps
pull_rows       = target_send_ready / yield, rounded up to the next 100

yield is send-ready rows divided by rows pulled on the user's last list for this segment; with no history, pull a first wave and measure it. Six mailboxes at 30 a day send 180 emails a day; with 3 steps that is 60 new prospects a day, or about 1,200 for a 20-day campaign. A list much larger than the campaign window ages past four weeks before it is sent; build it in waves.

3. Pick the sourcing path and count

InputPathCount call
Target domainsquery_lead_database with companyDomain batches (halve on a 503) plus the role title, seniority, notTitlelimit: 1 per batch
An ICPquery_lead_database with title, industry or keywords, size, location, hasEmail: truelimit: 1, read pagination.total
A people-search URL (Apollo.io and similar)query_lead_database with url; read search_translationlimit: 1
The URL needs filters the database cannot applyscrape_apollo with url and count (500 to 50,000), webhook registeredThe start response may report lead_database.matches
Local businessesBuild with local-business-list, then continue at step 6Per that skill

Show the count, the planned pull and the cost estimate. Call check_wallet. Wait for a go-ahead before any paid step.

4. Pull with cursor paging

Page with after: "0", limit: 100, then pagination.next_after until null or the planned rows. Add excludeDelivered: true when the account has pulled this ICP before and only new people are wanted. For domain lists, rank people per domain and keep the best by role, up to the contacts per company the user agreed (role ranking in decision-maker-finder).

5. Fill gaps

GapToolPrice
A target domain returned zero peoplescrape_website_finder, one jobTitle per run across those domains$0.0499 per contact found
A person with no emailfind_emails with first_name, last_name, domain (company_name only when no domain resolves; full_name when the name will not split)$0.05 per email found, free if not found
Still no emailDrop from the email file; keep in a gaps file$0

Leave autoValidateEmails off on Website Finder; validation runs once in step 7. Record a miss in the gaps file; no address is built from a naming pattern. Order follows price per usable result: a database email costs $0.00169 / valid share to validate into a usable address (about $0.0021 at the example's 80% valid share) against $0.05 from Email Finder and ($0.0499 + $0.00169) / valid share from Website Finder plus validation.

6. Normalize, dedupe, suppress

Normalize per references/list-quality-standard.md section 3. Dedupe on lowercased email, then LinkedIn URL, then first name + last name + domain. Suppress in this order: unsubscribes, bounces, complaints, current customers, open opportunities, competitors, anyone in another active campaign, anyone contacted inside the user's re-contact window. Count every drop by reason. Drop rows with no first name from a personalized campaign or move them to a non-personalized variant.

7. Validate

Send every email that did not come from Email Finder to validate_emails ($0.00169 each). Route: valid to the send file; valid_catch_all to a catch-all file for a separate campaign at a daily volume the user sets, paused if its bounces pass 2% (Berman), highest-evidence rows first (high email_quality, an address format that matches other known addresses at the domain, no security gateway); unknown held for one retry on a date in the report; invalid and disposable removed and added to the blocklist.

8. Cap and order per company

Keep no more contacts per company_domain than the user agreed, highest buying role first. Write company_contact_order (1, 2, 3...) so the sequencer can stagger them: contact 2 starts only after contact 1's sequence ends without a reply, per Berman's one-person-at-a-time guidance. Instantly and Smartlead both run campaigns per list, so put later contacts in a second wave file.

9. Personalization columns

Add only columns the copy uses, and only facts with a source: industry, city, employee_band, case_study (chosen per segment), trigger (account level, from account-researcher or a signal list). Smartlead's import guide says a blank cell means the value does not appear in the email, which leaves "Hi ," in the inbox; give every variable a fallback in the copy or drop rows where it is blank. Never generate facts about a prospect to fill a column.

10. Format for the sequencer

Map columns to the platform's headers with references/sequencer-import-specs.md: UTF-8 CSV, header row first, unique headers, Instantly headers of 20 characters or fewer starting with a capital letter, no more than 50 variables (Instantly) or 40 custom variables (Smartlead). One file per campaign. Turn on the platform's duplicate check across campaigns and import the blocklist first.

11. Report and launch checklist

Before export, read a random sample (size set by the user) against the brief: title in the agreed family, company in the ICP band, person still at the company on their profile or the team page, domain opens the company's own site. Misses that repeat on one check mean the filter or mapping is wrong: fix the query and re-pull instead of patching rows. Record the result as one launch-checklist line ("19 of 20 title fit, 20 of 20 company fit, 18 of 20 still there").

Fill templates/output-template.md, run the launch checklist in the reference, and run scripts/check-output.py.

ScraperCity Implementation

StepToolPrice (catalog)
Balance and plancheck_walletFree
Count and pullquery_lead_databaseIncluded on Growth and Scale; 100,000 new leads a day
Empty target domainsscrape_website_finder$0.0499 per contact found
Names without emailsfind_emails$0.05 per email found, free if not found
Validationvalidate_emails$0.00169 per email
URL-based people search the database cannot coverscrape_apollo$0.0039 per lead; usually 15 to 30 minutes, use a webhook
Runscheck_run_status, download_results, list_runsFree

Calls

ICP count:

{"title": "CFO, Chief Financial Officer, Controller, VP Finance, Director of Finance",
 "notTitle": "assistant, intern, former, retired, advisor, board, accounts payable",
 "industry": ["construction"], "minEmployees": 50, "maxEmployees": 500,
 "country": ["United States"], "hasEmail": true, "limit": 1}

Pull: the same body with "limit": 100, "after": "0", then each pagination.next_after. Confirm the industry label with the count; if it returns 0, use keywords instead.

Domain batch:

{"companyDomain": "ridgelinebuilders.example, coppercreekcontracting.example",
 "title": "CFO, Chief Financial Officer, Controller, VP Finance", "notTitle": "assistant, intern, former",
 "limit": 100, "after": "0"}

Website Finder for empty domains: {"domains": ["..."], "jobTitle": "Controller", "autoValidateEmails": false}. Email Finder: {"contacts": [{"first_name": "Lena", "last_name": "Ortiz", "domain": "graniteforge.example"}]}. Validation: {"emails": [...]} for a list that fits in one tool argument; for larger files the CLI's email-validate command with a one-address-per-line file, or POST /api/v1/scrape/email-validator.

Worked cost estimate

1,200 send-ready finance leaders at US construction firms, plus 140 priority domains from the user's CRM:

ItemUnitsUnit priceCost
Lead Database: ICP pull and priority domains1,696 leadsIncluded in Growth$0.00
Website Finder on 44 empty priority domains19 contacts found$0.0499$0.95
Email Finder for 15 names10 emails found$0.05$0.50
validate_emails after dedupe and suppression1,552 emails$0.00169$2.62
Total$4.07

Ceiling shown before the run: $2.20 (Website Finder, 44 domains) + $0.75 (Email Finder, 15 names) + $2.90 (validation, up to 1,715 emails) = $5.85. Run check_wallet, show it and wait for a yes. Cost per send-ready row in the example: $4.07 / 1,247 = $0.003.

Error handling

ResponseAction
402check_wallet, report the shortfall, stop before validation; never import unvalidated rows
409Same request moments ago; reuse the earlier run from list_runs
409 filters_not_applied_yetDrop the listed company filters or retry later
503 on a database callNarrow: add a state, a size band or fewer titles
Apollo run still RUNNINGUsually done in 15 to 30 minutes; wait for the webhook, check every few minutes
Sequencer rejects the CSVRe-export as UTF-8, check header length and duplicates per the reference

Output Format

  1. send-ready-[segment]-[date].csv: valid emails only, headers mapped for the chosen sequencer, plus email_status, email_validated_at and company_contact_order kept in a parallel master file.
  2. send-ready-[segment]-[date]-wave2.csv for second and third contacts per company.
  3. catch-all-[segment]-[date].csv for a separate campaign at the user's daily volume.
  4. suppression-additions-[date].csv with invalid and disposable addresses for the blocklist.
  5. The report from templates/output-template.md: campaign brief, sourcing plan, count check, pipeline funnel, validation, send capacity, sequencer import map, cost, sample rows, launch checklist.
  • What the reader receives is the deliverable only, in product names (Lead Database, Email Validator, Email Finder, Mobile Finder, Website Finder): no tool ids, JSON tool calls, run IDs, skill names, file paths, reference section numbers or runbook IDs; a downstream step is written out as the checks or commands the team runs, never handed to another skill by name; no tables of checks that were not run (one "Not checked:" line instead); and no repeated identical rows where one template plus a list will do. scripts/check-output.py fails a report that carries a tool id, skill name or path.

Quality Check

  • Count calls ran and the approved estimate appears before any paid step.
  • Funnel adds up stage by stage with a reason on every change.
  • Every row in the send file is valid, validated within four weeks of its send date or given a re-validation date.
  • 0 duplicate emails, 0 suppression matches, no more contacts per company than agreed, contact 1 only in wave 1.
  • Every personalization column used in the copy is filled on every row, or the copy has a fallback.
  • The QA sample result appears in the launch checklist, and any repeated miss led to a re-pull.
  • Send capacity: mailboxes x sends per mailbox equals daily sends, sends per mailbox is under 50 (Berman), and the sending days fit the campaign window.
  • Import headers are unique, under 20 characters and accepted by the platform.
  • scripts/check-output.py report.md prints PASS.

Common Pitfalls

MistakeWhy it failsFix
Importing straight from the databaseUnvalidated rows bounce; the sequencer auto-pausesValidate every non-Email Finder address first
Building 5,000 rows for 3 mailboxesRows age past four weeks before they are sent and decay (Hunter: over 2% in four weeks)Size from capacity; build in waves
Every contact at a company in one campaignSeveral people get the same email at once; Berman says that can get the sender blocked company wideCap at the agreed number, wave file for later contacts
Blank {{first_name}}"Hi ," in the inboxDrop or route blank-name rows to a variant
Catch-all rows in the main campaignBounces you could not see comingSeparate campaign at the user's daily volume, paused past 2% bounces
Headers like Company Name (from CRM)Instantly rejects headers over 20 charactersShort, unique headers from the reference map
Duplicate check off at uploadThe same person enters two campaignsKeep the cross-campaign duplicate check on
Generated "personal" facts in a columnOne wrong fact kills the replyOnly sourced facts; segment-level variables otherwise
Filling Email Finder misses with first.last@domainA built address is unverified; on a catch-all domain it passes validation and bounces on sendKeep the miss in the gaps file; try Website Finder for the domain
Batch Email Finder file with commas in names or company namesThe CLI --file reader splits on every comma, so "Acme, Inc." shifts the columnsStrip commas from those fields or send JSON through MCP or REST

Execution Artifacts

FileOpen it when
references/framework-notes.mdYou need the source behind a rule or the routing table.
references/pipeline-runbook.mdRunning steps 2-9: capacity sizing, sourcing recipes, gap fill, dedupe, suppression order, per-company waves, personalization columns.
references/sequencer-import-specs.mdStep 10: header rules and maps for Instantly, Smartlead, Lemlist and EmailBison, upload settings, launch checklist.
references/list-quality-standard.mdColumns, dedupe keys, validation buckets (shared).
references/deliverability-primer.mdMailbox provider sender rules and bounce monitoring (shared).
references/alex-berman-cold-email.mdTargeting, testing and account sequencing guidance (shared).
references/scrapercity-agent-patterns.mdWallet checks, polling, retries (shared).
references/scrapercity-catalog.mdPrices and parameters (shared).
references/scrapercity-platform.mdPlans and database rules (shared).
templates/output-template.mdWriting the report.
examples/worked-example.mdA finished Instantly-ready list of finance leaders at US construction firms.
scripts/check-output.pyFinal gate: python3 scripts/check-output.py report.md.

Related Skills

  • icp-list-builder for the ICP definition and filters behind the pull.
  • decision-maker-finder when the user has accounts and needs buying roles.
  • email-list-cleaner to re-validate a list that missed its launch window.
  • local-business-list for small-business campaigns before import prep.
  • cold-email-strategy for the offer, segment and sequence this list serves.
  • cold-email-copywriting for the emails that use the personalization columns.
  • sending-platforms for upload settings, blocklists and auto-pause.
  • email-deliverability for domain and mailbox health before launch.

Files bundled with it

These load only when the skill asks for them, so they cost nothing until it runs.

Other skills for the same job

Different authors, same problem. Matched on the words in the skill name, across every library in the catalogue except this one.

Need help setting it up?

This page tells you what cold-email-list does and what it needs. Cheetah builds the agent setup it runs inside: data, CRM, sequencing and the guardrails.

Book a call →

The directory stays free. There is nothing gated behind this.

Build my GTM engine

Tell us what you want to improve. Fedor reviews the workflow and replies by email when there is a useful fit.

Fedor reviews every request personally. If there is a useful fit, he will reply by email.