Email Love
Generate API key

API documentation

A fast JSON REST API over Email Love's library of real marketing emails from 8,400+ brands, plus the brands you follow, the emails you have liked, your collections, and requests for brands we do not collect yet. Using an AI tool? See the MCP docs instead.

Introduction

The base URL is https://dev.emaillove.com. All responses are JSON. Every request is read-only except POST /v1/brand-requests. Each record links back to its page on emaillove.com via a url field; attribution is required when you display the data (see Usage and attribution).

Authentication

Send your API key as a bearer token. Keys look like el_live_....

Authorization: Bearer $EMAILLOVE_API_KEY

The /v1/me endpoints return your own follows, likes and collections, so they always need a key, as do brand requests. A key only ever reads its owner's data.

No key? The library endpoints work anonymously too, at a lower rate limit and without the HTML emails. Generate a free key → (tied to your Email Love account).

Rate limits

The API is free for now: every endpoint and the whole library, with no monthly limit. Requests are limited per minute. Every response includes X-RateLimit-Limit and X-RateLimit-Tier; a 429 includes Retry-After.

TierLimitKeyed on
anonymous20 / minIP address
free1,200 / minyour account

The MCP server allows 600 requests a minute per account. Brand requests are limited to 50 per account per day (UTC), shared with the MCP; a brand already in the library, or one you already requested, does not count. Over that limit, POST /v1/brand-requests returns 429 with error.code daily_limit_reached and Retry-After in seconds to midnight UTC.

Response format

List responses wrap results in data with a meta object (pagination + attribution). Paginate with page and per_page.

{
  "data": [ /* ... */ ],
  "meta": {
    "page": 1, "per_page": 20, "count": 20,
    "attribution": { "source": "Email Love", "url": "https://emaillove.com" }
  }
}

Errors

Errors return a JSON body with an error object and a standard HTTP status.

{ "error": { "code": "not_found", "message": "No email with id 999." } }
StatusMeaning
400Bad request (e.g. malformed id, or a since that is not a date). A brand request for adult content returns not_allowed: Email Love does not collect it.
401Invalid or revoked API key, or no key on an endpoint that needs one.
404No such resource.
429Rate limit exceeded (rate_limited), or today’s brand requests are used up (daily_limit_reached). See Retry-After.
503A brand request could not be saved just now (unavailable). Retry shortly.

Usage and attribution

The emails in the library are marketing emails sent by other brands and remain their property. Your Email Love account gives you a limited licence to use what the API and the MCP return for your own research and internal use, plus work for your own clients, including private reports, reviews and presentations shared with them.

When you show library content, screenshots or data to anyone outside your organisation, credit Email Love and, where practical, link to the email or brand page on emaillove.com. Every record includes that link in url, and list responses include meta.attribution.

Not allowed: publishing or redistributing emails, screenshots or HTML from the library; reselling, sublicensing or providing the library or the API to others as a service; training or fine-tuning machine learning models on library content; sending email that copies another brand’s email; getting around the rate limits or sharing keys between accounts; and collecting library content other than through the API and the MCP. Brands can ask us to remove their content; when we remove something, stop using it. The full terms are section 24 of our Terms of Service.

Endpoints

Seven endpoints over the library, five for your own account data, and two for brand requests.

GET /v1/emails

List emails

Search and filter the email library. Returns a page of email summaries.

ParameterTypeDescription
qstringFree-text search (e.g. "welcome", "Black Friday").
brandstringFilter by brand slug (e.g. "glossier").
categorystringFilter by category slug (e.g. "apparel").
sincestringOnly emails captured after this date or ISO 8601 date-time, e.g. 2026-08-01.
pageintegerPage number, 1-based. Default 1. Paging is exact when brand or category is set.
per_pageintegerResults per page, 1-50. Default 20.
Request
curl "https://dev.emaillove.com/v1/emails?brand=glossier&per_page=2" \
  -H "Authorization: Bearer $EMAILLOVE_API_KEY"
Response
{
  "data": [
    {
      "id": 1522673,
      "title": "Email Inspiration from Glossier",
      "subject_line": "You asked, we listened",
      "from_email": "gTEAM@eml.glossier.com",
      "brand": "Glossier",
      "brand_slug": "glossier",
      "thumbnail_url": "https://.../glossier-..._thumbnail.jpg",
      "preview_url": "https://.../glossier-..._screenshot.jpg",
      "url": "https://emaillove.com/email-inspiration-from-glossier-12",
      "captured_date": "2026-05-18",
      "sent_date": "Wed, 14 May 2026 16:02:11 +0000"
    }
  ],
  "meta": {
    "page": 1, "per_page": 2, "count": 1,
    "query": { "q": null, "brand": "glossier", "category": null },
    "attribution": { "source": "Email Love", "url": "https://emaillove.com" }
  }
}
GET /v1/emails/{id}

