# Supabase: limite de e-mail do Auth e como resolver

> Erro email rate limit exceeded no Supabase? Veja os limites do Auth, onde mudar no painel, como ligar um SMTP próprio e o checklist para produção.

Fonte: https://couryo.com/blog/supabase-limite-de-email-auth · Publicado em 2026-10-20

O app está pronto, você chama o primeiro amigo para testar, ele se cadastra e o e-mail de confirmação não chega. Você tenta de novo e o Supabase responde `email rate limit exceeded`. Na terceira tentativa, nem o seu próprio e-mail recebe mais nada.

Não é bug do seu código. O Supabase Auth tem um envio de e-mail embutido feito para testar, com um limite muito baixo, e mais alguns limites que continuam valendo mesmo depois que você liga um SMTP próprio. Este artigo mostra cada um, onde mudar, como configurar o SMTP do jeito certo e o que conferir antes de abrir o app para o público.

## Por que aparece "email rate limit exceeded"?

O erro vem com o código `over_email_send_rate_limit` e o status HTTP `429`. Pela [lista de códigos de erro do Supabase Auth](https://supabase.com/docs/guides/auth/debugging/error-codes), ele quer dizer que e-mails demais foram enviados e que a pessoa precisa esperar um pouco antes de tentar de novo.

Na prática, ele aparece em três situações:

1. **Você ainda usa o envio embutido.** Ele tem um limite por hora para o projeto inteiro e esgota em poucos testes.
2. **Você ligou o SMTP próprio, mas não mexeu no limite.** Ao salvar o SMTP, o Supabase aplica um limite inicial baixo, que você precisa subir.
3. **A mesma pessoa pediu o e-mail duas vezes em menos de um minuto.** Há uma janela por usuário entre um pedido e outro.

## Os limites do envio embutido

A página de [SMTP personalizado do Supabase](https://supabase.com/docs/guides/auth/auth-smtp) é direta: o serviço padrão não é para produção. Verificado em 20/10/2026:

| O que | Como funciona no envio embutido |
|---|---|
| Para quem envia | só para endereços pré-autorizados, os membros da organização no Supabase. Os outros recebem o erro `email_address_not_authorized` |
| Quantos por hora | 2 mensagens por hora, e o próprio Supabase avisa que o número pode mudar sem aviso |
| Garantia | nenhuma garantia (SLA) de entrega nem de disponibilidade |

Com 2 por hora, um cadastro e uma recuperação de senha já gastam a cota. É por isso que o erro aparece tão cedo: não é o seu app que manda demais, é o limite que foi feito para testar.

## Os limites que continuam com SMTP próprio

Ligar o SMTP próprio tira a trava dos destinatários e permite subir o limite de e-mails. Mas os outros limites do Auth continuam valendo. Estes são os padrões da [página de limites do Supabase Auth](https://supabase.com/docs/guides/auth/rate-limits), verificados em 20/10/2026:

| Limite | Padrão | Por quem conta |
|---|---|---|
| E-mails enviados pelo Auth | 2 por hora no envio embutido; ao salvar o SMTP próprio, começa em 30 por hora (segundo a [página de SMTP](https://supabase.com/docs/guides/auth/auth-smtp)) | projeto inteiro |
| Novo pedido de link mágico ou código (`/otp`) | 60 segundos entre um pedido e outro | por usuário |
| Novo e-mail de confirmação de cadastro (`/signup`) | 60 segundos | por usuário |
| Novo pedido de recuperação de senha (`/recover`) | 60 segundos | por usuário |
| Cadastros e entradas | 30 pedidos a cada 5 minutos | por IP |
| Verificações (`/verify`) | 30 pedidos a cada 5 minutos | por IP |

Dois cuidados práticos:

- **O limite de e-mails é do projeto, não da pessoa.** Se 40 pessoas se cadastram na mesma hora e o limite está em 30, as 10 últimas recebem `429`.
- **O limite por IP pega escritórios e escolas.** Muita gente atrás do mesmo IP (uma empresa, uma sala de aula numa demonstração) bate no limite de cadastros por IP mais rápido do que você imagina.

### Onde mudar os limites no painel

No painel do Supabase, abra o projeto e vá em **Authentication > Rate Limits**. Ali ficam os campos que dá para ajustar, entre eles o de e-mails por hora. O campo de e-mails só pode ser mudado com SMTP próprio ou com o gancho de envio (Send Email Hook) ligado; com o envio embutido ele fica travado.

Também dá para mudar pela API de gerenciamento do Supabase, com a chave `rate_limit_email_sent`:

```bash title="Terminal"
curl -X PATCH "https://api.supabase.com/v1/projects/$PROJECT_REF/config/auth" \
  -H "Authorization: Bearer $SUPABASE_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "rate_limit_email_sent": 120 }'
```

O `120` é só um exemplo. Para achar o seu número, faça a conta com os seus dados:

> limite por hora = cadastros na hora de pico × e-mails por cadastro (confirmação, boas-vindas, recuperação) × uma folga (por exemplo, 2)

E lembre que o limite do Supabase não pode passar do limite do seu serviço de SMTP. Se o Supabase deixa sair 500 por hora e o seu SMTP aceita menos, os que sobrarem voltam com erro no log.

## Um exemplo real: o limite que aparece mesmo com o gancho

Nem todo `429` se resolve no primeiro ajuste. A [issue 45743 do repositório do Supabase](https://github.com/supabase/supabase/issues/45743), aberta em 9 de maio de 2026, relata o erro `over_email_send_rate_limit` no cadastro mesmo com o **Auth Email Hook** configurado (o gancho que manda o e-mail por uma função sua, em vez do SMTP). Quem abriu esperava que o limite não valesse nesse caso. Outras pessoas confirmaram o mesmo comportamento nos comentários, e uma delas contou que só saiu do problema ligando o SMTP próprio.

Até 20/10/2026, a issue seguia aberta e sem resposta da equipe do Supabase. A lição prática: se você usa o gancho e ainda recebe `429`, confira o campo de e-mails em **Authentication > Rate Limits** e teste com o mesmo endereço mais de uma vez por minuto para separar o limite do projeto da janela de 60 segundos por usuário.

## Como configurar um SMTP próprio no Supabase

Qualquer serviço de SMTP autenticado funciona. O passo a passo abaixo usa o SMTP do Couryo; com outro serviço, mudam só o servidor, o usuário e a senha.

### Antes de começar

1. **Verifique o domínio de onde os e-mails vão sair** no seu serviço de envio, com SPF, DKIM e DMARC. Sem isso, o e-mail sai, mas cai no spam. No Couryo, isso fica em **Domínios**, com o status de cada registro ao vivo e conexão com a Cloudflare em um clique; veja [Domínios e DNS](https://couryo.com/docs/domains.md) e o guia [SPF, DKIM e DMARC](https://couryo.com/guias/spf-dkim-dmarc.md).
2. **Crie uma chave de API com escopo `send`.** No Couryo, ela é a senha do SMTP.

### No painel do Supabase

Em **Authentication > Emails > SMTP Settings**, ligue **Enable Custom SMTP** e preencha:

| Campo | Valor |
|---|---|
| Sender email | um endereço do domínio verificado, como `nao-responda@auth.exemplo.com.br` |
| Sender name | o nome do seu app |
| Host | `smtp.couryo.com` |
| Port number | `587` (STARTTLS) ou `465` (TLS direto) |
| Username | `couryo` |
| Password | a chave de API (`ck_live_...`) |

Salve e, logo em seguida, vá em **Authentication > Rate Limits** e ajuste os e-mails por hora. Esse é o passo que mais gente esquece: sem ele, você troca o limite de 2 por hora pelo de 30 por hora e acha que o SMTP "não resolveu".

### Pela CLI, para projetos versionados

```toml title="supabase/config.toml"
[auth.email.smtp]
enabled = true
host = "smtp.couryo.com"
port = 587
user = "couryo"
pass = "env(COURYO_API_KEY)"
admin_email = "nao-responda@auth.exemplo.com.br"
sender_name = "Exemplo"

[auth.rate_limit]
email_sent = 120
```

Na [referência de configuração da CLI](https://supabase.com/docs/guides/local-development/cli/config), o `email_sent` vale 2 por padrão e o intervalo mínimo entre pedidos (`max_frequency`, em `[auth.email]`) é de `1m`. A chave fica na variável de ambiente, nunca no arquivo.

Mais detalhes, inclusive pela API de gerenciamento, na página [Supabase Auth com SMTP personalizado](https://couryo.com/docs/supabase.md).

## Como testar sem gastar o limite

1. **Teste com uma chave de teste.** No Couryo, uma chave `ck_test_` funciona no mesmo campo de senha, mas não entrega nada: o e-mail de confirmação é capturado e aparece no painel com o link clicável. Dá para repetir o fluxo quantas vezes quiser sem incomodar ninguém e sem gastar envios. Veja [Modo de teste](https://couryo.com/docs/test-mode.md).
2. **Cadastre um usuário de teste** no app, ou use **Authentication > Users > Add user > Send invitation**.
3. **Confira os dois lados.** No Supabase, **Logs > Auth** mostra se o envio saiu e o erro SMTP, se houver. No Couryo, **E-mails** mostra a linha do tempo da mensagem: aceita, enviada e entregue, com a resposta do servidor de destino e a explicação em português.
4. **Troque para a chave `ck_live_`** quando o fluxo estiver certo.

Se aparecer um código no log do Supabase:

| Código | O que quer dizer |
|---|---|
| `535 5.7.8` | usuário diferente de `couryo` ou chave inválida, revogada ou sem escopo `send` |
| `550 5.7.1` | o Sender email não está num domínio verificado, ou a conta ainda está no nível 0 |
| `452 4.7.0` | limite do nível ou do plano no Couryo; a mensagem traz o motivo e os números |
| `over_email_send_rate_limit` | limite do Supabase, em **Authentication > Rate Limits** |

Uma coisa honesta sobre o Couryo: a conta nova também começa no **nível 0 (sandbox)**, com 25 e-mails por dia só para os endereços da própria conta. Com o domínio verificado e um sinal de que há alguém real por trás (cartão, CNPJ conferido ou domínio com mais de 30 dias), a conta sobe sozinha para o nível 1, com 100 por dia. O plano Grátis tem 3 mil e-mails por mês. Os números de cada nível estão em [Limites e níveis](https://couryo.com/docs/limits.md).

## Mostre o erro para a pessoa, sem susto

Mesmo com tudo configurado, a janela de 60 segundos por usuário continua. Trate o `429` na tela, em vez de deixar a pessoa clicando em "reenviar":

```ts title="cadastro.ts"
import { createClient } from "@supabase/supabase-js";

const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_ANON_KEY!);

export async function cadastrar(email: string, senha: string) {
  const { error } = await supabase.auth.signUp({ email, password: senha });
  if (!error) return { ok: true, mensagem: "Enviamos um link para o seu e-mail." };

  if (error.code === "over_email_send_rate_limit" || error.status === 429) {
    return {
      ok: false,
      mensagem: "Já enviamos um e-mail há pouco. Confira a caixa de entrada e o spam; em 1 minuto você pode pedir de novo.",
    };
  }
  return { ok: false, mensagem: error.message };
}
```

Mostrar um contador de 60 segundos no botão "reenviar" evita o segundo pedido e a frustração.

## Checklist para produção

1. SMTP próprio ligado em **Authentication > Emails > SMTP Settings**.
2. E-mails por hora ajustados em **Authentication > Rate Limits**, com a conta do pico.
3. Domínio do remetente verificado, com SPF, DKIM e DMARC alinhados.
4. Um subdomínio só para o Auth (por exemplo, `auth.exemplo.com.br`), verificado como os outros domínios e separado do marketing. O próprio Supabase recomenda não misturar e-mails de autenticação com marketing.
5. **Site URL** e **Redirect URLs** certos em **Authentication > URL Configuration** (nada de `localhost` em produção).
6. Modelos em **Authentication > Emails > Templates** curtos, sem promoção, com o nome do app no assunto e sem encurtador de link.
7. Código de 6 dígitos (`{{ .Token }}`) como opção no celular: alguns filtros corporativos abrem o link antes da pessoa e gastam o link de uso único.
8. CAPTCHA no cadastro, como o Supabase recomenda, para robôs não gastarem o seu limite.
9. Um jeito de ver cada envio: no Couryo, a linha do tempo em **E-mails** e [webhooks](https://couryo.com/docs/webhooks.md) de devolução e reclamação.

Se o e-mail sai e mesmo assim não aparece para a pessoa, o problema já não é limite. Siga o guia [E-mail de confirmação não chega](https://couryo.com/guias/email-de-confirmacao-nao-chega.md) e o de [e-mail caindo no spam](https://couryo.com/guias/email-caindo-no-spam.md).

## Perguntas frequentes

### Qual é o limite de e-mail do Supabase Auth?

Com o envio embutido, 2 e-mails por hora para o projeto inteiro, e só para os membros da organização. Com SMTP próprio, o limite começa em 30 por hora e você ajusta em **Authentication > Rate Limits**. Há ainda uma janela de 60 segundos por usuário entre um pedido e outro (números verificados na documentação do Supabase em 20/10/2026).

### Liguei o SMTP próprio e o erro continua. Por quê?

Quase sempre porque o limite de e-mails por hora ficou no valor inicial. Abra **Authentication > Rate Limits** e suba o número. Se o erro aparece só quando a mesma pessoa pede duas vezes seguidas, é a janela de 60 segundos por usuário, e ela é proposital.

### O limite do Supabase substitui o limite do serviço de SMTP?

Não, os dois valem. O Supabase decide quantos e-mails pede por hora, e o serviço de SMTP decide quantos aceita. No Couryo, o limite do nível e do plano aparece no painel e no erro `452 4.7.0`, com o motivo e os números.

### Dá para testar o fluxo de cadastro sem mandar e-mail de verdade?

Sim. No Couryo, use uma chave `ck_test_` como senha do SMTP: o e-mail é capturado e aparece no painel com o link clicável, sem entregar nada. Localmente, a CLI do Supabase já captura os e-mails numa caixa de teste.

### Usar o Auth Email Hook livra do limite?

Pela documentação, o limite de e-mails fica ajustável quando você usa SMTP próprio ou o gancho. Mas a [issue 45743](https://github.com/supabase/supabase/issues/45743) relata `429` mesmo com o gancho, e até 20/10/2026 ela seguia aberta. Se for o seu caso, confira o campo em **Authentication > Rate Limits** ou use o SMTP próprio.
