Scenario
Your Display and YouTube campaigns run across thousands of placements — websites, YouTube channels, apps. Most spend is wasted on irrelevant sites. You pull the top-performing placements, send them to Mave for content theme analysis, and discover which content environments drive conversions. The output informs placement targeting, exclusion lists, and content partnerships.Architecture
Code
import os, requests
from google.ads.googleads.client import GoogleAdsClient
MV = os.environ["MAVERA_API_KEY"]
CUSTOMER_ID = os.environ["GOOGLE_ADS_CUSTOMER_ID"]
client = GoogleAdsClient.load_from_env()
ga_service = client.get_service("GoogleAdsService")
query = """
SELECT
group_placement_view.display_name,
group_placement_view.target_url,
group_placement_view.placement_type,
metrics.impressions,
metrics.clicks,
metrics.conversions,
metrics.cost_micros,
metrics.ctr
FROM group_placement_view
WHERE segments.date DURING LAST_30_DAYS
AND metrics.impressions > 100
ORDER BY metrics.conversions DESC
LIMIT 50
"""
response = ga_service.search(customer_id=CUSTOMER_ID, query=query)
placements = []
for row in response:
gpv = row.group_placement_view
m = row.metrics
placements.append({
"name": gpv.display_name,
"url": gpv.target_url,
"type": gpv.placement_type.name,
"impressions": m.impressions,
"clicks": m.clicks,
"conversions": m.conversions,
"cost": m.cost_micros / 1_000_000,
"ctr": m.ctr,
})
top_converting = [p for p in placements if p["conversions"] > 0]
bottom_spend = sorted(
[p for p in placements if p["conversions"] == 0],
key=lambda p: -p["cost"]
)[:10]
placement_block = "\n".join(
f"- [{p['type']}] {p['name']} ({p['url']}) — conv: {p['conversions']:.0f}, CTR: {p['ctr']:.2%}, cost: ${p['cost']:.2f}"
for p in top_converting[:25]
)
waste_block = "\n".join(
f"- [{p['type']}] {p['name']} — $0 conversions, spent ${p['cost']:.2f}, {p['impressions']} impressions"
for p in bottom_spend
)
mave = requests.post(
"https://app.mavera.io/api/v1/mave/chat",
headers={"Authorization": f"Bearer {MV}", "Content-Type": "application/json"},
json={"message": f"""Analyze these Google Display/YouTube placements and provide strategic recommendations.
TOP CONVERTING PLACEMENTS ({len(top_converting)}):
{placement_block}
TOP WASTED SPEND (0 conversions):
{waste_block}
Produce:
1. Content themes that drive conversions (group placements by topic/category)
2. Which YouTube channels or site categories perform best and why
3. Recommended placement exclusions (categories burning budget)
4. Content partnership opportunities (sites worth direct deals)
5. Audience insight: what do converting placements tell us about our buyer?"""},
).json()
print("--- Placement Analysis ---")
print(mave.get("content", "")[:2500])
const DEV_TOKEN = process.env.GOOGLE_ADS_DEVELOPER_TOKEN;
const ACCESS_TOKEN = process.env.GOOGLE_ADS_ACCESS_TOKEN;
const CUSTOMER_ID = process.env.GOOGLE_ADS_CUSTOMER_ID;
const MV = process.env.MAVERA_API_KEY;
const gaql = `
SELECT group_placement_view.display_name, group_placement_view.target_url,
group_placement_view.placement_type, metrics.impressions, metrics.clicks,
metrics.conversions, metrics.cost_micros, metrics.ctr
FROM group_placement_view
WHERE segments.date DURING LAST_30_DAYS AND metrics.impressions > 100
ORDER BY metrics.conversions DESC LIMIT 50`;
const gaRes = await fetch(
`https://googleads.googleapis.com/v23/customers/${CUSTOMER_ID}/googleAds:searchStream`,
{
method: "POST",
headers: { Authorization: `Bearer ${ACCESS_TOKEN}`, "developer-token": DEV_TOKEN, "Content-Type": "application/json" },
body: JSON.stringify({ query: gaql }),
}
).then((r) => r.json());
const placements = (gaRes[0]?.results || []).map((row) => ({
name: row.groupPlacementView.displayName,
url: row.groupPlacementView.targetUrl,
type: row.groupPlacementView.placementType,
impressions: parseInt(row.metrics.impressions),
clicks: parseInt(row.metrics.clicks),
conversions: parseFloat(row.metrics.conversions),
cost: parseInt(row.metrics.costMicros) / 1_000_000,
ctr: parseFloat(row.metrics.ctr),
}));
const topConverting = placements.filter((p) => p.conversions > 0);
const wastedSpend = placements.filter((p) => p.conversions === 0).sort((a, b) => b.cost - a.cost).slice(0, 10);
const placementBlock = topConverting.slice(0, 25)
.map((p) => `- [${p.type}] ${p.name} (${p.url}) — conv: ${p.conversions.toFixed(0)}, CTR: ${(p.ctr * 100).toFixed(1)}%, cost: $${p.cost.toFixed(2)}`)
.join("\n");
const wasteBlock = wastedSpend
.map((p) => `- [${p.type}] ${p.name} — $0 conv, spent $${p.cost.toFixed(2)}`)
.join("\n");
const mave = await fetch("https://app.mavera.io/api/v1/mave/chat", {
method: "POST",
headers: { Authorization: `Bearer ${MV}`, "Content-Type": "application/json" },
body: JSON.stringify({
message: `Analyze these Display/YouTube placements.\n\nTOP CONVERTING:\n${placementBlock}\n\nWASTED SPEND:\n${wasteBlock}\n\nProduce: 1) Content themes 2) Best categories 3) Exclusions 4) Partnership opps 5) Buyer insight`,
}),
}).then((r) => r.json());
console.log("--- Placement Analysis ---");
console.log((mave.content || "").slice(0, 2500));
Example Output
--- Placement Analysis ---
## Converting Content Themes
1. **B2B SaaS Review Sites** (G2, Capterra, TrustRadius) — 42% of conversions.
Buyers are actively evaluating. Increase bids on these placements.
2. **Marketing YouTube Channels** (HubSpot, Neil Patel, Ahrefs) — 28% of conversions.
Educational content contexts outperform entertainment.
3. **Industry Blogs** (MarTech.org, Search Engine Journal) — 18% of conversions.
## Recommended Exclusions
- Mobile game apps: $2,340 spent, 0 conversions. Add `adsenseformobileapps.com` to exclusion list.
- Parenting/lifestyle blogs: High impressions, 0 conversions. Exclude category.
## Partnership Opportunities
- G2 profile drives 15% of Display conversions at $4.20 CPA. Consider a sponsored profile.
- MarTech.org: 8 conversions at $6.10 CPA. Direct sponsorship would lower cost.
## Buyer Insight
Your converting buyers consume professional review content and educational marketing
videos. They're in active evaluation mode, not casual browsing.
Error Handling
Placement view requires Display/Video campaigns
Placement view requires Display/Video campaigns
Search-only accounts return empty results for
group_placement_view. Ensure you have active Display or YouTube campaigns.Mave context limits
Mave context limits
50 placements with URLs can produce a large prompt. The code limits to 25 converting + 10 wasted. For larger analyses, batch into multiple Mave calls.
All Google Ads jobs
View all 7 Google Ads integration jobs
Mave Agent
Full reference for POST /api/v1/mave/chat