BigStatsSign in

Developer documentation

JSON API & MCP access to your delivery data.

Pull deliveries, bounces, deferrals, opens and clicks into your own warehouse, dashboards or scripts over a read-only JSON/CSV API — or connect Claude, ChatGPT and other LLM clients directly to BigStats over the Model Context Protocol.

Authentication

Bearer tokens, scoped to one workspace.

Create a token in the dashboard under API & MCP clients. The full value is shown once; only a SHA-256 hash is stored. Send it on every request as Authorization: Bearer <token>. Tokens are read-only and cannot modify data or reach any other workspace. Each token carries explicit scopes — reports for aggregate endpoints and events for raw rows with recipient addresses. The same screen shows last-used timestamps, lists every connected MCP/OAuth assistant, and revokes either instantly.

Base URL

https://bigstats.email/api/public/v1

Endpoints

Read-only endpoints.

GET /api/public/v1/overview

Delivery KPIs and a time series for the workspace: injected, delivered, deferred, bounced, opened, clicked, complaints, plus the previous period for comparison.

Query: range=24h|7d|30d|90d (default 7d), cluster=<cluster id>

GET /api/public/v1/domains

Per-receiver performance grouped by recipient domain or ISP group.

Query: range=…, group_by=domain|isp (default domain), cluster=<cluster id>

GET /api/public/v1/bounces

Bounce and deferral diagnostics: category mix, sending-IP pressure and top SMTP response codes.

Query: range=…, cluster=<cluster id>

GET /api/public/v1/clusters

List the MTA clusters defined on the workspace with the /24 sending blocks each one covers. Use an id as the cluster filter on the other endpoints.

Query: none

GET /api/public/v1/pools

Shared IP pools: sending IPs used by more than one sender, senders rotating across several IPs, volume mix and rotation flags.

Query: range=…, cluster=<cluster id>

GET /api/public/v1/pool-detail

Per-member delivery health for one shared IP (split by sender) or one sender (split by IP): delivery, bounce, hard-bounce, deferral and complaint rates, bounce categories and top SMTP codes.

Query: mode=ip|sender (default ip), key=<ip or sender> (required), range=…, cluster=…, limit (1–500, default 100), compare=true|false

GET /api/public/v1/pool-domains

Drill through from one IP or sender to the receiving side — performance grouped by recipient domain or ISP group, so you can see where issues concentrate.

Query: mode=ip|sender, key=<ip or sender> (required), group_by=domain|isp (default domain), range=…, from/to (ISO 8601, override range), cluster=…, limit (1–500, default 100)

GET /api/public/v1/pool-series

Time-bucketed trend for one IP or sender — events, delivered, delivery rate, bounce rate, complaint rate and hard bounces per hour or per day, with an optional previous-period overlay.

Query: mode=ip|sender, key=<ip or sender> (required), range=…, bucket=hour|day, cluster=…, compare=true|false

GET /api/public/v1/events

Row-level events with all configurable fields (opens, clicks, bounce detail, SMTP code, DSN, diagnostic, VMTA, sending IP). JSON or CSV.

Query: event_type, domain, campaign_id, cluster, from (ISO 8601), to (ISO 8601), limit (1–5000, default 500), format=json|csv

Examples

Copy, paste, ship.

cURL — overview

curl -s "https://bigstats.email/api/public/v1/overview?range=7d" \
  -H "Authorization: Bearer $BIGSTATS_TOKEN"

cURL — bounce CSV export

curl -s "https://bigstats.email/api/public/v1/events?event_type=bounced&range=7d&format=csv&limit=5000" \
  -H "Authorization: Bearer $BIGSTATS_TOKEN" -o bounces.csv

cURL — shared pool diagnosis

# Which senders share 203.0.113.42, and how is each one doing?
curl -s "https://bigstats.email/api/public/v1/pool-detail?mode=ip&key=203.0.113.42&range=30d&compare=true" \
  -H "Authorization: Bearer $BIGSTATS_TOKEN"

# Where is that IP struggling, by ISP group?
curl -s "https://bigstats.email/api/public/v1/pool-domains?mode=ip&key=203.0.113.42&group_by=isp&range=30d" \
  -H "Authorization: Bearer $BIGSTATS_TOKEN"

JavaScript / TypeScript

const res = await fetch(
  "https://bigstats.email/api/public/v1/domains?range=30d&group_by=isp",
  { headers: { Authorization: `Bearer ${process.env.BIGSTATS_TOKEN}` } },
);
if (!res.ok) throw new Error(`BigStats API ${res.status}`);
const { rows } = await res.json();

