
apify-lead-scoring-enrichment
Score and enrich a CSV of B2B leads using Apify Actors. Takes a CSV with company URLs, free-text scoring rules, and an enrichment preference; runs BuiltWith (tech stack), Website Content Crawler (content classification), and Contact Info Scraper (company metadata) for scoring; enriches with either department-specific contacts (Contact Info Scraper + Bulk Email Finder fallback) or copywriter discovery (Google Search Scraper → AI Web Scraper → Bulk Email Finder). Outputs an enriched CSV with a numeric score and a per-lead outreachhook column that personalizes cold email copy (e.g. "uses Shopify → send Shopify install guide"). Use when user asks to score leads, qualify leads, enrich a lead list, detect a company's tech stack for outreach, find marketing/sales/engineering contacts at a list of companies, hunt down blog copywriters for guest-post pitches, personalize cold email at scale, or turn a raw domain list into a ready-to-pitch account list.
- Installs
- 59
- Last 24h
- —
- Trending rank
- —
- Security
- Not audited
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/apify/awesome-skills --skill apify-lead-scoring-enrichment- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads apify-lead-scoring-enrichment automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
Lead Scoring & Enrichment
Turn a CSV of company URLs into a scored, contact-enriched pitch list. The agent asks the user for scoring rules in plain English ("+10 if using Shopify", "-5 if company size <10"), picks an enrichment path (departments or copywriters), and orchestrates six Apify Actors through four helper scripts.
Prerequisites
- Apify account with an active
APIFY_TOKEN(Console → Settings → Integrations) - Node.js 20.6+ (needed for native
--env-filesupport) - A
.envfile at the skill root containingAPIFY_TOKEN=apify_api_... - One-time inside
scripts/:npm install(installscsv-parse,csv-stringify)
Optional but recommended: the Apify CLI (npm i -g apify-cli) for ad-hoc Actor
calls. The helper scripts hit the REST API directly and do not need the CLI.
Workflow
Copy this checklist and track progress:
Task Progress:
- [ ] Step 1: Collect CSV path and validate required column (company_url)
- [ ] Step 2: Collect scoring rules per source (tech / content / metadata)
- [ ] Step 3: Collect enrichment path (departments OR copywriters)
- [ ] Step 4: Run scoring Actors (writes scoring.json)
- [ ] Step 5: Apply scoring rules per lead → assign per-source scores + outreach_hook (writes scored.json)
- [ ] Step 5b: Compute theoretical min/max score, ask user for qualification threshold, filter leads → qualified_leads.csv
- [ ] Step 6: Run enrichment path against qualified_leads.csv (writes enrichment.json)
- [ ] Step 7: Merge scoring + enrichment onto the ORIGINAL CSV → leads.enriched.csv (qualified column marks who made the cut)
Step 1: CSV intake
Ask the user for the CSV path. Required column: company_url. Recognized
optional columns pass through untouched: company_name, first_name,
last_name, role, department. Reject the run if company_url is
missing. Trim to a bare domain (strip trailing slash, www. optional) when
feeding downstream Actors that expect a domain.
Step 2: Scoring rules (per source)
Ask one question per source so it's obvious to the user (and to you at
Step 5) which data each rule tests. Ask only for the sources the user
wants to fetch — each source has a matching --enable-* flag in Step 4.
2a. Tech-stack rules — applied against
scoring.json[url].tech (BuiltWith output). Only ask if the user wants
--enable-tech. Example rules to show:
- +10 if the company uses Shopify or WooCommerce (we sell a Shopify integration).
- +5 if the tech stack includes HubSpot, Marketo, or Segment (marketing-ops ICP).
- -3 if no analytics or CDP is detected (likely too early-stage).
2b. Website-content rules — applied against
scoring.json[url].content (Website Content Crawler markdown/text). Only
ask if the user wants --enable-content. Also ask for maxCrawlDepth
here (default 0 = homepage only; higher = more $). Example rules:
- +8 if the homepage describes a SaaS or platform business.
- -5 if the homepage describes a services agency (not our ICP).
- +3 if the homepage mentions "developers" or "API" (technical buyer).
2c. Company-metadata rules — applied against
scoring.json[url].metadata (Contact Info Scraper metadata). Only ask if
the user wants --enable-metadata. Example rules:
- +3 if industry is e-commerce, retail, or B2C.
- -3 if company size is under 10 employees (too small to buy).
- +5 if the company has a LinkedIn presence (bigger operation).
Store each rule block verbatim, tagged with its source. If the user folds a metadata rule into the tech block (e.g. "+3 if size >50" under tech), re-file it to the correct block before Step 5 and tell them why.
Any source the user has no rules for should also be dropped from the
Step 4 --enable-* flags — no point paying for a signal you won't
score on. Full example rule sets: examples/scoring-rules.example.md.
Step 3: Enrichment path (pick one)
Ask: "Which enrichment path?
- (A) Department contacts — find named people (with title + email) in a specific team at each company. Uses Contact Info Scraper's "Business leads enrichment" add-on, falls back to Bulk Email Finder for any lead without a discovered email.
- (B) Copywriter hunt — for each domain, Google-search
site:{domain} blog, extract author names from top posts, find their emails. Good for guest-post outreach."
For Path A, collect two more inputs:
- Department(s) — one or more from this enum (comma-separated):
c_suite,product,engineering_technical,design,education,finance,human_resources,information_technology,legal,marketing,medical_health,operations,sales,consulting. Example:marketing,sales. - Max leads per domain — integer. Recommend 3–5 for typical SDR work.
⚠️ This is a cost multiplier: 5 leads × 500 domains = up to 2500
billed leads. Apify only charges for leads successfully found. Warn
the user before running if
max_leads × domain_count > 500.
For Path B no additional input is needed.
Step 4: Run scoring Actors
node --env-file=.env scripts/run_scoring.js \
--input leads.csv \
--output scoring.json \
--enable-tech --enable-content --enable-metadata \
--content-crawl-depth 0
run_scoring.js batches all URLs into a single call per enabled Actor (not
one call per lead), then reshapes the datasets into a per-URL sidecar so the
agent can look up every signal by company_url. Actors that weren't
--enable-*'d are skipped. Read the resulting scoring.json — its shape
is { "https://acme.com": { "tech": {...}, "content": {...}, "metadata": {...} }, ... }.
Step 5: Apply scoring rules (per source, then sum)
For each lead in scoring.json, run one pass per source using only
that source's rules from Step 2. This keeps the score auditable — if
content_score = -5 on a lead the user expected to convert, you can
inspect exactly which content rule fired without re-deriving the whole
computation.
Produce five fields per lead:
tech_score(number, ornullif--enable-techwas off) — sum of Step 2a rule deltas againstscoring.json[url].tech.content_score(number, ornullif--enable-contentwas off) — sum of Step 2b rule deltas againstscoring.json[url].content.metadata_score(number, ornullif--enable-metadatawas off) — sum of Step 2c rule deltas againstscoring.json[url].metadata.score(number) — sum of the three above, treatingnullas0.outreach_hook(string, one sentence) — the single most-personalizable signal across all sources: a specific CMS ("uses Shopify"), a named analytics tool, an industry match, a hiring signal in the copy — whatever a human sales rep would open the email with.
The null vs 0 distinction matters: a source that wasn't fetched must
not be conflated with a source that was fetched and simply scored zero.
Downstream CSV columns render null as blank, 0 as "0".
Store scored rows as an intermediate scored.json (agent writes it
directly, keyed by canonical https://domain), then pass it to
filter_qualified.js at Step 5b and to merge_output.js at Step 7.
Step 5b: Qualification threshold gate
Enrichment is the expensive part — running it on unqualified leads burns credits with no ROI. Gate it with a user-set threshold before you call any enrichment Actor.
-
Compute the theoretical score range from the Step 2 rules the user gave. For each source's rule set, sum every positive delta into
max_sourceand every negative delta intomin_source. Thenmin_total = min_tech + min_content + min_metadataand same formax_total. This is a hard bound: no lead can score outside it. -
Also compute the observed range from
scored.json— the actual minimum and maximumscorevalues across all leads. Often the observed range is much narrower than the theoretical one. -
Present both to the user, plus a rough tiering suggestion:
"Theoretical range: {min_total} to {max_total}. Observed range in your list: {observed_min} to {observed_max} across {n_leads} leads. Distribution: {count above 75th percentile} / {count above 50th percentile} / {count above 25th percentile} at those thresholds. What threshold do you want? Leads scoring at or above the threshold move to enrichment; everything below is flagged in the final CSV as
qualified=falseand skipped."Recommend the 75th-percentile score as a starting point if the user is unsure — enrichment cost drops ~75% while keeping the top of the funnel. Warn if their chosen threshold would qualify 0 leads or qualify all of them (no filtering).
-
Mark
qualified: true|falseon every row inscored.jsonbased on the chosen threshold (write it back), then run:node scripts/filter_qualified.js \ --leads leads.csv \ --scores scored.json \ --output qualified_leads.csvfilter_qualified.jsis a pure-Node CSV filter — it readsscored.json, keeps only rows wherequalified === true, and writes them toqualified_leads.csvpreserving all original columns. The full lead list (including unqualified rows) still lives in the originalleads.csv— Step 7's merge uses that as the join base.
Step 6: Run enrichment path (qualified leads only)
Feed qualified_leads.csv from Step 5b into the enrichment scripts,
not the original leads.csv. This is where the threshold gate pays for
itself.
Path A — Department contacts:
node --env-file=.env scripts/enrich_departments.js \
--input qualified_leads.csv \
--department marketing,sales \
--max-leads 5 \
--output enrichment.json
Add --verify-emails to also validate every returned email (small extra
charge per verified/invalid/disposable result; catch-all and unknown are
free per the Actor docs).
Path B — Copywriter hunt:
node --env-file=.env scripts/enrich_copywriters.js \
--input qualified_leads.csv \
--output enrichment.json
Path A calls vdrmota/contact-info-scraper with the Business leads
enrichment add-on enabled (maximumLeadsEnrichmentRecords +
leadsEnrichmentDepartments) so the Actor returns actual people
per domain — name, title, work email, LinkedIn. For any lead that comes
back without a resolved email, the script calls
scalelist/email-finder on the (firstName, lastName, domain)
triple as a fallback. Path B chains
apify/google-search-scraper → apify/ai-web-scraper (with the
get-author-name-from-blog-post
example input) → scalelist/email-finder.
Step 7: Merge
node scripts/merge_output.js \
--leads leads.csv \
--scoring scoring.json \
--enrichment enrichment.json \
--scores scored.json \
--output leads.enriched.csv
Note that --leads is the original leads.csv, not
qualified_leads.csv. That way every input lead appears in the final
CSV — unqualified ones simply have blank enrichment columns and
qualified=false. This preserves the audit trail: you can see which
leads got scored below threshold and why.
merge_output.js is pure Node (no Actor calls). It left-joins on
company_url and emits leads.enriched.csv with the original columns plus:
tech_summary, content_summary, company_size, industry,
tech_score, content_score, metadata_score, score (sum),
qualified (true / false — matches Step 5b threshold), outreach_hook,
leads (full JSON of the per-domain people found via Path A),
lead_names and lead_titles (semicolon-separated summaries for CSV
readability), emails (semicolon-separated), and authors (Path B).
Actor routing
| User intent | Actor | Tier | Notes |
|---|---|---|---|
| Detect tech stack | builtwith/builtwith-official-technology-scraper | community | Input: { "startDomains": ["acme.com", ...] } (bare domains, no protocol). CMS, analytics, hosting drive outreach hooks. |
| Website content classification | apify/website-content-crawler | apify | Set maxCrawlDepth: 0 for homepage only; higher = more $. |
| Company metadata (scoring path) | vdrmota/contact-info-scraper | community | Add-on OFF. Returns emails/phones/socials + company metadata from About/Contact pages. |
| Dept-specific leads (Path A enrichment) | vdrmota/contact-info-scraper | community | Add-on ON via maximumLeadsEnrichmentRecords + leadsEnrichmentDepartments (enum). Returns actual people: name, title, work email, LinkedIn. |
| Blog discovery | apify/google-search-scraper | apify | Query site:{domain} blog, resultsPerPage: 5. |
| Blog author extraction | apify/ai-web-scraper | apify | Use example get-author-name-from-blog-post. |
| Email finder fallback | scalelist/email-finder | community | Input: { "leads": [{ "first_name", "last_name", "company_domain" }] }. Called only for leads with a name but no email. |
Full input schemas and quirks: references/actor-index.md.
Calling Actors — the CLI recipe
Every apify CLI call must carry three flags (CI-enforced):
apify actors call ACTOR_ID \
-i 'JSON_INPUT' \
--user-agent apify-awesome-skills/apify-lead-scoring-enrichment \
--json 2>/dev/null
apify actors info ACTOR_ID --input \
--user-agent apify-awesome-skills/apify-lead-scoring-enrichment \
--json 2>/dev/null
apify datasets get-items DATASET_ID \
--user-agent apify-awesome-skills/apify-lead-scoring-enrichment \
--format json 2>/dev/null
The helper scripts use the REST API directly and set the same
apify-awesome-skills/apify-lead-scoring-enrichment user-agent header on
every request, so attribution is consistent whether you drive by CLI or by
script.
Alternative interfaces
- Apify MCP connector — https://mcp.apify.com (docs). Skip the helper scripts; the agent calls Actors as MCP tools.
- mcpc — standalone MCP client, https://github.com/apify/mcpc.
If you skip the helper scripts, you still need to apply the Step 5 scoring logic yourself and produce the final CSV.
Troubleshooting
APIFY_TOKEN not set— the scripts read it from.envvianode --env-file=.env. Ensure.envis at the directory youcd'd into, not in the skill dir. Absolute paths help:node --env-file=/abs/path/.env scripts/....fetch failedon Node <20.6 —--env-filerequires 20.6+. Checknode --version. Upgrade or exportAPIFY_TOKENmanually in the shell.- BuiltWith returned empty for a URL — the domain is unreachable, WAF-blocked, or new (no historical detections). Feed the bare domain (
acme.com) not the full URL, and retry the failed rows only. - Contact Info Scraper returned 0 leads for a domain — the domain is filtered out by the Actor's built-in exclusion list (large chains, social platforms, retail giants, food-delivery services), or the site has no discoverable employees in the requested department. Try broader departments (e.g. add
c_suitealongsidemarketing) or fall back to the copywriter path for that segment. - Lead has a name but no email — the Business-Leads add-on couldn't resolve one. Path A auto-falls-back to
scalelist/email-finderon(firstName, lastName, domain). If the fallback also returns nothing, the person's email is genuinely not in Scalelist's index — try LinkedIn Sales Navigator manually or drop the row. - Copywriter path returns 0 authors for a domain — the domain has no blog, or blog posts don't expose an author byline. Skip the row; guest-post outreach isn't the right play for that domain.
- Ran out of Apify credits mid-run — no partial recovery in
run_scoring.jsv1. Re-run against a smaller CSV slice. See references/gotchas.md for cost estimates per Actor.
FAQ
What does the apify-lead-scoring-enrichment skill do?
Score and enrich a CSV of B2B leads using Apify Actors. Takes a CSV with company URLs, free-text scoring rules, and an enrichment preference; runs BuiltWith (tech stack), Website Content Crawler (content classification), and Contact Info Scraper (company metadata) for scoring; enriches with either department-specific contacts (Contact Info Scraper + Bulk Email Finder fallback) or copywriter discovery (Google Search Scraper → AI Web Scraper → Bulk Email Finder). Outputs an enriched CSV with a numeric score and a per-lead outreachhook column that personalizes cold email copy (e.g. "uses Shopify → send Shopify install guide"). Use when user asks to score leads, qualify leads, enrich a lead list, detect a company's tech stack for outreach, find marketing/sales/engineering contacts at a list of companies, hunt down blog copywriters for guest-post pitches, personalize cold email at scale, or turn a raw domain list into a ready-to-pitch account list.
How do I install apify-lead-scoring-enrichment?
Run npx skills add https://github.com/apify/awesome-skills --skill apify-lead-scoring-enrichment in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/apify-lead-scoring-enrichment/ (personal) or .claude/skills/apify-lead-scoring-enrichment/ (project).
Which AI agents can use apify-lead-scoring-enrichment?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is apify-lead-scoring-enrichment safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains apify-lead-scoring-enrichment?
It is published by apify in apify/awesome-skills. It has 59 recorded installs.
More from apify/awesome-skills
apify-ecommerce
apify/awesome-skills
Scrape e-commerce data for pricing, reviews, bestsellers, and seller discovery across 30+ platforms including Amazon, Walmart, eBay, Shopify, WooCommerce, and more. Use when user asks about product prices, competitor analysis, store scraping, tech stack detection, food delivery, real estate, or marketplace intelligence.
apify-easy-competitive-intelligence
apify/awesome-skills
Open the skill to read its SKILL.md instructions.
apify-verified-email-finder
apify/awesome-skills
Builds a list of verified business emails from Google Maps, Google SERPs, or a user-supplied URL list. Verification happens inside the same Apify run — no third-party verifier needed. Use when user asks to find verified emails, build a leads list, scrape emails from Maps or SERP, verify emails for a URL list, or find an Apollo / Hunter alternative.
apify-ads-intelligence
apify/awesome-skills
Research, spy on, and analyze ads across Meta (Facebook & Instagram), Google (Ads Transparency Center + paid search results), TikTok (Ads Library + Creative Center), LinkedIn Ad Library, and X (Twitter — promoted tweets, best-effort) using Apify Actors. Use when user asks about competitor ads, ad library research, winning creatives, ad copy analysis, landing page audits from ads, cross-platform ad audits, brand transparency checks, or any task involving paid ad creatives, advertiser data, or ad targeting from public ad libraries.
apify-link-prospecting-outreach
apify/awesome-skills
Find sites ranking for target keywords, score every prospect with Ahrefs domain authority and page-level traffic, identify the strongest pitch angle per row ("links to competitor", "mentions brand without linking", "top-3 SERP", "resource page", "outdated content"), generate brand-voice-matched outreach emails using an outreach-type-aware template (unlinked-mention claim, competitor-link replacement, resource-page inclusion, outdated-content replacement, topical niche-edit), and propose a concrete in-article link placement as three artifacts — the verbatim source sentence, the same sentence rewritten with the link spliced in, or a fully-drafted new insertion if no natural fit exists. Use when user asks to find link building opportunities, prospect link partners, recover unlinked brand mentions, replace competitor links, build a tiered outreach list, or run cold email outreach for SEO link building.
apify-ai-search-visibility-tracker
apify/awesome-skills
Track whether a brand and its competitors get cited or mentioned across Google AI Overviews, Google AI Mode, ChatGPT Search, Perplexity, Microsoft Copilot, and Google Gemini for a defined set of prompts, on a recurring schedule. Use when user asks to track AI visibility, monitor brand mentions in AI search, track ChatGPT citations, do AI search SEO tracking, GEO tracking (Generative Engine Optimization), AEO tracking (Answer Engine Optimization), monitor Perplexity citations, track AI Overviews mentions, see if their brand shows up in AI search, discover which prompts competitors rank for in AI search, find citation opportunities, or audit a website for AI visibility readiness.
Popular data & databases skills
supabase-postgres-best-practices
supabase/agent-skills
Postgres best practices maintained by Supabase, for Postgres running anywhere. Load this skill BEFORE writing or changing anything that lives in a Postgres database: creating or altering tables and columns (including choosing column types), schema design, migrations and declarative schema files, RLS policies and the tests that verify them, indexes, triggers, database functions, queues and scheduled jobs (pgcron, pgmq), vector/semantic search (pgvector), and restoring dumps (pgrestore) or importing data. Also load it when diagnosing slow queries, high CPU, timeouts, EXPLAIN plans, connection exhaustion, locking, bloat, or rows visible to the wrong user or tenant. This is not just a performance guide — schema, migration, security, and SQL authoring tasks need these rules too, even for a one-column change or a single query.
prisma-client-api
prisma/skills
Prisma Client API reference covering model queries, filters, operators, and client methods. Use when writing database queries, using CRUD operations, filtering data, or configuring Prisma Client. Triggers on "prisma query", "findMany", "create", "update", "delete", "$transaction".
prisma-database-setup
prisma/skills
Deprecated compatibility name for prisma-orm-setup. Use only when a user or existing integration explicitly invokes prisma-database-setup; otherwise select prisma-orm-setup.
prisma-cli
prisma/skills
Prisma ORM 7 CLI reference for existing Prisma 7 applications or explicitly selected Prisma 7 workflows. Covers prisma init, generate, migrate, db, dev, studio, and mcp. For setup, connection repair, or Prisma 8, use prisma-orm-setup; not the Prisma Platform CLI.
prisma-postgres
prisma/skills
Deprecated compatibility name for prisma-postgres-setup. Use only when a user or existing integration explicitly invokes prisma-postgres; otherwise select prisma-postgres-setup.
prisma-driver-adapter-implementation
prisma/skills
Required reference for Prisma ORM 7 SQL driver adapter work. Use when implementing or modifying adapters, adding database drivers, or touching SqlDriverAdapter, Transaction, savepoint, result mapping, or DriverAdapterError behavior. Covers current transaction lifecycle, optional savepoint hooks, original database-error preservation, and verification.