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.
| Tier | Limit | Keyed on |
|---|---|---|
| anonymous | 20 / min | IP address |
| free | 1,200 / min | your 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." } }| Status | Meaning |
|---|---|
| 400 | Bad 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. |
| 401 | Invalid or revoked API key, or no key on an endpoint that needs one. |
| 404 | No such resource. |
| 429 | Rate limit exceeded (rate_limited), or today’s brand requests are used up (daily_limit_reached). See Retry-After. |
| 503 | A 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.
List emails
Search and filter the email library. Returns a page of email summaries.
| Parameter | Type | Description |
|---|---|---|
| q | string | Free-text search (e.g. "welcome", "Black Friday"). |
| brand | string | Filter by brand slug (e.g. "glossier"). |
| category | string | Filter by category slug (e.g. "apparel"). |
| since | string | Only emails captured after this date or ISO 8601 date-time, e.g. 2026-08-01. |
| page | integer | Page number, 1-based. Default 1. Paging is exact when brand or category is set. |
| per_page | integer | Results per page, 1-50. Default 20. |
curl "https://dev.emaillove.com/v1/emails?brand=glossier&per_page=2" \ -H "Authorization: Bearer $EMAILLOVE_API_KEY"
{
"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 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.
| Parameter | Type | Description |
|---|---|---|
| id required | integer | Numeric email id from a list response. |
curl "https://dev.emaillove.com/v1/emails/1522673" \ -H "Authorization: Bearer $EMAILLOVE_API_KEY"
{
"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" } }
}List brands
List brands in the library. Omit q to get the top brands by email count.
| Parameter | Type | Description |
|---|---|---|
| q | string | Brand name search. |
| page | integer | Page number. Default 1. |
| per_page | integer | Results per page, 1-50. Default 20. |
curl "https://dev.emaillove.com/v1/brands?q=glossier"
{
"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 a brand
Get a brand and a page of its emails.
| Parameter | Type | Description |
|---|---|---|
| slug required | string | Brand slug. |
| page | integer | Page number. Default 1. |
| per_page | integer | Emails per page, 1-50. Default 20. |
curl "https://dev.emaillove.com/v1/brands/glossier?per_page=2"
{
"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" } }
}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.
| Parameter | Type | Description |
|---|---|---|
| slug required | string | Brand slug (e.g. "rei"). |
curl "https://dev.emaillove.com/v1/brands/rei/insights" \ -H "Authorization: Bearer $EMAILLOVE_API_KEY"
{
"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" } }
}List journeys
List brands that have a curated lifecycle journey.
| Parameter | Type | Description |
|---|---|---|
| page | integer | Page number. Default 1. |
| per_page | integer | Results per page, 1-50. Default 30. |
curl "https://dev.emaillove.com/v1/journeys?per_page=2"
{
"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 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.
| Parameter | Type | Description |
|---|---|---|
| brand required | string | Brand slug (e.g. "hims"). |
| emails_per_stage | integer | Emails returned per stage, 1-10. Default 5. |
curl "https://dev.emaillove.com/v1/journeys/hims?emails_per_stage=2"
{
"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.
Brands you follow
The brands the API key owner follows on emaillove.com, most recent follow first. Accounts can follow up to 50 brands.
curl "https://dev.emaillove.com/v1/me/follows" \ -H "Authorization: Bearer $EMAILLOVE_API_KEY"
{
"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" } }
}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.
| Parameter | Type | Description |
|---|---|---|
| since | string | Only emails captured after this date or ISO 8601 date-time, e.g. 2026-10-01. |
| page | integer | Page number, 1-based. Default 1. |
| per_page | integer | Results per page, 1-50. Default 20. |
curl "https://dev.emaillove.com/v1/me/follows/emails?since=2026-10-01&per_page=2" \ -H "Authorization: Bearer $EMAILLOVE_API_KEY"
{
"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" } }
}Emails you liked
The emails the API key owner has liked on emaillove.com, most recent like first. Same email fields as List emails.
| Parameter | Type | Description |
|---|---|---|
| page | integer | Page number, 1-based. Default 1. |
| per_page | integer | Results per page, 1-50. Default 20. |
curl "https://dev.emaillove.com/v1/me/likes?per_page=2" \ -H "Authorization: Bearer $EMAILLOVE_API_KEY"
{
"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" } }
}Your collections
The collections the API key owner has saved on emaillove.com, without their emails. Use Get a collection for the emails.
curl "https://dev.emaillove.com/v1/me/collections" \ -H "Authorization: Bearer $EMAILLOVE_API_KEY"
{
"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 a collection
One of your collections and a page of its emails, in collection order. Same email fields as List emails.
| Parameter | Type | Description |
|---|---|---|
| id required | string | Collection id from Your collections. |
| page | integer | Page number, 1-based. Default 1. |
| per_page | integer | Emails per page, 1-50. Default 20. |
curl "https://dev.emaillove.com/v1/me/collections/6669d9ac84f3e?per_page=2" \ -H "Authorization: Bearer $EMAILLOVE_API_KEY"
{
"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.
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.
| Parameter | Type | Description |
|---|---|---|
| brand required | string | The brand name, e.g. Allbirds. |
| website required | string | The brand website or domain, e.g. allbirds.com. |
| category | string | The 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_url | string | The page with the newsletter signup form, if you know it. |
| notes | string | Anything that helps us subscribe. Max 500 characters. |
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"}'{
"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" } }
}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.
| Parameter | Type | Description |
|---|---|---|
| limit | integer | Requests to return, 1-200. Default 100. |
curl "https://dev.emaillove.com/v1/brand-requests" \ -H "Authorization: Bearer $EMAILLOVE_API_KEY"
{
"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