get_placement_performance

Google Ads Read

๐Ÿšจ **IF THIS TOOL RETURNS A QUOTA ERROR:** - The error message will include a clickable upgrade link - Show the FULL error message to the user (it contains the upgrade link) - DO NOT attempt to work ar

Endpoint

POST https://api.adspirer.ai/api/v1/tools/get_placement_performance/execute

Headers

Description

๐Ÿšจ **IF THIS TOOL RETURNS A QUOTA ERROR:** - The error message will include a clickable upgrade link - Show the FULL error message to the user (it contains the upgrade link) - DO NOT attempt to work around the error or use alternative data - DO NOT create campaigns or perform actions without valid tool data - STOP and direct the user to upgrade via the provided link Per-PLACEMENT performance WITH conversions โ€” where your ads actually ran (websites, YouTube channels/videos, apps) and what each placement cost and produced. This tool retrieves READ-ONLY data via 1 live GAQL query (cached 5 min in Redis). Safe to call multiple times. **Why this tool:** `get_display_placements` shows only the placements you manually targeted (config, no metrics). `get_campaign_performance` stops at ad-group granularity. This is the ONLY tool that shows WHERE Display / Demand Gen / Video / Smart ads actually delivered โ€” including automatic placements Google chose โ€” with cost AND conversions per placement. It's the data behind the Google Ads UI "Where ads showed" / Content โ†’ Placements report. **Returns one row per placement** (or per domain/channel when group_view=true): - Identity: `placement_type` (WEBSITE / YOUTUBE_CHANNEL / YOUTUBE_VIDEO / MOBILE_APPLICATION / โ€ฆ), `display_name`, `target_url` (the site / channel / app), `campaign_name`, `ad_group_name` (detail view) - Metrics for the date range: `impressions`, `clicks`, `ctr`, `cost`, `conversions`, `conversion_value`, `all_conversions`, `cpc`, `cpa`, `roas` - `totals` block + `pagination` (page / total_pages / has_more) **These reports are huge** (a Demand Gen account can have 150,000+ placements). Filter SERVER-SIDE so you don't pull everything: - `min_cost` โ€” only placements that spent at least this much (account currency) - `min_impressions` โ€” only placements above an impression floor - `zero_conversions_only` โ€” only placements with ZERO conversions (spent money, no conversions). Pair with `min_cost` to find wasted spend to exclude. - `placement_type` โ€” restrict to WEBSITE / YOUTUBE_CHANNEL / etc. - `order_by` โ€” cost | conversions | impressions | clicks - `group_view=true` โ€” roll up per domain/channel for fewer rows **Pagination:** results are paged. Check `pagination.has_more` in the response โ€” if true, call again with `page` incremented (page=2, page=3, โ€ฆ) to get the next set of placements. Continue until has_more is false. **Coverage:** Display, Demand Gen, Video, Smart campaigns (any campaign that runs on the Display/YouTube network). Search campaigns have no placement rows. **These numbers are the account's own measured data โ€” this tool renders NO verdicts, invents no "good/bad" thresholds, and applies no baseline.** It shows the placements and their metrics so you (and the user) can decide. `zero_conversions_only` is a factual filter, not a judgement. **Parameters:** - customer_id: Optional (uses connected account if omitted) - lookback_days: 7, 30, 60, 90, 120 (default 30). Ignored when both start_date and end_date set. - start_date / end_date: YYYY-MM-DD (override lookback_days when both provided) - campaign_id: Optional โ€” scope to one campaign - placement_type / group_view / min_cost / min_impressions / zero_conversions_only / order_by: filters above - page / page_size: pagination (page_size default 50, max 200) **Execution time:** 1-4 seconds (1 GAQL query, then cached 5 min) **Use this tool when:** - User asks "where did my ads show?", "which websites / YouTube channels / apps converted?", "where is Display/Demand Gen wasting money?" - User wants to find placements with spend but no conversions to exclude them - User wants per-placement cost / conversions / CPA to optimize Display or Demand Gen **Tip:** Find waste with `zero_conversions_only=true` + `min_cost`, then exclude it with `add_placement_exclusions`.

