Install
$ agentstack add skill-revgrowth1-claude-code-skills-tam-map ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
TAM MAP - Total Addressable Market Builder
Build comprehensive business databases for any B2B vertical by discovering, scraping, and deduplicating data from every available source. Produces a unified, deduplicated CSV ready for enrichment via local-enrich.
Origin: Built for a roofing contractor client - 113K raw records from 20+ sources, deduplicated to 89,562 unique businesses, 20,557 campaign-ready leads at ~$15 total cost.
Script location: ./
The 7 Phases
| Phase | What Happens | Output | |---|---|---| | 1. Source Discovery | Research ALL data sources for the vertical | Source manifest (scored list) | | 1.5. Keyword Expansion | Discover adjacent keywords that share the same TAM | Expanded keyword list | | 2. Config Generation | Build TAMConfig with sources, keywords, NAICS codes | config.json | | 3. Collection | Run scrapers for each source | {source}/businesses.csv per source | | 4. Deduplication | 3-tier dedup: domain -> name+state -> phone | all_sources_deduped.csv | | 4.5. Existing Leads Exclusion | Remove leads already in CampaignPlatform (ALWAYS run, not optional) | net_new_leads.csv | | 5. Enrichment Hand-off | Feed net new CSV into local-enrich pipeline | Campaign-ready leads |
Phase 1: Source Discovery (THE MOST IMPORTANT PHASE)
The quality of the TAM depends on source breadth. For every vertical, research and catalog every available data source across ALL of these categories:
Source Categories Checklist
| # | Category | What to Look For | Example Sources | |---|---|---|---| | 1 | Government/regulatory | State licenses, professional registries, regulatory filings | State contractor boards, SEC, FDA, FCC, state bar associations, Socrata open data portals | | 2 | Federal contracts | Government contract recipients by NAICS | SAM.gov / USASpending.gov (free API, no auth) | | 3 | Industry associations | Trade orgs, certification bodies, member directories | NRCA (roofing), PHCC (plumbing), CompTIA (IT), ABA (legal) | | 4 | Manufacturer/vendor directories | Certified partner/contractor/reseller locators | GAF/Owens Corning (roofing), Kohler (plumbing), Salesforce AppExchange (SaaS) | | 5 | Company databases | Structured business databases searchable by industry | IcyPeas (any vertical), Crunchbase (tech), D&B | | 6 | Local/maps | Geographic business listings | Google Maps via Serper ZIP code mode (local-enrich step 1) | | 7 | Review platforms | Industry-specific review sites | G2/Capterra (SaaS), Clutch (agencies), Thumbtack/Houzz/Porch (home services), TrustPilot | | 8 | Social platforms | Business pages on social networks | Facebook pages, LinkedIn company search | | 9 | Business directories | General business listings | BBB (any B2B), Chamber of Commerce, industry yellow pages | | 10 | Awards/rankings | Recognition and "best of" lists | Inc 5000, Deloitte Fast 500, industry-specific awards, "Top X [vertical] companies" | | 11 | Job boards | Companies actively hiring for vertical-specific roles | Indeed, LinkedIn Jobs - signals active/growing companies | | 12 | Events/conferences | Exhibitor and sponsor directories | Trade show exhibitor lists, conference sponsor pages | | 13 | Investor/funding | Funded companies in the vertical | Crunchbase, PitchBook, AngelList (for tech/SaaS verticals) | | 14 | Open data | Government statistics with company lists | Census Business Patterns, BLS, state open data portals | | 15 | Marketplace/aggregator | Platform-specific directories | HomeAdvisor (home services), Upwork (agencies), AWS Marketplace (SaaS) | | 16 | Academic/research | Industry reports with company lists | Gartner quadrants, Forrester waves, industry census reports |
How to Research Sources
- Web search for
"{vertical}" site:*.gov databaseto find government/regulatory sources - Web search for
"{vertical}" association directory membersto find trade associations - Web search for
"{vertical}" certified contractorsor"{vertical}" partner directoryfor manufacturer/vendor directories - Check NAICS codes at https://www.naics.com/search/ - needed for SAM.gov
- Check state licensing - search
"{vertical}" license {state}for each major state - Check review platforms - which platforms does this vertical use? (G2 for SaaS, Houzz for home services, Clutch for agencies)
- Check if IcyPeas has coverage - run a free count query first
Source Manifest Output
Create manifest.json in the output directory:
{
"vertical": "plumbing",
"date": "2026-03-22",
"total_sources": 18,
"sources": [
{
"name": "icypeas_plumbing",
"category": "company_database",
"method": "api",
"url": "https://app.icypeas.com",
"estimated_records": 30000,
"has_owner_names": false,
"has_emails": false,
"has_addresses": false,
"has_phone": false,
"cost_estimate": "IcyPeas credits",
"priority": 1,
"notes": "Broad coverage, domains + LinkedIn URLs"
}
],
"total_estimated_records": 95000,
"estimated_unique_after_dedup": 45000
}
Priority Scoring
- Priority 1 (must-have): Sources with owner names baked in (state licenses), broad coverage databases (IcyPeas), Google Maps
- Priority 2 (high value): Industry association directories, manufacturer certs, BBB, SAM.gov
- Priority 3 (nice to have): Review platforms, Facebook pages, awards lists, job board signals
Phase 1.5: Keyword Expansion
Before configuring sources, discover adjacent keywords that target the same TAM. The primary keyword alone often misses 30-60% of the market.
How It Works
- Start with the primary keyword (e.g., "coffee shop")
- Brainstorm adjacent terms that describe the same businesses differently:
- Synonyms: "cafe", "coffeehouse"
- Subtypes: "espresso bar", "coffee roaster", "coffee stand"
- Related: "bakery cafe", "tea and coffee"
- Run IcyPeas free count queries for each candidate keyword to estimate incremental volume
- Check overlap - many adjacent keywords return 40-70% duplicates. That's fine - dedup handles it. The goal is to catch the 30-60% that only appear under one keyword.
Example: Coffee Shop Expansion
| Keyword | IcyPeas Count | Incremental After Dedup | |---|---|---| | coffee shop | 22,000 | 22,000 (base) | | cafe | 43,000 | ~9,855 net new | | coffee roaster | 9,000 | ~1,703 net new | | espresso bar | 16,000 | ~2,109 net new | | Total | 90,000 raw | ~35,667 unique |
Rules
- Always run counts before pulling - IcyPeas count queries are free. Don't pull 43K "cafe" records if count shows only 2K.
- Dedup cross-keyword overlap - use the same dedup pipeline. Adjacent keywords may overlap 40-70%.
- Run expanded keywords through ZIP scrape too - Google Maps queries for "cafe near [ZIP]" find different businesses than "coffee shop near [ZIP]".
- Document the expansion - save the keyword -> count -> incremental mapping in the manifest for future builds.
Phase 2: Config Generation
Build a TAMConfig JSON from the manifest. Use the TAMConfig and TAMSource dataclasses:
from tam_config import TAMConfig, TAMSource
config = TAMConfig(
vertical_name="plumbing",
keywords=["plumbing", "plumber", "plumbing contractor", "plumbing company", "plumbing repair"],
naics_codes=["238220"],
sources=[
TAMSource(
name="icypeas_plumbing",
category="company_database",
method="api",
config={"type": "icypeas"},
estimated_records=30000,
priority=1,
),
TAMSource(
name="sam_gov_plumbing",
category="federal_contracts",
method="api",
config={"type": "sam_gov"},
estimated_records=2000,
priority=2,
),
# ... more sources
],
generic_filters=["plumbing contractor", "best plumber", "plumber near me"],
output_dir=os.path.expanduser("./output/plumbing_20260322"),
)
config.save("config.json")
NAICS Code Reference (Common Verticals)
| Vertical | NAICS | Description | |---|---|---| | Roofing | 238160 | Roofing Contractors | | Plumbing | 238220 | Plumbing, Heating, AC Contractors | | Electrical | 238210 | Electrical Contractors | | HVAC | 238220 | (same as Plumbing) | | Landscaping | 561730 | Landscaping Services | | Pest Control | 561710 | Exterminating and Pest Control | | Painting | 238320 | Painting and Wall Covering | | General Construction | 236220 | Commercial Building Construction | | IT Services | 541512 | Computer Systems Design | | Marketing Agencies | 541810 | Advertising Agencies | | Management Consulting | 541611 | Administrative Management Consulting | | SaaS | 511210 | Software Publishers | | Accounting | 541211 | Offices of CPAs | | Legal | 541110 | Offices of Lawyers |
Phase 3: Collection
Automated Sources (lib/ scrapers)
These run automatically via run_tam.py:
| Source Type | Config type | Script | Notes | |---|---|---|---| | IcyPeas | icypeas | lib/icypeas_client.py | Set config.type = "icypeas" | | SAM.gov | sam_gov | lib/sam_gov_client.py | Set config.type = "sam_gov" | | Serper site-scoped | any | lib/serper_scraper.py | Set site_scope + query_templates in config |
Running Collection
# Run all sources
python3 ./run_tam.py config.json
# Run a single source
python3 ./run_tam.py config.json --source icypeas_plumbing
# Skip collection, just dedup
python3 ./run_tam.py config.json --dedup-only
Google Maps (via local-enrich)
For local/service businesses, run Google Maps scraping via the existing local-enrich pipeline:
# Nationwide ZIP code scrape
python3 ./run_pipeline.py \
--query "plumbing contractor" \
--zip-file ./data/us_zipcodes.txt \
--max-results 20 \
--step 1
# Copy output to TAM MAP source dir
cp ./output/{latest}/businesses.csv \
./output/plumbing_20260322/google_maps/businesses.csv
Bespoke Sources (Claude writes per-vertical)
For sources without a reusable scraper (state license DBs, niche directories), Claude writes a one-off script and saves it to sources/{vertical}/. Follow these patterns:
Serper site-scoped scrape (for any website with a directory):
from lib.serper_scraper import run_site_scrape
from lib.api_utils import empty_unified_row, normalize_state
def extract_fn(result, state, keyword):
row = empty_unified_row()
row["business_name"] = result.get("title", "").split(" | ")[0].strip()
row["state"] = normalize_state(state)
row["raw_source_url"] = result.get("link", "")
return row
def is_valid_fn(url, title):
return "/profile/" in url # Only profile pages
run_site_scrape(
source_name="bbb_plumbing",
site_scope="bbb.org",
query_templates=['site:{site} inurl:profile "{keyword}" "{state}"'],
keywords=["plumbing"],
extract_fn=extract_fn,
is_valid_fn=is_valid_fn,
output_dir="output/plumbing_20260322/bbb/",
api_key=os.environ["SERPER_API_KEY"],
)
State license database (requires per-state research):
- Check if state has an open data portal (Socrata)
- Check if state has a searchable license lookup
- Some states expose CSV/API (FL DBPR, CA CSLB, IL Socrata)
- Some require Selenium (CAPTCHAs, JS SPAs)
- Owner names from license records are the highest-value free data
Every bespoke scraper MUST output businesses.csv with the unified schema columns.
Phase 4: Deduplication
The dedup engine auto-detects CSV column names and maps them to the unified schema. No hardcoded source mappers needed.
# Run standalone
python3 ./dedup_engine.py output/plumbing_20260322/ \
--category plumbing
# Or via run_tam.py (runs automatically after collection)
python3 ./run_tam.py config.json --dedup-only
Three-Tier Dedup Algorithm
- Tier 1: Domain match - Records with the same normalized domain are merged. Keeps the record with the most populated fields, fills empty fields from duplicates.
- Tier 2: Company name + state - Domainless records matched to domain records by normalized company name + exact state.
- Tier 3: Phone match - Remaining unmatched records matched by normalized 10-digit phone number.
- Internal dedup - Remaining unmatched records deduplicated among themselves by name+state.
Output
all_sources_deduped.csv- unified deduplicated master listdedup_stats.json- per-source counts, overlap analysis, dedup rates
Phase 4.5: Existing Leads Exclusion (MANDATORY)
ALWAYS run exclusion before enrichment. Enriching leads that are already in your campaign platform wastes API credits. In one build, exclusion rate was 31.7% - that's 31.7% of enrichment spend saved by running exclusion first.
Removes leads already in CampaignPlatform or Supabase so only net new records proceed to enrichment. Prevents double-contacting prospects and wasting enrichment credits.
How It Works
- CampaignPlatform exclusion: Uses async parallel fetcher (aiohttp, 20 concurrent requests) to build domain cache from all leads. First run: ~28 min for 204K leads at 7.9 pages/sec. Subsequent runs use cache (instant, refreshes after 24h).
- Supabase exclusion: Queries
icp_companiestable for all known domains. - Merge: Combines both domain sets into a single exclusion list.
- Filter: Removes TAM records whose domain matches the exclusion set.
- Output:
net_new_leads.csv+ exclusion stats indedup_stats.json.
Usage
# Via run_tam.py (runs after Phase 4 automatically - always use --exclude-existing)
python3 ./run_tam.py config.json --exclude-existing
# Force refresh the CampaignPlatform cache
python3 ./run_tam.py config.json --exclude-existing --refresh-cache
# Standalone (on an existing deduped CSV)
python3 ./lib/existing_dedup.py \
--tam-csv output/all_sources_deduped.csv \
--output output/net_new_leads.csv
# Use workspace-specific API key for multi-workspace setups
YOUR_CAMPAIGN_API_KEY=$YOUR_CAMPAIGN_API_KEY python3 ./lib/existing_dedup.py \
--tam-csv output/all_sources_deduped.csv \
--output output/net_new_leads.csv
Performance
The async fetcher uses aiohttp with 20 concurrent requests, achieving ~7-8 pages/sec (vs ~0.4/sec sync). For large workspaces:
| Workspace Size | Pages | Sync Time | Async Time | |---|---|---|---| | 79K leads | ~5,300 | ~15-20 min | ~11 min | | 204K leads | ~13,600 | ~95 min | ~28 min |
Output Stats
The exclusion stats show exactly how much TAM is already contacted:
TAM input: 39,721
Excluded (domain): 10,870
Excluded (email): 1,711
Net new leads: 27,140
Exclusion rate: 31.7%
Cache
- Stored at
./cache/campaign_platform_domains.json - Contains all unique domains and emails from CampaignPlatform
- Auto-refreshes after 24 hours
- Use
--refresh-cacheto force rebuild - Use workspace-specific API keys -
YOUR_CAMPAIGN_API_KEY,YOUR_CAMPAIGN_API_KEY, etc. The generic key may not return all workspaces' data.
Phase 5: Enrichment Hand-off
Pre-Flight Checks (BEFORE starting enrichment)
- Check MillionVerifier credits:
curl -s "https://api.millionverifier.com/api/v3/credits?api=$MILLIONVERIFIER_API_KEY" -H "User-Agent: YourAgency/1.0"- need ~1 credit per email to verify. Budget for 30-50% of total leads having emails. - Estimate enrichment volume: After exclusion, how many leads need enrichment? If >10K, plan for batched processing.
- Check API availability: BlitzAPI, Prospeo, IcyPeas - ensure keys are valid and credits/limits are sufficient.
Adaptive Email Waterfall
For large TAMs (>5K leads), sample before committing to the full waterfall:
- Sample 100-200 domains from the net new CSV
- Run Step 3 (email finding) on the sample only
- **Measure hit rate*
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: Revgrowth1
- Source: Revgrowth1/claude-code-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.