# Sequencer Lead Push Instantly Smartlead Clay Outbound (`mambalabs/clay-to-instantly-smartlead-push`) Actor

Pushes Clay-enriched leads into an Instantly or Smartlead campaign. Maps Clay column names to sequencer fields, gates on ICP score, deduplicates against the campaign, and batches the push. Dry run returns the exact payload without sending. Instantly API v2 and Smartlead API v1.

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

## Pricing

from $8.50 / 1,000 lead pushed to sequencers

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

## Instantly and Smartlead Lead Push

Push Clay-enriched leads straight into an Instantly or Smartlead campaign. ICP score gating and deduplication are built in.

Give it a list of lead rows, or the dataset ID of an upstream Apify run. It maps your Clay column names to the sequencer's field names, drops anything below your ICP score threshold, drops anything already in the campaign, and pushes the rest in batches.

### Read this first, it might save you money

The real alternative to this actor is Clay's own sequencer push, which is built into Clay and costs nothing extra if you are on an eligible plan. If you have that, use it. It is native, it has a UI, and it is free to you.

This actor is for the people that one does not cover:

- Clay users on a plan tier that does not include the native Instantly or Smartlead integration, where the choice is a plan upgrade or a per-lead cost
- Apify-native pipelines with no Clay subscription at all, where leads are enriched by Apify actors and need to reach a sequencer
- Agencies running many client campaigns that want an API-triggered push step instead of a manual CSV import

This actor does not beat Clay's integration. It reaches the cases Clay's integration does not.

### Supported sequencers

| Sequencer | API | Notes |
| --- | --- | --- |
| Instantly | v2 | Needs a **v2** API key. Keys from v1 do not work: Instantly deprecated v1 on January 19, 2026. |
| Smartlead | v1 | Current. Smartlead has no v2. |

The campaign has to exist already. This actor adds leads to a campaign, it does not create one.

### How it works

1. **Read.** Takes either `leads` (an array of rows) or `dataset_id` (an Apify dataset from an upstream run). If you supply both, `leads` wins and the run says so.
2. **Validate.** Checks the sequencer name, the API key and the campaign ID. Rows with no usable email are counted and dropped here, not silently.
3. **ICP gate.** Drops rows whose `icp_score` is below `min_icp_score`. Rows with no score at all are kept, because unscored is not the same as low.
4. **Deduplicate.** Reads the campaign's existing leads and drops any email already there. Repeats inside your own input are dropped too.
5. **Map.** Renames your Clay columns to the sequencer's field names. Anything not mapped and not named in `custom_variables` is dropped.
6. **Push.** Sends in batches, 500 per call for Instantly and 400 per call for Smartlead, with a pause between batches to stay inside each vendor's rate limit.

### Input

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `sequencer` | string | yes | `instantly` or `smartlead` |
| `api_key` | string | yes | Your v2 Instantly key or your Smartlead key. Not needed for a dry run with deduplication off, which makes no calls at all. |
| `campaign_id` | string | yes | The target campaign. Instantly IDs are UUIDs, Smartlead IDs are numeric. |
| `leads` | array | one of | Lead rows. Each needs an `email` at minimum. |
| `dataset_id` | string | one of | An Apify dataset ID from an upstream run. Used only when `leads` is empty. |
| `min_icp_score` | integer | no | Drop leads scoring below this. Default 0, which pushes everything. |
| `deduplicate` | boolean | no | Check the campaign before pushing. Default true. |
| `field_mapping` | object | no | Override the default map. Keys are your Clay column names, values are sequencer field names. |
| `custom_variables` | array | no | Extra Clay column names to pass through under their own name. |
| `dry_run` | boolean | no | Preview without sending. Default false. |

### Default field mapping

| Clay column | Instantly | Smartlead |
| --- | --- | --- |
| `email` | `email` | `email` |
| `first_name` | `first_name` | `first_name` |
| `last_name` | `last_name` | `last_name` |
| `company_name` | `company_name` | `company_name` |
| `company_domain` | `website` | `company_url` |
| `linkedin_person_url` | `custom_variables.linkedin_url` | `linkedin_profile` |
| `personalization_line` | `personalization` | `custom_fields.personalization` |
| `icp_tier` | `custom_variables.icp_tier` | `custom_fields.icp_tier` |
| `icp_score` | `custom_variables.icp_score` | `custom_fields.icp_score` |

