Temporary Email for Selenium Automation: Zero-Setup Guide (2026)

Temporary Email for Selenium Automation: Zero-Setup Guide (2026)
If you automate signup-flow tests with Selenium, you've hit this wall: the test needs a real inbox to receive the verification email, but you can't use your real email (spam, rate limits, shared state) and spinning up mail servers is overkill.
The clean solution: a free temp-mail API you can poll from your test. No signup, no API key, no cost. Here's the complete pattern with copy-paste code.
The Problem
A typical signup E2E test does this:
- Fill the registration form with a test email
- Wait for the verification email
- Extract the code/link
- Complete verification
- Assert the account is active
Step 2 is where tests get flaky. Common bad solutions:
- Real Gmail + IMAP polling — works until Google rate-limits you, flags access, or a teammate's test run races yours on the same inbox.
- MailHog/MailSlurp-style local servers — great, but requires infrastructure your CI also needs.
- Hardcoded test inboxes on your own domain — now you're running mail infrastructure to test a signup form.
A disposable-email API gives you a fresh, isolated inbox per test run with zero infrastructure.
The Pattern (4 Steps)
- GET a random temp address from the API
- Use it in your Selenium signup flow
- Poll the API inbox until the verification email arrives
- Parse the code/link, finish the flow
Each test run gets its own address — no cross-test contamination, parallel-safe.
Code: Selenium + 1sec.email API (Python)
1sec.email exposes a free, no-key API: GET https://1sec.email/api/emails?address=<addr>@1sec.email returns the inbox as JSON. Here's a complete working example:
import random, re, string, time, requests
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
API = "https://1sec.email/api/emails"
def random_address():
name = "".join(random.choices(string.ascii_lowercase + string.digits, k=10))
return f"{name}@1sec.email"
def wait_for_email(address, timeout=60):
"""Poll the temp inbox until an email arrives. Returns the latest message."""
start = time.time()
while time.time() - start < timeout:
r = requests.get(API, params={"address": address}, timeout=10)
r.raise_for_status()
emails = r.json()
if emails:
return emails[0] # newest first
time.sleep(3)
raise TimeoutError(f"No email arrived for {address} within {timeout}s")
def extract_code(email):
"""Pull a 6-digit verification code from subject or body."""
text = f"{email.get('subject','')} {email.get('body','')}"
m = re.search(r"\b(\d{6})\b", text)
if not m:
raise ValueError("No verification code found in email")
return m.group(1)
# ---- the test ----
driver = webdriver.Chrome()
try:
address = random_address()
print("Using temp address:", address)
driver.get("https://your-app.example.com/signup")
driver.find_element(By.NAME, "email").send_keys(address)
driver.find_element(By.NAME, "password").send_keys("TestPass123!")
driver.find_element(By.CSS_SELECTOR, "button[type=submit]").click()
# wait for the "check your email" state, then poll the inbox
WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.NAME, "verification_code"))
)
email = wait_for_email(address)
code = extract_code(email)
driver.find_element(By.NAME, "verification_code").send_keys(code)
driver.find_element(By.CSS_SELECTOR, "button[type=submit]").click()
WebDriverWait(driver, 10).until(EC.url_contains("/dashboard"))
print("Signup verified OK")
finally:
driver.quit()
Key details that make this reliable:
- Unique address per run — parallel CI jobs never collide.
- Poll, don't sleep — polling every 3s up to 60s beats a fixed
time.sleep(30); fixed sleeps are the #1 cause of flaky email tests. - Parse defensively — regex for the code pattern your app actually sends; fall back to link extraction for magic-link flows.
JavaScript / Node.js Version
Same pattern with fetch (Node 18+):
const API = 'https://1sec.email/api/emails';
const rand = () => Math.random().toString(36).slice(2, 12);
async function waitForEmail(address, timeoutMs = 60000) {
const start = Date.now();
while (Date.now() - start < timeoutMs) {
const res = await fetch(`${API}?address=${encodeURIComponent(address)}`);
const emails = await res.json();
if (emails.length) return emails[0];
await new Promise(r => setTimeout(r, 3000));
}
throw new Error(`No email for ${address} in ${timeoutMs}ms`);
}
// in your Selenium (selenium-webdriver) test:
const address = `${rand()}@1sec.email`;
// ... fill form, submit ...
const email = await waitForEmail(address);
const code = (email.subject + ' ' + email.body).match(/\b(\d{6})\b/)[1];
// ... enter code, assert ...
CI/CD Notes
- No secrets needed. The API needs no key, so there are no credentials to rotate in CI.
- Rate limits: be polite — one inbox poll per 3 seconds per test is plenty. Stagger starts for hundreds of parallel tests.
- Cleanup: inboxes auto-expire. Nothing to tear down, no test data leaking between runs.
- Debugging failures: log the temp address on failure and re-query the inbox URL manually within the expiry window to see exactly what arrived.
When NOT to Use This
- Load testing — don't hammer a free API with 10k concurrent inboxes; that's what MailHog is for.
- Testing your own product's temp-mail blocking — use a known-blocked domain instead.
- Long-lived test accounts — inboxes expire; if the test needs the account tomorrow, use a real test mailbox.
Why 1sec.email for This
Honest comparison for the automation use case: Guerrilla Mail's API is free but its docs are frozen since 2011; temp-mail.io's API is paywalled; tmailor publishes no public API at all. 1sec.email's endpoint is free, keyless, returns clean JSON, and address generation is client-side (no round-trip to create an inbox). For CI test inboxes, that's the whole checklist.
Related: Testing Signup Flows with Playwright and a Free Temp Mail API · Temp Mail Not Receiving Emails? 9 Fixes · 1sec.email
