google-ads-report

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Google Ads Report

Google Ads 报告

Pull campaign, keyword, and conversion data from the Google Ads API.
从Google Ads API拉取广告系列、关键词和转化数据。

Prerequisites

前置条件

Requires:
  • GOOGLE_CLIENT_ID
    and
    GOOGLE_CLIENT_SECRET
    (OAuth)
  • GOOGLE_ADS_DEVELOPER_TOKEN
    (apply at https://ads.google.com/home/tools/manager-accounts/)
  • GOOGLE_ADS_CUSTOMER_ID
    (the account ID, format:
    XXX-XXX-XXXX
    , passed without dashes)
  • GOOGLE_ADS_LOGIN_CUSTOMER_ID
    (if using a manager account, the manager account ID)
Set in
.env
,
.env.local
, or
~/.claude/.env.global
.
需要:
  • GOOGLE_CLIENT_ID
    GOOGLE_CLIENT_SECRET
    (OAuth 认证)
  • GOOGLE_ADS_DEVELOPER_TOKEN
    (申请地址:https://ads.google.com/home/tools/manager-accounts/)
  • GOOGLE_ADS_CUSTOMER_ID
    (账户ID,格式:
    XXX-XXX-XXXX
    ,传入时需去掉连字符)
  • GOOGLE_ADS_LOGIN_CUSTOMER_ID
    (若使用经理账户,填写经理账户ID)
请在
.env
.env.local
~/.claude/.env.global
文件中配置以上参数。

Getting an Access Token

获取访问令牌

bash
undefined
bash
undefined

Same OAuth flow as other Google APIs

Same OAuth flow as other Google APIs

Exchange code for tokens

Exchange code for tokens

curl -s -X POST "https://oauth2.googleapis.com/token"
-d "code={AUTH_CODE}"
-d "client_id=${GOOGLE_CLIENT_ID}"
-d "client_secret=${GOOGLE_CLIENT_SECRET}"
-d "redirect_uri=urn:ietf:wg:oauth:2.0:oob"
-d "grant_type=authorization_code"

---
curl -s -X POST "https://oauth2.googleapis.com/token"
-d "code={AUTH_CODE}"
-d "client_id=${GOOGLE_CLIENT_ID}"
-d "client_secret=${GOOGLE_CLIENT_SECRET}"
-d "redirect_uri=urn:ietf:wg:oauth:2.0:oob"
-d "grant_type=authorization_code"

---

API Base

API 基础信息

Google Ads API uses GAQL (Google Ads Query Language) via REST.
POST https://googleads.googleapis.com/v17/customers/{CUSTOMER_ID}/googleAds:searchStream
Headers:
Authorization: Bearer {ACCESS_TOKEN}
developer-token: {DEVELOPER_TOKEN}
login-customer-id: {LOGIN_CUSTOMER_ID}  # Only if using manager account
Content-Type: application/json

Google Ads API 通过REST接口使用GAQL(Google Ads查询语言)。
POST https://googleads.googleapis.com/v17/customers/{CUSTOMER_ID}/googleAds:searchStream
请求头:
Authorization: Bearer {ACCESS_TOKEN}
developer-token: {DEVELOPER_TOKEN}
login-customer-id: {LOGIN_CUSTOMER_ID}  # Only if using manager account
Content-Type: application/json

1. Campaign Performance Report

1. 广告系列绩效报告

Overview of all campaigns with key metrics.
bash
curl -s -X POST \
  "https://googleads.googleapis.com/v17/customers/${GOOGLE_ADS_CUSTOMER_ID}:searchStream" \
  -H "Authorization: Bearer ${GADS_ACCESS_TOKEN}" \
  -H "developer-token: ${GOOGLE_ADS_DEVELOPER_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "SELECT campaign.name, campaign.status, metrics.impressions, metrics.clicks, metrics.ctr, metrics.average_cpc, metrics.cost_micros, metrics.conversions, metrics.cost_per_conversion, metrics.conversions_value FROM campaign WHERE segments.date DURING LAST_30_DAYS AND campaign.status != REMOVED ORDER BY metrics.cost_micros DESC"
  }'
所有广告系列的关键指标概览。
bash
curl -s -X POST \
  "https://googleads.googleapis.com/v17/customers/${GOOGLE_ADS_CUSTOMER_ID}:searchStream" \
  -H "Authorization: Bearer ${GADS_ACCESS_TOKEN}" \
  -H "developer-token: ${GOOGLE_ADS_DEVELOPER_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "SELECT campaign.name, campaign.status, metrics.impressions, metrics.clicks, metrics.ctr, metrics.average_cpc, metrics.cost_micros, metrics.conversions, metrics.cost_per_conversion, metrics.conversions_value FROM campaign WHERE segments.date DURING LAST_30_DAYS AND campaign.status != REMOVED ORDER BY metrics.cost_micros DESC"
  }'

