MINIMUM VALID CALL:
{ "queries": [{ "type": "cost", "name": "a", "metricId": "cost", "currency": "USD" }], "datePreset": "MTD", "aggBy": "Day" }
Required per series: type (cost|metric|usage|formula|budget|externalMetric) and name. Put labels in alias.
Unified query tool for cost data, custom metrics, usage metrics, external (live integration) metrics, period comparisons, formulas, and budgets. QUERY NAMING: set type and name (prefer short ids like a/b/c for formulas); put human labels in alias (e.g. "Cost by environment") — never in name. Example: { type: "cost", name: "a", alias: "Cost by environment", groupBy: "cos_environment", ... }. For costs: metricId (cost column, default "cost") and currency (default "USD"). Use costMetricId and currency from get when aligning with a budget. For custom business metrics: use [{ type: "metric", metricId: "..." }] — get IDs from list_metrics. For infra usage metrics (e.g. CPU hours, network bytes): use [{ type: "usage", metricId: "..." }] — call suggest_usage_metrics first to discover valid metricIds for your scope. For live external metrics (not saved as Costory metrics): use [{ type: "externalMetric", provider: "...", integrationId: "...", metricName: "...", aggregator: "SUM", groupByFields: [], conditions: "..." }] — discover provider, integrationId, and metricName via list_metrics with includeExternal: true and a specific search term. Tsuga: metricName is the provider metric name; groupByFields are provider metric attributes; conditions is an optional provider filter string. Datadog: same shape as Tsuga — metricName is the Datadog metric name (e.g. system.cpu.user), groupByFields are tag keys (e.g. host, service), conditions is an optional Datadog tag filter (e.g. env:prod). CloudWatch: set provider: "cloudwatch"; metricName is Namespace/MetricName (e.g. AWS/EC2/CPUUtilization); groupByFields are CloudWatch dimension names (e.g. InstanceId); conditions is an optional dimension filter. BigQuery: set provider: "bigquery"; metricName is the fully-qualified table id (project.dataset.table); dateColumn, metricColumn, and gapFillingMethod are required; groupByFields are string column names (not CEL). S3: set provider: "s3"; identical field shape to bigquery — metricName is the fully-qualified table id returned by list_metrics (a Costory-managed external table over the customer's mirrored Parquet); dateColumn, metricColumn, and gapFillingMethod are required; groupByFields are string column names. Use externalMetric for exploration when no saved metric matches; prefer saved { type: "metric" } when one exists. PERIOD: prefer `datePreset` (same DatePreset enum as dashboards/reports, e.g. MTD, LAST_MONTH, TRAILING_30_DAYS, LAST_3_MONTHS, YTD) over hand-computed from/to whenever a preset matches — mutually exclusive with from/to. Response includes the resolved period dates. For comparison: add compare: {} (or compare: { from, to }) — omit compare dates to auto-derive the preceding period (preset-aware, e.g. LAST_MONTH → previous calendar month). For formulas: add { type: "formula", formula: "a / b" } referencing other queries by name. For budgets: use [{ type: "budget", budgetId: "..." }] — despite the field name, this must be the budget version ID (same value as budgetVersionId from get); search returns the parent budget id only, so call get with that id to obtain budgetVersionId before querying. Optional chartType on each query: BAR, LINE, AREA, WATERFALL, or TABLE (defaults to LINE). groupBy is the SPLIT dimension, filterCel is the SCOPE (CEL). Before guessing CEL field names, call search with type: ["dimensions"] — empty query lists all fields; a keyword narrows to matching values. Costory label dimensions use a cos_ prefix (e.g. cos_service_name). Unlabelled resources have null on label dimensions; use filterCel with == null / != null (not is_null or string "null"). Custom virtual dimensions: use immutable `bqName` from list/get VDIM tools as `groupBy` / `filterCel` (not display `name`). Poll `computeStatus` until `COMPLETED` after publish. Optional limit (integer 1–1000): max groups/rows per series. Do NOT set limit unless you need a different cap — when omitted, results default to 100 groups. Set limit above 100 (e.g. 250 or 500) when the user asks for a long tail or full breakdown list. OPTIONAL: After receiving results, consider calling "list_events" for the same date range to correlate cost changes with events, and "suggest_actions" to present follow-up options to the user.
EXAMPLES:
• "What are my total costs this month?" → { queries: [{ type: "cost", name: "a", metricId: "cost", currency: "USD" }], datePreset: "MTD", aggBy: "Day" }
• "Break down AWS costs by service over the last 90 days" → { queries: [{ type: "cost", name: "a", alias: "AWS by service", metricId: "cost", currency: "USD", groupBy: "cos_service_name", filterCel: "cos_provider in [\"AWS\"]" }], datePreset: "TRAILING_90_DAYS", aggBy: "Week" }
• "Show costs for resources without an environment label" → { queries: [{ type: "cost", name: "a", metricId: "cost", currency: "USD", filterCel: "cos_environment == null" }], datePreset: "TRAILING_30_DAYS", aggBy: "Day" }
• "How did our costs change vs last month?" → { queries: [{ type: "cost", name: "a", metricId: "cost", currency: "USD" }], datePreset: "LAST_MONTH", compare: {} }
• "Show CPU hours alongside compute costs" (call suggest_usage_metrics first to get valid metricIds) → { queries: [{ type: "cost", name: "a", metricId: "cost", currency: "USD" }, { type: "usage", name: "b", metricId: "k8s_cpu_hours" }], datePreset: "TRAILING_30_DAYS", aggBy: "Week" }
• "What is our cost per request?" → { queries: [{ type: "cost", name: "a", metricId: "cost", currency: "USD" }, { type: "metric", name: "b", metricId: "<metric-id>" }, { type: "formula", name: "c", formula: "a / b" }], datePreset: "TRAILING_30_DAYS" }
• "Cost per request volume" (after list_metrics with includeExternal: true and search: "request") → { queries: [{ type: "cost", name: "a", metricId: "cost", currency: "USD" }, { type: "externalMetric", name: "b", provider: "tsuga", integrationId: "<integration-id>", metricName: "<metric-name>", aggregator: "SUM" }, { type: "formula", name: "c", formula: "a / b" }], datePreset: "TRAILING_30_DAYS", aggBy: "Week" }
• "Cost per BigQuery revenue table" (after list_metrics with includeExternal: true and search: "revenue") → { queries: [{ type: "cost", name: "a", metricId: "cost", currency: "USD" }, { type: "externalMetric", name: "b", provider: "bigquery", integrationId: "<integration-id>", metricName: "my-project.analytics.revenue", dateColumn: "event_date", metricColumn: "amount", gapFillingMethod: "ZERO", aggregator: "SUM" }, { type: "formula", name: "c", formula: "a / b" }], datePreset: "TRAILING_30_DAYS", aggBy: "Week" }
• "Cost per CPU usage from Datadog" (after list_metrics with includeExternal: true and search: "cpu") → { queries: [{ type: "cost", name: "a", metricId: "cost", currency: "USD" }, { type: "externalMetric", name: "b", provider: "datadog", integrationId: "<integration-id>", metricName: "system.cpu.user", aggregator: "AVG", groupByFields: ["host"] }, { type: "formula", name: "c", formula: "a / b" }], datePreset: "TRAILING_30_DAYS", aggBy: "Week" }
• "Cost per EC2 CPU from CloudWatch" (after list_metrics with includeExternal: true and search: "CPUUtilization") → { queries: [{ type: "cost", name: "a", metricId: "cost", currency: "USD" }, { type: "externalMetric", name: "b", provider: "cloudwatch", integrationId: "<integration-id>", metricName: "AWS/EC2/CPUUtilization", aggregator: "AVG", groupByFields: ["InstanceId"] }, { type: "formula", name: "c", formula: "a / b" }], datePreset: "TRAILING_30_DAYS", aggBy: "Week" }
• "Budget per calendar month" → { queries: [{ type: "budget", name: "a", budgetId: "<budgetVersionId>" }], datePreset: "LAST_3_MONTHS", aggBy: "Month" } (budgetVersionId from get, not the parent id from search)
• "Budget month-to-date by day (cumulative within each month — which day did we reach the budget?)" → { queries: [{ type: "budget", name: "a", budgetId: "<budgetVersionId>", rollingAggregation: { aggregator: "SUM", window: { preset: "MONTH" } } }], datePreset: "MTD", aggBy: "Day" }
• "Formula: month-to-date cost vs month-to-date budget (both rolling SUM per month, e.g. utilization a/b)" → { queries: [{ type: "cost", name: "a", metricId: "cost", currency: "USD", rollingAggregation: { aggregator: "SUM", window: { preset: "MONTH" } } }, { type: "budget", name: "b", budgetId: "<budgetVersionId>", rollingAggregation: { aggregator: "SUM", window: { preset: "MONTH" } } }, { type: "formula", name: "c", formula: "a / b" }], datePreset: "MTD", aggBy: "Day" }
• Custom one-off range → { queries: [{ type: "cost", name: "a", metricId: "cost", currency: "USD" }], from: "2026-01-15", to: "2026-02-12", aggBy: "Day" }