Request body

All tool arguments are wrapped in an arguments object.

FieldTypeDescription
start_datestring optionalStart date (YYYY-MM-DD). If provided with end_date, overrides lookback_days for custom date range queries.
end_datestring optionalEnd date (YYYY-MM-DD). If provided with start_date, overrides lookback_days for custom date range queries.
date_rangestring optionalDate range preset: 'last_7_days', 'last_14_days', 'last_30_days', 'last_60_days', 'last_90_days'. Overrides lookback_days. Ignored if start_date/end_date are provided.
raw_databoolean optionalIf true, return ONLY raw metrics as a JSON code block (spend, clicks, impressions, conversions, CPA, CPC, CTR, CVR, ROAS by campaign/ad/date). Strips severity labels, suggested bids/budgets, industry benchmarks, and optimization recommendations. Use when you run your own attribution model or want to minimize token usage. default: false
customer_idstring optionalGoogle Ads customer ID. Required for multi-account users. Get from get_connections_status.
lookback_daysinteger optionalNumber of days to analyze (7, 30, 60, 90, 120). Default 30. Ignored when both start_date and end_date are provided. default: 30
campaign_idstring optionalOptional. Scope to a single campaign (from list_campaigns). Omit for account-wide placement breakdown.
placement_typestring optionalFilter by placement type: ALL | WEBSITE | MOBILE_APPLICATION | MOBILE_APP_CATEGORY | YOUTUBE_VIDEO | YOUTUBE_CHANNEL | GOOGLE_PRODUCTS. Default ALL. default: "ALL"
group_viewboolean optionalFalse (default) = one row per exact placement (detail_placement_view). True = rolled up per domain / channel (group_placement_view) for fewer rows. default: false
min_costnumber optionalOnly return placements that spent at least this much (in the account's currency). Use to hide low-spend noise (these reports can have 150k+ rows).
min_impressionsinteger optionalOnly return placements with at least this many impressions. Defaults to >0 (placements that actually served) when omitted.
zero_conversions_onlyboolean optionalTrue = only placements with ZERO conversions in the window (spent money, produced no conversions). Pair with min_cost to find wasted spend to exclude. default: false
order_bystring optionalSort placements by: cost | conversions | impressions | clicks (descending). Default cost. default: "cost"
pageinteger optionalPage number (1-based). The response's pagination.has_more tells you whether to call again with page+1 for the next set of placements. default: 1
page_sizeinteger optionalPlacements per page (default 50, max 200). default: 50

Example request

{
  "arguments": {
    "start_date": "string",
    "end_date": "string",
    "date_range": "string",
    "raw_data": false,
    "customer_id": "string",
    "lookback_days": 30
  }
}

Example responses

200 โ€” Success

{
  "success": true,
  "data": {
    "text": "(tool-specific textual output for get_placement_performance)",
    "quota": {
      "used": 42,
      "limit": 150,
      "tier": "plus",
      "period_end": "2026-05-01"
    }
  },
  "tool": "get_placement_performance"
}

400 โ€” Tool-level error (bad arguments / multi-account selection)

{
  "success": false,
  "error": "You have 25 meta_ads accounts connected. Please specify which account to use by passing the ad_account_id parameter:\n  - Acme Holdings (ad_account_id=\"act_123456789\")\n  - Acme EU (ad_account_id=\"act_987654321\")",
  "is_error": true,
  "tool": "get_placement_performance"
}

402 โ€” Quota exhausted

{
  "success": false,
  "error": "\ud83d\udea8 Monthly limit reached (150/150 tool calls on Plus tier).\nUpgrade to Pro at https://adspirer.ai to keep building.",
  "is_error": true,
  "tool": "get_placement_performance",
  "quota": {
    "used": 150,
    "limit": 150,
    "tier": "plus",
    "period_end": "2026-05-01",
    "upgrade_url": "https://adspirer.ai"
  }
}

Try it live


Adspirer REST API โ€” get an API key at adspirer.ai/keys ยท adspirer.ai