for (const r of rows) {
  console.log(r.bucket, r.delivered, r.bounced);
}

Python

import os, requests

r = requests.get(
    "https://bigstats.email/api/public/v1/events",
    params={"event_type": "bounced", "limit": 1000},
    headers={"Authorization": f"Bearer {os.environ['BIGSTATS_TOKEN']}"},
    timeout=30,
)
r.raise_for_status()
for e in r.json()["events"]:
    print(e["occurred_at"], e["smtp_code"], e["bounce_category"], e["diagnostic"])

Example response — /overview

{
  "range": "7d",
  "from": "2026-07-28T19:00:00.000Z",
  "to": "2026-08-04T19:00:00.000Z",
  "totals": {
    "injected": 41293811, "delivered": 40510442, "deferred": 612004,
    "bounced": 402118, "opened": 9120344, "clicked": 1044219, "complained": 3122
  },
  "previous": { "delivered": 38911204, "bounced": 448190 },
  "series": [{ "bucket": "2026-07-28", "delivered": 5712001, "bounced": 61224 }]
}

Event fields

Configurable data elements.

occurred_atISO 8601 timestamp of the event
event_typeinjected · delivered · deferred · bounced · opened · clicked · unsubscribed · complained · rejected
recipient / recipient_domainAddress and receiving domain
isp_groupgmail · microsoft · yahoo · apple · other
campaign_id / mailing_id / message_idIdentifiers passed through from your MTA
sending_ip / vmta / remote_mtaWhich IP and binding sent it, and which server answered
ip_blockThe /24 the sending IP belongs to — the unit clusters are built from
smtp_code / dsn / diagnosticRaw SMTP reply, enhanced status code and diagnostic text
bounce_type / bounce_categoryhard · soft · block, and the classified reason
url / user_agentClick destination and the client that generated the open or click

Raw event queries are bounded to a rolling window for performance; aggregate endpoints read from pre-computed rollups and stay fast at billion-per-day volume. Rate limit: 60 requests per minute per token.

KumoMTA webhooks

Payload examples & schema reference.

Point a KumoMTA HTTP log hook at the ingest endpoint below and post records in the new_json log format. BigStats normalizes every record type into a single event model, so a KumoMTA deployment reports side by side with any other supported MTA.

Request

POST /api/public/ingest/kumomta?key=ga_live_…
Content-Type: application/json
# the key may also be sent as the header: x-bigstats-key: ga_live_…

# a single record …
{ "type": "Delivery", "recipient": "jordan@gmail.com", "timestamp": 1785000012 }

# … or a batch: a JSON array, newline-delimited JSON,
# or an object wrapper using "events", "records" or "logs"
{ "records": [
  { "type": "Reception", "recipient": "jordan@gmail.com", "timestamp": 1785000000 },
  { "type": "Delivery",  "recipient": "jordan@gmail.com", "timestamp": 1785000012 }
] }

Response

HTTP/1.1 200 OK
{ "ok": true, "received": 2 }

# failures
401 { "error": "missing ingest key" }      # no ?key= and no x-bigstats-key header
401 { "error": "invalid ingest key" }      # unknown or deactivated key
400 { "error": "no events found in payload" }
413 { "error": "too many events in one batch" }   # hard cap of 25,000 records per POST
500 { "error": "could not store events" }  # safe to retry; duplicates are dropped

Example payload per event type

Reception → injectedMessage accepted into a KumoMTA queue. Establishes the denominator for delivery and bounce rates.

KumoMTA record — Reception

{
  "type": "Reception",
  "id": "01HZX9K7Q2W8N4C6",
  "sender": "news@brand.example.com",
  "recipient": "jordan@gmail.com",
  "queue": "gmail.com",
  "site": "",
  "size": 42311,
  "timestamp": 1785000000,
  "tenant": "brand-weekly",
  "campaign": "weekly-digest-2026-08",
  "egress_pool": "pool-transactional",
  "egress_source": "203.0.113.42",
  "source_address": { "address": "203.0.113.42", "port": 0 },
  "headers": {
    "Subject": "Your weekly delivery digest",
    "Message-ID": "<a1b2c3@brand.example.com>",
    "X-Campaign-Id": "weekly-digest-2026-08",
    "X-Mailing-Id": "m-99231"
  },
  "meta": { "list_id": "weekly" }
}

Batched transaction — one record, one event per recipient