Parsing Campaign Data

解析广告系列数据

bash
curl -s -X POST "..." | python3 -c "
import json, sys
data = json.load(sys.stdin)
print(f\"{'Campaign':<35} {'Status':<10} {'Impr':>8} {'Clicks':>7} {'CTR':>7} {'Avg CPC':>8} {'Cost':>10} {'Conv':>6} {'CPA':>8}\")
print('-' * 110)
for batch in data:
    for row in batch.get('results', []):
        c = row.get('campaign', {})
        m = row.get('metrics', {})
        cost = int(m.get('costMicros', 0)) / 1_000_000
        cpc = int(m.get('averageCpc', 0)) / 1_000_000
        cpa = float(m.get('costPerConversion', 0)) / 1_000_000 if m.get('costPerConversion') else 0
        print(f\"{c.get('name',''):<35} {c.get('status',''):<10} {int(m.get('impressions',0)):>8} {int(m.get('clicks',0)):>7} {float(m.get('ctr',0))*100:>6.2f}% \${cpc:>7.2f} \${cost:>9.2f} {float(m.get('conversions',0)):>6.1f} \${cpa:>7.2f}\")
"
bash
curl -s -X POST "..." | python3 -c "
import json, sys
data = json.load(sys.stdin)
print(f\"{'Campaign':<35} {'Status':<10} {'Impr':>8} {'Clicks':>7} {'CTR':>7} {'Avg CPC':>8} {'Cost':>10} {'Conv':>6} {'CPA':>8}\")
print('-' * 110)
for batch in data:
    for row in batch.get('results', []):
        c = row.get('campaign', {})
        m = row.get('metrics', {})
        cost = int(m.get('costMicros', 0)) / 1_000_000
        cpc = int(m.get('averageCpc', 0)) / 1_000_000
        cpa = float(m.get('costPerConversion', 0)) / 1_000_000 if m.get('costPerConversion') else 0
        print(f\"{c.get('name',''):<35} {c.get('status',''):<10} {int(m.get('impressions',0)):>8} {int(m.get('clicks',0)):>7} {float(m.get('ctr',0))*100:>6.2f}% \${cpc:>7.2f} \${cost:>9.2f} {float(m.get('conversions',0)):>6.1f} \${cpa:>7.2f}\")
"

Important: Cost Micros

重要提示:成本微单位

All cost values in Google Ads API are in micros (1/1,000,000 of the currency unit). Divide by 1,000,000 to get the actual amount.

Google Ads API中的所有成本值均以微单位表示(即货币单位的1/1,000,000)。需除以1,000,000得到实际金额。

2. Keyword Performance Report

2. 关键词绩效报告

See how individual keywords perform.
bash
curl -s -X POST \
  "https://googleads.googleapis.com/v17/customers/${GOOGLE_ADS_CUSTOMER_ID}:searchStream" \
  -H "Authorization: Bearer ${GADS_ACCESS_TOKEN}" \
  -H "developer-token: ${GOOGLE_ADS_DEVELOPER_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "SELECT ad_group_criterion.keyword.text, ad_group_criterion.keyword.match_type, ad_group_criterion.quality_info.quality_score, metrics.impressions, metrics.clicks, metrics.ctr, metrics.average_cpc, metrics.cost_micros, metrics.conversions, metrics.conversions_value FROM keyword_view WHERE segments.date DURING LAST_30_DAYS AND ad_group_criterion.status != REMOVED ORDER BY metrics.cost_micros DESC LIMIT 50"
  }'
查看单个关键词的表现。
bash
curl -s -X POST \
  "https://googleads.googleapis.com/v17/customers/${GOOGLE_ADS_CUSTOMER_ID}:searchStream" \
  -H "Authorization: Bearer ${GADS_ACCESS_TOKEN}" \
  -H "developer-token: ${GOOGLE_ADS_DEVELOPER_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "SELECT ad_group_criterion.keyword.text, ad_group_criterion.keyword.match_type, ad_group_criterion.quality_info.quality_score, metrics.impressions, metrics.clicks, metrics.ctr, metrics.average_cpc, metrics.cost_micros, metrics.conversions, metrics.conversions_value FROM keyword_view WHERE segments.date DURING LAST_30_DAYS AND ad_group_criterion.status != REMOVED ORDER BY metrics.cost_micros DESC LIMIT 50"
  }'

Quality Score Breakdown

质量得分细分

bash
curl -s -X POST \
  "https://googleads.googleapis.com/v17/customers/${GOOGLE_ADS_CUSTOMER_ID}:searchStream" \
  -H "Authorization: Bearer ${GADS_ACCESS_TOKEN}" \
  -H "developer-token: ${GOOGLE_ADS_DEVELOPER_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "SELECT ad_group_criterion.keyword.text, ad_group_criterion.quality_info.quality_score, ad_group_criterion.quality_info.creative_quality_score, ad_group_criterion.quality_info.post_click_quality_score, ad_group_criterion.quality_info.search_predicted_ctr, metrics.impressions, metrics.average_cpc FROM keyword_view WHERE ad_group_criterion.quality_info.quality_score IS NOT NULL AND segments.date DURING LAST_30_DAYS ORDER BY ad_group_criterion.quality_info.quality_score ASC LIMIT 50"
  }'
Quality Score Components:
  • quality_score: Overall score (1-10)
  • creative_quality_score: Ad relevance (BELOW_AVERAGE, AVERAGE, ABOVE_AVERAGE)
  • post_click_quality_score: Landing page experience
  • search_predicted_ctr: Expected click-through rate

bash
curl -s -X POST \
  "https://googleads.googleapis.com/v17/customers/${GOOGLE_ADS_CUSTOMER_ID}:searchStream" \
  -H "Authorization: Bearer ${GADS_ACCESS_TOKEN}" \
  -H "developer-token: ${GOOGLE_ADS_DEVELOPER_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "SELECT ad_group_criterion.keyword.text, ad_group_criterion.quality_info.quality_score, ad_group_criterion.quality_info.creative_quality_score, ad_group_criterion.quality_info.post_click_quality_score, ad_group_criterion.quality_info.search_predicted_ctr, metrics.impressions, metrics.average_cpc FROM keyword_view WHERE ad_group_criterion.quality_info.quality_score IS NOT NULL AND segments.date DURING LAST_30_DAYS ORDER BY ad_group_criterion.quality_info.quality_score ASC LIMIT 50"
  }'
