API Key Authentication
Authenticate to the API using API keys generated in the web UI
Authentication Methods
Use your API key with Public API requests in the Authorization header:
API keys are only valid for public API endpoints under /api/v1/*.
Authorization Header
Include your API key in the Authorization header using the Bearer scheme:
Authorization: Bearer mlb_your_api_key_hereUsing DevMail Instead of other providers
Use your generated DevMail inbox and an API key to fetch inbound messages from /api/v1/messages and /api/v1/message/:id, without overhead with
OAuth or two-factor authentication.
DevMail's test-focused inbox and direct API workflow help keep automated email checks faster and more stable.
Example HTTP Requests
Playwright (APIRequestContext)
Use these real-usecase flows in Playwright tests against the DevMail public API.
Verify Email: Extract URL From Message Body
import { test, expect } from '@playwright/test';
test('fetch verify email and extract URL', async ({ request }) => {
// Base API configuration and filter values.
const API_BASE_URL = 'https://api.devmail.space/api/v1';
const API_KEY = 'mlb_your_api_key_here';
const SINCE_ISO = '2026-03-06T10:00:00.000Z';
const VERIFY_SUBJECT = 'Verify email address';
// Reuse auth headers across requests.
const authHeaders = {
Authorization: `Bearer ${API_KEY}`,
};
// 1) Find verification messages in a given date window.
const verifyMessagesResponse = await request.get(`${API_BASE_URL}/messages`, {
params: {
subject: VERIFY_SUBJECT,
since: SINCE_ISO,
limit: 20,
},
headers: authHeaders,
});
if (verifyMessagesResponse.status() !== 200) {
throw new Error(`Verify messages request failed: ${verifyMessagesResponse.status()}`);
}
const verifyMessagesData = await verifyMessagesResponse.json();
if (!verifyMessagesData.messages?.length) {
throw new Error('No verify email messages found for subject + since filter');
}
// 2) Fetch full message body by ID.
const verifyMessageId = verifyMessagesData.messages[0].id;
const verifyMessageResponse = await request.get(`${API_BASE_URL}/message/${verifyMessageId}`, {
headers: authHeaders,
});
if (verifyMessageResponse.status() !== 200) {
throw new Error(`Verify message detail request failed: ${verifyMessageResponse.status()}`);
}
const verifyMessageData = await verifyMessageResponse.json();
const urlRegex = /https?:\/\/[^\s"'<>]+/;
const extractedUrl =
verifyMessageData.body?.text?.match(urlRegex)?.[0] ||
verifyMessageData.body?.html?.match(urlRegex)?.[0] ||
'';
// 3) Assert and print the extracted verification URL.
if (!extractedUrl) {
throw new Error('Could not extract verification URL from message body');
}
console.log('Extracted verification URL:', extractedUrl);
expect(extractedUrl).toBeTruthy();
});Company Flow: Count Emails And Attachments
import { test, expect } from '@playwright/test';
test('count company messages and attachments', async ({ request }) => {
// Base API configuration and filter values.
const API_BASE_URL = 'https://api.devmail.space/api/v1';
const API_KEY = 'mlb_your_api_key_here';
const SINCE_ISO = '2026-03-06T10:00:00.000Z';
const COMPANY_SUBJECT = 'DevMail Corporation Test';
// Reuse auth headers across requests.
const authHeaders = {
Authorization: `Bearer ${API_KEY}`,
};
// 1) Request aggregate counts with subject + since filters.
const companyCountsResponse = await request.get(`${API_BASE_URL}/messages/count`, {
params: {
subject: COMPANY_SUBJECT,
since: SINCE_ISO,
},
headers: authHeaders,
});
if (companyCountsResponse.status() !== 200) {
throw new Error(`Company counts request failed: ${companyCountsResponse.status()}`);
}
// 2) Assert expected email and attachment totals.
const companyCountsData = await companyCountsResponse.json();
expect(companyCountsData.emailCount).toBeGreaterThanOrEqual(3);
expect(companyCountsData.attachmentCount).toBeGreaterThanOrEqual(2);
});cURL
# List messages with Authorization header
curl -X GET https://api.devmail.space/api/v1/messages \
-H "Authorization: Bearer mlb_your_api_key_here" \
-H "Content-Type: application/json"
# Incremental polling with stable cursor
curl -X GET "https://api.devmail.space/api/v1/messages?since=2026-03-01T10:00:00.000Z&sinceId=0&limit=20" \
-H "Authorization: Bearer mlb_your_api_key_here" \
-H "Content-Type: application/json"JavaScript (Fetch API)
// Using Authorization header
const response = await fetch('https://api.devmail.space/api/v1/messages', {
method: 'GET',
headers: {
'Authorization': 'Bearer mlb_your_api_key_here',
'Content-Type': 'application/json'
}
});
const data = await response.json();
console.log(data);Python (Requests)
import requests
# Using Authorization header
headers = {
'Authorization': 'Bearer mlb_your_api_key_here',
'Content-Type': 'application/json'
}
response = requests.get(
'https://api.devmail.space/api/v1/messages',
headers=headers
)
data = response.json()
print(data)Example API Response
A successful request returns 200 OK with JSON data:
{
"emails": [
{
"id": "em_7f3d8a1c",
"fromAddress": "[email protected]",
"toAddress": "[email protected]",
"subject": "Invoice for February",
"receivedAt": "2026-02-18T10:21:00.000Z",
"hasAttachments": false
}
],
"pagination": {
"page": 1,
"perPage": 20,
"totalCount": 1,
"totalPages": 1
}
}Rate Limiting
Public API requests are rate-limited per API key. Requests without a valid API key fall back to IP-based rate limiting:
- Rate limiter: 10 requests per second
- Primary bucket key: API key from the
Authorizationheader - Fallback bucket key: client IP address
Rate-limit responses come as 429 Too Many Requests.
Usage Policy
These rules apply to all use of DevMail (API and web app):
- No illegal activity: Usage of DevMail for illegal activity is strictly prohibited.
- No reselling: Do not build a paid product that is primarily a wrapper around DevMail.
- No proxy services: Do not mirror or proxy DevMail APIs under a different domain or brand.
See Terms and Conditions for full policy details.
Mailbox Aliases
DevMail supports plus-address aliases for all mailboxes. Use aliases to segment scenarios without creating new inboxes.
- Base mailbox:
[email protected] - Alias mailbox:
[email protected] - Free and Pro plans: alias routes to the same generated mailbox as the base address
Email and Mailbox Limits
| Plan | Max email | Max single attachment | Max total attachments | Mailbox storage |
|---|---|---|---|---|
| Free | 1 MB | 512 KB | 1 MB | 5 MB |
| Pro | 8 MB | 3 MB | 6 MB | 50 MB |
Error Responses
API returns standard HTTP status codes for request outcomes:
401 Unauthorized
Your API key is invalid, missing, or has been revoked.
{
"error": "Invalid API key"
}Common causes:
- API key is missing from the request
- API key format is incorrect
- API key has been revoked
- API key doesn't exist
429 Too Many Requests
You have exceeded your rate limit.
{
"error": "Rate limit exceeded. Please try again later."
}Response headers:
Retry-After: Number of seconds to wait before retrying
400 Bad Request
The request is malformed or contains invalid parameters.
{
"error": "Invalid request parameters"
}500 Internal Server Error
An unexpected error occurred on the server.
{
"error": "An error occurred. Please try again later."
}Best Practices
- Keep your API keys secure: Never commit API keys to version control or share them publicly.
- Use environment variables: Store API keys in environment variables rather than hardcoding them.
- Rotate keys regularly: Generate new API keys periodically and revoke old ones.
- Use descriptive names: Give your API keys meaningful names to identify their purpose.
- Revoke unused keys: Delete API keys that are no longer in use to minimize security risks.
- Monitor usage: Check the "Last Used" timestamp in your API keys list to identify inactive keys.
- Handle rate limits gracefully: Implement exponential backoff when you receive 429 responses.
Getting Started
- Sign in, then open API Keys settings to generate your first API key
- Give your key a descriptive name (e.g., "Production CI/CD")
- Copy the generated key and store it securely (it will only be shown once)
- Use the key in your API requests using one of the authentication methods above
- Monitor your key usage and revoke keys when no longer needed