Skip to content

Rate Limiting ​

Strategy ​

Rate limiting uses three complementary layers:

  1. Group-level default — Every authenticated route group includes throttle:authenticated (300 req/min per user in production). This is the safety net that catches all ~60 authenticated endpoints.
  2. Individual route throttles — Write-heavy and abuse-attractive endpoints have tighter per-route limits (e.g., file uploads at 10/min, placement requests at 5/min).
  3. Business-level limits — Some resources have application-level caps independent of HTTP throttling (e.g., invitation/day caps and daily API quota by user plan).

When both group and route throttles apply, both middleware run and the stricter effective limit wins.

Named Rate Limiters ​

Defined in AppServiceProvider::boot():

LimiterProductionDev/Test/E2EKeyed By
authenticated300/min300/minUser ID (or IP if unauthenticated)
public-api150/min300/minIP address

Fortify also registers its own limiters for login, registration, 2FA, and password reset flows.

Endpoint Limits ​

File Uploads ​

EndpointLimit
POST /pets/{pet}/photos10/min
POST /users/me/avatar5/min
POST /pets/{pet}/medical-records/{record}/photos10/min
POST /pets/{pet}/vaccinations/{record}/photo10/min

Messaging ​

EndpointLimit
POST /msg/chats10/min
POST /msg/chats/{chat}/messages30/min

Placement & Adoption ​

EndpointLimit
POST /placement-requests5/min
POST /placement-requests/{placementRequest}/responses10/min

Pet & Record Creation ​

EndpointLimit
POST /pets10/min
POST /pets/{pet}/medical-records15/min
POST /pets/{pet}/vaccinations15/min
POST /pets/{pet}/microchips10/min
POST /pets/{pet}/weights15/min

Profiles & Subscriptions ​

EndpointLimit
POST /pets/{pet}/invitations10/min
POST /resource-invitations/{token}/acceptresource-invitation-consume (10/min user+IP and per-token)
POST /resource-invitations/{token}/declineresource-invitation-consume (10/min user+IP and per-token)
POST /helper-profiles5/min
POST /push-subscriptions5/min

Invitations ​

EndpointLimit
POST /invitations10/hour (throttle) + 10/day (business)
DELETE /invitations/{id}20/hour
POST /invitations/validate20/min

Public Endpoints ​

EndpointLimit
GET /pets/placement-requestspublic-api (150/min)
GET /pets/featuredpublic-api (150/min)
GET /pet-typespublic-api (150/min)

Auth Endpoints (Fortify) ​

EndpointLimit
POST /login5/min (prod)
POST /register10/min (prod)
POST /forgot-password5/min (prod)
POST /email/verification-notification6/min
GET /email/verify/{id}/{hash}6/min

What Is NOT Throttled ​

  • Webhook endpoints (/webhooks/mailgun, /webhooks/telegram) — external services may retry on 429, and they have their own signature verification.
  • Admin routes — admins need operational freedom; the group-level default is sufficient.
  • Public individual-resource GETs (e.g., GET /pets/{pet}) — require knowing IDs, less useful for bulk scraping.
  • Notification polling (GET /notifications, GET /msg/unread-count) — the group default (300/min) is generous enough for normal polling intervals.

Environment-Aware Behavior ​

In local, testing, and e2e environments, the named authenticated and public-api limiters use 300 req/min instead of their production values.

Minute-based route throttles also relax to 300 req/min in those environments. This keeps E2E and local workflows from tripping production-oriented write limits, while longer-window business caps such as invitation-per-hour limits remain unchanged.

Frontend Handling ​

The frontend Axios interceptor returns errors on 429 responses like any other HTTP error. This includes minute-based throttles and daily API quota errors.

Daily quota denials are also written to api_request_logs, so support/admin tooling sees the same request trail as successful API calls.

Business-Level Limits ​

Some limits are enforced at the application layer, not via HTTP middleware:

  • Invitations: 10 per user per day (revoked invitations are excluded from this count)
  • Relationship invitations: Validated by policy rules beyond just rate limiting
  • Daily API quota (plan-aware):
    • Regular users: 1000 requests/day (configurable)
    • Premium users: unlimited
    • Reset boundary: UTC calendar day
    • Over-quota response: 429 with data.error_code = API_DAILY_QUOTA_EXCEEDED and data.quota.reset_at_utc
    • Enforced in every environment. Local development can set API_QUOTA_DISABLED=true in backend/.env to switch it off; the middleware then logs a warning once per worker so a deployment carrying the flag by mistake shows up in the logs

See Invitation System for details on invitation-specific limits.