Anything not in this table and not in `custom_variables` is dropped.

The two sequencers do not accept the same field names, which is why the columns differ. Instantly has a native `personalization` field and no LinkedIn field; Smartlead has a native `linkedin_profile` field and no personalization field. Each one's leftovers go into that vendor's custom bucket.

#### Overriding the mapping

`field_mapping` merges over the defaults. Add a column, point a column somewhere else, or set a value to an empty string to drop it.

```json
{
  "field_mapping": {
    "job": "job_title",
    "company_domain": "",
    "icp_tier": "tier"
  }
}
```

You do not have to know which target names are native. If you name a field the sequencer does not have, it is sent as a custom variable instead of breaking the request. This matters on Instantly, where one unrecognized top-level key rejects the entire batch.

### custom\_variables

`field_mapping` renames columns. `custom_variables` passes them through under the name they already have.

```json
{ "custom_variables": ["headcount", "funding_stage", "tech_stack"] }
```

Those three columns land in `custom_variables` on Instantly and `custom_fields` on Smartlead, ready to use as merge tags in your sequence copy. Values that are not text or numbers are converted to JSON text, because neither vendor accepts nested objects in a custom field.

### dry\_run

`dry_run: true` runs every step, including the deduplication read, then publishes the exact request batches it would have sent and stops. No lead is created, and nothing is billed.

Use it to check a mapping before spending anything, especially the first time you point the actor at a new campaign. The published payload is the real one, so what you see is what a live run would send. Your API key is never included in it.

A dry run with `deduplicate: false` makes no calls at all and needs no API key, so you can try the actor before you have credentials to hand.

### Output

One row per run.

| Field | Type | Description |
| --- | --- | --- |
| `total_leads_received` | integer | Rows read from the input |
| `leads_invalid` | integer | Rows dropped for having no usable email |
| `leads_below_min_score` | integer | Rows dropped by the ICP gate |
| `leads_deduplicated` | integer | Rows dropped as duplicates |
| `leads_eligible` | integer | Rows that passed every gate and were sent, or on a dry run would have been |
| `leads_pushed` | integer | Leads the sequencer confirmed it created. This is the number billed. |
| `leads_skipped_by_sequencer` | integer | Sent, accepted as a request, but not created by the vendor. Usually a blocklist or duplicate rule. Not billed. |
| `leads_failed` | integer | Leads in a batch the sequencer rejected. Not billed. |
| `failed_emails` | array | One `{email, error}` per failure, carrying the vendor's own message |
| `dry_run_payload` | array | On a dry run, the exact batches that would have been sent |
| `vendor_api_version` | string | Which vendor API the run used |
| `dedupe_complete` | boolean | False when the existing-lead lookup stopped early, so the check was partial |
| `run_date` | string | ISO 8601 timestamp |
| `sequencer` | string | Echoed from the input |
| `campaign_id` | string | Echoed from the input |
| `dry_run` | boolean | Whether this run wrote anything |
| `is_summary_row` | boolean | True when the run could not process leads, for example a validation failure |
| `notice` | string | Anything the run needs to tell you that is not a count |

The counts always add up, in both directions:

```
total_leads_received = leads_invalid + leads_below_min_score + leads_deduplicated + leads_eligible
leads_eligible       = leads_pushed + leads_skipped_by_sequencer + leads_failed
```

If a lead does not arrive in your campaign, one of those counters says why. Nothing disappears quietly.

### Sample input

```json
{
  "sequencer": "instantly",
  "api_key": "your-instantly-v2-key",
  "campaign_id": "019fdec7-6915-70ab-be87-8bdc0bb78acf",
  "min_icp_score": 70,
  "deduplicate": true,
  "dry_run": false,
  "custom_variables": ["headcount"],
  "leads": [
    {
      "email": "jane.doe@example.com",
      "first_name": "Jane",
      "last_name": "Doe",
      "company_name": "Example Inc",
      "company_domain": "example.com",
      "linkedin_person_url": "https://www.linkedin.com/in/example",
      "personalization_line": "Saw you are hiring two SDRs this quarter.",
      "icp_tier": "A",
      "icp_score": 88,
      "headcount": 250
    }
  ]
}
```

