import os, requests, time
from google.ads.googleads.client import GoogleAdsClient
MV = os.environ["MAVERA_API_KEY"]
CUSTOMER_ID = os.environ["GOOGLE_ADS_CUSTOMER_ID"]
MB = "https://app.mavera.io/api/v1"
MH = {"Authorization": f"Bearer {MV}", "Content-Type": "application/json"}
client = GoogleAdsClient.load_from_env()
ga_service = client.get_service("GoogleAdsService")
gender_query = """
SELECT
ad_group_criterion.gender.type,
metrics.impressions, metrics.clicks, metrics.conversions,
metrics.cost_micros, metrics.conversions_value
FROM gender_view
WHERE segments.date DURING LAST_90_DAYS
ORDER BY metrics.conversions DESC
"""
age_query = """
SELECT
ad_group_criterion.age_range.type,
metrics.impressions, metrics.clicks, metrics.conversions,
metrics.cost_micros, metrics.conversions_value
FROM age_range_view
WHERE segments.date DURING LAST_90_DAYS
ORDER BY metrics.conversions DESC
"""
gender_data = {}
for row in ga_service.search(customer_id=CUSTOMER_ID, query=gender_query):
g = row.ad_group_criterion.gender.type_.name
gender_data.setdefault(g, {"impressions": 0, "clicks": 0, "conversions": 0, "value": 0})
gender_data[g]["impressions"] += row.metrics.impressions
gender_data[g]["clicks"] += row.metrics.clicks
gender_data[g]["conversions"] += row.metrics.conversions
gender_data[g]["value"] += row.metrics.conversions_value
age_data = {}
for row in ga_service.search(customer_id=CUSTOMER_ID, query=age_query):
a = row.ad_group_criterion.age_range.type_.name
age_data.setdefault(a, {"impressions": 0, "clicks": 0, "conversions": 0, "value": 0})
age_data[a]["impressions"] += row.metrics.impressions
age_data[a]["clicks"] += row.metrics.clicks
age_data[a]["conversions"] += row.metrics.conversions
age_data[a]["value"] += row.metrics.conversions_value
total_conv = sum(d["conversions"] for d in age_data.values()) or 1
top_ages = sorted(age_data.items(), key=lambda x: x[1]["conversions"], reverse=True)[:3]
top_gender = sorted(gender_data.items(), key=lambda x: x[1]["conversions"], reverse=True)[:2]
created = []
for age_name, age_metrics in top_ages:
for gender_name, gender_metrics in top_gender:
conv_share = (age_metrics["conversions"] + gender_metrics["conversions"]) / (2 * total_conv)
if conv_share < 0.05:
continue
cpa = ((age_metrics.get("cost_micros", 0) or 0) / 1_000_000) / max(age_metrics["conversions"], 1)
persona = requests.post(f"{MB}/personas", headers=MH, json={
"name": f"Google Ads: {gender_name.replace('_', ' ').title()} {age_name.replace('AGE_RANGE_', '').replace('_', '-')}",
"description": (
f"Data-backed persona from Google Ads (last 90 days). "
f"Age: {age_name.replace('AGE_RANGE_', '').replace('_', '-')}. Gender: {gender_name}. "
f"Conversions: {age_metrics['conversions']:.0f} ({age_metrics['conversions']/total_conv:.0%} of total). "
f"CPA: ${cpa:.2f}. Conv value: ${age_metrics['value']:.0f}."
),
"demographic": {
"age_range": age_name.replace("AGE_RANGE_", "").replace("_", "-"),
"gender": gender_name.lower(),
},
}).json()
created.append({"name": persona.get("name"), "id": persona["id"]})
time.sleep(0.3)
print(f"Created {len(created)} demographic personas")
for p in created:
print(f" {p['name']} → {p['id']}")