📱 OpenInbox is now on Android & iOS! Get the app today.

OpenInbox on Telegram

Get a temp inbox right inside Telegram.

Open Telegram bot

Custom domains

Use your own domain on paid plans.

Learn More
Email Testing • /selenium

/selenium Email Testing

Drive a real browser with Selenium WebDriver and pull OTP codes and verification links out of disposable inboxes via the OpenInbox API — shown in Python, with notes for Java and C#.

Selenium automates the browser, but it can't open the mailbox the verification email lands in. The fix is to give each test a disposable inbox over the OpenInbox REST API: WebDriver fills the form, then a few lines of HTTP polling pull the code or link straight out of the email body. The examples below are real Selenium + Python and run against the live OpenInbox API.

How the flow works

  • Create a disposable inbox: POST /api/inbox (no auth) → { id, email, expiresAt }.
  • WebDriver types that address into your signup/login form and submits it.
  • Poll GET /api/inbound/api/emails?inboxEmail=<email> with X-API-KEY until the email arrives.
  • Extract the OTP (or link) from the email textBody with a regex.
  • WebDriver enters the code / visits the link and you assert the result.

Setup

Install Selenium plus requests (for the API calls). webdriver-manager keeps the browser driver in sync so the same test runs locally and in CI. The inbox-polling endpoint is premium, so put your key in an environment variable.

terminalbash
pip install selenium requests webdriver-manager

export OPENINBOX_API_KEY="your-premium-api-key"

An OpenInbox helper module

Keep the API out of your test logic with a small module. create_inbox creates a fresh inbox; wait_for_otp polls the inbox and returns the first 4–8 digit code it finds in textBody.

openinbox.pypython
import os, re, time, requests

API = "https://api.openinbox.io/api"
HEADERS = {"X-API-KEY": os.environ["OPENINBOX_API_KEY"]}


def create_inbox() -> dict:
    """Create a disposable inbox (no auth required)."""
    r = requests.post(f"{API}/inbox")
    r.raise_for_status()
    return r.json()  # { "id", "email", "expiresAt", ... }


def wait_for_otp(inbox_email: str, timeout: int = 30) -> str:
    """Poll the inbox until an email arrives, then return the OTP from textBody."""
    deadline = time.time() + timeout
    while time.time() < deadline:
        r = requests.get(
            f"{API}/inbound/api/emails",
            params={"inboxEmail": inbox_email},
            headers=HEADERS,
        )
        emails = r.json().get("emails", [])
        if emails:
            match = re.search(r"\b\d{4,8}\b", emails[0].get("textBody") or "")
            if match:
                return match.group(0)
        time.sleep(2)
    raise TimeoutError("No OTP email arrived in time")

The full Selenium + pytest test

Now the test reads top to bottom: create an inbox, run the UI signup with WebDriver, fetch the OTP from the API, type it in, and assert you reached the dashboard. WebDriverWait handles UI timing; wait_for_otp handles the email.

test_signup.pypython
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
from openinbox import create_inbox, wait_for_otp


def test_email_otp_signup():
    inbox = create_inbox()

    options = webdriver.ChromeOptions()
    options.add_argument("--headless=new")
    driver = webdriver.Chrome(options=options)

    try:
        # 1. Open signup and submit the disposable address
        driver.get("https://staging.yourapp.com/signup")
        driver.find_element(By.NAME, "email").send_keys(inbox["email"])
        driver.find_element(By.CSS_SELECTOR, "button[type=submit]").click()

        WebDriverWait(driver, 10).until(
            EC.visibility_of_element_located(
                (By.XPATH, "//*[contains(text(), 'Check your email')]")
            )
        )

        # 2. Pull the OTP from the inbox and enter it
        otp = wait_for_otp(inbox["email"])
        driver.find_element(By.CSS_SELECTOR, "[data-testid=otp]").send_keys(otp)
        driver.find_element(By.XPATH, "//button[contains(., 'Verify')]").click()

        # 3. Assert we reached the dashboard
        WebDriverWait(driver, 15).until(EC.url_contains("/dashboard"))
        assert driver.find_element(
            By.CSS_SELECTOR, "[data-testid=user-menu]"
        ).is_displayed()
    finally:
        driver.quit()

Other languages (Java, C#)

Selenium is the same REST flow in every binding — only the HTTP client changes. In Java, create the inbox with java.net.http.HttpClient and poll /api/inbound/api/emails with an X-API-KEY header; in C#, use HttpClient the same way. The browser-driving half stays pure Selenium. See the language-specific API guides for ready-made clients.

Running headless in CI

The --headless=new flag runs Chrome without a display, and webdriver-manager fetches a matching driver, so the test works unchanged on a GitHub Actions runner. Store your key as a secret and expose it as OPENINBOX_API_KEY.

.github/workflows/selenium.ymlyaml
name: Selenium E2E
on: [push]
jobs:
  selenium:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - run: pip install selenium requests webdriver-manager pytest
      - run: pytest test_signup.py
        env:
          OPENINBOX_API_KEY: ${{ secrets.OPENINBOX_API_KEY }}

Common pitfalls

  • Use a polling loop with a deadline (like wait_for_otp) rather than time.sleep() guesses — emails usually land in 2–5s but occasionally take longer, and a fixed sleep is either flaky or slow.
  • Creating an inbox needs no auth, but GET /api/inbound/api/emails is premium — a 401/403 means the X-API-KEY env var is missing or the plan is not paid.
  • WebDriverWait covers UI timing only; it cannot wait for an email. Keep the two waits separate (WebDriverWait for the page, wait_for_otp for the inbox).
  • The \b\d{4,8}\b regex matches any 4–8 digit run, so it can catch unrelated numbers. If the email has other digits, anchor it (e.g. r"code[:\s]*(\d{6})").
  • In CI, a Chrome/driver version mismatch is the most common failure — webdriver-manager (or Selenium Manager in Selenium 4.6+) resolves the right driver automatically.

Frequently Asked Questions

Can Selenium read emails on its own?

No — Selenium only controls the browser. To verify email flows you create a disposable inbox via the OpenInbox API and poll it with an HTTP client (requests in Python), all from inside the same test.

Do I need an API key?

Creating an inbox (POST /api/inbox) is unauthenticated. Polling received emails (GET /api/inbound/api/emails) requires a premium X-API-KEY, supplied via the OPENINBOX_API_KEY environment variable.

How do I extract the OTP in Python?

Run re.search(r"\b\d{4,8}\b", email["textBody"]) against the email body returned by the poll endpoint. There is no separate parsed field — it is a one-line regex on textBody.

Does this work with headless Chrome and in CI?

Yes. Add the --headless=new argument and the test runs without a display. Because the API calls are plain HTTPS, no SMTP ports or special networking are needed on the runner.

Can I use this with Selenium in Java or C#?

Yes — the browser steps are identical; only the HTTP client for the API differs. Use java.net.http.HttpClient (Java) or HttpClient (C#) to call POST /api/inbox and GET /api/inbound/api/emails with the X-API-KEY header.

How long do inboxes last?

Free inboxes expire after one hour, which is ample for a test. Create a fresh inbox per test so parallel runs stay isolated.

Related Pages

Explore More

Your private inbox is one click away

Free, instant, no registration needed. Works with any service. Expires automatically after 1 hour.

View API Docs