Read an OTP From Gmail: Gmail API, IMAP or a Rented Inbox

Yes, Gmail has an API. The Gmail API, or plain IMAP, can read an OTP from a Gmail account you already control, once OAuth or an app password is set up. Neither creates an address or "delivers" codes. A rented-inbox API works the other way round: your script receives a new @gmail.com or @icloud.com address and the extracted code.

Written for developers who searched "does Gmail have an API" or "rent Gmail API" and want to pick a route before coding. Google's rules below come from its developer and help pages, read on 13 October 2026.

Three routes, side by side

IMAP + app password Gmail API + OAuth Rented-inbox API (OTPGmail)
Whose mailbox Yours Yours A real inbox rented per order
One-time setup 2-Step Verification on, create an app password Cloud project, consent screen, OAuth client, token storage Copy your API key
Ongoing upkeep App password revoked when the account password changes Refresh token can expire or be revoked Keep the key secret
Finding the code Search, fetch and parse the MIME body yourself Query, fetch, decode and parse yourself otp[] holds the extracted code or link
New address per sign-up No, every code lands in one mailbox No Yes
Parallel runs Runs share one mailbox, so codes can collide Same Each run has its own order
Cost No charge No charge within quotas Per inbox; refunded if no code arrives

The first two routes suit codes for an account you own and keep; the third suits a script that needs an address it did not have.

Route 1: read the OTP from Gmail over IMAP

