Email Parsing API: MailParse vs Mailgun Inbound Routing

Compare MailParse and Mailgun Inbound Routing for Email Parsing API. Features, performance, and developer experience.

Why Email Parsing API capability matters

Engineering teams evaluating an email-parsing-api are choosing how inbound emails become reliable, structured events that power product features. Whether you ingest support replies, automate workflows from receipts, or capture leads from forwarded messages, two factors define success: parsing fidelity of raw MIME into clean JSON, and dependable delivery to your services via REST or webhook APIs. This comparison of MailParse and Mailgun Inbound Routing focuses specifically on API behavior, developer experience, and how each service handles real-world edge cases, not generic marketing claims.

If you are exploring product ideas that depend on inbound email, see Top Email Parsing API Ideas for SaaS Platforms for concrete patterns and pitfalls to consider.

How MailParse Handles Email Parsing API

This platform provisions instant inbound addresses, receives mail, parses MIME into a normalized JSON schema, then delivers via webhook or exposes a REST polling API. The design keeps the raw message intact, while giving you a developer-friendly structure for common fields and parts.

Data model

  • Message metadata: id, received_at, size_bytes, checksum
  • Envelope: mail_from, rcpt_to[], helo
  • Headers: canonicalized map with original casing preserved for signature verification
  • Content: subject, text, html, parts[] with content-type, content-id, disposition, charset, and decoded content where safe
  • Attachments: array with filename, length, content-type, content-id, content-disposition, sha256, and presigned_url for streaming download
  • Original MIME: raw_mime_url for exact-bytes retrieval
  • Auth and spam: spf, dkim, arc verdicts, spam_score, spam_report
  • Threading: message_id, in_reply_to, references, extracted thread_key when present
  • Deduping: rfc_message_id plus a stable event_id to enforce idempotency

Webhook delivery

Inbound messages are pushed as JSON to your HTTPS endpoint. Security features include HMAC signatures with rotation, optional mTLS, and IP allowlisting. Each delivery includes an idempotency key in the X-Event-Id header. Retries use exponential backoff with jitter, honoring a maximum horizon and respecting Retry-After headers from your server. You can acknowledge processing by returning any 2xx response. Non-2xx responses trigger retries with a dead-letter queue for post-mortem or replay.

REST polling API

For workloads that prefer pull over push, a REST API exposes:

  • GET /v1/messages?status=ready&limit=100 to list pending messages
  • GET /v1/messages/{id} to fetch a single message
  • POST /v1/messages/{id}/ack to mark as processed, with optional deadline_at extension
  • POST /v1/messages/{id}/nack to requeue on transient failures

This flow avoids webhooks entirely and works well for jobs that run inside private networks or batch processors. It also makes blue-green deploys simpler: new workers can warm up by polling a small page size.

Parsing fidelity and edge cases

  • Nested multiparts: correct traversal of multipart/alternative inside multipart/mixed
  • Character sets: RFC 2047 encoded-words and RFC 2231 filename parameters are decoded
  • Inline attachments: content-id links resolved for cid: references, with mapping back to parts
  • TNEF winmail.dat: extracted where possible, with original blob retained
  • S/MIME and PGP: surfaces signature blocks and retains canonicalized originals so downstream verification is possible
  • Large attachments: streamed to object storage, exposed via short-lived presigned URLs to keep webhook payloads small

How Mailgun Inbound Routing Handles Email Parsing API

Mailgun Inbound Routing lets you define routes and filters that receive emails to a webhook endpoint. It posts multipart form-data with fields such as from, recipient, subject, body-plain, body-html, and stripped-text, plus files for attachments. Mailgun's signatures include timestamp, token, and signature for HMAC verification.

Developers commonly enable message storage in order to fetch the raw MIME after receiving the webhook. In that mode, the webhook payload includes a message-url that retrieves the original content from Mailgun storage for a limited window. Without storage, you rely on parsed fields and attachment uploads contained in the webhook. There is no first-class REST polling API for inbound content, although you can query Events for logs and pair that with storage to fetch messages.

Routing rules are flexible and can filter by recipient, catch-all patterns, and priority. Attachments arrive as multipart file uploads. The form-data model is straightforward for simple use cases, but mapping multipart structures to your own JSON model usually requires a second fetch of the raw MIME via message-url.

Mailgun operates at large scale and offers signed webhooks and retries. Some teams report that high throughput or endpoint congestion can lead to inconsistent webhook delay or gaps, especially when multiple domains share infrastructure. Costs can grow quickly with storage and high inbound volume, which matters for APIs that process every message byte.

Side-by-Side Comparison