Get an email

Retrieve one email, including its cleaned, render-safe HTML (the html field), the full-length screenshot (preview_url), the small thumbnail (thumbnail_url), and the categories and lifecycle stages it is filed under. The list endpoints return everything here except html, fetch a single email to get the markup. html needs an API key; html_status says why html is null: key_required, or not_available for older emails captured before HTML was stored.

ParameterTypeDescription
id requiredintegerNumeric email id from a list response.
Request
curl "https://dev.emaillove.com/v1/emails/1522673" \
  -H "Authorization: Bearer $EMAILLOVE_API_KEY"
Response
{
  "data": {
    "id": 1522673,
    "title": "Email Inspiration from Glossier",
    "subject_line": "You asked, we listened",
    "from_email": "gTEAM@eml.glossier.com",
    "brand": { "name": "Glossier", "slug": "glossier", "url": "https://emaillove.com/authors/glossier" },
    "thumbnail_url": "https://.../_thumbnail.jpg",
    "preview_url": "https://.../_screenshot.jpg",
    "categories": [ { "slug": "apparel", "name": "Apparel" } ],
    "journey_stages": [],
    "captured_date": "2026-05-18",
    "sent_date": "Wed, 14 May 2026 16:02:11 +0000",
    "emaillove_url": "https://emaillove.com/email-inspiration-from-glossier-12",
    "html": "<!DOCTYPE html><html>... cleaned email HTML ...</html>",
    "html_status": "ok"
  },
  "meta": { "attribution": { "source": "Email Love", "url": "https://emaillove.com" } }
}
GET /v1/brands

List brands

List brands in the library. Omit q to get the top brands by email count.

ParameterTypeDescription
qstringBrand name search.
pageintegerPage number. Default 1.
per_pageintegerResults per page, 1-50. Default 20.
Request
curl "https://dev.emaillove.com/v1/brands?q=glossier"
Response
{
  "data": [
    { "name": "Glossier", "slug": "glossier", "description": "...", "email_count": 142,
      "url": "https://emaillove.com/authors/glossier" }
  ],
  "meta": { "page": 1, "per_page": 20, "count": 1, "attribution": { "source": "Email Love", "url": "https://emaillove.com" } }
}
GET /v1/brands/{slug}

Get a brand

Get a brand and a page of its emails.

ParameterTypeDescription
slug requiredstringBrand slug.
pageintegerPage number. Default 1.
per_pageintegerEmails per page, 1-50. Default 20.
Request
curl "https://dev.emaillove.com/v1/brands/glossier?per_page=2"
Response
{
  "data": {
    "brand": { "name": "Glossier", "slug": "glossier", "email_count": 142, "url": "https://emaillove.com/authors/glossier" },
    "emails": [ { "id": 1522673, "title": "...", "subject_line": "...", "brand_slug": "glossier", "preview_url": "...", "url": "...", "captured_date": "2026-05-18" } ]
  },
  "meta": { "page": 1, "per_page": 2, "count": 1, "attribution": { "source": "Email Love", "url": "https://emaillove.com" } }
}
GET /v1/brands/{slug}/insights

Brand insights

Aggregated analytics for one brand: send frequency, ESP mix, sender addresses, subject-line metrics, day-of-week and hour-of-day send patterns, technology flags, content metrics, and deliverability (SPF/DKIM/DMARC). The same data the MCP get_brand_insights tool returns. Returns 404 (no_insights) for tracked brands with too few captured emails to aggregate.

ParameterTypeDescription
slug requiredstringBrand slug (e.g. "rei").
Request
curl "https://dev.emaillove.com/v1/brands/rei/insights" \
  -H "Authorization: Bearer $EMAILLOVE_API_KEY"
Response
{
  "data": {
    "brand": { "slug": "rei", "name": "REI", "url": "https://emaillove.com/authors/rei" },
    "frequency": { "total_sent": 38, "avg_per_week": 0.32, "first_send": "2024-03-01 11:41:26", "last_send": "2026-06-16 17:42:32" },
    "technology": { "esp_breakdown": [ { "name": "Iterable", "count": "28" }, { "name": "Oracle Responsys", "count": "5" } ],
      "responsive_pct": 1, "amp_pct": 0, "dark_mode_pct": 0, "outlook_opt_pct": 1 },
    "subject_line": { "avg_length": 36.8, "contains_emoji_pct": 0.42, "top_words": [ { "word": "deals", "count": 8 } ] },
    "send_patterns": { "by_day_of_week": { "Fri": 11, "Sun": 6 }, "by_hour": { "11": 9, "12": 7 } },
    "content": { "avg_word_count": 1221.5, "avg_image_count": 34.2, "animated_gif_pct": 0 },
    "deliverability": { "spf_pass_pct": 1, "dkim_pass_pct": 1, "dmarc_pass_pct": 1 },
    "senders": [ { "from_email": "rei_email@email.rei.com", "count": "29" } ],
    "dashboard_url": "https://emaillove.com/brand-insights?brand=rei"
  },
  "meta": { "attribution": { "source": "Email Love", "url": "https://emaillove.com" } }
}
GET /v1/journeys

