At a glance
The percentage of requests to your Supabase auto-generated REST API that returned a 5xx server error over the last five minutes. PostgREST is the layer that turns your Postgres schema into a REST API: every time your app calls supabase.from('orders').select(...), it hits PostgREST, which translates that into SQL and runs it. For most Supabase apps, PostgREST is the backend. A 5xx here is not a client mistake (that would be a 4xx); it is the server failing to fulfil a valid request. When this climbs, your app is throwing errors to real users: products will not load, carts will not save, checkout fails. Anything above 1% means a measurable slice of your traffic is breaking.
Calculation
The gauge is the ratio of 5xx responses to total responses over the window:- 4xx codes (400, 401, 403, 404, 409, 422) are client errors: a malformed query, a missing JWT, a Row Level Security policy denial, a unique-constraint conflict. These are not server failures and belong on a different signal; lumping them in would mask real outages behind routine auth rejections.
- 2xx and 3xx are successes and form the bulk of the denominator.
- Connection pool exhaustion: PostgREST cannot get a database connection, so it returns 503/504. This is the single most common cause and links directly to Supavisor saturation.
- Statement timeouts: a query exceeds the configured
statement_timeoutand PostgREST returns an error. - Database errors: a query hits a deadlock, an out-of-memory condition, or a Postgres-side fault that bubbles up as a 5xx.
- PostgREST restarts / schema-cache reloads: during a schema reload or a restart, in-flight requests can briefly 5xx.
- Upstream gateway issues: the API gateway in front of PostgREST returns 502/503 if PostgREST is unreachable.
Worked example
A platform team runs a headless storefront whose entire data layer is Supabase via PostgREST. The app calls PostgREST for product listings, cart operations, and order creation. Snapshot reviewed on 28 Apr 26 at 20:30 BST during an evening promotion.
At 20:28 the card read 2.70%, deep red. The promotion drove request volume up 3x; PostgREST could not get database connections fast enough and started returning 503s. Crucially, the failures clustered on the order-creation path (a multi-statement transaction that holds a connection longer), so checkout was hit hardest while product browsing mostly succeeded.
- Found the linked cause fast. The pinned Supavisor Pool Saturation % panel read 98% at the same moment, so this was pool exhaustion, not a code bug. That told them to relieve connection pressure, not roll back.
- Shed connection load. They reduced the order-creation transaction from four statements to two and added a
statement_timeoutso a slow order could not hold a connection open. Saturation fell, and the 5xx rate dropped to 0.25% within five minutes. - Sized for next time. They scheduled a compute resize to lift the connection cap before the next promotion, so the same traffic leaves pool headroom.
- 5xx here means the app is down for that slice of users. Unlike latency (slow but working), a 5xx is a hard failure the customer sees. Treat any sustained reading over 1% as a live incident.
- The cause is usually downstream, not in PostgREST. PostgREST rarely fails on its own; it fails because it cannot get a connection (pool), the query times out, or the database errors. Always pair this card with pool saturation, memory, and query-error rate to find the real cause.
- Watch which path fails. A flat 2.7% project rate hid the fact that checkout was failing at 12% while browsing was fine. The path breakdown is where the money is; a 5xx on order-creation costs far more than a 5xx on a product image.
Sibling cards
Reconciling against the source
Where to look in Supabase’s own tooling:
Project Dashboard → Logs → API / PostgREST for the per-request log stream with status code, path, and timing. Filter by status_code >= 500 to isolate the failures and see which paths are affected.
Project Dashboard → Reports → API for the request and error-rate charts the card aggregates, with status-code breakdowns over time.
Logs Explorer with a SQL filter on the API logs source (for example a count of requests where the status code is 500 or above) for an exact count over an arbitrary window when you need to reconcile a specific period.
Why our number may legitimately differ from the Supabase UI:
Cross-connector reconciliation: if your app has its own front-end error tracking (an APM or RUM tool), a PostgREST 5xx spike should correspond to a rise in client-side API errors. If your front-end tool shows users hitting errors but this card is flat, the failure is upstream of PostgREST (CDN, the app’s own server, a third-party API). If this card spikes but the front-end tool is quiet, the failing calls may be background jobs or Edge Functions rather than user sessions; cross-check Edge Function Error Rate %.
Known limitations / FAQs
Why count only 5xx and not 4xx? 4xx codes are client errors: a bad query, a missing or expired JWT, a Row Level Security denial, a unique-constraint conflict. They are routine and expected (an unauthenticated user hitting a protected table generates a 401 by design). Counting them would bury real server outages under normal auth traffic. This card isolates 5xx so a spike unambiguously means the server is failing valid requests. Track 4xx separately if you need to watch auth or RLS rejection rates. The rate spiked but my database CPU and memory look fine. What failed? Almost certainly the connection pool. PostgREST returns 503/504 when it cannot get a database connection, and that happens at the pool layer while CPU and memory stay healthy. Check Supavisor Pool Saturation % at the time of the spike; a reading near 100% confirms it. The fix is to relieve connection pressure (shorter transactions, astatement_timeout, harder pooling, or a tier with a higher cap), not to roll back code.
The rate jumped right after a deploy. Is it always pool related?
No. A spike that coincides exactly with a deploy is often a code or schema regression: a migration that broke a view PostgREST exposes, a renamed column the app still queries, or a schema-cache reload that briefly 5xx’d in-flight requests. Check the failing paths in the API logs; if they all hit one new or changed endpoint, roll back or fix the schema rather than scaling.
My rate is noisy on a low-traffic project. How do I stop false alarms?
The denominator effect: a few 5xx against a small request count is a large percentage. Set a minimum-request floor in the Alert Rules tab so a quiet 5-minute window does not trip the alert on three stray errors. The floor only suppresses the alert; the raw error count is still recorded for review.
Do statement timeouts count as 5xx here?
Yes. When a query exceeds statement_timeout, PostgREST returns a 5xx (typically 504), so timeouts inflate this rate. If the spike is dominated by timeouts rather than 503s, the cause is slow queries or lock contention rather than pool exhaustion. Cross-reference Slow-Query Rate % and the deadlocks card to confirm.
Can I see which endpoint is failing, not just the overall rate?
The card surfaces the aggregate rate; the per-path breakdown lives in the Supabase API logs (filter by status_code >= 500 and group by path). This matters because a flat overall rate can hide a critical path failing badly while everything else is healthy. Always drill into the path breakdown during an incident: a 5xx on checkout costs far more than a 5xx on a non-critical read.
Can I tune the 1% alert threshold?
Yes, in the Alert Rules tab. For a customer-facing API, 1% is already a lot of broken sessions, so many teams tighten it to 0.5%. A background-only API with retry logic might tolerate a higher threshold. Avoid setting it so high that an incident is well underway before the alert fires; the point of a 5-minute window is early detection.