Query logs with SQL
This guide explains how to query project logs with ClickHouse SQL. Use MCP or the Management API for programmatic access, or Explorer in Studio. To filter events without SQL, use Logs in Studio.
Query events
Every event is a row in logs. Select a service with source, use a bounded time range, and limit the returned rows. For example, this query returns the latest API server errors within the supplied time range:
-- recent API server errorsselect timestamp, id, toInt32OrZero(log_attributes['response.status_code']) as status, log_attributes['request.path'] as pathfrom logswhere source = 'edge_logs' and toInt32OrZero(log_attributes['response.status_code']) between 500 and 599order by timestamp desclimit 100;Use the returned timestamp, ID, status, and path to investigate an event. No rows means no matching recorded events in that window; check the source, filters, and retention before concluding that there were no errors.
MCP
Connect Supabase MCP with project_ref and read_only=true. Call query_logs with the SQL and an explicit time range, using the tool's input schema. Use execute_sql for Postgres database diagnostics, not ClickHouse logs.
Management API
Set SUPABASE_ACCESS_TOKEN to a Management API access token authorized to read project logs, and PROJECT_REF to the project reference. Set START and END to UTC timestamps such as 2026-09-07T09:00:00Z, with a range of 24 hours or less. Save the query above as logs.sql, then run:
curl --get "https://api.supabase.com/v1/projects/$PROJECT_REF/analytics/endpoints/logs" \ --header "Authorization: Bearer $SUPABASE_ACCESS_TOKEN" \ --data-urlencode "sql@logs.sql" \ --data-urlencode "iso_timestamp_start=$START" \ --data-urlencode "iso_timestamp_end=$END"Inspect both the HTTP status and the response for query errors before interpreting the results. Without sql, this endpoint queries API Gateway events only. See the logs endpoint reference for request and response fields.
Explorer
- Open Explorer and select Run SQL.
- Open the query source menu and select Logs.
- Choose the time range in that menu.
- Enter the query and select Run.
The selected range is applied to the query. The Logs query source chooses ClickHouse; source = 'edge_logs' chooses API Gateway events within it. Select Database instead when running Postgres SQL.
Terminal access
Supabase CLI v2 does not query ClickHouse logs. Use the Management API command above. Supabase CLI v3 supports ClickHouse queries with supabase logs --remote --sql. For live database statistics, use supabase inspect db.
Sources and fields
Use the Log sources and fields reference to choose the service and query expressions. API Gateway events and a service's own logs describe different layers of a request.
Read structured fields
Read a map key with bracket access, retaining its full dotted path. Values in log_attributes are strings. Cast numeric values before comparing them. toInt32OrZero treats missing or non-numeric values as zero; do not interpret that zero as a measured status or duration.
When a field is missing or unfamiliar, discover the keys present on recorded events:
-- discover Postgres attributesselect arrayJoin(mapKeys(log_attributes)) as key, count() as eventsfrom logswhere source = 'postgres_logs'group by keyorder by events desclimit 100;Time ranges
timestamp is a UTC DateTime64 value. Compare and order it directly. Explorer supplies the chosen time range; MCP and API callers must supply their own bounded range. To compare more than 24 hours through the API, fetch separate windows within retention and combine their aggregates.
Search messages
Use ilike for a case-insensitive substring, or ClickHouse's match for a regular expression:
-- find connection failuresselect timestamp, id, event_messagefrom logswhere source = 'postgres_logs' and event_message ilike '%connection%' and match(event_message, '(?i)failed|refused|timeout')order by timestamp desclimit 100;Combine predicates with and, or, and not. Select only the fields needed for the investigation. To correlate sources, use an identifier present in both; a shared timestamp alone does not establish that events belong to the same request.
Filter by Postgres error code
Filter postgres_logs by SQLSTATE to surface errors at a specific category rather than by message text. The code lives in log_attributes['parsed.sql_state_code'].
To see which error codes are occurring across a time window:
select log_attributes['parsed.sql_state_code'] as sqlstate, count() as occurrencesfrom logswhere source = 'postgres_logs' and log_attributes['parsed.sql_state_code'] != ''group by sqlstateorder by occurrences desclimit 50;During a platform-level incident (such as the database entering read-only mode), codes like 25006 (read_only_sql_transaction) and 53100 (disk_full) will dominate the results. To focus on application errors while a platform incident is active, exclude the known platform codes:
select timestamp, log_attributes['parsed.sql_state_code'] as sqlstate, event_messagefrom logswhere source = 'postgres_logs' and log_attributes['parsed.sql_state_code'] not in ('25006', '53100', '57P03') and log_attributes['parsed.error_severity'] = 'ERROR'order by timestamp desclimit 50;SQLSTATE codes starting with 25 or 53 usually indicate platform-level resource events rather than application bugs. See the PostgREST error codes reference and Database size guide for context.
Correlate requests
Use a request ID or trace ID to join events across sources. These fields are potentially present on any source but are not guaranteed to be populated.
Correlating by request ID
Every API Gateway response includes an sb-request-id header. The gateway unpacks that value into log_attributes['request_id'] — there is no log_attributes['request.headers.sb-request-id'] key. To find the edge log for a known request ID:
-- find the edge log for a given sb-request-idselect timestamp, log_attributesfrom logswhere source = 'edge_logs' and log_attributes['request_id'] = '<sb-request-id>'limit 1;If the response header log_attributes['response.headers.cf_cache_status'] is HIT, the request was served from Cloudflare and never reached PostgREST, Auth, or Storage — no downstream logs exist for that request_id.
Correlate logs by trace ID
If the client sends a traceparent header (W3C Trace Context), the gateway records it in log_attributes['request.headers.traceparent'] and extracts the trace and span IDs into log_attributes['trace_id'] and log_attributes['span_id']. Use the trace_id to correlate across all sources:
-- correlate across all sources by trace_idselect source, timestamp, log_attributesfrom logswhere log_attributes['trace_id'] = '<trace_id>'order by timestamp;See Client-side tracing for how to attach trace context from Supabase client SDKs.
Follow a user's activity
Track a user across services
To trace what a specific user did, find their recent auth events by user ID, then use the request_id from those events to find the other service logs that belong to the same request.
-- step 1: find the user's recent auth eventsselect timestamp, log_attributes['user_id'] as user_id, log_attributes['request_id'] as request_id, event_messagefrom logswhere source = 'auth_logs' and log_attributes['user_id'] = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'order by timestamp desclimit 20;-- step 2: use a request_id from step 1 to find all related logsselect timestamp, source, event_messagefrom logswhere log_attributes['request_id'] = '<request_id from step 1>'order by timestamp;The user ID appears in Auth and in your application's user table.
Investigate an Edge Function invocation
function_edge_logs records the HTTP request and response for each invocation. function_logs records the console output from inside the function. Both share an execution_id that lets you view them together.
-- find a failing invocationselect timestamp, log_attributes['execution_id'] as execution_id, log_attributes['response.status_code'] as status, log_attributes['request.path'] as pathfrom logswhere source = 'function_edge_logs' and toInt32OrZero(log_attributes['response.status_code']) >= 500order by timestamp desclimit 20;-- fetch the HTTP layer and console output for one invocationselect timestamp, source, event_messagefrom logswhere log_attributes['execution_id'] = '<execution_id from above>'order by timestamp;Query limits
Use an explicit limit and narrow time range. The logs query surface rejects select * and count(*); list columns and use count(). A result limit bounds returned rows, not the time range scanned.
Record additional events
For HTTP header capture, see Captured HTTP headers. Configure event recording separately from querying:
Postgres connections
See Configure connection logging.
Postgres statements
See Configure statement logging.
Statement classes
See pgAudit configuration for session and role scope.