API documentation
Public API /v1 — authenticate with an API key and automate inbox rentals from your own software
Your API key
You are not logged in — log in to get your own API key.
Send this key in the Authorization: Bearer <key> header or the ?api_key=<key> query parameter. Click the eye icon to reveal the full key; click Generate new key when you need to replace it (the old key stops working immediately).Overview
Base URL
https://otpgmail.netAuthentication
Add your API key to every request, either in the
Authorization: Bearer og_xxx... header (recommended) or in the ?api_key=og_xxx... query string.Idempotency-Key (prevents duplicate orders)
Every
POST /v1/orders request should include the Idempotency-Key: <new UUID per order> header. If the request times out or the connection drops, send it again with the same key — the server returns the original response instead of creating another order. Generate a new UUID for each new order and reuse a UUID only when retrying.Inbox type: Gmail or iCloud
By default you get an
@gmail.com inbox. To rent an @icloud.com inbox, add "domain": "icloud.com" to the POST /v1/orders body. Omitting domain means gmail.com — existing integrations keep working without changes. iCloud is available only for services whose icloud field is not null in GET /v1/services; the iCloud price may differ from the Gmail price (see icloud.price). Every order response includes a domain field that tells you which inbox type the order uses.Complete sample code on GitHub
Ready-to-run clients for Python, Node.js, PHP, C# (.NET) and curl — rent an inbox, wait for the code, cancel automatically for a refund, and switch between iCloud and Gmail when stock runs out: github.com/letoan0902/otpgmail-email-otp-api (English) · thue-otp-gmail (Vietnamese). Python note: use
requests rather than bare urllib (its default User-Agent gets a 403 from the CDN).Endpoints
GET/v1/balance
Returns the account balance (in VND) for the API key used in the request.
Examplecurl
Python
Node.js
curl "https://otpgmail.net/v1/balance" \
-H "Authorization: Bearer og_xxx..."{
"success": true,
"data": {
"balance": 25000
}
}Possible errors: UNAUTHORIZEDGET/v1/services
Lists the enabled services — code, name, Gmail price (in VND) and current Gmail stock — plus an icloud block with the price and stock for @icloud.com inboxes. The data is cached and refreshed from the provider every 30 minutes.
icloud = null means the service does not support iCloud yet (the provider does not offer it, or it is temporarily disabled) — only Gmail can be rented. For services with an icloud block, send "domain": "icloud.com" when creating the order to get an @icloud.com inbox at icloud.price.
Examplecurl
Python
Node.js
curl "https://otpgmail.net/v1/services" \
-H "Authorization: Bearer og_xxx..."{
"success": true,
"data": [
{
"code": "ig",
"name": "Instagram",
"price": 1060,
"stock": 342,
"icloud": {
"price": 950,
"stock": 120
}
},
{
"code": "tg",
"name": "Telegram",
"price": 530,
"stock": 1200,
"icloud": {
"price": 480,
"stock": 860
}
},
{
"code": "fb",
"name": "Facebook",
"price": 1590,
"stock": 88,
"icloud": null
}
]
}Possible errors: UNAUTHORIZEDPOST/v1/orders
Creates an order for an inbox that receives verification codes — Gmail by default, or iCloud when you send domain: "icloud.com". Your balance is charged as soon as the provider allocates the inbox. Returns a list of orders (one, or several when quantity > 1).
The Idempotency-Key header (a UUID) is required. Generate a new UUID for each new rental; when retrying after a timeout, reuse the SAME key to avoid creating a duplicate order. This call waits for the provider to allocate an inbox, so it usually takes about 0.3 seconds but can take up to 10 seconds at peak times (rare) — set your client timeout to about 15 seconds rather than giving up after 5 seconds and retrying early.
Parameters| Name | Type | Required | Description |
|---|---|---|---|
service | string | Yes | Service code (from GET /v1/services, e.g. "ig", "tg", "fb") |
quantity | number | No | Number of inboxes to rent (default 1, up to 50 per request) |
domain | string | No | Inbox type: "gmail.com" (default) or "icloud.com". Omitted = gmail.com — existing integrations need no changes. iCloud can be rented only for services whose icloud field is not null in GET /v1/services; otherwise the call fails with DOMAIN_UNAVAILABLE. Any other value ⇒ VALIDATION_ERROR. |
curl
Python
Node.js
# Generate a random Idempotency-Key for every NEW order
# REUSE the same key when retrying after a timeout (prevents duplicate orders)
# domain: "gmail.com" (default, optional) | "icloud.com" (rent an @icloud.com inbox)
curl -X POST "https://otpgmail.net/v1/orders" \
-H "Authorization: Bearer og_xxx..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"service":"ig","quantity":1,"domain":"gmail.com"}'{
"success": true,
"data": [
{
"orderId": "abc123xyz",
"service": "ig",
"domain": "gmail.com",
"email": "[email protected]",
"status": "waiting_code",
"price": 1060,
"otp": [],
"codeDeadlineAt": 1753270000,
"rentExpiresAt": 1753356400,
"createdAt": 1753268200
}
]
}Possible errors: UNAUTHORIZED, VALIDATION_ERROR, INSUFFICIENT_BALANCE, OUT_OF_STOCK, DOMAIN_UNAVAILABLE, NO_MAILS_AVAILABLE, QUANTITY_EXCEEDED, WAITING_LIMIT_REACHED, DUPLICATE_REQUEST, MAINTENANCEGET/v1/orders/{id}
Returns an order by ID, including the codes received so far (otp[]). Poll this endpoint every 3–5 seconds to pick up the code while the order is in the waiting_code status.
The otp[] array is ordered by arrival: oldest first, NEWEST LAST — read the latest code with otp[otp.length - 1]. After the first code arrives, the system AUTOMATICALLY keeps listening for the next one — no extra call is needed. Just keep polling this endpoint; each new code is appended to the end of otp[] (free of charge, for 24 hours after the rental). If the service allows only one code per inbox, otp[] simply stops at that code — responses stay normal, with no error.
Parameters| Name | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Order ID (the orderId returned when the order was created) |
curl
Python
Node.js
curl "https://otpgmail.net/v1/orders/ORDER_ID" \
-H "Authorization: Bearer og_xxx..."{
"success": true,
"data": {
"orderId": "abc123xyz",
"service": "ig",
"domain": "gmail.com",
"email": "[email protected]",
"status": "completed",
"price": 1060,
"otp": [
{
"code": "123456",
"receivedAt": 1753268500
},
{
"code": "789012",
"receivedAt": 1753269000
}
],
"codeDeadlineAt": 1753270000,
"rentExpiresAt": 1753356400,
"createdAt": 1753268200
}
}Possible errors: UNAUTHORIZED, NOT_FOUNDPOST/v1/orders/{id}/cancel
Cancels the order and refunds 100% of the price. Allowed only while the order is in the waiting_code status AND no code has been received.
An order that has received a code (even a single one) cannot be cancelled or refunded — this applies to both the website and the API.
Parameters| Name | Type | Required | Description |
|---|---|---|---|
id | string | Yes | ID of the order to cancel |
curl
Python
Node.js
curl -X POST "https://otpgmail.net/v1/orders/ORDER_ID/cancel" \
-H "Authorization: Bearer og_xxx..." \
-H "Content-Type: application/json"{
"success": true,
"data": {
"orderId": "abc123xyz",
"service": "ig",
"domain": "gmail.com",
"email": "[email protected]",
"status": "cancelled",
"price": 1060,
"otp": [],
"codeDeadlineAt": 1753270000,
"rentExpiresAt": 1753356400,
"createdAt": 1753268200
}
}Possible errors: UNAUTHORIZED, NOT_FOUND, ORDER_NOT_CANCELLABLEOrder lifecycle
Important notes:
- Poll
GET /v1/orders/{id}every 3–5 seconds while the order is waiting_code so you pick up the code promptly. - As soon as the first code arrives, the status changes to completed immediately.
- After 30 minutes without a code, the system cancels the order and refunds 100% (status = cancelled).
- The
otp[]array is ordered oldest first, newest last. Read the latest code withotp[otp.length - 1](Python:otp[-1]). - A completed order keeps receiving codes for free for 24 hours after it was created, and it does so automatically — no extra call is needed. Right after each code, the system is already listening for the next one. Just keep polling
GET /v1/orders/{id}; new codes show up at the end ofotp[]. - If the service allows only one code per inbox,
otp[]stops at that code. Responses stay normal, with no error — you decide when to stop polling. POST /v1/orderswaits for the provider to allocate an inbox, so it usually takes ~0.3 seconds but can take up to 10 seconds at peak times (rare). Set a client timeout of ≈ 15 seconds rather than giving up after 5 seconds and retrying early — and when you do retry, reuse the sameIdempotency-Key.- An order that has received a code cannot be cancelled or refunded — the cancel call returns
ORDER_NOT_CANCELLABLE. - To rent an @icloud.com inbox, send
"domain": "icloud.com"when creating the order (omit it for Gmail; existing bots need no changes). If the service does not support iCloud, or iCloud is temporarily disabled, you getDOMAIN_UNAVAILABLE— switch to Gmail and the rental goes through right away. The lifecycle, the cancellation and refund rules, and the way you read codes are exactly the same as for Gmail.
Rate limits
Limits apply per API key:
- 10 requests/second — short bursts over a few seconds
- 300 requests/minute — total traffic per minute
Going over a limit returns HTTP 429 with a
Retry-After: <seconds> header and the body {"success":false,"error":{"code":"RATE_LIMITED",...}}. Wait the number of seconds given in the header before retrying.Error codes
Every error has the same shape:
{"success":false,"error":{"code":"...","message":"..."}}