Feature MailParse Mailgun Inbound Routing
Primary delivery style Webhook and REST polling, both first class Webhook via routes, REST polling not first class for inbound content
Webhook payload format Compact JSON with normalized MIME parts and presigned attachment URLs Multipart form-data with text fields and attachment uploads, optional message-url
Raw MIME access Always available via raw_mime_url with short-lived tokens Available if storage is enabled and message-url is present
Idempotency Explicit X-Event-Id, stable event_id and ack endpoints Deduplicate via Message-Id and your own store, no explicit idempotency key
Security HMAC signatures, optional mTLS, IP allowlisting, key rotation HMAC signatures using timestamp and token
Attachment handling Streaming via presigned URLs, SHA-256 checksums Attached as multipart uploads, or fetch from message-url
Parsing fidelity Nested multiparts, RFC 2231 filenames, inline cid: mapping, TNEF extraction Body fields and attachments provided, raw MIME fetch recommended for complex cases
Retries and DLQ Exponential backoff with jitter, dead-letter queue and replay controls Retries on non-2xx responses with backoff, DLQ controls are limited
Operational model Push or pull, ack and nack semantics for safe processing Push only, rely on retries and your system for dedup
Scale and cost Optimized for high-volume REST and webhook throughput Can be expensive at scale, storage adds cost
Webhook consistency Consistent latency with backpressure controls and idempotency Can show inconsistent delivery under load according to user reports

Code Examples

Polling inbound messages via REST

This example shows a worker that polls for messages, processes them, then acknowledges completion. It uses Node.js with fetch, but the pattern is similar in any language.


// Environment:
//   API_KEY - your API key
//   BASE_URL - the REST base, for example https://api.your-inbound-service.com/v1
import crypto from 'crypto';
import fetch from 'node-fetch';

const BASE_URL = process.env.BASE_URL;
const API_KEY = process.env.API_KEY;

async function listReady(limit = 50) {
  const res = await fetch(`${BASE_URL}/messages?status=ready&limit=${limit}`, {
    headers: { Authorization: `Bearer ${API_KEY}` }
  });
  if (!res.ok) throw new Error(`List failed ${res.status}`);
  return res.json();
}

async function processMessage(msg) {
  // Download raw MIME if needed
  if (msg.raw_mime_url) {
    const res = await fetch(msg.raw_mime_url, { headers: { Authorization: `Bearer ${API_KEY}` } });
    const raw = await res.text();
    // verify DKIM using a library if your use case requires
  }

  // Use normalized fields
  console.log(msg.subject, msg.from, msg.to);

  // Stream an attachment if present
  for (const a of msg.attachments || []) {
    const r = await fetch(a.presigned_url);
    console.log(`Attachment ${a.filename} length=${r.headers.get('content-length')}`);
  }
}

async function ack(id) {
  const res = await fetch(`${BASE_URL}/messages/${id}/ack`, {
    method: 'POST',
    headers: { Authorization: `Bearer ${API_KEY}` }
  });
  if (!res.ok) throw new Error(`Ack failed ${res.status}`);
}

async function main() {
  const batch = await listReady(25);
  for (const msg of batch.items) {
    try {
      await processMessage(msg);
      await ack(msg.id);
    } catch (err) {
      console.error('Processing error', err);
      // Optionally POST /messages/{id}/nack to requeue
    }
  }
}

main().catch(console.error);

Webhook handler for JSON payloads

This Express handler verifies an HMAC signature and responds quickly so retries are not triggered.


// Environment:
//   SHARED_SECRET - webhook signing secret
import crypto from 'crypto';
import express from 'express';

const app = express();
app.use(express.json({ limit: '10mb' }));

function verifySignature(req) {
  const sig = req.header('X-Signature');
  const ts = req.header('X-Timestamp');
  const body = JSON.stringify(req.body);
  const h = crypto.createHmac('sha256', process.env.SHARED_SECRET);
  h.update(ts + '.' + body);
  return crypto.timingSafeEqual(Buffer.from(sig, 'hex'), Buffer.from(h.digest('hex'), 'hex'));
}

app.post('/inbound', async (req, res) => {
  if (!verifySignature(req)) return res.status(401).send('invalid signature');

  const eventId = req.header('X-Event-Id');
  const msg = req.body;

  // Idempotent processing
  const already = await checkStore(eventId);
  if (already) return res.status(200).send('ok');

  await saveToDb(msg);
  queueWork(msg.id);

  res.status(202).send('accepted');
});

app.listen(3000, () => console.log('listening'));

Mailgun inbound webhook handler

Mailgun posts multipart form-data. This example shows how to verify the signature, read fields, and optionally fetch the raw MIME using message-url if storage is enabled.


// Environment:
//   MG_API_KEY - Mailgun API key
import express from 'express';
import crypto from 'crypto';
import multer from 'multer';
import fetch from 'node-fetch';

const upload = multer({ storage: multer.memoryStorage() });
const app = express();

function verifyMailgunSignature(ts, token, signature) {
  const h = crypto.createHmac('sha256', process.env.MG_API_KEY);
  h.update(ts + token);
  const digest = h.digest('hex');
  return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(digest));
}

