Sendfinch — Send Email via SMTP, Resend, SendGrid & Mailgun avatar

Sendfinch — Send Email via SMTP, Resend, SendGrid & Mailgun

Pricing

$1.00 / 1,000 email sents

Go to Apify Store
Sendfinch — Send Email via SMTP, Resend, SendGrid & Mailgun

Sendfinch — Send Email via SMTP, Resend, SendGrid & Mailgun

Send email from Apify runs, webhooks and AI agents through your own SMTP, Resend, SendGrid, Mailgun or Postmark: mail merge from datasets, result digests with CSV, attachments, per-recipient status. $0.001 per email; failures free.

Pricing

$1.00 / 1,000 email sents

Rating

0.0

(0)

Developer

CyberMax

CyberMax

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

3 hours ago

Last modified

Share

Sendfinch: Send Email API via SMTP, Resend, SendGrid, Mailgun & Postmark

Send email from Apify runs, webhooks, schedules and AI agents, to anyone, from your own domain. Sendfinch sends through your own SMTP server (Gmail / Google Workspace, Microsoft 365, Amazon SES, Zoho, Fastmail, any host) or your Resend, SendGrid, Mailgun or Postmark API key. One Actor covers every email job an automation needs:

  • Single email: alert, notification or report to one or many addresses, with cc/bcc, reply-to and attachments.
  • Digest: email the results of any Actor run as a clean table with the full data attached as CSV. Monitor mode sends only the rows that are new since the last run.
  • Mail merge: one personalised email per dataset row (Hi {{firstName}}), with invalid and duplicate addresses skipped for free.
  • List of emails: an AI agent or script sends several different emails in one call.

Every email gets a delivery record with the provider's message ID, the SMTP/HTTP response, rejected recipients and a readable error when something fails.

$0.001 per email sent (per recipient your provider accepts). Dry runs, failed sends, invalid addresses and empty digests are free. No start fee, no subscription.

Why Sendfinch

  • Email anyone, not just yourself. The free Apify "Send Email" utility only delivers to the Apify account owner's own address (its runs fail with "Users can only send an email to their own email address"). Sendfinch sends to customers, teammates and clients, from your domain, so replies come back to you and SPF/DKIM pass.
  • Five providers, one input. Switch from SMTP to Resend, SendGrid, Mailgun (US/EU) or Postmark by changing one field; the templates, digests and reports stay the same.
  • Made for chaining Actors. Point datasetId at {{resource.defaultDatasetId}} in an Apify webhook or integration and every finished run is emailed as a table + CSV. Attach a PDF or screenshot that another Actor saved in its key-value store.
  • Safe by default. Credentials are Apify secret inputs (stored encrypted, never logged). SMTP logins are checked before the first email, so a wrong password fails fast with a hint ("Gmail and Outlook need an app password") and nothing is sent or charged. maxEmails caps every run (default 100). dryRun previews every rendered email for free.
  • Reliable delivery. Rate limits (HTTP 429), provider outages (5xx) and temporary SMTP errors (4xx) are retried with backoff; permanent errors are reported per email, never silently dropped. Pooled SMTP connections and parallel API sends keep runs short.
  • Better inbox placement. A plain-text part is generated from your HTML automatically, and unsubscribeUrl adds the one-click List-Unsubscribe headers that Gmail and Yahoo expect from bulk senders.
  • Tiny and fast. 256 MB, no browser; a single email run takes about 4 s on Apify.

Ready-made inputs (first try in one click)

Free dry run, no credentials needed: renders a two-row mail merge into the dataset, sends nothing, charges nothing.

{
"mode": "merge",
"dryRun": true,
"from": "Acme Alerts <alerts@acme.com>",
"subject": "Hi {{firstName}}, your {{plan}} report is ready",
"html": "<p>Hi {{firstName}},</p><p>Your {{plan}} report for <b>{{company}}</b> is ready.</p>",
"items": [
{ "email": "ann@example.com", "firstName": "Ann", "company": "Acme Corp", "plan": "Pro" },
{ "email": "bob@example.com", "firstName": "Bob", "company": "Beta LLC", "plan": "Team" }
]
}

One alert email through Resend (swap provider for smtp, sendgrid, mailgun or postmark):

{ "provider": "resend", "apiKey": "re_...", "from": "Alerts <alerts@yourdomain.com>", "to": "you@yourdomain.com", "subject": "Run finished", "text": "Your scheduled run finished." }

Example

Email yourself the results of any Actor run (here: new articles found by Inkpluck), every morning, only the new ones:

{
"provider": "resend",
"apiKey": "re_...",
"from": "Reports <reports@yourdomain.com>",
"to": "you@yourdomain.com",
"datasetId": "{{resource.defaultDatasetId}}",
"digestFields": ["title", "siteName", "publishDate", "url"],
"onlyNew": true,
"monitorName": "morning-news",
"idField": "url"
}

Output (one record per email; this is the real record from a test run on Apify, recipient redacted; charged is 0 only because the developer's own runs aren't billed, so for you it would be 1):