质量得分组成部分:
  • quality_score: 整体得分(1-10)
  • creative_quality_score: 广告相关性(BELOW_AVERAGE, AVERAGE, ABOVE_AVERAGE)
  • post_click_quality_score: 着陆页体验
  • search_predicted_ctr: 预估点击率

3. Ad Group Performance

3. 广告组绩效报告

bash
curl -s -X POST \
  "https://googleads.googleapis.com/v17/customers/${GOOGLE_ADS_CUSTOMER_ID}:searchStream" \
  -H "Authorization: Bearer ${GADS_ACCESS_TOKEN}" \
  -H "developer-token: ${GOOGLE_ADS_DEVELOPER_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "SELECT campaign.name, ad_group.name, ad_group.status, metrics.impressions, metrics.clicks, metrics.ctr, metrics.average_cpc, metrics.cost_micros, metrics.conversions FROM ad_group WHERE segments.date DURING LAST_30_DAYS AND ad_group.status != REMOVED ORDER BY metrics.cost_micros DESC LIMIT 50"
  }'

bash
curl -s -X POST \
  "https://googleads.googleapis.com/v17/customers/${GOOGLE_ADS_CUSTOMER_ID}:searchStream" \
  -H "Authorization: Bearer ${GADS_ACCESS_TOKEN}" \
  -H "developer-token: ${GOOGLE_ADS_DEVELOPER_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "SELECT campaign.name, ad_group.name, ad_group.status, metrics.impressions, metrics.clicks, metrics.ctr, metrics.average_cpc, metrics.cost_micros, metrics.conversions FROM ad_group WHERE segments.date DURING LAST_30_DAYS AND ad_group.status != REMOVED ORDER BY metrics.cost_micros DESC LIMIT 50"
  }'