List journeys

List brands that have a curated lifecycle journey.

ParameterTypeDescription
pageintegerPage number. Default 1.
per_pageintegerResults per page, 1-50. Default 30.
Request
curl "https://dev.emaillove.com/v1/journeys?per_page=2"
Response
{
  "data": [
    { "brand": "hims", "brand_slug": "hims", "email_count": 10, "url": "https://emaillove.com/journeys/hims" }
  ],
  "meta": { "page": 1, "per_page": 2, "count": 1, "attribution": { "source": "Email Love", "url": "https://emaillove.com" } }
}
GET /v1/journeys/{brand}

Get a journey

Get one brand's lifecycle journey, broken into stages, with the emails filed under each stage in capture order. This is Email Love's most differentiated data.

ParameterTypeDescription
brand requiredstringBrand slug (e.g. "hims").
emails_per_stageintegerEmails returned per stage, 1-10. Default 5.
Request
curl "https://dev.emaillove.com/v1/journeys/hims?emails_per_stage=2"
Response
{
  "data": {
    "brand": "hims", "brand_slug": "hims", "url": "https://emaillove.com/journeys/hims",
    "stages": [
      { "stage": "Transactional", "stage_slug": "hims-transactional-emails", "email_count": 4,
        "emails": [ { "id": 19273, "title": "Hims Transactional Email", "brand_slug": "hims", "preview_url": "...", "url": "...", "captured_date": "2024-11-08" } ] }
    ]
  },
  "meta": { "attribution": { "source": "Email Love", "url": "https://emaillove.com" } }
}

Your account

These return the API key owner's own Email Love data, so they always need a key, and a key only ever reads its owner's data. Follow brands, like emails, and save collections on emaillove.com; the API reads them.

GET /v1/me/follows

Brands you follow

The brands the API key owner follows on emaillove.com, most recent follow first. Accounts can follow up to 50 brands.

Request
curl "https://dev.emaillove.com/v1/me/follows" \
  -H "Authorization: Bearer $EMAILLOVE_API_KEY"
Response
{
  "data": [
    { "name": "Glossier", "slug": "glossier", "description": "...", "email_count": 142,
      "url": "https://emaillove.com/authors/glossier", "logo_url": "https://.../glossier-logo.jpg" }
  ],
  "meta": { "count": 1, "total": 1, "max": 50, "attribution": { "source": "Email Love", "url": "https://emaillove.com" } }
}
GET /v1/me/follows/emails

Emails from brands you follow

The newest emails from every brand you follow, in one feed, newest first. Pass since to get only what is new since your last check. Same email fields as List emails.

ParameterTypeDescription
sincestringOnly emails captured after this date or ISO 8601 date-time, e.g. 2026-10-01.
pageintegerPage number, 1-based. Default 1.
per_pageintegerResults per page, 1-50. Default 20.
Request
curl "https://dev.emaillove.com/v1/me/follows/emails?since=2026-10-01&per_page=2" \
  -H "Authorization: Bearer $EMAILLOVE_API_KEY"
Response
{
  "data": [
    { "id": 1848588, "title": "...", "subject_line": "...", "brand": "Glossier", "brand_slug": "glossier",
      "thumbnail_url": "...", "preview_url": "...", "url": "...", "captured_date": "2026-10-07" }
  ],
  "meta": { "page": 1, "per_page": 2, "count": 1, "since": "2026-10-01T00:00:00", "following": 12,
    "attribution": { "source": "Email Love", "url": "https://emaillove.com" } }
}
GET /v1/me/likes

Emails you liked

The emails the API key owner has liked on emaillove.com, most recent like first. Same email fields as List emails.

ParameterTypeDescription
pageintegerPage number, 1-based. Default 1.
per_pageintegerResults per page, 1-50. Default 20.
Request
curl "https://dev.emaillove.com/v1/me/likes?per_page=2" \
  -H "Authorization: Bearer $EMAILLOVE_API_KEY"
Response
{
  "data": [ { "id": 1522673, "title": "...", "subject_line": "...", "brand_slug": "glossier", "preview_url": "...", "url": "...", "captured_date": "2026-05-18" } ],
  "meta": { "page": 1, "per_page": 2, "count": 1, "total": 37, "attribution": { "source": "Email Love", "url": "https://emaillove.com" } }
}
GET /v1/me/collections