{
  "type": "Delivery",
  "id": "01HZXA13S5U7W9Y1",
  "sender": "news@brand.example.com",
  "recipient": ["a@gmail.com", "b@gmail.com", "c@gmail.com"],
  "timestamp": "2026-08-04T12:15:00Z",
  "egress_pool": "pool-transactional",
  "source_address": { "address": "203.0.113.42" },
  "peer_address": { "name": "gmail-smtp-in.l.google.com" },
  "response": { "code": 250, "content": "2.0.0 OK - gsmtp" }
}

Schema reference

How each field on a KumoMTA record maps onto the BigStats event columns you query over the API, MCP or CSV export. Unknown fields are never dropped — the full record is stored on raw.

KumoMTA fieldBigStats fieldNotes
typeevent_typereception→injected, delivery→delivered, bounce/expiration/adminbounce/oob→bounced, transientfailure/delayed→deferred, rejection→rejected, feedback→complained. Anything else lands as other.
timestampoccurred_atUnix seconds, milliseconds or an ISO 8601 string. reception_timestamp / created are accepted as fallbacks. Missing timestamps default to receipt time.
recipientrecipient / recipient_domain / isp_groupString, object with email/address, or an array for batched transactions — an array produces one BigStats event per recipient. Lowercased; the domain is split off and grouped into gmail · microsoft · yahoo · apple · other.
headers['Message-ID']message_idFalls back to id, then queue_id.
sender(dedupe key)Envelope sender. Combined with type, message_id, recipient and timestamp into the dedupe hash, so replayed records never double-count.
source_address.addresssending_ipFalls back to egress_source. The /24 is derived from it for clustering.
egress_poolvmtaFalls back to egress_source, then queue.
peer_address.name / .addrremote_mtaFalls back to site, then provider_name — the receiving MX that answered.
response.codesmtp_codeValidated to 200–599. If absent, the first 3-digit code inside response.content is used.
response.enhanced_codedsnclass.subject.detail joined, e.g. 5.1.1. Falls back to an enhanced code parsed out of response.content.
response.contentdiagnosticFull remote reply text. Falls back to reason (used by admin bounces and expirations).
bounce_classificationbounce_type / bounce_categoryUsed as the declared hint; BigStats still classifies from the SMTP code and diagnostic text into hard · soft · block plus a reason category.
headers['X-Campaign-Id'] / campaign / tenantcampaign_idFirst match wins; tenant is the usual KumoMTA multi-brand key.
headers['X-Mailing-Id'] / mailing_id / campaign_idmailing_idFirst match wins.
headers['Subject']subjectHeader lookups are case-insensitive.
(whole record)rawThe complete JSON record is retained on the event row for drill-down and export.

KumoMTA does not emit open or click records; those streams stay empty unless your tracking layer posts them separately. Records are de-duplicated on event type, message id, recipient, timestamp and sender, so log hook retries and replays are safe. See the KumoMTA setup guide.

MCP server

Connect Claude, ChatGPT or any MCP client.

BigStats exposes a Model Context Protocol server so an LLM can query your delivery reporting directly and answer questions like “why did Microsoft bounce rate double yesterday?”. The connection uses OAuth: you sign in to BigStats and approve the client, and every tool call then runs as your account — scoped to your workspaces, read-only, with recipient addresses never exposed.

MCP endpoint (Streamable HTTP)

https://bigstats.email/mcp

Client config (Claude Desktop / Cursor style)

{
  "mcpServers": {
    "bigstats": {
      "url": "https://bigstats.email/mcp"
    }
  }
}

Claude

Settings → Connectors → Add custom connector, paste https://bigstats.email/mcp, then complete the BigStats sign-in and approve access.

ChatGPT & others

Add BigStats as a remote MCP server using the same URL. Clients that support dynamic registration configure themselves; no client ID or secret to create.

list_workspacesList the workspaces the signed-in account can report on.
delivery_overviewKPIs and time series for a workspace and range.
domain_breakdownPer-domain or per-ISP performance.
bounce_analysisBounce categories, IP pressure and SMTP codes.
shared_poolsShared and rotating sending IPs, with sender mix and rotation flags.
pool_detailPer-member delivery health for one IP or sender, with optional previous-period comparison.
pool_domainsWhere one IP or sender is struggling, by recipient domain or ISP group.
search_eventsRow-level drill-down, without recipient addresses.

Errors

What the API returns when something is off.

401Missing or invalid bearer token — create a new token in API & MCP clients.
403The token lacks the scope this endpoint needs (reports or events). Issue a token with that scope.
400Invalid query parameter. The response includes a detail array naming the field.
500Query failed. Retry with a smaller range or narrower filters.