4. Search Terms Report

4. 搜索词报告

See what users actually searched for (vs. your keywords).
bash
curl -s -X POST \
  "https://googleads.googleapis.com/v17/customers/${GOOGLE_ADS_CUSTOMER_ID}:searchStream" \
  -H "Authorization: Bearer ${GADS_ACCESS_TOKEN}" \
  -H "developer-token: ${GOOGLE_ADS_DEVELOPER_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "SELECT search_term_view.search_term, segments.keyword.info.text, segments.keyword.info.match_type, metrics.impressions, metrics.clicks, metrics.ctr, metrics.cost_micros, metrics.conversions FROM search_term_view WHERE segments.date DURING LAST_30_DAYS ORDER BY metrics.impressions DESC LIMIT 100"
  }'
Use this to:
  • Find new keyword opportunities (high-converting search terms)
  • Identify negative keyword candidates (irrelevant terms with spend)
  • Discover match type issues (broad match pulling in junk traffic)

查看用户实际搜索的内容(与您设置的关键词对比)。
bash
curl -s -X POST \
  "https://googleads.googleapis.com/v17/customers/${GOOGLE_ADS_CUSTOMER_ID}:searchStream" \
  -H "Authorization: Bearer ${GADS_ACCESS_TOKEN}" \
  -H "developer-token: ${GOOGLE_ADS_DEVELOPER_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "SELECT search_term_view.search_term, segments.keyword.info.text, segments.keyword.info.match_type, metrics.impressions, metrics.clicks, metrics.ctr, metrics.cost_micros, metrics.conversions FROM search_term_view WHERE segments.date DURING LAST_30_DAYS ORDER BY metrics.impressions DESC LIMIT 100"
  }'
该报告可用于:
  • 发现新的关键词机会(高转化搜索词)
  • 识别否定关键词候选(有支出但不相关的搜索词)
  • 发现匹配类型问题(广泛匹配带来无效流量)

5. Conversion Tracking

5. 转化追踪报告

bash
curl -s -X POST \
  "https://googleads.googleapis.com/v17/customers/${GOOGLE_ADS_CUSTOMER_ID}:searchStream" \
  -H "Authorization: Bearer ${GADS_ACCESS_TOKEN}" \
  -H "developer-token: ${GOOGLE_ADS_DEVELOPER_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "SELECT campaign.name, metrics.conversions, metrics.conversions_value, metrics.cost_micros, metrics.conversions_from_interactions_rate, metrics.value_per_conversion FROM campaign WHERE segments.date DURING LAST_30_DAYS AND campaign.status = ENABLED ORDER BY metrics.conversions DESC"
  }'
bash
curl -s -X POST \
  "https://googleads.googleapis.com/v17/customers/${GOOGLE_ADS_CUSTOMER_ID}:searchStream" \
  -H "Authorization: Bearer ${GADS_ACCESS_TOKEN}" \
  -H "developer-token: ${GOOGLE_ADS_DEVELOPER_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "SELECT campaign.name, metrics.conversions, metrics.conversions_value, metrics.cost_micros, metrics.conversions_from_interactions_rate, metrics.value_per_conversion FROM campaign WHERE segments.date DURING LAST_30_DAYS AND campaign.status = ENABLED ORDER BY metrics.conversions DESC"
  }'

