Ad-hoc analytics queries tools
- execute_analytics_eql: Run a one-off analytics query written in EQL.
- estimate_eql_cost: Estimate an EQL query's cost before running it.
- calculate_report: Run a saved report by ID and return its computed results.
execute_analytics_eql
Runs an ad-hoc analytics query written in EQL (Exponea Query Language) against a project. Nothing is saved in the project — the result is computed and returned.
The query must be a select report query or a funnel query. Examples:
select count customers by customer.countryselect sum event purchase.revenuefunnel session_start followed by purchase
Standalone metric expressions (such as count event purchase) are not executable on their own. Wrap them in a select, like select count event purchase.
EQL parse errors are returned as success: false with the parser's message in the error field — they are not raised as exceptions.
Match customers on event absence
Questions like "who bought but never consented" depend on matching customers by an event they didn't trigger. The idiom for that is not exists[event X] inside a customers matching clause, and its counterpart exists[event X] matches customers who did trigger the event. Examples:
customers matching not exists[event purchase]matches customers with no purchase event at all.customers matching not exists[event purchase in last 30 days]bounds the absence to a window, so it matches customers who haven't purchased recently even if they purchased long ago.customers matching exists[event purchase] and not exists[event consent where .action = "accept"] and not exists[event consent where .action = "reject"]matches customers who bought something but never accepted or rejected consent — the audience to review before a re-permissioning campaign.
Wrap the same clause in a select to size the audience or break it down over time, for example select count customers by any event timestamp format year month customers matching exists[event purchase] and not exists[event consent] in last 12 months.
Note
not exists[...]works in acustomers matchingclause, because customer filters support full boolean logic including negation. It doesn't work in awhereevent filter, which accepts onlyand-joined conditions. Inside awhereclause, express absence ascount[...] = 0instead.
Request parameters
| Name | Type | Required? | Description |
|---|---|---|---|
project_id | str | Yes | The project ID returned by list_projects. |
query | str | Yes | The EQL query string. Must be a select report query or a funnel query. A customers matching clause supports full boolean logic, including exists[event X] and not exists[event X]. |
execution_time | int | null | No | A Unix timestamp (in seconds) used as the upper boundary for events to consider. Defaults to the current time. |
Response parameters
The tool returns an EqlExecutionResponse object with the following parameters:
| Parameter | Type | Description |
|---|---|---|
success | bool | Whether the query was parsed and computed successfully. |
query | str | The EQL query string that was executed. |
analysis_type | str | null | The analysis type inferred from the EQL query: report, trend, or funnel. Null if parsing failed before the type could be determined. |
data | any | null | The computed result. The shape depends on analysis_type. For reports: rows with headers and metric values. For funnels: step-by-step conversion counts and rates. |
error | str | null | Error message if parsing or computation failed. |
estimate_eql_cost
Parses an EQL query and estimates its cost without running it. The response gives you a score from 0 to 100, a bucketed level, the factors that drive the cost, and suggestions for bringing it down.
Use this before execute_analytics_eql when you're unsure whether a query will pass the cost limit, or when you want to compare the cost of query variants before you commit to one. The cost score is structural: it models the query shape, such as date range, funnel steps, breakdowns, and filter complexity, but doesn't account for the project's actual data volume.
This tool is in alpha, so its API or response shape may change.
Request parameters
| Name | Type | Required? | Description |
|---|---|---|---|
project_id | str | Yes | The project ID returned by list_projects. |
query | str | Yes | The EQL query string to estimate, using the same syntax as execute_analytics_eql. |
Response parameters
The tool returns an EqlCostEstimate object with the following parameters:
| Parameter | Type | Description |
|---|---|---|
score | int | The structural cost score from 0 to 100. Higher means more expensive. Based on query shape only. |
level | str | The bucketed level: low (0–15), medium (16–35), high (36–60), or very_high (61–100). |
factors | list[str] | The cost-contributing factors found in the query structure, for example lifetime date range, 3-step funnel, or unbounded breakdown dimension. |
suggestions | list[str] | Actionable fixes for each factor, for example add 'in last N days' to bound the scan or add 'grouping top 10' after each 'by' clause. |
calculate_report
Runs a saved report by ID and returns its computed results. Unlike execute_analytics_eql, which always recomputes from scratch, this tool routes through the analysis cache by default and returns pre-warmed results when they're available.
This is the right tool for reports that contain running aggregates. Computing running aggregates on demand is expensive, but the platform's periodic recalculation keeps saved reports warm, so their results are ready right away. Cached results depend on the cache being warm. If the report has never been opened or calculated in the UI, the cache may be cold, and results will be empty or stale. When the UI shows populated numbers for the report, this tool returns the same values.
The report must already exist in the project. Use search_reports to browse available reports and find the report_id.
Parametrized reports need a second step. When search_reports shows parametrized as true, pass parameter_values with a value for each name in the report's parameters list. Doing so switches the calculation to the live preview path instead of the cache, so the values apply to that one calculation and are never written back to the saved report. The global_filters parameter can't supply parameter values. If a parametrized report is calculated without them and nothing is stored on the report, the response comes back with success as false and an error that names the missing parameter, for example Missing value for parameter 'banner_name'.
Request parameters
| Name | Type | Required? | Description |
|---|---|---|---|
project_id | str | Yes | The project ID returned by list_projects. |
report_id | str | Yes | The report ID returned by search_reports. |
execution_time | int | null | No | A Unix timestamp (in seconds) used as the upper boundary for events to consider. Defaults to the current time. |
global_filters | dict | null | No | Optional global filters to apply to the analysis. Supports date-range narrowing and customer attribute value filters. Can't supply values for a parametrized report's parameters — use parameter_values for those. Validated server-side; an invalid structure returns a 400 error. |
parameter_values | dict[str, str] | null | No | Runtime values for a parametrized report, as a map of parameter name to string value. Use the names from search_reports parameters. Merged over any values stored on the report, and not persisted. |
Response parameters
The tool returns a CalculateReportResponse object with the following parameters:
| Parameter | Type | Description |
|---|---|---|
success | bool | Whether the calculation succeeded. |
as_dict | any | null | The computed report data. Without parameter_values, a flattened map of dimension keys to metric values from the cache path. With parameter_values, the hierarchical preview result, the same shape family as execute_analytics_eql. |
definition | any | null | The report definition in the same agent-facing shape as search_reports detail mode: the server analysis body plus parameter metadata, with chart and table display settings and per-metric UI modifiers stripped. With parameter_values, the parameter_values it reports are the merged runtime values rather than the stored ones. |
error | str | null | Error message if the calculation failed. Includes the upstream HTTP status and response body, so a rejected calculation names what went wrong instead of returning a bare 400. |
Updated 3 days ago

