Security basics
Supabase "Email Rate Limit Exceeded": two limits, one error code
Supabase "Email Rate Limit Exceeded" caps every email your project sends: 2 an hour on the built-in sender. What it counts and how to lift it.

In short
- Supabase "Email Rate Limit Exceeded" means your project has sent as many emails this hour as it is allowed. On the built-in sender that is 2 an hour, and every sign-up confirmation, magic link and password reset counts.
- The same code, over_email_send_rate_limit, also comes back as "For security purposes, you can only request this after N seconds", a 60-second wait for one address. Only the message tells the two apart.
- Your own SMTP provider lifts the hourly cap once you also raise the limit on the Rate Limits page, which starts it at 30 an hour. The 60-second wait stays, and your form should explain it.
You are trying your sign-up form before you share the app. You sign up with
your own address, then with you+2@gmail.com, then once more to check the
confirmation email looks right. One of those attempts stops on this:
AuthApiError: email rate limit exceeded
Supabase "Email Rate Limit Exceeded" means your project has used up its allowance of emails for the hour, so Supabase Auth, the part of Supabase that signs people in, refused to send another. On the email service every project starts with, that allowance is two emails an hour for the whole project.
Here is the part that is easy to get wrong: the error code covers two
different limits. Supabase's own list of Auth error codes describes
over_email_send_rate_limit as "Too many emails have been sent to this email
address." That is true of one of them. The same code also comes back when the
whole project has run out, whoever the email was for, so trying a different
address changes nothing. And the usual advice, "connect your own SMTP", is half
the fix, because Supabase starts a newly connected email server at 30 emails an
hour.
What does "Email rate limit exceeded" mean in Supabase?
It means your project has sent as many emails this hour as Supabase Auth allows, and the next one was refused.
Think of the allowance as a sheet of stamps. Supabase gives each project a fixed number of stamps an hour, and every email Supabase Auth sends uses one, whatever kind of email it is and whoever it is for:
- the confirmation link after somebody signs up
- a magic link or a one-time code to sign in
- a password reset
- an invitation you send from the Users page
- the confirmation when somebody changes their email address
When the sheet is empty, Supabase Auth answers with HTTP 429, which means "too
many requests", the code over_email_send_rate_limit and the message email rate limit exceeded. Those words come from
the Supabase Auth server's own code.
On the built-in sender, the email service a new project starts with, the sheet holds two stamps an hour. That is the figure on Supabase's rate limits page when we read it on 20 October 2026, and Supabase's guide to custom SMTP says it "can change without notice". At two an hour, one sign-up and one password reset are the whole hour's allowance.
If nobody can sign in at all, or the whole app is blank, you are looking at something else, and is Supabase down, or is it your app is where to start.
Two limits share one error code
The message tells you which limit you hit. The code is the same for both:
| The message | What it limits | What it counts | The default |
|---|---|---|---|
email rate limit exceeded | The whole project | Every email Supabase Auth sends, per hour | 2 an hour on the built-in sender, 30 when you first connect your own |
For security purposes, you can only request this after N seconds. | One address | The same kind of email, sent again to the same address | One every 60 seconds |
Both answer HTTP 429 with over_email_send_rate_limit. Supabase's
list of Auth error codes
asks you to identify errors by their code and "not string matching on error
messages". That is right for your app's code, and it leaves one gap here: only
the message says which limit you hit. So read the message to find out what
happened, and let your app react to the code.
If your app only says "something went wrong", the exact message is in the
response to the failed request, in the Network tab of your browser's developer
tools. A code of over_request_rate_limit is a third limit, on requests from one
IP address, and Supabase's list says it can point to a bug in your app that
sends too many of them.
Why does it say "you can only request this after 0 seconds"?
Because less than a second of the wait was left. The per-address limit allows one email of each kind to the same address every 60 seconds: one sign-up confirmation, one password reset, one magic link or code. Ask again sooner and Supabase Auth works out how long is left and puts that in the message.
It counts in whole seconds and drops the rest. The function in the Auth server
that writes this message
(errors.go)
divides the time left by one second, so 0.8 of a second prints as 0. Ask
again a moment later and it goes through, as long as the project's hourly cap
still has a stamp.
This wait is working as intended. It is what stops a resend button, pressed twenty times, from sending twenty emails. Leave it at 60 seconds and let your form explain it.
The hourly cap, and who it is for
The built-in sender is for trying Supabase out, and Supabase says so in its SMTP guide. It lists what the service is intended for: "Exploring and getting started with Supabase Auth", "Setting up and testing email templates with the members of the project's team", and "Building toy projects, demos or any non-mission-critical application". It also comes with "No SLA guarantee on message delivery or uptime".
The same guide has a second rule, and it decides who sees which error. Until you connect your own email server, Supabase Auth "will refuse to deliver messages to addresses that are not part of the project's team". A customer signing up with their own address is turned away before any stamp is counted, with a different message:
Email address "customer@example.com" cannot be used as it is not authorized
That is HTTP 400 with the code email_address_not_authorized, and in the Auth
server's code this check runs before either limit. If sign-up works when you try
it and fails for everybody else, this is why.
So on the built-in sender, email rate limit exceeded mostly meets you and your
team, testing. When the Auth server checks whether an address is on your team,
it ignores anything between a + and the @. That makes you+test1@gmail.com
and you+test2@gmail.com both count as you, and every test sign-up uses one of
the same two stamps.
How do I raise the Supabase email rate limit?
Connect your own email provider, then raise the limit Supabase puts on it. They are two separate steps, and the second is the one people miss.
- Pick a sending service and get its SMTP settings. Supabase's guide names
Resend, AWS SES, Postmark, Twilio SendGrid, ZeptoMail and Brevo, and says any
service that supports SMTP will work. You need the host, the port, a user
name and a password, plus a From address such as
no-reply@yourdomain.com. - Turn on custom SMTP in your project's Authentication settings and enter those details. From then on Supabase sends to every address.
- Raise the email limit on the Rate Limits page, also under Authentication. The guide says a newly connected server gets "a low rate-limit of 30 messages per hour" to protect its reputation, and that you change it there. Set it to what your provider allows and what your busiest hour needs.
Three more things from the same guide belong with this change:
- Set up DKIM, DMARC and SPF for your sending domain with your provider. Supabase says this "will significantly increase the deliverability of your messages".
- Turn on CAPTCHA for sign-ups. The guide names bots signing up lists of addresses as "a common source of abuse", and every one of those sign-ups is an email drawn from your hour. It calls CAPTCHA "the most effective way" to stop them.
- Check that people are not being signed out. The guide says short sessions force "active users to sign in frequently, increasing the number of messages", and if your users sign in by email link, each unexpected logout costs a stamp. Why Supabase users get logged out covers the causes.
For the other limits a free project runs into, see what the Supabase free plan includes.
Handling the error in your sign-up form
Catch it by its code and tell the person what to do in plain words. Supabase's
JavaScript client puts the server's code on the error it returns, as
error.code, so the check is one line:
const { error } = await supabase.auth.signUp({ email, password })
if (error?.code === 'over_email_send_rate_limit') {
// show: "We couldn't send your email just now. Please try again in a minute."
}
Ask your builder, whether that is Lovable, Bolt, v0, Cursor, Claude Code or Replit, for three things:
- the same check on every form that sends email: sign-up, "forgot password", sign-in by email link, and "resend"
- a message that asks the person to try again shortly, in place of the raw error or a form that silently does nothing
- the resend button disabled for 60 seconds after each send, so nobody can press it into the per-address wait
Leave out automatic retries. While the sheet is empty each retry is refused, and
sign-up requests are also limited per IP address, to 30 every 5 minutes by
default. A retry loop can trip that limit too, and it answers with
over_request_rate_limit.
What not to change while sign-ups are stuck
Email confirmation, and where the emails are sent from. Two shortcuts make the error disappear and leave you worse off.
Leave email confirmation on. Switching off "Confirm email" stops sign-ups sending anything, so the error goes away. Supabase's SMTP guide lists "Do not disable email confirmations under pressure" among its defences, because without confirmation somebody can create an account in another person's name.
Keep the emails out of the browser. Ask an assistant to "just send the confirmation with Resend" and it can put the provider's API key into your app's frontend code, where every visitor can read it and send mail as you. Send email through Supabase's SMTP settings or a server-side function, never with a key in the page. If you are not sure one slipped in, our free scan reads the code your live app sends to every visitor and flags the secret keys it finds there. It takes about 20 seconds and needs no account: scan your app.
Keeping watch while you change how your app sends email
Two things can go wrong while you rewire email: a key ends up in your app's code, and a message saying "nobody can sign up" sends you hunting for an outage.
Reeve Monitor keeps checking from outside:
- whether your app is up, checked every minute, so you know at once if a sign-up complaint is an outage or something narrower
- the full security check every hour, on up to three apps, including secret keys in the code your app sends to visitors
- a message when a result changes, so a key that ships in a later deploy reaches you without you going to look
- a monthly report, in plain language, of what it saw
Reeve Care keeps a copy of your Supabase database:
- an encrypted copy on a schedule, on Reeve's own storage in the EU, outside your Supabase account
- the auth schema, where every account that did sign up lives
- each copy verified before it counts, by counting the rows in every table
- your uploaded files as well, once you connect a Storage credential
- a one-click restore, with a safety copy of the current state taken first
- everything Monitor does
What to do now
What to do
- Read the message as well as the code:
email rate limit exceededis the project's hourly cap, andFor security purposes…is the 60-second wait for one address. - If customers see
Email address … cannot be used as it is not authorized, your project is still on Supabase's built-in sender, which only mails your team. - Connect your own SMTP provider, then raise the email limit on the Rate Limits page, which starts at 30 an hour.
- Set up DKIM, DMARC and SPF with your provider, and turn on CAPTCHA for sign-ups.
- Make every form that sends email check for
over_email_send_rate_limit, show a plain message, and hold the resend button for 60 seconds. - Keep email confirmation on, and never put an email provider's key in your frontend.
Start with the message in the failed response, because it tells you which of the two limits you hit. Then, once email goes out through your own provider, scan your app to check that no key came along with it.
FAQ
What does "email rate limit exceeded" mean in Supabase?
It means your project has sent as many emails this hour as Supabase Auth allows, so the next one was refused. The count covers every email Auth sends, to anyone: sign-up confirmations, magic links, password resets and invitations. On the built-in sender the allowance is 2 an hour. With your own SMTP provider you set it yourself.
Why does it say I can only request this after 0 seconds?
Because less than a second of the 60-second wait was left. Supabase Auth counts the time remaining in whole seconds and drops the rest, so the last second reads as 0. Ask again a moment later and the email goes out, as long as the hourly cap still has room.
How many emails can Supabase send for me?
On the built-in sender, 2 an hour for the whole project, and only to people on your Supabase team. Supabase says that figure can change without notice. With your own SMTP provider there is no fixed number: Supabase starts it at 30 an hour and you raise it on the Rate Limits page.
Do I need my own SMTP provider?
Yes, as soon as anybody outside your team signs up by email. The built-in sender refuses addresses that are not on your team, and Supabase describes it as being for trying things out, testing templates and demos. Resend, AWS SES, Postmark, SendGrid, ZeptoMail and Brevo are among the services Supabase lists as working with it.
How do I stop this crashing my sign-up form?
Check the code on the error before anything reaches the screen. When it is over_email_send_rate_limit, show a plain message asking the person to try again in a minute, and keep the resend button disabled for 60 seconds after each send so nobody can press it into the wait.
Why do my customers get "Email address not authorized"?
Because your project still sends through Supabase's built-in sender, which only delivers to members of your Supabase organization. Everybody else is refused with the code email_address_not_authorized. Connecting your own SMTP provider fixes it, and it is the same change that lifts the hourly cap.