Skip to main content

Magic Link Email Limit Exceeded

magic_link_email_limit_exceeded429

Rate Limiting · Affects all endpoints

Too many magic link requests for this email address. Wait 1 hour before requesting another.

What this means

A specific email address has requested too many magic links in a short window. The retry_after field is 3600 (one hour) by contract. This limit exists to prevent email-bombing of individual inboxes — even from different IPs, even from rotating clients, one email address can only receive a limited number of magic links per hour.

When you'll see this

  • A user clicked "send magic link" many times rapidly when the email felt slow.
  • An attacker is targeting one user's inbox with sign-in spam.
  • A QA process is automating magic-link requests against a fixed test email.
  • The user has multiple devices and each one tried to send a magic link independently.
Learn more about how this works

Where magic_link_ip_limit_exceeded protects against one client flooding many emails, this limit protects one email address from being flooded by many clients. Together they make magic-link spam impractical — an attacker can't rotate IPs to bypass per-IP limits, and they can't rotate target emails to bypass per-email limits.

The most common gotcha: this limit is per email, not per user account. If a user has multiple browser tabs open, each one might queue up a magic-link request independently, and they'll all hit this limit together. The fix is debouncing in your UI, not raising the limit.

Example response

{
"success": false,
"error": "magic_link_email_limit_exceeded",
"message": "Too many magic link requests for this email.",
"details": [],
"retry_after": 3600,
"doc_url": "https://docs.asterwise.com/reference/errors/magic_link_email_limit_exceeded",
"request_id": "req_01HXYZABCDEFGH",
"timestamp": "2026-05-25T12:34:56Z"
}
NEW TO APIS?
Quick fix
  1. Wait 60 minutes. The per-email limit will clear automatically.
  2. If the user can still access their account through another mechanism (existing session, password if applicable), use that path instead while the limit clears.
  3. If a QA flow keeps tripping this, switch to a rotating pool of test emails (e.g. test+{uuid}@yourdomain.com).
PRODUCTION ENGINEER
Recovery pattern

This and magic_link_ip_limit_exceeded should usually share a single handler — both are 429s with a 1-hour cooldown, and both need the same UX treatment.

Python:

Production handler

import httpx

MAGIC_LINK_LIMITS = {
"magic_link_ip_limit_exceeded",
"magic_link_email_limit_exceeded",
}

def request_magic_link(email, base_url, headers):
response = httpx.post(
f"{base_url}/v1/auth/magic-link",
headers=headers,
json={"email": email},
timeout=10,
)
if response.status_code == 429:
body = response.json()
if body.get("error") in MAGIC_LINK_LIMITS:
wait_minutes = (body.get("retry_after", 3600)) // 60
return {
"ok": False,
"user_message": (
f"Please wait {wait_minutes} minutes before "
"requesting another sign-in email."
),
}
response.raise_for_status()
return {"ok": True}

Avoid this error by

  • Debounce the "send magic link" button for 60 seconds after a successful request. This single change eliminates most legitimate hits on this error.
  • Show users the most recent send timestamp in the UI: "Magic link sent 30 seconds ago. Check your inbox." This stops the "did it work?" reflex click.
  • In test environments, use rotating email addresses (test+{timestamp}@yourdomain.com) so each test gets a fresh per-email counter.
  • For high-trust users (recently active sessions, etc.), consider a session refresh flow that doesn't require a new magic link at all.