/Rate limits & fraud

Safeguards

Rate limits & fraud

OTPRelay protects your budget and your users from abuse out of the box - sensible per-number limits and Fraud Guard are on by default, with no configuration required.

Every OTPRelay account ships with built-in safeguards that cap how often a single number can be messaged and detect artificially inflated traffic before it reaches a carrier. You don't have to opt in: the limits below and Fraud Guard apply to all live traffic by default. Use them as a backstop, and layer your own application-level checks on top.

Built-in limits

These limits are enforced per verification and per destination number. When a limit is reached, the verification is closed (or the request is rejected) and a matching error code is returned - see Errors for the full envelope.

LimitValue
Sends per number, per verification5, then max_send_attempts (429)
Checks per verification5, then max_check_attempts (429)
Concurrent verifications per numberGuarded, then too_many_requests (429)

Re-sending within the active expiry window does not generate a new passcode - OTPRelay delivers the samecode again, so a user who didn't receive the first message can retry without invalidating it. Each resend still counts toward the 5-send limit. The default window is 600 seconds; see Send a verification to tune expiry.

Fraud Guard

Fraud Guard is enabled by default and protects you from SMS pumping - also called artificially inflated traffic (AIT) - where attackers trigger high volumes of OTP sends to numbers they control in order to earn a share of the messaging revenue. Left unchecked, AIT can quietly drain your messaging budget.

Fraud Guard scores each send before it leaves OTPRelay, combining signals such as:

  • Velocity checks - bursts of sends from one account, IP, or toward a single number or prefix in a short window.
  • Suspicious-country rules - traffic to destinations or ranges with no prior legitimate history for your account.
  • Sequential-number detection - runs of consecutive or patterned numbers that indicate scripted enumeration rather than real users.

When Fraud Guard blocks a send, the request fails with delivery_blocked (HTTP 402) and the message is never billed. Inspect the error body and your dashboard to review what was blocked.

Blocked sends aren't charged

A send rejected by Fraud Guard returns delivery_blocked (402) and is not billed. If a legitimate user is caught, start a fresh verification once the risk signals clear - passing the end-user IP and metadata (see below) improves scoring accuracy.

Rate-limit responses

When a built-in limit is hit, OTPRelay returns a 429 Too Many Requests with an error body of type rate_limit_error. The example below shows the concurrency guard firing with code too_many_requests; the same envelope shape is used for max_send_attempts and max_check_attempts.

429 Too Many Requests
{  "error": {    "type": "rate_limit_error",    "code": "too_many_requests",    "message": "Too many concurrent verifications for this number. Try again shortly.",    "doc_url": "https://api.otprelay.io/docs/rate-limits"  }}

Treat 429 responses as retryable, but back off - retrying immediately will keep tripping the same guard. Use exponential backoff(for example 1s, 2s, 4s, 8s) with a little jitter, and surface a "try again shortly" state to the user rather than looping. The closing limits (max_send_attempts, max_check_attempts) are not retryable on the same verification - start a new one instead.

Programmable rate limits

Today the limits above are fixed. Configurable, programmable rate limits - per-IP and per-user buckets that you define and attach to a send - are planned but not yet finalized. The exact API shape (bucket definitions, intervals, and how rules are keyed) is still open; reach out if a specific limit shape would unblock you.

Send risk signals for better scoring

Pass the end-user's IP address (a planned device_ip-style field) and relevant metadata on each send so Fraud Guard can score risk more accurately and reduce false positives for your real users. See Errors for every code these safeguards can return.