
Moosend exposes a single REST API-v3, at https://api.moosend.com/v3-covering subscribers, lists, segments, campaigns, reporting and transactional sends. It works, and it is visibly dated: auth is an apikey query-string parameter, response format is chosen by a .json or .xml extension on the path, and pagination uses path segments. On the agent side the verdict is blunt: as of August 2026 there is no official Moosend MCP server, and no dedicated community one either.
That shapes how you build. The API is simple enough that a plain HTTP client beats every official SDK, all nine of which are abandoned, but the developer surface has not moved in years, so treat Moosend as a marketing back end you script against, not a platform you expect to grow with you. For relay configuration, see SMTP settings.
Model Context Protocol gives an AI agent typed, permissioned access to a platform without a hand-rolled wrapper, for an ESP, an assistant adding a subscriber or triggering an automation as a first-class tool call. Moosend has not shipped that surface, and neither has its parent company.
Verified August 19, 2026: the Moosend GitHub organisation holds 18 public repositories and none is MCP-related, docs.moosend.com never mentions MCP, and public directories list no first-party implementation. Constant Contact, owner since June 2025, has none either, no parent-company umbrella to inherit. Agent access runs through iPaaS servers that wrap the Moosend connector.
All three are third-party, none endorsed by Moosend, and none exposes the full v3 surface, you get whatever the intermediary modelled. Ranked by practicality.
The most practical route in 2026. Surfaces the official Moosend Zapier app as agent tools. Create Subscriber, Set Custom Field Value, Run Automation, plus New Subscriber and New Unsubscriber triggers. Moosend is not a premium Zapier app, so these run on the free plan.
A Moosend app with prebuilt actions and an HTTP/webhook bridge, exposed through Pipedream MCP. Useful when you need custom code steps around the call.
Listed in Moosend’s own native directory and drivable from agents via Make’s agent tooling. Best when the Moosend call is one step in a longer multi-system scenario.
MCP availability is a proxy for how alive a vendor’s developer organisation is, and Moosend’s absence fits the picture: SDKs untouched since 2017, release notes silent since February 2023, webhooks that exist only as an automation step. If agents writing to your ESP are on the 2026 roadmap, budget for your own MCP wrapper around the v3 API, or for paying an iPaaS to be that layer. Brevo and MailerLite sit in the same price band with fresher tooling.
Moosend v3 is a resource-and-action API rather than a strict REST-verb API: writes are POST to a named action path such as /subscribe.json, reads are GET on a matching path. Two conventions catch out newcomers.
| Base URL | https://api.moosend.com/v3 |
| Version | v3-the only documented version |
| Response format | Selected by path extension: .json or .xml. No Accept-header negotiation. |
| Authentication | apikey query-string parameter on every request |
| Envelope | Every response wraps payload in { Code, Error, Context } |
| Pagination | Path segments: /campaigns/{Page}/{PageSize}.json. No cursor, no Link header. |
| Rate limiting | Per-endpoint, per-key. HTTP 429 with {"Code": 429, "Error": "RATE-LIMITING"} |
| Webhooks | Automation-workflow action only, no subscription endpoints |
| Inbound parse | Not offered |
| Request timeout | Not published, set your own client timeout |
The format extension is load-bearing: /campaigns/create without .json is not the same route, and putting the query string before the extension produces a path Moosend does not recognise. Order is always path → .json → ?apikey=-centralise URL construction in one helper. Pagination has the same problem: {Page} and {PageSize} are positional segments, so omitting page size changes the route rather than applying a default, and no default is documented. Pass both, and stop paging when a collection returns shorter than the size you asked for.
There is exactly one documented method. Generate a key at More › Settings › API key and append it as ?apikey=YOUR_API_KEY. No Bearer token, no header key, no HMAC signing and no OAuth flow appears anywhere in the v3 reference, a connector that assumes header auth needs a special case for Moosend.
A Moosend key is not a global credential: it carries the permissions of the role that generated it. Owner, Admin, Viewer, Manager, Designer, Admin observer or GDPR viewer, so a key made by a limited role only reaches what that role reaches. That is the closest thing to scoped keys on offer. For a read-only reporting key, create a dedicated user with a restricted role and generate the key as that user.
Because the key travels as a URL parameter rather than a header, it lands in access logs, browser history, proxy logs, referrer headers and any error tracker capturing full request URLs. Treat a Moosend key as compromised the moment it reaches a log aggregator: never call the API from client-side JavaScript, scrub the apikey parameter in logging middleware, and rotate on a schedule rather than only after an incident.
Sub-accounts carry their own credentials, so generate the key while signed in as the sub-account, a parent key does not reach sub-account data. The relay is a separate system: smtp.mailendo.com authenticates with your Moosend account login, and a sub-account uses a domain-prefixed username of the form myaccount\username. Moosend issues no SMTP-only credential, see SMTP settings.
Moosend publishes no global requests-per-minute figure. Limits are per endpoint, enforced per API key, and the spread is wide enough that your architecture depends on which endpoints you touch.
| Endpoint | Limit | Practical effect |
|---|---|---|
| POST subscribe | 10 calls / 10s | About 1 subscriber/second sustained |
| POST subscribe_many | 2 calls / 10s | Pace bulk imports, never parallelise |
| POST transactional/send | 6 calls / 1s | Tightest per-second limit published |
| GET campaign stats | 2 calls / 60s | Dashboards must cache aggressively |
| Unsubscribe endpoints | 20 calls / 10s | Fine for preference-centre traffic |
| DELETE subscriber | 40 calls / 10s | Most permissive documented endpoint |
| Batch delete subscribers | 8 calls / 10s | Prefer to single deletes for GDPR sweeps |
The workaround differs per pinch point. For migration, drive everything through subscribe_many at one call per five seconds and accept that a large import runs for hours, parallel single subscribe calls just produce a stream of 429s. For reporting, the 2 calls / 60s statistics ceiling makes per-campaign polling impractical: build on /view_summary.json, cache recipient-level activity for an hour or more, and refresh on a queue rather than on page load. For transactional volume, 6 calls / 1s is about 21,600 messages an hour at perfect saturation, fine for receipts, thin for a real transactional workload, where Postmark fits better. No Retry-After header is documented, so use exponential backoff with jitter.
Moosend publishes API wrappers for nine languages. All are Swagger-generated and none is maintained: eight were last committed on October 2-3, 2017, the PHP wrapper alone reaching February 2020. Read them for request shapes; do not add them to a manifest.
| Language | Package | Install | Repo |
|---|---|---|---|
| JavaScript | moosend-api | npm install moosend-api --save | js |
| PHP | moosend/api-wrappers-php | composer require moosend/api-wrappers-php | php |
| Python | moosend_api_wrapper | pip install git+https://github.com/moosend/api-wrappers-python.git | python |
| C#, Java, Ruby, Go, Swift, Scala | Source only, no package published | git clone the repo | 6 repos in the Moosend org |
Only two ship through a package manager: moosend-api on npm and moosend/api-wrappers-php on Packagist. The Python wrapper was never published to PyPI, installation is git-only and its README still advertises Python 2.7.
The JavaScript wrapper is callback-style with no Promise support, so no async/await. The Python wrapper predates f-strings. None of the nine has been patched for a transitive CVE in six to nine years. Because the API is trivially simple, one query parameter for auth, one path extension for format-requests, fetch or Guzzle is less code and materially safer.
There is no meaningful third-party SDK ecosystem here. npm, PyPI and Packagist surface no maintained community client with real adoption, unsurprising for an API this thin, where a wrapper would be a hundred lines. The community routed around it with iPaaS connectors instead: the official Zapier app, the Make app in Moosend’s own directory, plus Pipedream, viaSocket, ApiX-Drive and Pabbly Connect. Any of those is a more realistic abstraction layer than a hand-rolled SDK, and Zapier doubles as your MCP path. The only first-party code with recent commits is the website tracking library (June 2024), a browser snippet, not an API client.
Fifteen of the most-used routes are below. Every path takes a {Format} extension of json or xml plus the apikey parameter. Request bodies and field types live in the official API documentation.
| Resource | Method | Description |
|---|---|---|
| Subscribers /subscribers/{ListID}/subscribe.{Format} | POST | Add one subscriber with custom fields. |
| Subscribers /subscribers/{ListID}/subscribe_many.{Format} | POST | Bulk-add subscribers in one call. |
| Subscribers /subscribers/{ListID}/view.{Format} | GET | Look up a subscriber by email address. |
| Subscribers /subscribers/{ListID}/update/{SubscriberID}.{Format} | POST | Update a subscriber’s email or custom fields. |
| Subscribers /subscribers/{ListID}/unsubscribe.{Format} | POST | Unsubscribe from a single list. |
| Subscribers /subscribers/unsubscribe.{Format} | POST | Unsubscribe account-wide rather than per list. |
| Subscribers /subscribers/{ListID}/remove.{Format} | POST | Permanently remove, distinct from unsubscribing. |
| Mailing lists /lists/{Page}/{PageSize}.{Format} | GET | Page active lists; /lists.json returns all. |
| Mailing lists /lists/create.{Format} | POST | Create a new empty mailing list. |
| Mailing lists /lists/{ListID}/subscribers/{Status}.{Format} | GET | Members by status: Subscribed, Bounced, Removed. |
| Custom fields /lists/{ListID}/customfields/create.{Format} | POST | Define a custom field; update and delete share the prefix. |
| Segments /lists/{ListID}/segments/create.{Format} | POST | Create a segment, then add /criteria/add.json rules. |
| Campaigns /campaigns/create.{Format} | POST | Create a draft; edit via /{CampaignID}/update.json. |
| Campaigns /campaigns/{CampaignID}/send.{Format} | POST | Send a draft now; /schedule and /send_test exist too. |
| Transactional /campaigns/transactional/send.{Format} | POST | Send a transactional message. |
Moosend has no ping endpoint. /senders/find_all.json is the cheapest read that proves a key works, and it doubles as the fastest way to confirm from code that a sending domain cleared SPF, DKIM and DMARC.
curl -s "https://api.moosend.com/v3/senders/find_all.json?apikey=YOUR_API_KEY"
# Success looks like:
# { "Code": 0, "Error": null, "Context": { "Senders": [ ... ] } }
#
# A bad key still returns HTTP 200. Check Error, not the status code.import requests
API_KEY = "YOUR_API_KEY"
LIST_ID = "YOUR_MAILING_LIST_ID"
BASE = "https://api.moosend.com/v3"
# Note the .json extension and the apikey QUERY parameter - Moosend has
# no header-based auth and no Accept-header negotiation.
url = f"{BASE}/subscribers/{LIST_ID}/subscribe.json"
resp = requests.post(
url,
params={"apikey": API_KEY},
json={
"Name": "Ada Lovelace",
"Email": "ada@example.com",
"CustomFields": ["Country=Greece", "Plan=Pro"],
},
timeout=30,
)
# {"Code": 429, "Error": "RATE-LIMITING"} - subscribe is capped at 10 calls / 10s
if resp.status_code == 429:
raise SystemExit("Rate limited by Moosend, back off and retry")
data = resp.json()
# Moosend returns HTTP 200 even for logical failures: always check Error.
if data.get("Error"):
raise SystemExit(f"Moosend error: {data['Error']}")
print(data["Context"])Custom fields are an array of Name=Value strings rather than an object, and each must already exist on the list, posting an unknown field name returns HTTP 200 with a populated Error.
// The official 'moosend-api' package is untouched since 2017 - use fetch.
const { MOOSEND_API_KEY: KEY, MOOSEND_LIST_ID: LIST } = process.env;
async function subscribe(email, name) {
const url = `https://api.moosend.com/v3/subscribers/${LIST}`
+ `/subscribe.json?apikey=${KEY}`;
const res = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ Name: name, Email: email,
CustomFields: ["Country=Greece", "Plan=Pro"] }),
});
if (res.status === 429) throw new Error("RATE-LIMITING: 10 calls / 10s");
// Every response is { Code, Error, Context } with HTTP 200 even on
// failure - never trust the status code alone.
const data = await res.json();
if (data.Error) throw new Error(`Moosend error: ${data.Error}`);
return data.Context;
}Moosend v3 returns 200 for failures other APIs signal with a 4xx: an unknown list ID, an invalid custom field name, a malformed address. Code branching on response.ok or resp.status_code alone treats these as successes and silently drops subscribers, invisible until someone notices a list is not growing. Parse the body and assert Error is null before trusting Context. Only rate limiting surfaces as a real status, at 429.
Campaign statistics allow 2 calls per 60 seconds, so a dashboard polling per-campaign metrics beyond a handful of campaigns needs an aggressive cache. Bulk import allows 2 calls per 10 seconds, so a large migration must be paced rather than fanned out across workers, going one at a time to dodge the batch limit is slower in aggregate.
Moosend’s webhook capability exists only as a “Then post a webhook” step inside an automation, POSTing JSON to a URL you type into the builder. There is no endpoint to register, list or delete subscriptions, no event catalogue for bounce or complaint, and no documented HMAC signature, retry policy or payload size limit, so you cannot verify an inbound POST came from Moosend. Mitigate with a long unguessable receiver path plus an IP allowlist, and treat payloads as untrusted.
An API key does not authenticate the relay, and account credentials do not authenticate the API. The relay at smtp.mailendo.com uses your Moosend account login over STARTTLS on port 587. Ports 465 and 25000 were retired on October 1, 2025 and port 25 is unencrypted, so 587 is the only encrypted path. Rotating the account password rotates the relay password too, detail on the SMTP settings tab.
Moosend’s official release notes have been silent since early 2023, so the two most consequential recent changes, an ownership transfer and an SMTP port retirement, were announced elsewhere. The record below stitches those sources together.
No API version has been deprecated: v3 is the only documented version and no v4 is announced. The real risk is stagnation, not a version cut. Check the official release notes before assuming anything here has changed, and read the absence of entries as data.
Go to More › Settings › API key and append the value to every request as ?apikey=YOUR_API_KEY. The key inherits the permissions of the role that generated it. Owner, Admin, Viewer, Manager, Designer, Admin observer or GDPR viewer, so for a read-only key, create a restricted-role user and generate it as that user. Sub-accounts need their own key.
There is no global limit; limits are per endpoint, per key. subscribe allows 10 calls / 10s, subscribe_many 2 / 10s, unsubscribe 20 / 10s, delete 40 / 10s, batch delete 8 / 10s, transactional send 6 / 1s, and campaign statistics just 2 / 60s. Breaching one returns HTTP 429 with {"Code": 429, "Error": "RATE-LIMITING"} and no Retry-After header, so use backoff with jitter.
Yes. REST API v3 at https://api.moosend.com/v3, the only documented version, with no successor announced. Two conventions differ from modern ESP APIs: response format comes from a .json or .xml path extension rather than an Accept header, and pagination uses positional path segments such as /campaigns/{Page}/{PageSize}.json. Coverage spans subscribers, lists, custom fields, segments, campaigns, reporting and transactional sends.
POST to /subscribers/{MailingListID}/subscribe.json?apikey=YOUR_API_KEY with Name, Email and optionally CustomFields-an array of Name=Value strings, each field already defined on the list. Capped at 10 calls / 10s; use subscribe_many.json at 2 calls / 10s for imports. Always check the Error field: a bad list ID returns HTTP 200 with the error inside.
Only as an automation action, not a subscribable event API. You add a “Then post a webhook” step inside a workflow and supply a URL; Moosend POSTs JSON when the step is reached. There is no endpoint to register or delete subscriptions, no catalogue of delivery events such as bounced or complained, and no documented HMAC signature, retry policy or payload size limit. For subscriber events without automations, the official Zapier app offers New Subscriber, Updated Profile and New Unsubscriber triggers.
No. As of August 2026 none exists: the Moosend GitHub organisation holds 18 public repositories with none MCP-related, docs.moosend.com does not mention MCP, and public directories list nothing. No dedicated community server was found either, and Constant Contact, owner since June 2025, has none. Agent access runs through iPaaS servers; Zapier MCP is the most practical, since Moosend is not a premium Zapier app. Re-verify before committing to an architecture.
This review follows our email infrastructure testing methodology. We disclose affiliate relationships in our editorial independence policy.