# Lead Scorer and Enricher (`advantageous_subcontra/lead-scorer-and-enricher`) Actor

Score a list of company URLs against your own scoring rules, then enrich the qualified ones with department-specific contacts (name, title, email, LinkedIn). Turns a raw domain list into an SDR-ready pitch list in one Run.

- **URL**: https://apify.com/advantageous\_subcontra/lead-scorer-and-enricher.md
- **Developed by:** [Fabian Maume](https://apify.com/advantageous_subcontra) (community)
- **Categories:** Lead generation, Automation
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-usage

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

**Score a list of company URLs against your own scoring rules, then enrich the qualified ones with department-specific contacts** (name, title, email, LinkedIn). Turns a raw domain list into an SDR-ready pitch list in one Run.

Under the hood the Actor chains four Apify Actors and one OpenRouter LLM step:

1. [`builtwith/builtwith-official-technology-scraper`](https://apify.com/builtwith/builtwith-official-technology-scraper) — detects the tech stack.
2. [`apify/website-content-crawler`](https://apify.com/apify/website-content-crawler) — pulls the homepage markdown.
3. [`apify/openrouter`](https://apify.com/apify/openrouter) (Standby) — evaluates yes/no questions against the homepage.
4. [`vdrmota/contact-info-scraper`](https://apify.com/vdrmota/contact-info-scraper) — finds contacts in the requested departments.
5. [`scalelist/email-finder`](https://apify.com/scalelist/email-finder) — optional fallback for leads that come back without an email.

Steps 1 and 2 run in parallel to keep wall-clock time down.

### Why use this Actor?

- **One-shot pipeline**, no glue code. Point it at a CSV column of URLs and it emits a per-contact dataset ready for import into Apollo, Instantly, or a Google Sheet.
- **Auditable scoring**. Every score decomposes into `tech_score` + `content_score`, and unqualified companies are preserved in a separate dataset so you can inspect what was dropped and why.
- **Cost-capped**. Set `maxTotalChargeUsd` on the Run and the orchestrator refuses to overshoot.

### How to use it

1. Open the Actor in the Apify Console.
2. Paste your company URLs into **Company URLs** (one per line, full URLs or bare domains).
3. Pick the **Target departments** for the contact hunt (multi-select).
4. Define **Tech-stack scoring rules** (e.g. `Shopify` +10) and/or **Website-content scoring rules** (e.g. "Does the homepage describe a SaaS business?" +8). Leave a set empty to skip that scoring step.
5. Set the **Qualification threshold** — companies at or above pass to enrichment.
6. On the Run configuration screen set **Max total charge (USD)** to your budget ceiling. This is a Run option, not an input field. See [Apify docs on Run options](https://docs.apify.com/platform/actors/running#run-options).
7. Start the Run. Watch the **Storages** tab — three datasets appear as the Run progresses.

### Input

| Field | Type | Notes |
|---|---|---|
| `companyUrls` | array of strings, **required** | Full URLs or bare domains. |
| `targetDepartments` | array of enum | 14 department options mirroring `vdrmota/contact-info-scraper`. Default `[marketing, sales]`. |
| `maxLeadsPerCompany` | integer | Cap on contacts discovered per qualified company. Cost multiplier. Default `3`. |
| `useScalelistFallback` | boolean | Retry name-only leads via Scalelist. Default `true`. |
| `techScoringRules` | array of `{tech, weight}` | Empty ⇒ skip BuiltWith. |
| `contentScoringRules` | array of `{question, weight}` | Empty ⇒ skip WCC + LLM. Each question is yes/no; weight added on yes. |
| `qualificationThreshold` | number | Threshold at or above which a company is enriched. Default `0`. |
| `llmModel` | string | OpenRouter model id for content scoring. Default `openai/gpt-4o-mini`. |

Example input JSON:

```json
{
  "companyUrls": ["https://apify.com", "https://vercel.com"],
  "targetDepartments": ["marketing", "product"],
  "maxLeadsPerCompany": 3,
  "useScalelistFallback": true,
  "techScoringRules": [
    { "tech": "Shopify", "weight": 10 },
    { "tech": "HubSpot", "weight": 5 }
  ],
  "contentScoringRules": [
    { "question": "Does the homepage describe a SaaS or platform business?", "weight": 8 },
    { "question": "Does the homepage mention developers or API?", "weight": 3 }
  ],
  "qualificationThreshold": 5,
  "llmModel": "openai/gpt-4o-mini"
}
```

### Output

Three datasets per Run:

#### `qualified_leads` (default dataset)

One row per **discovered contact** at a qualified company. Company scoring columns are duplicated per contact so a CSV export is directly usable. A qualified company that returns zero contacts still gets one placeholder row so it doesn't silently vanish.

```json
{
  "company_url": "https://apify.com",
  "first_name": "Ada",
  "last_name": "Lovelace",
  "title": "Head of Growth",
  "email": "ada@apify.com",
  "email_source": "contact-info-scraper",
  "linkedin": "https://linkedin.com/in/ada",
  "score": 13,
  "tech_score": 5,
  "content_score": 8,
  "detected_tech": ["HubSpot", "Segment", "Cloudflare"]
}
```

#### `qualified_companies` (named dataset)

One row per **qualified company** — the scorecard view.

```json
{
  "company_url": "https://apify.com",
  "tech_score": 5,
  "content_score": 8,
  "score": 13,
  "qualified": true,
  "detected_tech": ["HubSpot", "Segment", "Cloudflare"],
  "lead_count": 3
}
```

#### `droped_companies` (named dataset)

One row per **unqualified company** — the audit trail.

```json
{
  "company_url": "https://someagency.example",
  "tech_score": 0,
  "content_score": -5,
  "score": -5,
  "qualified": false,
  "detected_tech": ["WordPress"]
}
```

You can download any dataset as JSON, HTML, CSV, or Excel from the Storages tab or via the Apify API.

### Data table (default dataset)

| Column | Type | Meaning |
|---|---|---|
| `company_url` | link | Normalized `https://<domain>` for the company. |
| `first_name`, `last_name`, `title`, `email`, `linkedin` | text | Contact fields. |
| `email_source` | text | `contact-info-scraper`, `scalelist-fallback`, or blank. |
| `score`, `tech_score`, `content_score` | number | `null` for skipped sources. |
| `detected_tech` | array | Technologies BuiltWith detected. |

### Pricing / cost estimation

The parent Actor itself is thin — cost is dominated by the sub-Actors.

| Cost driver | Rough figure |
|---|---|
| BuiltWith | $0.50–$2 per 100 URLs |
| Website Content Crawler (homepage only) | $0.20–$1 per 100 URLs |
| OpenRouter (gpt-4o-mini, ~3k tokens/homepage) | ~$0.001 per company |
| Contact Info Scraper (add-on ON) | $0.03–$0.10 per **found** lead × qualified companies |
| Scalelist (fallback) | $0.01–$0.04 per resolved email |

Ballpark: **~$3–$10 per 100 companies** with default settings (3 leads/company, both scoring sources on, Scalelist fallback on). Half of that if you skip content scoring.

**Set a hard ceiling** by supplying `maxTotalChargeUsd` on the Run. The orchestrator splits it evenly across four steps and refuses to launch a step if the running total would exceed the cap.

### Advanced tips

- **Skip a scoring source** by passing an empty array for its rules. The corresponding sub-Actor is not launched, and its share of the cost cap is not spent.
- **Start with a small batch** (10–20 URLs) to calibrate the qualification threshold before running the full list.
- **Broaden departments** if enrichment returns few contacts. Some domains (large chains, consumer platforms) are excluded by Contact Info Scraper's internal filter — add `c_suite` alongside a functional department for better coverage.
- **BuiltWith empty output** on a URL usually means it's Cloudflare-protected or newly registered. Retry once with the bare domain if the site is reachable in a browser.

### FAQ, disclaimers, and support

**Is this legal?** The Actor uses publicly available data. Complying with each target site's Terms of Service and applicable privacy law (GDPR/CCPA) is your responsibility, including any B2B outreach based on emails the enrichment step returns.

**Why do some qualified companies have zero contacts?** Contact Info Scraper filters certain enterprise chains and consumer platforms silently. The company still appears once in `qualified_leads` with blank contact fields, and its scoring is still audit-visible in `qualified_companies`.

**Can I run this workflow as a Claude skill?** Yes. You can use [this skill](https://github.com/apify/awesome-skills/blob/main/skills/apify-lead-scoring-enrichment/SKILL.md).

# Actor input Schema

## `companyUrls` (type: `array`):

List of company websites to score and enrich. Accepts full URLs (https://acme.com) or bare domains (acme.com). Normalized internally.

## `techScoringRules` (type: `array`):

Weights added to a company's score when BuiltWith detects the named technology. Empty list skips the BuiltWith scoring step entirely.

## `contentScoringRules` (type: `array`):

Yes/no questions the LLM evaluates against each company's homepage markdown. Weight is added to the score when the LLM answers yes. Empty list skips the Website Content Crawler and LLM scoring step entirely.

## `llmModel` (type: `string`):

OpenRouter model id used for content scoring. Cheap defaults: openai/gpt-4o-mini, google/gemini-2.5-flash.

## `qualificationThreshold` (type: `number`):

Companies whose total score is greater than or equal to this value proceed to enrichment; the rest land in the droped\_companies dataset.

## `targetDepartments` (type: `array`):

Which departments to hunt for contacts in during enrichment. Mirrors vdrmota/contact-info-scraper's leadsEnrichmentDepartments field.

## `maxLeadsPerCompany` (type: `integer`):

Upper bound on contacts discovered per qualified company. Cost multiplier: maxLeadsPerCompany x number-of-qualified-companies is the ceiling on billed leads. Apify only charges for leads successfully found.

## `useScalelistFallback` (type: `boolean`):

When enabled, any lead returned with a name but no email is retried via scalelist/email-finder. Small extra cost per resolved email.

## Actor input object example

```json
{
  "companyUrls": [
    "https://apify.com",
    "https://vercel.com"
  ],
  "techScoringRules": [
    {
      "tech": "Shopify",
      "weight": 10
    },
    {
      "tech": "HubSpot",
      "weight": 5
    }
  ],
  "contentScoringRules": [
    {
      "question": "Does the homepage describe a SaaS or platform business?",
      "weight": 8
    },
    {
      "question": "Does the homepage mention developers or API?",
      "weight": 3
    }
  ],
  "llmModel": "openai/gpt-4o-mini",
  "qualificationThreshold": 0,
  "targetDepartments": [
    "marketing",
    "sales"
  ],
  "maxLeadsPerCompany": 3,
  "useScalelistFallback": false
}
```

# Actor output Schema

## `qualifiedLeads` (type: `string`):

One row per discovered contact at a qualified company. This is the SDR-ready pitch list.

## `qualifiedCompanies` (type: `string`):

One row per qualified company — scorecard view before enrichment fan-out.

## `dropedCompanies` (type: `string`):

One row per company that scored below the qualification threshold — audit trail.

## `subDatasets` (type: `string`):

Links to each sub-Actor's raw dataset (BuiltWith, WCC, Contact Info Scraper, Scalelist) for debugging.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "companyUrls": [
        "https://apify.com",
        "https://vercel.com"
    ],
    "techScoringRules": [
        {
            "tech": "Shopify",
            "weight": 10
        },
        {
            "tech": "HubSpot",
            "weight": 5
        }
    ],
    "contentScoringRules": [
        {
            "question": "Does the homepage describe a SaaS or platform business?",
            "weight": 8
        },
        {
            "question": "Does the homepage mention developers or API?",
            "weight": 3
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("advantageous_subcontra/lead-scorer-and-enricher").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "companyUrls": [
        "https://apify.com",
        "https://vercel.com",
    ],
    "techScoringRules": [
        {
            "tech": "Shopify",
            "weight": 10,
        },
        {
            "tech": "HubSpot",
            "weight": 5,
        },
    ],
    "contentScoringRules": [
        {
            "question": "Does the homepage describe a SaaS or platform business?",
            "weight": 8,
        },
        {
            "question": "Does the homepage mention developers or API?",
            "weight": 3,
        },
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("advantageous_subcontra/lead-scorer-and-enricher").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "companyUrls": [
    "https://apify.com",
    "https://vercel.com"
  ],
  "techScoringRules": [
    {
      "tech": "Shopify",
      "weight": 10
    },
    {
      "tech": "HubSpot",
      "weight": 5
    }
  ],
  "contentScoringRules": [
    {
      "question": "Does the homepage describe a SaaS or platform business?",
      "weight": 8
    },
    {
      "question": "Does the homepage mention developers or API?",
      "weight": 3
    }
  ]
}' |
apify call advantageous_subcontra/lead-scorer-and-enricher --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,advantageous_subcontra/lead-scorer-and-enricher"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/etAgb8xYYL3zKQy3r/builds/N7L5TtDBWlvU5pudE/openapi.json
