
Benchmark Email ships a modern REST API-Benchmark Email API v1.0.0, launched April 2026 at developers.benchmarkemail.io, with scoped X-API-Key auth, an OpenAPI spec and per-account regional base URLs. What it does not ship is a way to send: campaigns can be created, configured and duplicated over the API, but scheduling, sending and test-sending are web-app only, and no SMTP relay or transactional endpoint exists at any plan tier.
On the agent side there is no official MCP server as of August 2026-only third-party access via Zapier or Composio. The ceiling here is structural, not a pricing gate.
Model Context Protocol gives an AI assistant a typed tool interface instead of guessing at HTTP calls. For an ESP the real question is not whether an agent can read your lists, any wrapper does that, but whether it can close the loop and send. For Benchmark the answer is no, and no MCP server can change it.
As of August 19, 2026 Benchmark publishes no first-party MCP server. Its GitHub organisation holds 7 public repositories, all legacy XML-RPC wrappers, and the April 2026 API launch never mentioned MCP. Access runs through Zapier MCP or the commercial Composio toolkit, both inherit the same ceiling: an agent drafts, a human presses send.
Two production-usable options, both authenticating with your own key, so their traffic spends your budget.
Hosted server exposing 8 contact actions: create, update, find, add to list, remove from list, unsubscribe, create list, create tag. Generate a private MCP URL at mcp.zapier.com. No campaign send. Benchmark exposes none to wrap.
Commercial hosted toolkit advertising roughly 298 tools across campaigns, lists, reports and account admin, breadth that comes from wrapping the legacy API, not v1.0.0. Adapters exist for Pydantic AI, AutoGen and the Vercel AI SDK.
If your criterion is “can my agent run the newsletter end to end”, Benchmark fails structurally, and no MCP announcement fixes that without an API change first. It is agent-assistable, not agent-operable: an assistant enriches contacts, segments lists, drafts a campaign and pulls reports, then hands you the draft. If you need agents that actually send, evaluate Mailchimp or MailerLite, whose APIs expose send and schedule operations an MCP layer can wrap. Benchmark does publish an agent-readable SKILL.md manifest and an llms.txt index, documentation for a model, not a server your client can connect to.
The current API is documented at developers.benchmarkemail.io with an OpenAPI document at /openapi.json. The detail that trips up most first integrations: the base URL is not global. Each account is pinned to a region and cluster, so hardcoding the docs example ships a client that 401s everywhere else.
| Base URL | https://api-{region}-{cluster}.benchmarkemail.io-per account |
| Docs example host | api-us-west-2-a.benchmarkemail.io-regions us · eu · jp |
| Version | v1.0.0 CURRENT |
| Legacy base URL | clientapi.benchmarkemail.com-v3.0, deprecated, still live |
| Auth header | X-API-Key: bme_{region}_{43 chars}–50 chars |
| Pagination | Inconsistent, body params, query params, or none, by endpoint |
| Error shape | {"errors":[{"errorType","message","field"}]} |
| Budget headers | X-RateLimit-* and X-Monthly-* on every response |
| Send / schedule | Not available via API, web application only |
| SMTP relay | Does not exist at any plan tier |
The resource model is narrow and marketing-shaped: contacts, contact structures (the container defining your custom-field schema), lists, campaigns, templates, domains, reports. Note the hierarchy, lists are not top-level, they live at /api/contact-structure/{id}/lists, so most list work starts by resolving a structure id. GET /api/contact-structure is therefore the smoke test for a new key: cheap, read-scope, and it confirms base URL and region at once.
Three schemes are live at once because three APIs are. Only the first applies to anything you should build in 2026.
Keys are created by the account owner only, at Settings › API Keys, and displayed exactly once, a lost key means issuing a new one. The format is bme_{2-letter region}_{43 random chars}, 50 characters; the embedded region is a handy check that you are calling the matching host. There is no OAuth flow and no bearer variant.
Keys carry per-resource scopes-contacts, campaigns, reports, domains, each :read or :write, with write implying read. Active keys are capped at 2 on Free and 10 on Pro.
One key per integration is right for auditing and revocation, but it does not partition the rate limit. The 3,600 requests/hour ceiling is enforced per account across every key, so ten keys split one budget ten ways. Size polling intervals against the account total.
The deprecated v3.0 API at clientapi.benchmarkemail.com uses an AuthToken header carrying an admin token from ui.benchmarkemail.com/Integrate#AP. It is unscoped and account-wide, still works, has no published sunset date, and outranks the current docs in search, which is why so many 2026 integrations get built against it by accident.
The oldest surface, at apidocs.benchmarkemail.com, uses XML-RPC with token-based security methods. Every SDK in the BenchmarkEmail GitHub org targets it. Treat anything mentioning XML-RPC as archaeology, not a starting point.
Two independent ceilings stack, and both return the same status code, so the response alone will not tell you which wall you hit.
| Limit | Value | Scope |
|---|---|---|
| Hourly ceiling | 3,600 req/hour | Per account, shared across all keys |
| Sustained rate | 60 req/minute | Roughly 1 req/second |
| Monthly quota (Free) | contacts × 10 | 500 contacts = 5,000 calls/month |
| Monthly quota (paid) | contacts × 100 | 5,000 contacts = 500,000 calls/month |
| Active API keys | 2 Free · 10 Pro | Owner-created only |
| Legacy v3.0 ceiling | 500 / 2 min · 60,000/day | Different API, do not mix the sets |
| Platform send rate | ~20,000/hour | ~25,000/hour with a paid dedicated IP |
Breaching either limit returns HTTP 429 with errorType: TooManyRequestsError and a Retry-After header in seconds; the same 429 also signals IP-level blocks, which expire on their own. The workaround is budget-aware batching, not backoff tuning: retry only on 429, seeding backoff from Retry-After-a 401 or 403 is a config error that retrying just burns quota on. Drive contact sync through POST /api/contact/search at pageSize 50 so 50,000 contacts cost 1,000 calls, and check X-Monthly-Remaining before a full resync.
The weakest part of the developer story: there is no official SDK for the current API. The five wrappers in the BenchmarkEmail GitHub org all describe themselves as XML-RPC clients for the legacy v1.0 API and predate the 2026 rewrite by years. They are listed so you can avoid them.
| Language | Package | Install | Repo |
|---|---|---|---|
| PHP | benchmarkemail-php-API-wrapper | git clone-XML-RPC | GitHub |
| Java | benchmarkemail-java-API-wrapper | git clone-XML-RPC | GitHub |
| Java | bmejavawrapper | git clone-near-duplicate | GitHub |
| C# / .NET | benchmarkemail-.NET-cSharp-API-wrapper | git clone-XML-RPC | GitHub |
| Ruby | bmerubywrapper | git clone-XML-RPC | GitHub |
| Any (v1.0.0) | none published | Generate from /openapi.json | API reference |
All 5 first-party wrappers are XML-RPC clients for the oldest Benchmark API. None speaks X-API-Key, none knows the regional hosts, none parses the current {"errors":[...]} envelope, and two near-identical Java repos is itself a maintenance signal. Generate a client from the OpenAPI document instead.
There is no real community ecosystem either, unsurprising for an API four months old with no send capability to build on. The closest substitutes are workflow clients: the certified Power Automate connector and the community n8n node. Neither gives you typed models in your own codebase; for that, openapi-generator against /openapi.json emits a serviceable client in one command.
The full surface is in the official API reference; prefix every path with your base URL. Note what is absent: no /send, no /schedule, no /transactional, no webhooks.
| Resource | Methods | Description |
|---|---|---|
| Contacts /api/contact | GET, POST | List all contacts (unpaginated) or create one. |
| Contacts /api/contact/{id} | GET, PUT, PATCH, DELETE | Read, replace, update or delete one contact. |
| Contacts /api/contact/search | POST | Paginated, filtered search, use on large databases. |
| Contacts /api/contact/export | POST | Bulk export contacts out of the account. |
| Contacts /api/contact/{id}/events | GET | Opens, clicks and bounces for one contact. |
| Contacts /api/contact/events | GET | Account-wide contact events across all contacts. |
| Contact structure /api/contact-structure | GET, POST | List or create structures; smoke test for a key. |
| Contact structure /api/contact-structure/{id} | GET, PUT | Read or update a structure and its custom fields. |
| Lists /api/contact-structure/{id}/lists | GET, POST, DELETE | List, create, or bulk-delete lists under a structure. |
| Lists /api/contact-structure/{id}/lists/{listId} | GET, PATCH, DELETE | Single-list read, update, delete; duplicate alongside. |
| Campaigns /api/email/campaign | GET, POST | List with ?page=&size=, or create a draft. |
| Campaigns /api/email/campaign/{id} | GET, PATCH, DELETE | Read, update sender/subject/body/lists, delete. |
| Campaigns /api/email/campaign/{id}/duplicate | POST | Clone an existing campaign into a new draft. |
| Templates /api/email/template | GET | Browse templates and categories, or fetch one by id. |
| Domains /api/email/domain | GET | Sending domains and auth status; needs domains:read. |
Reporting sits at /api/reports/dashboard, /api/reports/email/{campaignId} and a sibling /engagement path. Duplication is the closest thing to a templating workflow: clone a known-good campaign, PATCH subject and lists, hand the draft over.
Never call GET /api/contact against a real database; page through POST /api/contact/search, retry only on 429, and read the budget headers before a full resync.
import os, requests
# NOT a global endpoint - read yours from Settings > API Keys.
BASE = os.environ["BENCHMARK_API_BASE_URL"].rstrip("/")
s = requests.Session()
s.headers.update({"X-API-Key": os.environ["BENCHMARK_API_KEY"],
"Content-Type": "application/json"})
def search(page=1, page_size=50):
"""GET /api/contact returns EVERY contact unpaginated. Use this."""
r = s.post(f"{BASE}/api/contact/search",
json={"page": page, "pageSize": page_size}, timeout=30)
if r.status_code == 429: # retry ONLY on 429
raise RuntimeError(f"retry in {r.headers.get('Retry-After', 60)}s")
r.raise_for_status()
return r.json()
def iter_contacts(page_size=50):
first = search(1, page_size)
yield from first["contacts"]
pages = -(-first["totalRecords"] // page_size) # ceil division
for p in range(2, pages + 1):
yield from search(p, page_size)["contacts"]
probe = s.get(f"{BASE}/api/contact-structure", timeout=30)
print("monthly budget left:", probe.headers["X-Monthly-Remaining"])One client covering contacts and campaigns, plus a draft. Note where the pipeline stops.
// Benchmark Email API v1.0.0 - Node 18+
const BASE = process.env.BENCHMARK_API_BASE_URL.replace(/\/$/, '');
const headers = { 'X-API-Key': process.env.BENCHMARK_API_KEY,
'Content-Type': 'application/json' };
async function call(path, options = {}) {
const res = await fetch(`${BASE}${path}`, { ...options, headers });
if (res.status === 429) // retry ONLY on 429
throw new Error(`retry in ${res.headers.get('Retry-After') ?? 60}s`);
if (!res.ok) throw new Error(`${res.status}`); // {errors:[{errorType}]}
return res.json();
}
// Contacts: page/pageSize go in the BODY...
const searchContacts = (page = 1, pageSize = 50) =>
call('/api/contact/search',
{ method: 'POST', body: JSON.stringify({ page, pageSize }) });
// ...campaigns use the QUERY STRING and rename pageSize to `size`.
const listCampaigns = (page = 1, size = 25) =>
call(`/api/email/campaign?page=${page}&size=${size}`);
// Create a DRAFT. Only `name` is required. Pipeline stops here:
// no send, no schedule, no webhook.
const draft = await call('/api/email/campaign',
{ method: 'POST', body: JSON.stringify({ name: 'August Update' }) });The official guide is blunt: “scheduling, sending, canceling, and test-sending campaigns is not available via API key”. You can POST /api/email/campaign, set sender, subject, body and lists, then the pipeline stops dead and a human presses send. No webhook, no queue, no plan tier unlocks it, and there is no SMTP relay either: build in a manual approval gate, or pick another ESP.
Current: API v1.0.0 at developers.benchmarkemail.io, April 2026, X-API-Key with bme_ keys. Deprecated: RESTful API v3.0 at clientapi.benchmarkemail.com with an AuthToken header. Oldest: XML-RPC v1.0. All three are live and indexed, and v3 outranks v1.0.0 in search because it is older and better linked. Their rate limits differ, so the wrong doc breaks your backoff. The same split runs through the product itself: the rebuilt platform of October 2025 and “Benchmark Email Classic” have separate logins, separate Zapier apps and diverging knowledge bases, and only the new platform is served by API v1.0.0.
The 3,600 requests/hour ceiling is shared across every key, so one key per integration splits the budget rather than multiplying it. On top sits a monthly quota of contacts × 10 on Free and × 100 on paid: a 500-contact Free account gets 5,000 calls a month, which one naive sync exhausts. Both breaches return 429, watch X-RateLimit-Remaining and X-Monthly-Remaining to know which wall you hit.
GET /api/contact returns every matching contact in one unpaginated array; nothing stops you firing it at 50,000 contacts. POST /api/contact/search wants page and pageSize in the JSON body. GET /api/email/campaign wants them in the query string and renames the parameter to size. Three conventions in one API, code written against the contacts pattern silently returns only page one of campaigns.
X-API-Key auth, OpenAPI spec, regional base URLs, key caps of 2 Free / 10 Pro. Supersedes RESTful API v3.0, with no sunset date published for it.Changelog content is split across product updates, the KB release notes and the Help Scout centre, and none is complete, product-updates surfaces nothing newer than Q1 2025.
The current API is Benchmark Email API v1.0.0 at developers.benchmarkemail.io, launched April 2026. There is no global host: the base URL is https://api-{region}-{cluster}.benchmarkemail.io, unique to your account. Regions us, eu and jp appear in official docs; copy your own host from Settings › API Keys.
Only the account owner can create keys, at Settings › API Keys, and a key is shown once at creation with no retrieval path afterwards. Format is bme_{region}_{43 chars}, 50 characters, sent in the X-API-Key header. Scopes cover contacts, campaigns, reports and domains, read or write, with write implying read. Free holds 2 keys, Pro up to 10.
Two stacked limits. Hourly: 3,600 requests per hour per account, about 60 per minute, shared across every key, extra keys buy no throughput. Monthly: contact limit × 10 on Free and × 100 on paid, so 5,000 contacts on Pro gives 500,000 calls a month, a 500-contact Free account just 5,000. Both return 429 with Retry-After.
No. You can create a draft with POST /api/email/campaign, configure sender, subject, body and lists with PATCH, and duplicate campaigns, but the official guide states that scheduling, sending, canceling and test-sending are not available via API key and must happen in the web app. No plan tier unlocks it, and there is no SMTP relay either.
Yes, despite the higher version number. RESTful API v3.0 on clientapi.benchmarkemail.com, with its AuthToken header, is the older generation, superseded by API v1.0.0 in April 2026; an older XML-RPC v1.0 sits behind it. All three remain live, with no sunset date published for the legacy pair. Confirm the hostname, not the version string, before trusting a tutorial.
No. As of August 2026 there is no first-party MCP server, nothing on the BenchmarkEmail GitHub org, nothing at developers.benchmarkemail.io, no mention in the April 2026 API launch. Third-party access exists via Zapier MCP and the commercial Composio toolkit. No MCP server can make Benchmark send, because the API exposes no send operation to wrap.
This review follows our email infrastructure testing methodology. We disclose affiliate relationships in our editorial independence policy.