Gmail's IMAP server is imap.gmail.com on port 993, SSL required, and since January 2025 IMAP access is always on. Google's developer guide describes signing in with OAuth 2.0 credentials through the SASL XOAUTH2 mechanism (Google's IMAP and SMTP guide). For a quick script, many developers use an app password instead: a 16-digit passcode that works only with 2-Step Verification on, may be missing on work or school accounts and with Advanced Protection, and is revoked whenever the account password changes. Google says app passwords "aren't recommended and are unnecessary in most cases", so keep this to a dedicated test mailbox.

import email, email.policy, imaplib, re, time

def read_otp_imap(user, app_password, sender, since_ts, timeout=300):
    end = time.time() + timeout
    while time.time() < end:
        with imaplib.IMAP4_SSL("imap.gmail.com", 993) as box:
            box.login(user, app_password)
            box.select("INBOX")
            day = time.strftime("%d-%b-%Y", time.gmtime(since_ts))
            _, ids = box.search(None, "FROM", sender, "SINCE", day)
            for num in reversed(ids[0].split()):
                _, data = box.fetch(num, "(RFC822)")
                msg = email.message_from_bytes(data[0][1], policy=email.policy.default)
                if msg["Date"].datetime.timestamp() < since_ts:
                    continue  # older mail from the same sender
                part = msg.get_body(preferencelist=("plain", "html"))
                text = re.sub(r"<[^>]+>", " ", part.get_content()) if part else ""
                m = re.search(r"code\D{0,40}(\d{4,8})", text, re.I)
                if m:
                    return m.group(1)
        time.sleep(5)
    return None

What usually goes wrong:

Route 2: read the OTP with the Gmail API and OAuth

  1. Create a Google Cloud project, enable the Gmail API, set up the OAuth consent screen and create an OAuth client.
  2. Request the narrowest scope that works, https://www.googleapis.com/auth/gmail.readonly. Google classes it as restricted: an app used by other people needs restricted-scope verification, plus a security assessment if it stores or transmits that data on its servers.
  3. Run the consent flow once, store the refresh token, and let the client library exchange it for access tokens.
  4. Search with the same syntax as the Gmail search box, then fetch and decode the message.
from googleapiclient.discovery import build

def find_code_messages(creds, sender, since_ts):
    gmail = build("gmail", "v1", credentials=creds)
    query = f"from:{sender} after:{int(since_ts)}"  # Unix seconds, not a date
    res = gmail.users().messages().list(userId="me", q=query, maxResults=5).execute()
    return [gmail.users().messages().get(userId="me", id=m["id"], format="full").execute()
            for m in res.get("messages", [])]
# then base64url-decode payload.parts[].body.data and run the same pattern as Route 1

Details from Google's documentation that bite OTP scripts:

Route 3: what “rent Gmail API” gets you

People who search "rent Gmail API" usually want this route: an API that hands the script an address it did not have. With OTPGmail, the job the two snippets above do looks like this:

import time, uuid, requests

HOST = "otpgmail.net"
H = {"Authorization": "Bearer YOUR_API_KEY"}

body = requests.post(f"https://{HOST}/v1/orders", json={"service": "git"},  # git = GitHub
                     headers={**H, "Idempotency-Key": str(uuid.uuid4())}, timeout=15).json()
if not body["success"]:
    raise SystemExit(body["error"]["code"])
order = body["data"][0]  # data is a list, one entry per inbox
print("Type this address into the form:", order["email"])

while True:
    o = requests.get(f"https://{HOST}/v1/orders/{order['orderId']}", headers=H, timeout=15).json()["data"]
    if o["status"] == "completed":
        print("Code:", o["otp"][-1]["code"])  # newest code is last
        break
    if o["status"] == "cancelled":
        print("No code before codeDeadlineAt, refunded")
        break
    time.sleep(4)

What disappears: OAuth, MIME parsing and the stale-code filter, because everything in otp[] belongs to this order. What you take on: a charge per inbox, refunded in full when no code arrives before the order's codeDeadlineAt (25 minutes at most; see the rental refund rules). You never log in to the inbox, and it receives codes only within the order window. Calls are rate limited per key, by default 20 requests per second and 900 per minute; current values are on the docs page.

The full walkthrough with error handling is Gmail OTP API in Python, and the Gmail OTP API overview lists every endpoint. Service codes come from GET /v1/services; a site that is not listed uses ot (Any service), priced higher.

Which route fits your job

Your situation Route Why
Login or reset codes for an account you keep, such as your bot's own login IMAP or Gmail API on a mailbox you own The address must outlive any rental
A one-off script IMAP with an app password on a separate test mailbox Fastest setup; Google discourages it beyond that
A tool other people install to read their own mail Gmail API with OAuth Google's supported route; plan for verification
Your own sign-up flow tested by many parallel runs A rented inbox under ot One address per run, no collisions
A new account on a third-party service for a project A rented inbox per sign-up, within that service's terms A fresh real address; move the account to one you control if you keep it

If you are not sure whether you need to send codes or receive them, sending vs receiving email OTP APIs sorts that out first.

Keep mailboxes and keys safe

Frequently asked questions

Is there a Gmail “OTP API key”?

No. Google has no product for reading OTPs; the Gmail API uses an OAuth client from the Google Cloud console plus a token for each user. The "key" in many tutorials is either an app password or a third-party API key, such as the OTPGmail key shown on the API docs page once you are logged in.

Can the same IMAP code read iCloud Mail?

Yes, with Apple's settings: server imap.mail.me.com, port 993, SSL required, and an app-specific password generated for your Apple Account. Apple says the username is usually the part before @icloud.com, and iCloud Mail does not support POP.

Does the Gmail API cost money?

Normal use is not billed. Google's quota page (updated 10 September 2026) lists a daily threshold of 80,000,000 quota units per project before charges apply, far above what an OTP poller uses.

Why does my Gmail API script say no OTP was received?

Check in this order: whether the sender really sent it (resend once), your q filter (a wrong from: or a date read as midnight PST hides the message), the junk and trash folders that messages.list skips by default, and whether the refresh token still works.

Related guides

Get your verification code now

Sign up free. You are only charged when an inbox is issued. No code within 25 minutes — 100% refunded automatically.

Can't find the service you need? Use Add service on the rental page and we will add it if our infrastructure supports it.

Need a Gmail account you can sign in to, not just a code? See buy short-lived Gmail accounts · Refund and warranty