# Cold Email Compliance Check (UK, US, Canada, Germany, France) (`gabe_ao/cold-email-compliance-check`) Actor

Before you cold email a list, check every address: ok to email, consent needed or blocked. Each answer quotes the rule and its source, says what your footer must include and comes with a signed receipt. Covers UK PECR and GDPR, US CAN-SPAM, Canada's CASL, Germany's UWG and France.

- **URL**: https://apify.com/gabe_ao/cold-email-compliance-check.md
- **Developed by:** [Gabriel Imevbore](https://apify.com/gabe_ao) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $30.00 / 1,000 address checks

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

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

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Cold Email Compliance Check

Check every address before you cold email it. For each one you get:

- a **verdict**: ok to email, consent needed, blocked or unknown
- the **rule** that decided it, with its source
- what your email's **footer must include**
- the **next step**
- a **signed receipt** you can show later

Covers the UK (PECR and UK GDPR), the US (CAN-SPAM), Canada (CASL), Germany (UWG) and France. Powered by [Precleared](https://precleared.dev).

### Why use it

The rules turn on details that are easy to get wrong. In the UK you can usually cold email a limited company, but usually not a sole trader without their consent. In Germany you need consent even when the recipient is a business. This Actor works it out address by address:

1. It checks the domain's mail server, the company register (Companies House for UK companies) and the company's own website.
2. It applies the rules for the recipient's country and yours.
3. It saves the answer as a receipt signed with Precleared's key, so you have a record of the check.

Nothing is sent to the recipient.

### How to use it

1. Paste the addresses into **Email addresses**, one per line.
2. Choose where your business is based and where the addresses came from.
3. Run it. You get one row per address in the dataset, plus a summary.

To check leads as they arrive, connect the Actor to Google Sheets, Make, Zapier or n8n with Apify's integrations. AI agents can call it as a tool through Apify's MCP server.

### Input

| Field | What it does |
| --- | --- |
| Email addresses | One per line. Each address is one check. Duplicates are checked once. |
| Where your business is based | United Kingdom or United States. |
| Where the addresses came from | Published by them, bought, scraped, given to you, or not sure. Some rules treat a bought or scraped list differently. |
| What you're offering | Optional. Helps where a rule depends on whether the offer is relevant to the recipient's job. |
| Recipients' country | Optional. Used only when the country can't be worked out, which mostly matters for .com addresses. |
| Leads with details | Optional. Objects with `email` and, if you have them, `address_source`, `company_number` and `recipient_country`. |
| Your Precleared API key | Optional. Use your own Precleared allowance instead of paying per check here. |

Example:

```json
{
  "emails": ["jane@northwind-example.co.uk", "sam@samsplumbing-example.co.uk"],
  "senderCountry": "GB",
  "addressSource": "published",
  "pitch": "bookkeeping software for small agencies"
}
```

### Output

One row per address:

```json
{
  "email": "jane@northwind-example.co.uk",
  "verdict": "ok_to_email",
  "confidence": "high",
  "rule": "Under PECR the consent rule for marketing email applies to individual subscribers only. This recipient is a corporate subscriber (a company, LLP, Scottish partnership or public body), so no prior consent is needed. Where the address names a person, UK GDPR still applies to their name: rely on legitimate interests, say who you are, and make it easy to object.",
  "source_title": "PECR 2003 regulation 22(1) and ICO Guide to PECR, electronic mail marketing",
  "source_url": "https://www.legislation.gov.uk/uksi/2003/2426/regulation/22",
  "footer_requirement": "Say who you are and who the email is from, include a valid postal address, and give a simple free way to stop further emails (PECR regulation 23). Never disguise the sender.",
  "next_step": "Send. Log the receipt against the lead.",
  "recipient_country": "GB",
  "legal_form": "corporate",
  "company_name": "NORTHWIND EXAMPLE LTD",
  "receipt_id": "cc_rcpt_0123456789ABCDEF",
  "receipt_url": "https://precleared.dev/api/v1/receipt?id=cc_rcpt_0123456789ABCDEF",
  "checked_at": "2026-10-16T09:00:00.000Z",
  "billed": true,
  "error": null
}
```

What the verdicts mean:

- **ok_to_email**: send it, with a footer that meets `footer_requirement`. The requirement describes what to include; it isn't wording to paste.
- **consent_needed**: don't send a cold email. Follow `next_step`.
- **blocked**: don't send.
- **unknown**: a fact couldn't be established. Treat it as consent needed, unless you can add the missing fact, such as a company number or the recipient's country.

The run also saves a summary with the counts for each verdict in the key-value store, under `OUTPUT`.

### Pricing

**$0.03 per address checked**, charged through Apify. Invalid addresses, duplicates and addresses that couldn't be checked aren't charged. If you set a maximum cost for a run, the Actor stops before it goes over.

Already a Precleared customer? Paste your key into **Your Precleared API key**. Checks then come out of your Precleared allowance and packs, and Apify doesn't charge per check.

### FAQ

**Is this legal advice?** No. It's decision support with the rule quoted and a dated source. You decide, and you remain responsible.

**Which countries?** The UK, the US, Canada, Germany and France. Other European countries get a cautious default.

**Do I need it for invoices or password resets?** No. It's for cold or unsolicited marketing email. Transactional emails don't need a check.

**What's stored?** Precleared keeps each check and its receipt, with the address masked on public receipt pages. To count customers, it records a one-way hash of your Apify user ID, not who you are. Email support@precleared.dev to have a check deleted.

**How fast is it?** A check usually takes a few seconds. Lists run four addresses at a time.

### Support

Email support@precleared.dev. The full API and MCP docs are at [precleared.dev/docs](https://precleared.dev/docs).

# Actor input Schema

## `emails` (type: `array`):

One address per line. Each address is one check. Duplicates are checked once.

## `senderCountry` (type: `string`):

The rules depend on where you send from as well as where the recipient is.

## `addressSource` (type: `string`):

Some rules treat a bought or scraped list differently from an address the company published itself.

## `pitch` (type: `string`):

A few words, such as "bookkeeping software for small agencies". It helps where a rule depends on whether the offer is relevant to the recipient's job.

## `defaultRecipientCountry` (type: `string`):

Used only when the country can't be worked out from the address, company records or the company's website, which mostly matters for .com addresses.

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

A list of objects instead of plain addresses, for example \[{"email": "jane@acme.co.uk", "address_source": "published", "company_number": "01234567", "recipient_country": "GB"}]. The settings above fill in anything a lead leaves out.

## `preclearedApiKey` (type: `string`):

Paste your key (pc_live\_...) and checks come out of your Precleared allowance and packs, so Apify doesn't charge per check. Leave it empty to pay per check through Apify.

## Actor input object example

```json
{
  "emails": [
    "hello@example.com"
  ],
  "senderCountry": "GB",
  "addressSource": "unknown"
}
```

# Actor output Schema

## `verdicts` (type: `string`):

One row per address: verdict (ok_to_email, consent_needed, blocked or unknown), confidence, the rule and its source, what the footer must include, the next step, country, legal form, company and a link to the signed receipt. Addresses that could not be checked have the reason in the error field instead.

## `summary` (type: `string`):

Counts for each verdict, how many addresses were checked and billed, how many duplicates were skipped, and why the run stopped if it stopped early.

# 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 = {
    "emails": [
        "hello@example.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("gabe_ao/cold-email-compliance-check").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 = { "emails": ["hello@example.com"] }

# Run the Actor and wait for it to finish
run = client.actor("gabe_ao/cold-email-compliance-check").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 '{
  "emails": [
    "hello@example.com"
  ]
}' |
apify call gabe_ao/cold-email-compliance-check --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,gabe_ao/cold-email-compliance-check"
        }
    }
}
```

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/o3tZMF2RoZoxFwhd6/builds/4BHVQV1RXQZwCRfSG/openapi.json