{
"index": 0,
"mode": "digest",
"provider": "smtp",
"to": [
"you@yourdomain.com"
],
"cc": [],
"bcc": [],
"subject": "1 new result (2026-09-24)",
"attachments": [
"results-2026-09-24.csv"
],
"recipients": 1,
"rows": 1,
"ok": true,
"messageId": "<a4c8c4d3-b13b-d89f-f4e5-d9e6ebc24f04@ethereal.email>",
"status": "250 Accepted [STATUS=new MSGID=arRuDHr.3nbIbauCarRvOvLvAxtdqkONAAAABlF4myyownocuD12LRj-bfc]",
"charged": 0,
"ms": 1575,
"sentAt": "2026-09-24T00:30:50.325Z"
}

Mail merge from rows (or from a dataset of leads):

{
"provider": "smtp",
"smtpHost": "smtp.gmail.com",
"smtpUser": "you@yourdomain.com",
"smtpPassword": "<app password>",
"from": "You <you@yourdomain.com>",
"mode": "merge",
"items": [
{ "email": "ann@example.com", "firstName": "Ann", "company": "Acme Corp", "plan": "Pro" },
{ "email": "raj@example.org", "firstName": "Raj", "company": "Northwind", "plan": "Team" }
],
"subject": "Hi {{firstName}}, your {{plan}} report is ready",
"html": "<p>Hi {{firstName}},</p><p>Your {{plan}} report for <b>{{company}}</b> is ready.</p>",
"dryRun": true
}

With dryRun: true the dataset shows each rendered email (recipient, subject, text and HTML) and nothing is sent. More examples in examples.

Input

