Run flexible analytics queries across ads and CRM data. Supports arbitrary metrics, dimensions (group by), filters, date presets, ordering, and limits. Send 1-5 queries per request.
## ADS Data
**Metrics:**
- spend: Total advertising spend (currency)
- impressions: Total ad impressions (times ad was shown) (number)
- clicks: Total ad clicks (number)
- conversions: Total tracked conversions — what counts as a conversion depends on the campaign objective (app installs, purchases, leads, etc.). Use the 'objective' dimension to distinguish. (number)
- reach: Total unique users reached (number)
- ctr: Click-through rate (clicks / impressions * 100) (percentage)
- cpc: Cost per click (currency)
- cpm: Cost per 1000 impressions (currency)
- conversion_rate: Conversion rate (conversions / clicks * 100) (percentage)
- cost_per_conversion: Cost per conversion (currency)
**Dimensions (group by):**
- channel: Ad platform (Google, Meta, TikTok)
- account: Ad account name (e.g., Carmico, DLCT)
- campaign: Campaign name
- campaign_id: Flyweel's internal campaign UUID. Filter values must be UUIDs returned by this dimension; for a platform campaign ID use campaign_external_id.
- campaign_external_id: The ad platform's own campaign ID (Google/Meta/TikTok). Use this to filter by an ID copied from the platform.
- campaign_status: Campaign status (ENABLED, PAUSED, etc.)
- objective: Campaign objective/goal — determines what 'conversions' measures (e.g., OUTCOME_APP_PROMOTION = app installs, OUTCOME_SALES = purchases, OUTCOME_LEADS = leads, MAXIMIZE_CONVERSIONS for Google, TRAFFIC/VIDEO_VIEWS/PRODUCT_SALES for TikTok). Critical context for interpreting conversion metrics.
- destination_domain: Root domain(s) the campaign's ads send traffic to (e.g. example.com), comma-joined when several. Group by this to spot campaigns pointing at an unexpected domain — a hijacked-account signal. Empty = no web landing page (app install, lead form); null = not fetched yet.
- destination_url: Full landing-page URL(s) of the campaign's ads, ' | '-joined when several. Use destination_domain to group; this one for the exact pages.
- currency: Account currency code (USD, AUD, EUR, etc.)
- date: Date of the metrics
- week: Week (grouped)
- month: Month (grouped)
## CRM Data
**Metrics:**
- deals_created: Number of deals created (number)
- deals_won: Number of deals marked as won (number)
- deals_lost: Number of deals marked as lost (number)
- deals_open: Number of deals still in progress (not won or lost) (number)
- revenue: Total revenue from won deals (currency)
- pipeline_value: Total value of all deals in pipeline (currency)
- expected_value: Total expected amount of deals (currency)
- avg_deal_size: Average deal size (currency)
- avg_won_deal_size: Average size of won deals (currency)
- win_rate: Percentage of closed deals that were won (percentage)
- velocity_days: Average days from deal creation to close (days)
**Dimensions (group by):**
- stage: Current deal stage name
- pipeline_id: Pipeline ID
- lead_source: Where the lead came from (as reported in CRM)
- source_channel: Lead source grouped into channels (Google, Meta, etc.)
- deal_status: Deal outcome status (won, lost, open)
- created_date: Date deal was created
- created_week: Week deal was created
- created_month: Month deal was created
- close_date: Date deal was closed
**Date range presets:** today, yesterday, last_7_days, last_14_days, last_30_days, last_60_days, last_90_days, this_week, last_week, this_month, last_month, this_quarter, last_quarter, this_year, last_6_months, last_12_months
Or use explicit start/end dates (YYYY-MM-DD).
**Scan budget:** ads queries are bounded by campaigns x days. A query refused as over budget says how many days fit; narrow the date range or filter to one account or channel instead of retrying the same range.
**Extended metrics:** Extended platform metrics (e.g., frequency, video_trueview_views, purchases, leads) are always available. Use the exact key name as a metric name.
**Money and currencies:** Money values come labelled: each amount carries its currency code or "(currency unknown)"; a value starting with "~" was converted into the reporting currency and is approximate. Quote an amount with its label exactly as given, and never drop, change or add a currency label; keep the label (and the "~", if it has one) when you round or paraphrase an amount. Never add, compare, convert or compute across currencies yourself: no "X times" or "% higher" between amounts in different currencies unless both are converted values. Use the tool's `cpc`, `cpm` and `cost_per_conversion` (CPA) rather than computing them; compute a ratio the tool lacks separately for each currency from that currency's amounts only, or from a converted total if you label it with `totals._reporting_currency` and call it approximate. When the summary has a "Converted total" line, give that overall figure with its "~", and say once in the answer that converted figures use ECB rates as of the line's date; per-currency amounts side by side do not replace it. With no such line nothing was converted, so give no converted figure. Name the amounts in `totals._unconverted` as left out of the converted total. If `totals._reporting_currency_fallback_from` is set, say the total is in USD because that currency has no exchange rate. If the summary says "Groups not convertible: N", say those N groups have no exchange rate, so they could not be ranked and are left out of any top-N list. Two rows with the same campaign name and a `campaign_id` are different campaigns; keep them apart. Totals cover all matching rows, not only the rows shown.
**Example:**
{ "queries": [{ "dataSource": "ads", "metrics": ["spend", "clicks", "ctr"], "dimensions": ["channel"], "dateRange": { "preset": "last_30_days" } }] }
**Organization:** results cover one organization, named in the `organization` field of the response. Pass `org` to query a different one — see list_organizations.