### Full pipeline

This actor is built to sit at the end of a chain. Each step passes its dataset ID to the next.

1. **GTM Hiring Signal Scraper** finds companies hiring for go-to-market roles
2. **GTM Tech Stack Signal Scraper** reads what they run
3. **ICP Fit Scorer** turns those signals into an `icp_score` and an `icp_tier`
4. **This actor** gates on that score and pushes what survives into your campaign

Set `dataset_id` to the ICP Fit Scorer's output and `min_icp_score` to your threshold, and the whole chain runs without a CSV.

### Pricing

Pay per event. You are charged for one thing: a lead the sequencer confirms it created.

| Plan | Per lead pushed |
| --- | --- |
| Free | $0.0100 |
| Bronze | $0.0095 |
| Silver | $0.0090 |
| Gold | $0.0085 |

Free, in every case:

- Leads dropped by the ICP gate
- Leads dropped by deduplication
- Leads with no usable email
- Leads the sequencer rejected or skipped
- Every lead in a dry run

A 500-lead campaign costs $4.75 on Bronze. If half your list fails the ICP gate, it costs $2.38, because the gated half was never charged.

The Apify actor start fee of $0.00005 applies per run, as it does to every actor on the platform.

### Known limitations

- **The campaign must already exist.** This actor adds leads to a campaign. It does not create campaigns or write sequence copy.
- **Instantly needs a v2 key.** A v1 key returns an authentication error. v1 was deprecated on January 19, 2026 and v2 keys are generated separately.
- **Smartlead deduplication walks the campaign.** Smartlead's lead list has no email filter, so checking a large campaign means paging through it. The walk stops at 20,000 leads and sets `dedupe_complete` to false when it does. Instantly is not affected: its API filters by email directly.
- **Vendor-side skips are visible but not itemized.** Both sequencers report how many leads they skipped for their own blocklist and duplicate rules, but neither says which ones. The count lands in `leads_skipped_by_sequencer`.
- **No proxy configuration.** These are authenticated API calls to two vendors, not scraping, so there is nothing to rotate.

### Related actors

- **ICP Fit Scorer** produces the `icp_score` and `icp_tier` this actor gates on
- **GTM Hiring Signal Scraper** and **GTM Tech Stack Signal Scraper** produce the signals that feed the scorer
- **Domain Deliverability Checker** checks your sending domains before you point a campaign at them

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

# Actor input Schema

## `sequencer` (type: `string`):

Which sequencer to push to. Instantly uses API v2, Smartlead uses API v1.

## `api_key` (type: `string`):

Your Instantly v2 API key or your Smartlead API key. An Instantly v1 key will not work: v1 was deprecated on January 19, 2026. Required for any run that calls the sequencer. A dry run with deduplication off makes no calls and needs no key.

## `campaign_id` (type: `string`):

The target campaign in the sequencer. The campaign must already exist. Instantly campaign IDs are UUIDs; Smartlead campaign IDs are numeric.

## `leads` (type: `array`):

Lead rows to push. Each row needs an email at minimum. Takes precedence over Upstream dataset ID when both are set.

## `dataset_id` (type: `string`):

An Apify dataset ID from an upstream run, for example the output of the ICP Fit Scorer. Used only when Leads is empty.

## `min_icp_score` (type: `integer`):

Drop leads whose icp\_score is below this number. 0 pushes everything. Rows with no icp\_score are always kept.

## `deduplicate` (type: `boolean`):

Read the campaign's existing leads first and drop any email already there. This is a read, so it needs an API key even on a dry run. Off by default on the sample input so the actor can be tried without credentials.

## `dry_run` (type: `boolean`):

Run every step, build the exact payload that would be sent, and publish it to the dataset without making a single write call. Charges nothing.

## `field_mapping` (type: `object`):

Override the default Clay column to sequencer field map. Keys are your Clay column names, values are the sequencer field names. Set a value to an empty string to drop that column. Anything that is not a native sequencer field is sent as a custom variable instead of being rejected.

## `custom_variables` (type: `array`):

Extra Clay column names to pass through as custom variables under their own name. Columns that are neither mapped nor listed here are dropped.