ROAS Calculation

ROAS 计算

bash
undefined
bash
undefined

ROAS = conversions_value / (cost_micros / 1_000_000)

ROAS = conversions_value / (cost_micros / 1_000_000)

curl -s -X POST "..." | python3 -c " import json, sys data = json.load(sys.stdin) print(f"{'Campaign':<35} {'Cost':>10} {'Conv Value':>12} {'ROAS':>8}") for batch in data: for row in batch.get('results', []): c = row['campaign']['name'] m = row['metrics'] cost = int(m.get('costMicros', 0)) / 1_000_000 value = float(m.get('conversionsValue', 0)) roas = value / cost if cost > 0 else 0 print(f"{c:<35} ${cost:>9.2f} ${value:>11.2f} {roas:>7.2f}x") "

---
curl -s -X POST "..." | python3 -c " import json, sys data = json.load(sys.stdin) print(f"{'Campaign':<35} {'Cost':>10} {'Conv Value':>12} {'ROAS':>8}") for batch in data: for row in batch.get('results', []): c = row['campaign']['name'] m = row['metrics'] cost = int(m.get('costMicros', 0)) / 1_000_000 value = float(m.get('conversionsValue', 0)) roas = value / cost if cost > 0 else 0 print(f"{c:<35} ${cost:>9.2f} ${value:>11.2f} {roas:>7.2f}x") "

---

6. Daily Spend Trend

6. 每日支出趋势报告

bash
curl -s -X POST \
  "https://googleads.googleapis.com/v17/customers/${GOOGLE_ADS_CUSTOMER_ID}:searchStream" \
  -H "Authorization: Bearer ${GADS_ACCESS_TOKEN}" \
  -H "developer-token: ${GOOGLE_ADS_DEVELOPER_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "SELECT segments.date, metrics.impressions, metrics.clicks, metrics.cost_micros, metrics.conversions FROM customer WHERE segments.date DURING LAST_30_DAYS ORDER BY segments.date DESC"
  }'

bash
curl -s -X POST \
  "https://googleads.googleapis.com/v17/customers/${GOOGLE_ADS_CUSTOMER_ID}:searchStream" \
  -H "Authorization: Bearer ${GADS_ACCESS_TOKEN}" \
  -H "developer-token: ${GOOGLE_ADS_DEVELOPER_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "SELECT segments.date, metrics.impressions, metrics.clicks, metrics.cost_micros, metrics.conversions FROM customer WHERE segments.date DURING LAST_30_DAYS ORDER BY segments.date DESC"
  }'

GAQL Date Ranges

GAQL 日期范围

Use these built-in date ranges in GAQL:
  • TODAY
    ,
    YESTERDAY
  • LAST_7_DAYS
    ,
    LAST_14_DAYS
    ,
    LAST_30_DAYS
  • THIS_MONTH
    ,
    LAST_MONTH
  • THIS_QUARTER
    ,
    LAST_QUARTER
  • Custom:
    segments.date BETWEEN '2024-01-01' AND '2024-03-31'

可在GAQL中使用以下内置日期范围:
  • TODAY
    ,
    YESTERDAY
  • LAST_7_DAYS
    ,
    LAST_14_DAYS
    ,
    LAST_30_DAYS
  • THIS_MONTH
    ,
    LAST_MONTH
  • THIS_QUARTER
    ,
    LAST_QUARTER
  • 自定义范围:
    segments.date BETWEEN '2024-01-01' AND '2024-03-31'

Workflow: Monthly Google Ads Report

工作流:月度Google Ads报告

When asked for a full ads report:
  1. Account Overview: Total spend, impressions, clicks, conversions, ROAS
  2. Campaign Performance: All active campaigns ranked by spend
  3. Top Keywords: Top 20 keywords by spend with quality scores
  4. Search Terms: Top search terms and negative keyword candidates
  5. Quality Score Distribution: How many keywords at each QS level
  6. Conversion Analysis: Conversions and ROAS by campaign
  7. Daily Trend: Spend and conversion trend over the period
