# Outbound Stack Detector Cold Email Sending Domains Clay (`mambalabs/outbound-infrastructure-fingerprint`) Actor

Detects whether a company runs cold email outbound and on what stack. Finds the lookalike sending domains behind the program, the inbox provider, the sequencer, warmup and infrastructure vendors, plus SPF, DKIM and DMARC posture. One flat Clay ready row per domain.

- **URL**: https://apify.com/mambalabs/outbound-infrastructure-fingerprint.md
- **Developed by:** [Mamba Labs](https://apify.com/mambalabs) (community)
- **Categories:** Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.40 / 1,000 domain analyzeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## 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

## Outbound Infrastructure Fingerprint

Give it a domain. It tells you whether that company runs cold outbound, and on what stack.

One flat row per domain, snake\_case, ready to drop into a Clay column or a CRM field.

```
domain                  smartlead.ai
runs_outbound           program
confidence              0.85
sending_domain_count    20
sending_domain_names    get-smartlead.co, getsmartlead.co, gosmartlead.co, ...
inbox_provider          google_workspace
sending_platform_names  null
dmarc_policy            none
```

### What it detects

**Lookalike sending domains.** A real outbound program does not send from the domain the business runs on, because one spam complaint would burn it. It buys lookalikes instead: `getcompany.com`, `company-mail.com`, `trycompany.co`. This actor generates those patterns, checks which ones carry mail, and keeps the ones whose web root redirects back to the primary domain. That redirect is the attribution: it is what separates a company's own sending domain from an unrelated business that happens to own `mycompany.com`.

This is the part nobody else does, and it is the reason the verdict is worth anything.

**Inbox provider.** Google Workspace, Microsoft 365, Zoho, Proofpoint, Mimecast and about fifteen more, read from MX. Reported for the primary domain and separately for every sending domain, so you can see when the outbound tenant is deliberately kept apart from the corporate one.

**Sending platform.** Outreach, Salesloft, Lemlist, Instantly, Smartlead, Apollo, Woodpecker, Reply.io, Klenty, Mixmax and others, plus the marketing and transactional layers (Marketo, Pardot, HubSpot, Mailgun, SendGrid, Mandrill, SES). Detected from tracking-subdomain CNAMEs and SPF include hosts. Read the honesty section below before you trust a negative here.

**Registration clustering.** Four sending domains bought on the same day is a program. One domain bought two years ago is not. Clusters come back as their own field.

**Cold email infrastructure vendors.** Maildoso, MailReef, Zapmail, Mailscale and similar sellers of pre-warmed inboxes. Nothing but a cold email program uses these, so finding one is close to proof.

**Deliverability posture.** SPF record and policy, DMARC policy, DKIM selectors. Optionally a blacklist check and a 0 to 100 score by way of [Domain Deliverability Checker](https://apify.com/mambalabs/domain-deliverability-checker).

### The verdict

| `runs_outbound` | What it means |
|---|---|
| `program` | A deliberate cold outbound setup. Multiple sending domains, a registration cluster, a sending domain plus a sequencer, or an infrastructure vendor. |
| `light` | One sending domain, or a sequencer with no sending domains. Real but small, or a marketing tool rather than a program. |
| `none` | Nothing found on a domain that resolved cleanly. |
| `unknown` | The domain did not resolve, or the sending domain scan was turned off. |

`verdict_reason` names the rule that fired, so you can disagree with the reading and rebuild it from the raw fields. That matters because two buyers want opposite things from the same row: a vendor selling to outbound teams reads `program` as "these people buy tools like mine", and a prospector reads it as "their inbox is crowded, expect a hard landing". Both readings come out of the same facts, and neither is baked in.

### How well it works

Measured 2026-08-06 on nine live domains across three bands, plus a sixteen-domain feasibility probe before the build.

**Sending domain detection: 6 of 6** on companies that demonstrably run outbound (smartlead.ai, instantly.ai, apollo.io, lemlist.com, clay.com, rippling.com). **0 false positives of 3** on controls (irs.gov, wikipedia.org, redcross.org).

The earlier sixteen-domain probe scored 6 of 8, missing gong.io and outreach.io. Both misses have the same cause and it has not gone away: when the brand token is an ordinary English word, the lookalike patterns collide with unrelated businesses that own them legitimately. `getgong.com` and `outreachone.com` are real companies that owe nothing to Gong or Outreach. The actor flags this case in `brand_is_common_word` and caps confidence rather than reporting a confident `none`. **If you are scanning brands like "Gong", "Clay" or "Ramp", read the confidence field, not just the verdict.**

**Sending platform recall is structurally partial, and this is the honest limit of the product.** Instantly, Smartlead, Lemlist, Apollo and Salesloft connect to a customer's own mailbox over OAuth or SMTP. They publish no SPF include host at all: all fourteen documented SPF hostnames for those vendors returned NXDOMAIN when checked. The only DNS-visible trace they leave is a custom tracking domain, and only when the customer bothered to configure one. So `sending_platform_names` being empty tells you very little, while `sending_platforms` containing an entry is solid. Smartlead itself, a cold email company with twenty of its own sending domains, returns no platform at all.

Warmup detection is weaker still. None of the major warmup vendors publishes a usable public DNS fingerprint, so `warmup_detected: false` means "no public trace", never "not warming up".

### Input

| Field | Default | Notes |
|---|---|---|
| `domain` | `smartlead.ai` | One domain. |
| `domains` | `[]` | Batch. Takes precedence over `domain`. |
| `scan_sending_domains` | `true` | The differentiator. Turning it off makes runs fast and the verdict much weaker. |
| `sending_domain_depth` | `deep` | `deep` covers .com .co .io .net .org. `standard` drops the last two. Deep costs no measurable wall-clock because the lookups run in parallel. |
| `check_deliverability` | `false` | See the note below. |
| `batchSize` | `3` | Concurrent domains, 1 to 10. |
| `max_sending_domain_probes` | `80` | Per-domain cap on HTTP attribution probes. Anything skipped is named in `evidence`, never dropped silently. |
| `request_timeout_ms` | `9000` | |
| `dns_timeout_ms` | `4000` | |
| `skipCache` | `false` | Results cache for 7 days. |

**On `check_deliverability`.** It is off by default, and not because the integration is missing. As of 2026-08-06, Domain Deliverability Checker returns an empty audit for every domain when called as a sub-actor: an unhandled DNS rejection while probing DKIM selectors fails its whole audit, and the empty record it falls back to carries `deliverability_score: 0` and `risk_level: high`. That reads as "this domain has terrible deliverability" when it actually means "the audit did not happen". This actor detects that shape and discards it rather than republishing a confident wrong number, but you would still be paying that actor's per-domain rate for nothing. SPF, DKIM and DMARC are read directly from DNS either way, at no extra cost. Turn this on once that actor is fixed and you want the blacklist check and the score.

### Output

Every run emits at least one row. Rows where `is_summary_row` is true are notices (empty input, free tier reached, QA run), not data.

Key fields: `runs_outbound`, `verdict_reason`, `confidence`, `sending_domains[]`, `sending_domain_count`, `sending_domain_names`, `registration_clusters[]`, `sending_platforms[]`, `sending_platform_names`, `sequencer_detected`, `inbox_provider`, `mx_records[]`, `tracking_domains[]`, `warmup_detected`, `infrastructure_vendors[]`, `spf_status`, `spf_record`, `spf_policy`, `dkim_status`, `dmarc_policy`, `dmarc_record`, `deliverability_score`, `evidence[]`, `signals_fired[]`, `signals_missing[]`, `checked_at`.

`evidence[]` is the quotable part. It holds strings like `Sending domain trysmartlead.com redirects to smartlead.ai, registered 2024-08-29` and `4 sending domains registered together on 2024-08-29`.

Read `signals_missing` before trusting a `none`.

### Speed and cost

6 to 24 seconds per domain, dominated by DNS fan-out. A few hundred DNS lookups per domain, then an HTTP probe only for the lookalikes that carry mail. No headless browser, no proxy, no LLM.

Free Apify-plan users get 15 results per calendar month. Upgrade for unlimited.

### Notes

Nothing here reads a mailbox, sends a message, or touches anything that is not public DNS and a public HTTP redirect.

Certificate transparency search was measured as a discovery channel and dropped: 10 of 16 crt.sh queries timed out at 25 seconds, and the ones that returned added nothing the pattern generator had not already found. DMARC `rua` attribution was measured too and added one domain across sixteen. Both are documented in the source rather than silently omitted.

Built by [Mamba Labs](https://apify.com/mambalabs).

# Actor input Schema

## `domain` (type: `string`):

A single company domain, for example smartlead.ai. Ignored when Company domains is filled.

## `domains` (type: `array`):

Analyze many domains in one run. Takes precedence over the single domain field.

## `scan_sending_domains` (type: `boolean`):

The strongest signal and the slowest one. Generates lookalike domains from the brand, checks which carry mail, and keeps the ones whose web root redirects back to the primary domain. Turning this off makes runs much faster but caps the verdict at what platform and deliverability signals alone can prove.

## `sending_domain_depth` (type: `string`):

deep checks .com, .co, .io, .net and .org and is the default: it costs about 65 percent more DNS lookups but no measurable wall-clock time, because the lookups run in parallel and only the mail-carrying candidates get an HTTP probe. standard drops .net and .org, which carried only 3 of the 41 lookalike domains found in testing but were the ONLY hit for one of them.

## `check_deliverability` (type: `boolean`):

Adds a blacklist check and a 0 to 100 health score by running Mamba Labs Domain Deliverability Checker, which bills your account separately at its own per-domain rate on top of this one. Off by default: as of 2026-08-06 that actor returns an empty audit when called as a sub-actor, so it costs money and adds nothing. SPF, DKIM and DMARC are read directly from DNS either way, at no extra cost.

## `batchSize` (type: `integer`):

How many domains to analyze at once. 1 to 10. Each domain runs a few hundred DNS lookups, so raising this raises memory pressure more than it raises throughput.

## `max_sending_domain_probes` (type: `integer`):

Cap on how many mail-carrying lookalikes get an HTTP attribution probe per domain. Anything skipped is reported in the evidence array rather than dropped silently.

## `request_timeout_ms` (type: `integer`):

Per-request timeout for the attribution probes. 3000 to 20000.

## `dns_timeout_ms` (type: `integer`):

Per-lookup DNS timeout. 1000 to 15000.

## `skipCache` (type: `boolean`):

Ignore the 7 day result cache and re-analyze every domain from scratch.

## `qa_test_mode` (type: `boolean`):

Emit a single summary row and skip all network work. Used for automated checks.

## Actor input object example

```json
{
  "domain": "smartlead.ai",
  "domains": [],
  "scan_sending_domains": true,
  "sending_domain_depth": "deep",
  "check_deliverability": false,
  "batchSize": 3,
  "max_sending_domain_probes": 80,
  "request_timeout_ms": 9000,
  "dns_timeout_ms": 4000,
  "skipCache": false,
  "qa_test_mode": false
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# 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 = {
    "domain": "smartlead.ai",
    "domains": [],
    "scan_sending_domains": true,
    "sending_domain_depth": "deep",
    "check_deliverability": false,
    "batchSize": 3,
    "max_sending_domain_probes": 80,
    "request_timeout_ms": 9000,
    "dns_timeout_ms": 4000,
    "skipCache": false,
    "qa_test_mode": false
};

// Run the Actor and wait for it to finish
const run = await client.actor("mambalabs/outbound-infrastructure-fingerprint").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 = {
    "domain": "smartlead.ai",
    "domains": [],
    "scan_sending_domains": True,
    "sending_domain_depth": "deep",
    "check_deliverability": False,
    "batchSize": 3,
    "max_sending_domain_probes": 80,
    "request_timeout_ms": 9000,
    "dns_timeout_ms": 4000,
    "skipCache": False,
    "qa_test_mode": False,
}

# Run the Actor and wait for it to finish
run = client.actor("mambalabs/outbound-infrastructure-fingerprint").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{
  "domain": "smartlead.ai",
  "domains": [],
  "scan_sending_domains": true,
  "sending_domain_depth": "deep",
  "check_deliverability": false,
  "batchSize": 3,
  "max_sending_domain_probes": 80,
  "request_timeout_ms": 9000,
  "dns_timeout_ms": 4000,
  "skipCache": false,
  "qa_test_mode": false
}' |
apify call mambalabs/outbound-infrastructure-fingerprint --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=mambalabs/outbound-infrastructure-fingerprint",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

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