Free vs Pro OTPBox for CI: When 10 Creates/Hour Is Enough
Which tier for parallel OTP E2E? Free covers laptop and light CI; Pro unlocks matrix headroom. Decision framework, create quota, TTL, and wiring compared.
Disposable inboxes for OTP and magic-link tests need enough create quota and TTL to survive the longest CI job without racing other workers. OTPBox offers a Free tier at zero cost and a Pro tier at $9/month. Both use the same API; the question is which one matches your create-rate and parallel-job pattern.
This versus post walks through when Free (laptop, single-job smoke, light CI) is enough and when Pro (matrix, parallel shards, shared NAT) becomes the deliberate answer. Facts below match the live OTPBox API and pricing. No invented competitor market share or benchmark data.
The shared problem: disposable inboxes in parallel CI
A reliable OTP test in CI needs:
- Isolation — a unique address per job so parallel workers do not steal codes
- Enough create quota — stay under the hourly ceiling on the shared egress IP
- TTL long enough — the inbox must outlive the longest test step that polls for a code
- Poll headroom — retry on 404 without hitting another rate limit
When create quota is too low or TTL expires mid-job, tests flake. See 7 Mistakes That Make Email OTP Tests Flake in CI and the CI Email OTP Checklist.
What Free gets you
From /pricing:
$0, no account
- 10 inbox creates/hr per IP
- 1h TTL
- Poll
/latestunrestricted - OTP + magic-link extraction
Only POST /api/inbox counts toward create quota. GET /api/inbox/:token and GET /api/inbox/:token/latest poll freely — no key, no limit. Create once, poll many times.
Live Free 429 body (from /docs):
{
"error": "Free tier: 10 inbox creates/hr per IP. Need more for CI? See Pro → https://otpbox.app/pricing"
}
When Free works:
- Laptop and local Playwright
- Single-job GitHub Actions smoke test (one create per run)
- Light CI where total creates across all workflows stay under 10/hr on the shared egress IP
- Short suites that finish well under 1h from inbox create to last poll
When Free breaks:
- Matrix strategies (OS × browser × shard) that fan out 12+ creates at once
- Self-hosted runners behind one NAT with many developers pushing
- Suites that need 90+ minutes from create to final verification
- Any workflow pattern that exceeds 10 creates/hr from the same egress IP
GitHub-hosted runners use dynamic IPs from shared pools. Self-hosted runners often share one NAT IP. Treat that IP's 10/hr budget as shared across all workflows that create inboxes.
What Pro gets you
From /pricing:
$9/month
- API key (Bearer or
x-api-key) - 120 creates/hr
- 6h TTL
- Same API — more headroom for parallel CI
No seats, no teams, no SLA. You pay for create-rate and longer TTL. Polling remains unrestricted.
Head-to-head
| Dimension | Free | Pro |
|---|---|---|
| Price | $0, no account | $9/mo |
| Inbox creates | 10/hr per IP | 120/hr with API key |
| Inbox TTL | 1h | 6h |
Poll /latest |
Unrestricted | Unrestricted |
| Auth on create | None | Authorization: Bearer KEY or x-api-key: KEY |
| Auth on poll | None | None (unauthenticated) |
| OTP extraction | Built-in | Built-in |
| Magic-link extraction | Built-in | Built-in |
| Best for | Laptop, single smoke, light CI | Matrix / parallel shards |
| Inbox domain | *@box.otpbox.app |
*@box.otpbox.app |
Only the create step requires Pro authentication. Poll calls stay simple:
# Free create (no key)
RESPONSE=$(curl -s -X POST https://otpbox.app/api/inbox)
TOKEN=$(echo "$RESPONSE" | jq -r '.token')
# Pro create (with key)
RESPONSE=$(curl -s -X POST https://otpbox.app/api/inbox \
-H "Authorization: Bearer $OTPBOX_API_KEY")
TOKEN=$(echo "$RESPONSE" | jq -r '.token')
# Poll on both (no key)
OTP=$(curl -s "https://otpbox.app/api/inbox/$TOKEN/latest" | jq -r '.otp')
Choose Free when…
Create rate is low. One inbox per laptop test run, or a single GitHub Actions job that runs a few times per hour.
TTL is short. Your E2E completes in well under 60 minutes from inbox create to the last poll.
No parallel matrix. One job, one worker, sequential runs spread across hours.
Shared IP has budget. Self-hosted runners (or local dev) that do not burn 10 creates/hr from teammates.
Free pattern (from /docs)
#!/bin/bash
set -e
# 1. Create inbox (Free, no key)
RESPONSE=$(curl -s -X POST https://otpbox.app/api/inbox)
TOKEN=$(echo "$RESPONSE" | jq -r '.token')
EMAIL=$(echo "$RESPONSE" | jq -r '.email')
echo "Inbox: $EMAIL"
# 2. Trigger your app to send to $EMAIL
# Example: curl -X POST https://yourapp.com/signup -d "email=$EMAIL"
# 3. Poll /latest (unrestricted)
for i in {1..10}; do
OTP=$(curl -s "https://otpbox.app/api/inbox/$TOKEN/latest" | jq -r '.otp // empty')
if [ -n "$OTP" ] && [ "$OTP" != "null" ]; then
echo "✓ Got OTP: $OTP"
exit 0
fi
echo " Attempt $i/10 - waiting..."
sleep 3
done
echo "✗ Timeout"
exit 1
No secrets, no setup. Stays green as long as total creates from the egress IP stay under 10/hr.
Choose Pro when…
Matrix or parallel shards multiply creates. strategy.matrix that fans out 12 jobs at once, or Playwright with --workers=8 where each worker creates an inbox.
Shared NAT burns Free budget. Self-hosted runners or org-wide CI that shares one egress IP and routinely hits 10/hr.
Long suites need more TTL. Suites that take 90+ minutes from create to final poll. The 1h Free TTL expires mid-job; 6h Pro keeps the inbox alive.
Reliable headroom matters. Remove "will we 429?" from your CI planning when parallel OTP tests are part of every PR.
Pro wiring in GitHub Actions
Store the key as OTPBOX_API_KEY (repo or org secret). Send it only on create:
jobs:
e2e-otp:
runs-on: ubuntu-latest
strategy:
fail-fast: false
max-parallel: 4 # or remove cap with Pro
matrix:
shard: [1, 2, 3, 4, 5, 6, 7, 8]
steps:
- uses: actions/checkout@v4
- name: Create OTPBox inbox
id: inbox
env:
OTPBOX_API_KEY: ${{ secrets.OTPBOX_API_KEY }}
run: |
# Pro create with key
RESPONSE=$(curl -s -X POST https://otpbox.app/api/inbox \
-H "Authorization: Bearer $OTPBOX_API_KEY")
echo "token=$(echo "$RESPONSE" | jq -r '.token')" >> "$GITHUB_OUTPUT"
echo "email=$(echo "$RESPONSE" | jq -r '.email')" >> "$GITHUB_OUTPUT"
- name: Run E2E with inbox email
env:
TEST_EMAIL: ${{ steps.inbox.outputs.email }}
INBOX_TOKEN: ${{ steps.inbox.outputs.token }}
run: npm test -- --shard=${{ matrix.shard }}/8
# Poll /latest stays unauthenticated
From /docs → Using Pro in CI: send Authorization: Bearer $OTPBOX_API_KEY (or x-api-key: $OTPBOX_API_KEY) on POST /api/inbox only. Polls stay unauthenticated.
Full parallel-job patterns: GitHub Actions Matrix + OTPBox Create Quota.
The create-once-then-poll pattern (both tiers)
Rule 1: Never create inside the poll loop.
Create once per job, trigger the real send, then poll /latest until a code or link appears. Recreating the inbox on every retry is the fastest way to 429 yourself — even on Pro.
// Playwright example (Free or Pro)
import { test, expect } from '@playwright/test';
test('signup OTP flow', async ({ page }) => {
// 1. Create inbox once
const authHeader = process.env.OTPBOX_API_KEY
? { Authorization: `Bearer ${process.env.OTPBOX_API_KEY}` }
: {};
const inbox = await fetch('https://otpbox.app/api/inbox', {
method: 'POST',
headers: authHeader,
}).then((r) => r.json());
console.log(`Using inbox: ${inbox.email}`);
// 2. Trigger signup
await page.goto('https://yourapp.com/signup');
await page.fill('#email', inbox.email);
await page.click('button[type=submit]');
// 3. Poll /latest (no auth)
const otp = await expect
.poll(
async () => {
const res = await fetch(
`https://otpbox.app/api/inbox/${inbox.token}/latest`
);
if (!res.ok) return null;
const data = await res.json();
return data.otp || null;
},
{ intervals: [3000], timeout: 30_000 }
)
.not.toBe(null);
// 4. Use the code
await page.fill('#code', otp);
await page.click('button[type=submit]');
await expect(page.locator('text=Welcome')).toBeVisible();
});
See How Playwright expect.poll Beats Fixed Sleeps for Email OTP for the full expect.poll pattern.
Decision framework
| Your situation | Tier |
|---|---|
| Laptop, single job, <1h suite | Free |
Matrix 2–4 jobs with max-parallel: 2 |
Free (stagger creates) |
| Matrix 8+ jobs that create at once | Pro |
| Shared NAT runners burning 10/hr | Pro |
| Suite needs 90+ minutes from create to poll | Pro (6h TTL) |
| Parallel Playwright workers (each creates inbox) | Pro if total workers × runs > 10/hr |
Free is not a trial. It is a permanent zero-cost tier for laptop and light CI. Pro is for matrix headroom and longer TTL when parallel OTP E2E is part of every PR.
Do / don't checklist
Do
- Create one inbox per job or worker, then poll freely
- Log the disposable
emailon failure for provider debugging - Use
max-parallelon Free to stagger creates when matrix jobs share an IP - Put
OTPBOX_API_KEYin GitHub Actions secrets (or CI env) for Pro; omit it for Free - Keep polling simple — no auth required on
/latestfor either tier
Don't
- Create inside retry/poll loops
- Share one inbox across parallel workers (cross-talk)
- Assume Free is a trial you must upgrade from — it is not
- Add auth to poll calls — only the create needs the Pro key
- Treat polling volume as the rate-limit risk — it is not; only creates count
Common mistakes
Burning create quota in a loop. The suite retries on 404 by creating a fresh inbox each time. Fix: create once, poll many.
Mixing Free and Pro without env flags. Half the team uses Pro keys, half uses Free, and nobody documents which workflows expect which. Fix: OTPBOX_API_KEY as optional; Free works when absent, Pro works when present.
Hitting 1h TTL on long suites. The E2E takes 75 minutes; Free inboxes expire at 60. Fix: Pro for 6h TTL.
Assuming Free is insufficient. Many teams run green on Free indefinitely because one create per job × a few runs per hour stays under 10/hr. You do not need Pro unless you hit the wall.
See 7 Mistakes That Make Email OTP Tests Flake in CI for the full list.
Related reading
- GitHub Actions Matrix + OTPBox Create Quota —
max-paralleland Pro wiring for matrix jobs - How to Test Email OTP Verification in CI — end-to-end patterns
- Ultimate Guide to Email OTP Testing with Playwright —
expect.pollpatterns - How Playwright expect.poll Beats Fixed Sleeps for Email OTP — async wait best practices
- CI Email OTP Checklist: 10 Steps That Kill Flaky Verification Tests — isolation, polling, logging
- Should You Mock the Mailer in Email OTP E2E Tests? — when real send matters
- Mailpit vs OTPBox for CI Verification Codes — local SMTP catcher vs disposable inbox
Resources
- OTPBox — create a disposable inbox in the browser
- OTPBox API —
POST /api/inbox,GET .../latest, Free vs Pro limits - Pricing — Free $0 (10/hr, 1h TTL) vs Pro $9/mo (120/hr, 6h TTL)
Free is not a trial. It is a permanent tier for laptop and light CI. Pro is for matrix headroom and longer TTL when parallel OTP E2E becomes part of every PR. Both use the same create-once-then-poll API — pick the tier that matches your create-rate and concurrency pattern.