当需要生成完整广告报告时:
  1. 账户概览: 总支出、展示量、点击量、转化量、ROAS
  2. 广告系列绩效: 所有活跃广告系列按支出排序
  3. 核心关键词: 按支出排名前20的关键词及质量得分
  4. 搜索词分析: 核心搜索词及否定关键词候选
  5. 质量得分分布: 各质量得分等级的关键词数量
  6. 转化分析: 各广告系列的转化量及ROAS
  7. 每日趋势: 周期内的支出和转化趋势

Report Format

报告格式

undefined
undefined

Google Ads Report: {Account Name}

Google Ads Report: {Account Name}

Period: {date range}

Period: {date range}

Account Summary

Account Summary

MetricValuevs Previous
Total Spend$X+Y%
ImpressionsX+Y%
ClicksX+Y%
CTRX%+Y pp
Avg CPC$X+Y%
ConversionsX+Y%
ROASXx+Y%
MetricValuevs Previous
Total Spend$X+Y%
ImpressionsX+Y%
ClicksX+Y%
CTRX%+Y pp
Avg CPC$X+Y%
ConversionsX+Y%
ROASXx+Y%

Campaign Performance

Campaign Performance

CampaignSpendClicksConvCPAROAS
..................
CampaignSpendClicksConvCPAROAS
..................

Top Keywords (by spend)

Top Keywords (by spend)

KeywordMatchQSSpendClicksConvCPC
.....................
KeywordMatchQSSpendClicksConvCPC
.....................

Recommendations

Recommendations

  • Pause: Keywords with high spend and zero conversions
  • Increase Bids: Keywords with high conversion rate but limited budget
  • Negative Keywords: Search terms wasting budget
  • Quality Score Fixes: Keywords with QS < 5 and actions to improve
  • Budget Reallocation: Shift budget from low-ROAS to high-ROAS campaigns

---
  • Pause: Keywords with high spend and zero conversions
  • Increase Bids: Keywords with high conversion rate but limited budget
  • Negative Keywords: Search terms wasting budget
  • Quality Score Fixes: Keywords with QS < 5 and actions to improve
  • Budget Reallocation: Shift budget from low-ROAS to high-ROAS campaigns

---

Error Handling

错误处理

ErrorCause
AUTHENTICATION_ERROR
Invalid or expired access token
AUTHORIZATION_ERROR
Developer token issue or account access
REQUEST_ERROR
GAQL syntax error
QUOTA_ERROR
API quota exceeded
错误类型原因
AUTHENTICATION_ERROR
访问令牌无效或已过期
AUTHORIZATION_ERROR
开发者令牌问题或账户权限不足
REQUEST_ERROR
GAQL语法错误
QUOTA_ERROR
API配额已用尽

Common GAQL Mistakes

常见GAQL错误

  • Missing
    WHERE segments.date DURING ...
    (required for most metric queries)
  • Using
    REMOVED
    status filter incorrectly
  • Forgetting to handle
    costMicros
    division by 1,000,000
  • Requesting incompatible resource + segment combinations
  • 缺少
    WHERE segments.date DURING ...
    (大多数指标查询必填)
  • 错误使用
    REMOVED
    状态过滤器
  • 忘记将
    costMicros
    除以1,000,000
  • 请求了不兼容的资源和细分维度组合

Tips

小贴士

  • Always filter out REMOVED campaigns/ad groups/keywords
  • Use
    searchStream
    instead of
    search
    for large result sets (no pagination needed)
  • Cache results when building multi-section reports
  • Quality Score of 0 means "not enough data" -- treat as null
  • 始终过滤掉已移除(REMOVED)的广告系列/广告组/关键词
  • 对于大量结果集,使用
    searchStream
    而非
    search
    (无需分页)
  • 生成多部分报告时缓存结果
  • 质量得分为0表示“数据不足”,需视为空值处理