FieldWhat it does
providersmtp (default), resend, sendgrid, mailgun, postmark
fromSender, e.g. Acme Alerts <alerts@acme.com>; must be allowed by your SMTP login or verified with your provider
to, cc, bcc, replyToComma-separated addresses (Name <a@b.com> works)
subject, html, textContent with {{field}} placeholders ({{company.name}}, {{tags.0}}; {{{field}}} = raw HTML)
modeauto, single, messages, merge, digest (auto picks from the other fields)
datasetId / itemsRows for merge or digest: an Apify dataset (e.g. another run's) or inline JSON
toField, nameFieldMerge: which row fields hold the address and the name (default: email, emailAddress, contactEmail…)
digestFields, digestTitle, maxDigestRows, attachCsv, skipIfEmptyDigest layout
onlyNew, monitorName, idFieldMonitor mode for scheduled runs
messagesA list of different emails [{to, subject, html, text, cc, bcc, replyTo, attachments}]
attachments, keyValueStoreIdFiles by URL, from an Apify key-value store record, inline text or base64 (20 MB total)
variables, headers, unsubscribeUrl, sendSeparatelyExtra placeholders, custom headers, List-Unsubscribe, one email per to address
smtpHost, smtpPort, smtpSecure, smtpUser, smtpPasswordSMTP settings (password is a secret input)
apiKey, mailgunDomain, region, postmarkStreamAPI provider settings (key is a secret input)
dryRun, maxEmails, maxConcurrency, delayBetweenEmailsMsPreview, safety cap, speed and rate limiting

Common SMTP settings: Gmail/Google Workspace smtp.gmail.com:587 with an app password; Microsoft 365 smtp.office365.com:587; Amazon SES email-smtp.<region>.amazonaws.com:587 with SMTP credentials; Zoho smtp.zoho.com:587; Resend smtp.resend.com:587 (user resend, password = API key); SendGrid smtp.sendgrid.net:587 (user apikey).

Output

One dataset record per email: ok, to/cc/bcc, subject, recipients, charged, messageId, status, rejected (SMTP recipients refused), attachments, rows (digest), error, ms, sentAt; in dry runs also the rendered text and html. Skipped rows (invalid or duplicate addresses, bad messages) are records with ok: false and the reason. The SUMMARY record in the run's key-value store has the totals (prepared, sent, failed, skipped). The run fails, uncharged, only when every email failed or the input/credentials are wrong.

Pricing

$0.001 per email sent (event email-sent, one per recipient your provider accepted: To + Cc + Bcc). 1,000 emails cost $1. Dry runs, failed sends, invalid or duplicate addresses and "no new rows" digests are free. You also pay your email provider as usual (most have free tiers: Resend 3,000/month, Amazon SES from $0.10 per 1,000).

For comparison (Apify Store, 24 Sep 2026): the free Apify utility can only email the account owner; the other SMTP sender on the Store charges $0.01 per email plus a start fee.

Benchmark

Same job for each Actor: one HTML email to one external address (a test SMTP inbox), default settings, run on Apify on 24 Sep 2026. Rivals are the email-sending Actors on the Apify Store.

DeliveredSends to any addressRun timeMemoryPrice per emailMessage ID + SMTP response in output
Sendfinch1/1yes4.0 s256 MB$0.001yes
Apify Send Email (free)0/1: run failed, "Users can only send an email to their own email address"no1.4 s1,024 MBfreeno
SMTP bulk sender1/1yes10.2 s4,096 MB$0.01 + start feeno (status text only)

Apify's public run statistics for the free utility show 10,462 failed runs out of 66,453 in the last 30 days (16%).

Run it on a schedule (monitor mode)

Get a daily email with only what's new from any scraper:

  1. Create a task from your scraper and a task from Sendfinch with "datasetId" set to the scraper task's dataset, or simpler: add an integration to the scraper task: Integrations → Connect Actor or task → cybermax/send-email, input
    { "provider": "sendgrid", "apiKey": "SG....", "from": "Bot <bot@acme.com>", "to": "team@acme.com",
    "datasetId": "{{resource.defaultDatasetId}}", "onlyNew": true, "monitorName": "new-leads", "idField": "url" }
  2. Put the scraper task on an Apify Schedule (e.g. 0 7 * * *, 07:00 daily).
  3. Every run, Sendfinch compares the rows with what it already emailed and sends one digest of the new ones. No new rows: no email, no charge.

Mail merge + monitor mode (mode: "merge", onlyNew: true) emails each new address exactly once, e.g. a welcome email to every new sign-up in a dataset.

Integrations

  • Apify webhooks and integrations: run Sendfinch when any Actor or task finishes, with {{resource.defaultDatasetId}} (digest of results) or {{resource.defaultKeyValueStoreId}} (attach its OUTPUT PDF/screenshot via keyValueStoreId + attachments: [{"key": "OUTPUT", "filename": "page.png"}]).
  • Make: Apify → Run an Actor (cybermax/send-email) with the JSON input; map fields from earlier modules into to, subject, html.
  • Zapier: Apify → Run Actor action; e.g. new Google Sheets row → personalised email from your domain.
  • n8n: the Apify node (Run Actor) with {"to": "{{ $json.email }}", "subject": "…", "html": "…"}.
  • Google Sheets: export a sheet as CSV rows into items (or a dataset) and use mode: "merge"; results (ok, messageId, error) come back as a dataset you can import with IMPORTDATA.
  • JavaScript:
    import { ApifyClient } from 'apify-client';
    const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
    const run = await client.actor('cybermax/send-email').call({
    provider: 'postmark', apiKey: process.env.POSTMARK_TOKEN, from: 'Acme <hi@acme.com>',
    to: 'ann@example.com', subject: 'Your export is ready', html: '<p>Download it <a href="https://acme.com/x">here</a>.</p>',
    });
    const { items } = await client.dataset(run.defaultDatasetId).listItems();
    console.log(items[0].ok, items[0].messageId);
  • Python:
    from apify_client import ApifyClient
    client = ApifyClient("<APIFY_TOKEN>")
    run = client.actor("cybermax/send-email").call(run_input={
    "provider": "smtp", "smtpHost": "smtp.office365.com", "smtpUser": "me@acme.com", "smtpPassword": "<password>",
    "from": "me@acme.com", "to": "boss@acme.com", "subject": "Weekly numbers", "text": "See attached.",
    "attachments": [{"filename": "numbers.csv", "text": csv_string}]})
    print(next(client.dataset(run["defaultDatasetId"]).iterate_items())["ok"])

Use it from an AI agent (MCP)

  • Apify MCP server: connect your MCP client (Claude, Cursor, VS Code, n8n…) to https://mcp.apify.com/?tools=cybermax/send-email with your Apify token. The agent gets one tool that takes the input above (put your provider key in the agent's configuration, not in prompts).
  • Local MCP server (send-email-mcp, Node 18+): keeps your provider credentials in environment variables, so the agent only chooses recipients, subject and body, and previews with dry_run before sending.
  • API (synchronous): POST https://api.apify.com/v2/acts/cybermax~send-email/run-sync-get-dataset-items?token=<TOKEN> returns the delivery records.
  • Typical agent prompts: "Email this summary to the team", "Send each lead in this dataset a personalised intro", "Email me the new jobs every morning", "Send the PDF you just made to the client".

When NOT to use it

  • Cold spam or bought lists. Sendfinch sends through your own account; your provider's rules apply and spam gets your domain blocked. Use it for your own customers, sign-ups, team and people who expect your email. Add unsubscribeUrl for anything that isn't a one-to-one message.
  • You have no email provider. You need an SMTP login or an API key. Free options: Resend (3,000 emails/month), Brevo, Gmail with an app password (about 500/day); check each provider's current limits.
  • Very large newsletters (100,000+). Use your provider's own campaign tool; Sendfinch caps a run at 5,000 emails.
  • Receiving or reading email. Sendfinch only sends.
  • Sending from port 25. Cloud hosts block it; use 587 or 465.

FAQ

Is my password safe? smtpPassword and apiKey are Apify secret inputs: encrypted at rest, hidden in the Console and API after saving, and never written to logs or the dataset.

Am I charged when an email bounces later? Sendfinch charges when your provider accepts the email; later bounces are reported by your provider. Recipients the SMTP server refuses during sending are listed in rejected and not charged.

Can I send HTML with images? Yes: use absolute image URLs in the HTML, or attach files.

How do I test without sending? Set dryRun: true: every email is rendered into the dataset, nothing is sent, nothing is charged, and no credentials are needed.

Also from CyberMax

All tools: apify.com/cybermax.

© 2026 CyberMax. All rights reserved.