> ## Documentation Index
> Fetch the complete documentation index at: https://docs.followw.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Rate limit

# Redis e rate limit

A API usa os SDKs assíncronos oficiais do
[Redis](https://upstash.com/docs/redis/sdks/py/gettingstarted) e
[rate limit](https://upstash.com/docs/redis/sdks/ratelimit-py/gettingstarted) da Upstash por HTTP,
compatíveis com a execução serverless na Vercel. `api/redis.py` cuida da conexão opcional;
`api/rate_limit.py` aplica a política antes das rotas, sem depender do banco ou do SIGAA.

Copie `UPSTASH_REDIS_REST_URL` e `UPSTASH_REDIS_REST_TOKEN` do console da Upstash para o ambiente da
API. Sem qualquer uma delas, a API funciona sem Redis e sem limitação. Os padrões estão em
`api/core/config.py` e as variáveis em `.env.example`:

* `RATE_LIMIT_REQUESTS=600`: requisições por IP, compartilhadas entre as rotas.
* `RATE_LIMIT_LOGIN_REQUESTS=30`: cota separada para `POST /auth/sigaa`, incluindo tentativas
  inválidas.
* `RATE_LIMIT_WINDOW_SECONDS=60`: duração de cada janela fixa, em segundos.
* `RATE_LIMIT_SECRET_KEY`: assina o IP encaminhado pelo SSR (veja abaixo).
* `REDIS_TIMEOUT_SECONDS=1`: espera máxima pelo Redis, sem retentativa.

As cotas são compartilhadas entre instâncias da API pelo Redis e separadas por ambiente. Os
contadores expiram automaticamente; as chaves são um HMAC do IP com uma chave derivada de
`JWT_SECRET_KEY`, sem cookies ou credenciais. Endereços IPv6 são agrupados pelo prefixo `/64`, já
que cada cliente controla o prefixo inteiro. Uma janela fixa permite rajadas na virada da janela.
Pessoas na mesma rede compartilham a cota; os padrões consideram as redes da universidade, atrás de
NAT.

O excesso retorna `429`, `Cache-Control: no-store` e `Retry-After` com os segundos até a próxima
janela. `OPTIONS` e `POST /jobs` não consomem cota: o QStash já exige assinatura e controla o fluxo
dos jobs. `DELETE /auth/sigaa` consome a cota geral, mas sair sempre apaga os cookies: com a cota
esgotada, só a chamada de logout ao SIGAA é pulada. Fora da Vercel, o IP vem de `request.client`;
cabeçalhos enviados pelo usuário são ignorados. Na Vercel (`VERCEL=1`, definido pela plataforma),
usamos `X-Vercel-Forwarded-For`,
[sobrescrito pela plataforma](https://vercel.com/docs/headers/request-headers#x-vercel-forwarded-for).
Em outra hospedagem com proxy, configure os proxies confiáveis no servidor ASGI.

No SSR e nas prévias do cardápio, o servidor web encaminha o IP original com HMAC-SHA256 e validade
de 60 segundos. A API verifica a assinatura antes de usar esse IP; cabeçalhos falsos, alterados ou
vencidos são ignorados. Assim, chamadas SSR e diretas do mesmo IP compartilham a cota, sem agrupar
todos os visitantes no IP de saída do servidor web. `RATE_LIMIT_SECRET_KEY` deve ter o mesmo valor
no ambiente dos servidores web e API, nunca em uma variável `VITE_`. É um segredo próprio: o
servidor web não recebe `JWT_SECRET_KEY`, que protege os cookies com as credenciais. Fora da Vercel,
o servidor web usa o endereço fornecido pelo runtime, sem confiar em `X-Forwarded-For` arbitrário.
Sem segredo ou endereço confiável no SSR, não há encaminhamento e a API usa o IP da conexão.

Falhas de rede, respostas inválidas ou timeout liberam a requisição (fail-open) e geram
`rate_limit.unavailable`, sem registrar token ou mensagens de erro. A falha desativa a proteção
daquela requisição; não derruba a API. O cliente Redis é reaproveitado entre requisições do mesmo
event loop e nunca atravessa loops. Avisos e atestados de matrícula ficam fora desta integração.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.