app.post('/mailgun/inbound', upload.any(), async (req, res) => {
  const { timestamp, token, signature } = req.body;
  if (!verifyMailgunSignature(timestamp, token, signature)) {
    return res.status(401).send('invalid signature');
  }

  const from = req.body.from;
  const to = req.body.recipient;
  const subject = req.body.subject;
  const text = req.body['body-plain'];
  const html = req.body['body-html'];
  const messageUrl = req.body['message-url'];

  // Attachments are in req.files as buffers if not using storage
  for (const f of req.files || []) {
    console.log(`Attachment ${f.originalname} size=${f.size}`);
  }

  // Optional: fetch raw MIME to reconstruct full structure
  if (messageUrl) {
    const r = await fetch(messageUrl, {
      headers: { Authorization: `Basic ${Buffer.from('api:' + process.env.MG_API_KEY).toString('base64')}` }
    });
    const rawMime = await r.text();
    console.log('raw length', rawMime.length);
  }

  // Persist and respond quickly to avoid retries
  res.status(200).send('ok');
});

app.listen(3000);

Performance and reliability

Latency and throughput

An email-parsing-api must parse quickly and not block your pipeline. The JSON-first design in this platform keeps webhooks small. Attachments and raw MIME are obtained via presigned URLs that stream directly from storage, which reduces webhook payload size and improves client memory usage. The REST polling API supports pagination and backpressure so your workers can scale horizontally without overwhelming your database.

Mailgun Inbound Routing is optimized for webhook delivery. Latency is generally good for most workloads. Because payloads include attachments as multipart files or require a second request to message-url, throughput can be bound by your webhook server's ability to accept large uploads. If you fetch raw MIME frequently, plan capacity for two network hops per message.

Retries, idempotency, and backpressure

  • This platform provides a stable event id, ack and nack endpoints, and at-least-once delivery with idempotency keys. You can replay dead-lettered deliveries by id. This is useful for long-running transformations or downstream outages.
  • Mailgun retries on non-2xx responses. You should store a hash of Message-Id or the combination of timestamp and token to deduplicate. There is no server-side ack control, so your logic must handle at-least-once delivery.

Edge cases that break naive parsers

  • Non-UTF8 charsets and odd filename parameters: normalized here, whereas Mailgun often leaves filename decoding to the application unless you fetch the raw MIME and reparse.
  • Inline images and signatures: this platform provides a map of cid to attachment metadata to simplify rendering and linking. With Mailgun, you piece this mapping together from attachment parts in the webhook or by parsing the stored message.
  • Winmail.dat and calendar invites: TNEF extraction and ICS detection are built into the JSON payload here. Mailgun will pass the blob or file and you handle extraction downstream.

If your inbound processing ties to deliverability tuning, review the Email Deliverability Checklist for SaaS Platforms. For operational architecture patterns, see the Email Infrastructure Checklist for SaaS Platforms.

Verdict: Which is better for Email Parsing API?

For teams that need a developer-centric email parsing api with both webhook and REST options, strong idempotency, and high-fidelity JSON, MailParse offers a streamlined model that reduces custom parsing code and helps keep pipelines reliable under load. Mailgun Inbound Routing is a solid choice if you already use Mailgun for sending and your inbound needs are simple body extraction plus attachments. If you require consistent webhook delivery at scale, frequent raw MIME access, or pull-based processing, the JSON model and polling support give this platform a practical edge.

Cost at high volume and the need to fetch message-url for full fidelity can make Mailgun less attractive for data-heavy ingestion. Conversely, if your use case is lightweight and already integrated with Mailgun's ecosystem, its inbound routes are quick to set up. Consider running a proof of concept with production-like traffic before committing to either platform.

FAQ

Do I need webhooks, or can I use REST only?

Both patterns are valid. Webhooks minimize latency and infrastructure, but require public endpoints and robust retry handling. REST polling is easier behind firewalls, provides natural backpressure, and simplifies blue-green deployments. Choose the model that matches your runtime and failure handling approach.

How do I verify inbound authenticity?

Use HMAC signatures on webhook payloads and rotate secrets periodically. Also verify SPF and DKIM results exposed by the parser. If you need non-repudiation, store the raw MIME bytes and header set for later verification.

What about very large attachments?

Push systems should avoid sending attachment bytes in the webhook body. Prefer short-lived URLs that stream from object storage so your application stays responsive. If you use Mailgun and cannot enable storage, ensure your webhook server can accept large multipart uploads and increase request size limits.

How do I handle duplicates safely?

Implement idempotency keyed by the parser's event id where provided, or by RFC 5322 Message-Id plus a hash of key headers. Design handlers to be side effect free until the final commit step, then mark messages as processed in a durable store.

Can I extract structured data like orders or tickets?

Yes. Parse normalized fields, then apply domain-specific extractors to the text or HTML parts. Keep the raw MIME for audits. If you need inspiration, see Top Inbound Email Processing Ideas for SaaS Platforms for patterns that map email content to product events.

Ready to get started?

Start parsing inbound emails with MailParse today.

Get Started Free