API для отримання коду з пошти: приклад на Python для тестів
Щоб отримати код підтвердження з пошти в скрипті на Python, достатньо трьох HTTP-запитів: орендувати скриньку, опитувати замовлення, доки не з'явиться код, і скасувати замовлення, якщо чекати більше не хочете. Нижче — робочий приклад саме такого сценарію з бібліотекою requests і стандартною бібліотекою, на справжньому API OTPGmail.
Типовий читач цього посібника — розробник чи QA-інженер, якому треба автоматично пройти реєстрацію з підтвердженням пошти: у тестах, у CI чи під час перевірки власної форми. Скринька тут справжня — @gmail.com або @icloud.com, тож сайти сприймають її як звичайну адресу.
Що має робити скрипт
Кожен акаунт OTPGmail має власний ключ API на сторінці «Акаунт»; передавайте його в заголовку Authorization: Bearer <ключ_API>. Увесь сценарій такий:
POST /v1/orders— орендувати адресу для конкретного сервісу.GET /v1/orders/{orderId}кожні кілька секунд, доки в поліotpне з'явиться запис.POST /v1/orders/{orderId}/cancel, якщо чекати більше не хочете: доки код не надійшов, кошти повертаються повністю.
Навіть якщо нічого не робити, замовлення без коду скасується автоматично й поверне кошти через 25 хвилин, тож третій крок — це ввічливість, а не обов'язок.
Ключ не варто зберігати в коді: читайте його зі змінної середовища, як у прикладах нижче.
Оренда скриньки
import os
import uuid
import requests
HOST = "otpgmail.net"
HEADERS = {"Authorization": f"Bearer {os.environ['OTPGMAIL_API_KEY']}"}
def rent(service, domain="gmail.com"):
resp = requests.post(
f"https://{HOST}/v1/orders",
json={"service": service, "domain": domain},
headers={**HEADERS, "Idempotency-Key": str(uuid.uuid4())},
timeout=15,
)
body = resp.json()
if not body.get("success"):
raise RuntimeError(body["error"]["code"])
return body["data"][0] # data — список, по одному запису на кожну скриньку
Параметр domain необов'язковий, типово це gmail.com; передайте "icloud.com", якщо сервіс його пропонує — iCloud зазвичай дешевший за Gmail, залежно від сервісу. Заголовок Idempotency-Key важливіший, ніж здається: створюйте новий UUID для кожного нового замовлення, а якщо запит обірвався за тайм-аутом, повторіть його з тим самим ключем — API поверне вже створене замовлення замість того, щоб списати кошти вдруге.
Використовуйте requests, а не urllib зі стандартної бібліотеки: його типовий user agent Python-urllib CDN перед API відхиляє з помилкою HTTP 403.
Очікування коду
import time
def wait_for_code(order_id, max_wait=600, interval=4):
deadline = time.time() + max_wait
while time.time() < deadline:
resp = requests.get(f"https://{HOST}/v1/orders/{order_id}", headers=HEADERS, timeout=15)
order = resp.json()["data"]
if order["otp"]:
return order["otp"][-1]["code"] # найновіший код — останній
if order["status"] == "cancelled":
return None
time.sleep(interval)
# Здаємося раніше за тайм-аут API — скасовуємо, щоб одразу повернути кошти
requests.post(f"https://{HOST}/v1/orders/{order_id}/cancel", headers=HEADERS, timeout=15)
return None
Інтервал 3–5 секунд — золота середина: достатньо часто, щоб не гальмувати тест, і достатньо рідко, щоб не перевищити ліміт запитів. Список otp упорядкований від найстарішого, тож otp[-1] — завжди найновіший код. Це знадобиться, якщо те саме замовлення згодом отримає другий код (вхід, підтвердження пристрою), поки ще може приймати листи — діють ліміт кодів сервісу й вікно джерела.
Збираємо все разом
try:
order = rent("git", domain="icloud.com")
except RuntimeError as e:
if str(e) in ("DOMAIN_UNAVAILABLE", "NO_MAILS_AVAILABLE"):
order = rent("git", domain="gmail.com") # запасний варіант — Gmail
else:
raise
print("Адреса:", order["email"])
# ...тут ваш тест заповнює форму реєстрації адресою order["email"] і надсилає її...
code = wait_for_code(order["orderId"])
print("Код:", code or "не надійшов")
git — код сервісу GitHub із GET /v1/services; замініть його на код іншого сервісу, щоб тестувати іншу реєстрацію. Заповніть форму сервісу адресою order["email"], надішліть її і лише тоді починайте опитування — до відправлення форми коду ще не існує.
Обробка помилок
rent() кидає виняток, коли success дорівнює false. Перехоплюйте конкретні коди помилок, а не всі винятки поспіль, щоб скрипт реагував осмислено:
NO_MAILS_AVAILABLE— скриньок для цього сервісу тимчасово немає. Нічого не списано; повторіть після паузи або змінітьdomain.DOMAIN_UNAVAILABLE— сервіс не має варіанта iCloud або iCloud ненадовго вимкнений. Повторіть із"gmail.com".INSUFFICIENT_BALANCE— поповніть баланс перед наступною орендою.VALIDATION_ERROR— звірте код сервісу зGET /v1/services: імовірно, такого коду немає або в ньому помилка.- HTTP 429 (
RATE_LIMITED) — перевищено ліміт запитів. Прочитайте заголовокRetry-Afterі зачекайте вказану кількість секунд.
Ліміти запитів, повна таблиця помилок і структура відповідей — у документації API.
У тестах: pytest, Playwright, Cypress
У наборі автотестів оренду зручно винести у фікстуру: вона орендує скриньку перед тестом і скасовує замовлення в teardown, якщо код так і не знадобився. Для паралельного запуску кожен тест отримує власну адресу, а Idempotency-Key захищає від подвійної оренди під час повторних спроб. Якщо браузерну частину пише Playwright чи Cypress, логіка та сама: тест заповнює форму, а окремий крок опитує API й вставляє код у поле підтвердження.
Кілька практичних порад:
- закладіть 25-хвилинний тайм-аут як очікуваний результат тесту, а не як «падіння»;
- не опитуйте частіше ніж раз на 3 секунди;
- для повторного прогону створюйте нове замовлення замість нескінченного очікування старого.
Коли код не надходить навіть у ручному режимі, причина зазвичай на боці сайту — капча чи перевірка ризиків. Перелік причин — у посібнику не приходить код підтвердження на пошту. Чим відрізняються скриньки двох типів, пояснює Gmail чи iCloud для кодів підтвердження.
Тестові акаунти створюйте для перевірки власних продуктів або там, де це дозволяють умови платформи.
Поширені запитання
Навіщо запиту заголовок Idempotency-Key?
Щоб повторний запит після обірваного з'єднання не орендував другу скриньку. Використовуйте той самий UUID лише для повтору того самого замовлення, а для кожного нового — новий.
Яким має бути max_wait?
Залежить від сервісу: у GitHub типовий час очікування — приблизно 1 хвилина, у ChatGPT — приблизно 1 хвилина, а іноді й довше. Більше ніж 25 хвилин ставити немає сенсу: після цього API однаково скасує замовлення без коду й поверне кошти.
Чи можна орендувати кілька скриньок одним запитом?
Так, полем quantity у тілі запиту (типово 1, до 200). Тоді data містить по запису на кожну скриньку, а не лише data[0].
Що поверне order["otp"][-1]["code"], поки коду немає?
IndexError, бо до першого коду otp — порожній список. Саме тому wait_for_code спершу перевіряє if order["otp"]:.