## Actor input object example

```json
{
  "sequencer": "instantly",
  "campaign_id": "00000000-0000-0000-0000-000000000000",
  "leads": [
    {
      "email": "jane.doe@example.com",
      "first_name": "Jane",
      "last_name": "Doe",
      "company_name": "Example Inc",
      "company_domain": "example.com",
      "linkedin_person_url": "https://www.linkedin.com/in/example",
      "personalization_line": "Saw you are hiring two SDRs this quarter.",
      "icp_tier": "A",
      "icp_score": 88
    },
    {
      "email": "sam.smith@example.org",
      "first_name": "Sam",
      "last_name": "Smith",
      "company_name": "Example Org",
      "company_domain": "example.org",
      "icp_tier": "B",
      "icp_score": 61
    }
  ],
  "dataset_id": "",
  "min_icp_score": 0,
  "deduplicate": true,
  "dry_run": true,
  "field_mapping": {},
  "custom_variables": []
}
```

# 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 = {
    "sequencer": "instantly",
    "campaign_id": "00000000-0000-0000-0000-000000000000",
    "leads": [
        {
            "email": "jane.doe@example.com",
            "first_name": "Jane",
            "last_name": "Doe",
            "company_name": "Example Inc",
            "company_domain": "example.com",
            "linkedin_person_url": "https://www.linkedin.com/in/example",
            "personalization_line": "Saw you are hiring two SDRs this quarter.",
            "icp_tier": "A",
            "icp_score": 88
        },
        {
            "email": "sam.smith@example.org",
            "first_name": "Sam",
            "last_name": "Smith",
            "company_name": "Example Org",
            "company_domain": "example.org",
            "icp_tier": "B",
            "icp_score": 61
        }
    ],
    "dataset_id": "",
    "min_icp_score": 0,
    "deduplicate": false,
    "dry_run": true,
    "field_mapping": {},
    "custom_variables": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("mambalabs/clay-to-instantly-smartlead-push").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 = {
    "sequencer": "instantly",
    "campaign_id": "00000000-0000-0000-0000-000000000000",
    "leads": [
        {
            "email": "jane.doe@example.com",
            "first_name": "Jane",
            "last_name": "Doe",
            "company_name": "Example Inc",
            "company_domain": "example.com",
            "linkedin_person_url": "https://www.linkedin.com/in/example",
            "personalization_line": "Saw you are hiring two SDRs this quarter.",
            "icp_tier": "A",
            "icp_score": 88,
        },
        {
            "email": "sam.smith@example.org",
            "first_name": "Sam",
            "last_name": "Smith",
            "company_name": "Example Org",
            "company_domain": "example.org",
            "icp_tier": "B",
            "icp_score": 61,
        },
    ],
    "dataset_id": "",
    "min_icp_score": 0,
    "deduplicate": False,
    "dry_run": True,
    "field_mapping": {},
    "custom_variables": [],
}

# Run the Actor and wait for it to finish
run = client.actor("mambalabs/clay-to-instantly-smartlead-push").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 '{
  "sequencer": "instantly",
  "campaign_id": "00000000-0000-0000-0000-000000000000",
  "leads": [
    {
      "email": "jane.doe@example.com",
      "first_name": "Jane",
      "last_name": "Doe",
      "company_name": "Example Inc",
      "company_domain": "example.com",
      "linkedin_person_url": "https://www.linkedin.com/in/example",
      "personalization_line": "Saw you are hiring two SDRs this quarter.",
      "icp_tier": "A",
      "icp_score": 88
    },
    {
      "email": "sam.smith@example.org",
      "first_name": "Sam",
      "last_name": "Smith",
      "company_name": "Example Org",
      "company_domain": "example.org",
      "icp_tier": "B",
      "icp_score": 61
    }
  ],
  "dataset_id": "",
  "min_icp_score": 0,
  "deduplicate": false,
  "dry_run": true,
  "field_mapping": {},
  "custom_variables": []
}' |
apify call mambalabs/clay-to-instantly-smartlead-push --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=mambalabs/clay-to-instantly-smartlead-push",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/0Jv27VeWM5tSZQs9x/builds/KpWWmvsRKKg9KCMGU/openapi.json
