Skip to content
Observability

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 errors
select timestamp, id,
toInt32OrZero(log_attributes['response.status_code']) as status,
log_attributes['request.path'] as path
from logs
where source = 'edge_logs'
and toInt32OrZero(log_attributes['response.status_code']) between 500 and 599
order by timestamp desc
limit 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 #

  1. Open Explorer and select Run SQL.
  2. Open the query source menu and select Logs.
  3. Choose the time range in that menu.
  4. 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 #

The Supabase CLI does not query ClickHouse logs. Use the Management API command above. 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 attributes
select arrayJoin(mapKeys(log_attributes)) as key, count() as events
from logs
where source = 'postgres_logs'
group by key
order by events desc
limit 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 failures
select timestamp, id, event_message
from logs
where source = 'postgres_logs'
and event_message ilike '%connection%'
and match(event_message, '(?i)failed|refused|timeout')
order by timestamp desc
limit 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.

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.

Realtime connections #

See Configure Realtime logging.