Find the right query
surfaces for each query family: logs, metrics, traces,
errors_and_events, ai_observability, monitors, and incidents. Each entry has:
Log and event search
q also accepts field://pattern// regular expressions (RE2 syntax, at
most 64 characters). See Logs search syntax.
The request needs the project:read scope. Call it first when you do not know which operation
fits. For monitor and incident operations, see Monitors and
Incidents.
Read the SQL schema
timestamp_column, and its
retention_days. The safe SQL API accepts only these tables and columns.
Every table uses
timestamp as its time column.
The attributes and resource columns hold OpenTelemetry attributes. Read one with a dotted
path, for example attributes.http.request.method. Selecting the whole column returns a flat
object with dotted keys, for example {"http.request.method": "GET"}. Use current attribute
names. A path with an older name, such as attributes.http.method, reads the current name first
and then the older one, and the result column keeps the name you wrote. See
Use current attribute names.
Run a safe SQL query
The response has
rows, columns (each with name and type), scanned_rows, scanned_bytes,
and duration_ms. scanned_rows and scanned_bytes are the rows and bytes the query read, not
the rows it returned. Use them to see the cost of a query and to narrow the time window or filters
of an expensive one. Squasher adds the project filter to the query itself, so a query can read only
its own project.
To check a query before you run it, send the same body to /query/explain. It returns
estimated_rows, estimated_bytes, suggested_indexes, and exceeds_budget, and it reads no
rows.
Write a query that passes validation
Squasher checks every query before it runs:- Use one
SELECTstatement on one table. Joins,UNION, and scalar subqueries are rejected. A subquery or a CTE inFROMis allowed. - Put a lower bound on
timestampin theWHEREclause of every table scan. A bound insideNOT, or on only one side of anOR, does not count. - Keep the window to 30 days, or 90 days with
long_range. - Name your columns.
SELECT *is rejected. - Do not read outside the scanned rows. Dictionary and Join-table functions (
dictGet,joinGet), file, URL and remote functions, server functions (hostName,getSetting,currentUser),sleep, andSELECT ... INTOare rejected. - Keep
LIMITat 10,000 or less.
Fix a rejected query
A rejected query returns status 400 with a code inerror, a message, and often a hint.
A status of 503 with
QUERY_TEMPORARILY_UNAVAILABLE means the query backend is busy or briefly
unavailable. Telemetry reads on other endpoints answer the same way. Wait for the Retry-After
seconds, then retry, with a narrower window if the backend was busy.
Use the CLI or MCP
--sql or --file. Over MCP, call search("query.run"), search("query.explain"), or
search("schema.get"), then execute the operation.
Ready-made security queries
Squasher has ready-made queries over security events (OCSF attributes on logs), such as failed login bursts, root logins, and MFA changes. Each one has its SQL and, when it can alert, a query monitor config.install adds the queries to the project’s saved queries. A query the project already has is not
added again. install needs a user token, because a saved query has an owner. run takes a query
id from list, or its saved query id, and an optional { "long_range": true } body.
A security query reads only the logs table and only ocsf.* and squasher.* attributes, plus
cloud.provider, cloud.region, cloud.account.id, and user_agent.original. A rejected run
returns QUERY_ORG_SCOPE_MISSING or QUERY_SCOPE_DENIED (403), QUERY_TABLE_NOT_ALLOWED,
QUERY_ATTRIBUTE_NOT_ALLOWED, QUERY_TIME_RANGE_TOO_WIDE, or QUERY_ROW_LIMIT (400), or
QUERY_BUDGET_EXCEEDED (429 with Retry-After). A query monitor that searches ocsf.* attributes
gets the same checks.
Next steps
Find the API for a task
Map a task to its operations.
Logs API
Search logs with filters instead of SQL.