Your collections

The collections the API key owner has saved on emaillove.com, without their emails. Use Get a collection for the emails.

Request
curl "https://dev.emaillove.com/v1/me/collections" \
  -H "Authorization: Bearer $EMAILLOVE_API_KEY"
Response
{
  "data": [
    { "id": "6669d9ac84f3e", "name": "Welcome series ideas", "description": "",
      "url": "https://emaillove.com/collections?collection=6669d9ac84f3e", "email_count": 14 }
  ],
  "meta": { "count": 1, "attribution": { "source": "Email Love", "url": "https://emaillove.com" } }
}
GET /v1/me/collections/{id}

Get a collection

One of your collections and a page of its emails, in collection order. Same email fields as List emails.

ParameterTypeDescription
id requiredstringCollection id from Your collections.
pageintegerPage number, 1-based. Default 1.
per_pageintegerEmails per page, 1-50. Default 20.
Request
curl "https://dev.emaillove.com/v1/me/collections/6669d9ac84f3e?per_page=2" \
  -H "Authorization: Bearer $EMAILLOVE_API_KEY"
Response
{
  "data": {
    "collection": { "id": "6669d9ac84f3e", "name": "Welcome series ideas", "description": "", "url": "...", "email_count": 14 },
    "emails": [ { "id": 1522673, "title": "...", "brand_slug": "glossier", "preview_url": "...", "url": "...", "captured_date": "2026-05-18" } ]
  },
  "meta": { "page": 1, "per_page": 2, "count": 1, "total": 14, "attribution": { "source": "Email Love", "url": "https://emaillove.com" } }
}

Brand requests

Missing a brand? Ask us to collect it, then check on it. Both need a key. Each brand files into one of the library’s categories; leave category out and we pick the closest one.

POST /v1/brand-requests

Request a brand

Ask Email Love to start collecting a brand that is not in the library yet. A brand already in the library comes straight back with status already_in_library (200) and is not counted. Otherwise the request is queued (201) and our signup agent subscribes; its emails appear in the library as the brand sends them, usually within a few days. Asking again for a brand you already requested returns its status and is not counted. Up to 50 requests per account per UTC day; over the limit returns 429 daily_limit_reached with Retry-After. Email Love does not collect adult content: those requests return 400 not_allowed.

ParameterTypeDescription
brand requiredstringThe brand name, e.g. Allbirds.
website requiredstringThe brand website or domain, e.g. allbirds.com.
categorystringThe library category the brand belongs in: Apparel, Automotive, Baby, Beauty, Beverage, Cannabis, Crypto, Digital Products, Electronics, Food, Health, Higher Education, Home, Jewelry & Watches, Luggage, Non-Profit, Pets, Restaurants, SaaS, Sports Teams, Travel, Video Games. Leave it out and we pick the closest one.
signup_urlstringThe page with the newsletter signup form, if you know it.
notesstringAnything that helps us subscribe. Max 500 characters.
Request
curl -X POST "https://dev.emaillove.com/v1/brand-requests" \
  -H "Authorization: Bearer $EMAILLOVE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"brand": "Allbirds", "website": "allbirds.com", "category": "Apparel"}'
Response
{
  "data": {
    "brand": "Allbirds", "domain": "allbirds.com", "category": "Apparel", "status": "queued",
    "message": "Queued. Our signup agent works through requests twice a day.",
    "library_brand": null, "requested_at": "2026-10-11T23:40:00.000Z",
    "requests_today": 1, "daily_limit": 50
  },
  "meta": { "attribution": { "source": "Email Love", "url": "https://emaillove.com" } }
}
GET /v1/brand-requests

Your brand requests

The brands the API key owner has requested, newest first. status is queued, signed_up (subscribed, waiting for the first email), first_email_captured (library_url is the brand page; search its emails with brand=<slug>) or could_not_subscribe (for example, the brand only offers signup through an app or an account). Statuses update every 15 minutes.

ParameterTypeDescription
limitintegerRequests to return, 1-200. Default 100.
Request
curl "https://dev.emaillove.com/v1/brand-requests" \
  -H "Authorization: Bearer $EMAILLOVE_API_KEY"
Response
{
  "data": [
    { "brand": "Allbirds", "domain": "allbirds.com", "category": "Apparel", "status": "signed_up",
      "message": "Subscribed. Emails appear in the library as the brand sends them, usually within a few days.",
      "library_url": null, "requested_at": "2026-10-11T23:40:00.000Z" }
  ],
  "meta": { "count": 1, "requests_today": 1, "daily_limit": 50, "attribution": { "source": "Email Love", "url": "https://emaillove.com" } }
}

Wiring this into an AI tool? The same library is a native MCP server. Read the MCP docs →

Questions? hello@emaillove.com