Skip to main content
OTP.ID enforces two independent rate-limiting policies: a global per-API-key limit that applies to every endpoint, and a per-destination limit that protects individual users from receiving too many OTP messages in a short period. Both limits return HTTP 429 responses with distinct error codes so you can handle them separately.

Summary


Global per-API-key limit

Every request you make to any /v3/* endpoint counts toward your global rate limit. The window is a fixed 1-second bucket. If your API key sends more than 20 requests within that second — across POST /v3/request, POST /v3/send, POST /v3/verify, GET /v3/otp/{otp_id}, or any other v3 endpoint — all additional requests within that window are rejected. Error response:

Handling RATE_LIMITED

Implement exponential backoff when you receive this error. A simple strategy:
  1. On the first RATE_LIMITED response, wait ~100 ms and retry.
  2. On each subsequent failure, double the wait time (200 ms, 400 ms, 800 ms…).
  3. Add a small random jitter (±20%) to prevent synchronized retries from multiple instances of your service all hitting the limit at the same moment.
  4. Set a maximum retry count (e.g., 5 attempts) and surface an error to the user if all retries are exhausted.
If you regularly hit the global limit in production, batch your OTP requests or spread them more evenly over time. Contact OTP.ID support to discuss a higher limit for high-volume use cases.

Per-destination limit

When you call POST /v3/request or POST /v3/send, OTP.ID also enforces a per-destination limit. This limit applies to the combination of your API key (merchant) and the destination phone number or email address — so separate merchants are counted independently. Two windows are enforced simultaneously:
  • Short window: No more than 5 OTPs per 10 minutes to the same destination
  • Long window: No more than 10 OTPs per hour to the same destination
Both conditions must be satisfied for a request to succeed. If either window is exhausted, the request is rejected.
This limit applies only to OTP creation endpoints (POST /v3/request and POST /v3/send). Calls to POST /v3/verify and GET /v3/otp/{otp_id} are not subject to the per-destination policy.
Error response:

Why this limit exists

The per-destination limit protects your users from OTP flooding — a situation where an attacker (or a bug in your application) repeatedly triggers OTP messages to the same phone number or email address. Without this limit, a single user could receive dozens of messages in quick succession, which is a poor experience and a potential harassment vector.

Handling DESTINATION_RATE_LIMITED

Unlike the global limit, you cannot simply retry immediately — you must wait for the current window to expire before sending another OTP to that destination. Recommended approach:
  1. Track OTP send counts on your side. Maintain a counter per destination in your own cache or database so you can check the limit before calling the API and show users a friendly waiting message instead of an opaque error.
  2. Surface a user-facing message. Tell the user something like “Too many OTP requests. Please wait a few minutes before requesting a new code.”
  3. Do not retry automatically. This error is not a transient failure — retrying the same destination will continue to fail until the window resets.
Do not silently swallow DESTINATION_RATE_LIMITED errors and retry in the background — this will burn through your API balance and still fail. Always propagate a meaningful message to the user.

Both limits together

It is possible to receive either limit in a single high-traffic session. The table below summarises how to differentiate them: If you need to increase the global rate limit beyond 20 requests/second, contact OTP.ID support. Per-destination limits are fixed and cannot be adjusted.