重要前提
安装AI Skills的关键前提是:必须科学上网,且开启TUN模式,这一点至关重要,直接决定安装能否顺利完成,在此郑重提醒三遍:科学上网,科学上网,科学上网。查看完整安装教程 →
google-ads-report by openclaudia/openclaudia-skills
npx skills add https://github.com/openclaudia/openclaudia-skills --skill google-ads-report从 Google Ads API 拉取广告系列、关键词和转化数据。
需要:
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 或 中设置。
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
~/.claude/.env.global# 与其他 Google API 相同的 OAuth 流程
# 所需作用域:https://www.googleapis.com/auth/adwords
echo "https://accounts.google.com/o/oauth2/v2/auth?client_id=${GOOGLE_CLIENT_ID}&redirect_uri=urn:ietf:wg:oauth:2.0:oob&scope=https://www.googleapis.com/auth/adwords&response_type=code&access_type=offline"
# 用授权码交换令牌
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"
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} # 仅在使用经理账户时需要
Content-Type: application/json
包含关键指标的所有广告系列概览。
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"
}'
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}\")
"
Google Ads API 中的所有成本值均以微单位表示(货币单位的 1/1,000,000)。除以 1,000,000 以获得实际金额。
查看单个关键词的表现。
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"
}'
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"
}'
质量得分组成部分:
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"
}'
查看用户实际搜索的内容(与您的关键词对比)。
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"
}'
使用此报告可以:
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 = 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 \
"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 中使用这些内置日期范围:
TODAY, YESTERDAYLAST_7_DAYS, LAST_14_DAYS, LAST_30_DAYSTHIS_MONTH, LAST_MONTHTHIS_QUARTER, LAST_QUARTERsegments.date BETWEEN '2024-01-01' AND '2024-03-31'当要求提供完整的广告报告时:
## Google Ads 报告:{账户名称}
### 期间:{日期范围}
### 账户摘要
| 指标 | 数值 | 与前期对比 |
|--------|-------|-------------|
| 总花费 | $X | +Y% |
| 展示次数 | X | +Y% |
| 点击次数 | X | +Y% |
| 点击率 | X% | +Y 个百分点 |
| 平均每次点击费用 | $X | +Y% |
| 转化次数 | X | +Y% |
| 广告支出回报率 | Xx | +Y% |
### 广告系列效果
| 广告系列 | 花费 | 点击次数 | 转化次数 | 每次转化费用 | 广告支出回报率 |
|----------|-------|--------|------|-----|------|
| ... | ... | ... | ... | ... | ... |
### 热门关键词(按花费)
| 关键词 | 匹配类型 | 质量得分 | 花费 | 点击次数 | 转化次数 | 每次点击费用 |
|---------|-------|-----|-------|--------|------|-----|
| ... | ... | ... | ... | ... | ... | ... |
### 建议
- **暂停**:花费高但零转化的关键词
- **提高出价**:转化率高但预算有限的关键词
- **否定关键词**:浪费预算的搜索词
- **质量得分修复**:质量得分 < 5 的关键词及改进措施
- **预算重新分配**:将预算从低 ROAS 广告系列转移到高 ROAS 广告系列
| 错误 | 原因 |
|---|---|
AUTHENTICATION_ERROR | 访问令牌无效或已过期 |
AUTHORIZATION_ERROR | 开发者令牌问题或账户访问权限 |
REQUEST_ERROR | GAQL 语法错误 |
QUOTA_ERROR | API 配额已用尽 |
WHERE segments.date DURING ...(大多数指标查询必需)REMOVED 状态过滤器costMicros 除以 1,000,000searchStream 而非 search(无需分页)每周安装量
70
仓库
GitHub 星标数
341
首次出现
2026年2月14日
安全审计
安装于
opencode64
gemini-cli63
claude-code62
codex60
github-copilot59
cursor58
Pull campaign, keyword, and conversion data from the Google Ads API.
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.
# Same OAuth flow as other Google APIs
# Scope needed: https://www.googleapis.com/auth/adwords
echo "https://accounts.google.com/o/oauth2/v2/auth?client_id=${GOOGLE_CLIENT_ID}&redirect_uri=urn:ietf:wg:oauth:2.0:oob&scope=https://www.googleapis.com/auth/adwords&response_type=code&access_type=offline"
# 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"
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
Overview of all campaigns with key metrics.
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"
}'
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}\")
"
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.
See how individual keywords perform.
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"
}'
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:
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"
}'
See what users actually searched for (vs. your keywords).
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:
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 = 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 \
"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"
}'
Use these built-in date ranges in GAQL:
TODAY, YESTERDAYLAST_7_DAYS, LAST_14_DAYS, LAST_30_DAYSTHIS_MONTH, LAST_MONTHTHIS_QUARTER, LAST_QUARTERsegments.date BETWEEN '2024-01-01' AND '2024-03-31'When asked for a full ads report:
## Google Ads Report: {Account Name}
### Period: {date range}
### Account Summary
| Metric | Value | vs Previous |
|--------|-------|-------------|
| Total Spend | $X | +Y% |
| Impressions | X | +Y% |
| Clicks | X | +Y% |
| CTR | X% | +Y pp |
| Avg CPC | $X | +Y% |
| Conversions | X | +Y% |
| ROAS | Xx | +Y% |
### Campaign Performance
| Campaign | Spend | Clicks | Conv | CPA | ROAS |
|----------|-------|--------|------|-----|------|
| ... | ... | ... | ... | ... | ... |
### Top Keywords (by spend)
| Keyword | Match | QS | Spend | Clicks | Conv | CPC |
|---------|-------|-----|-------|--------|------|-----|
| ... | ... | ... | ... | ... | ... | ... |
### 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
| Error | Cause |
|---|---|
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 |
WHERE segments.date DURING ... (required for most metric queries)REMOVED status filter incorrectlycostMicros division by 1,000,000searchStream instead of search for large result sets (no pagination needed)Weekly Installs
70
Repository
GitHub Stars
341
First Seen
Feb 14, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode64
gemini-cli63
claude-code62
codex60
github-copilot59
cursor58
专业文案撰写指南:转化文案写作技巧、框架与SEO优化原则
61,000 周安装
AI产品需求文档(PRD)撰写指南:功能规格模板、用户故事与成功指标
463 周安装
React前端开发模式实战:组件组合、复合组件与渲染属性模式详解
464 周安装
Playwriter:AI代理浏览器自动化工具,连接真实Chrome会话,保留登录状态
464 周安装
Story Coach AI写作教练:提问诊断引导创作,不代写故事,激发作者潜能
477 周安装
OpenAI API 完整指南:GPT-5、GPT-4o、DALL-E 3、Whisper 集成与Node.js/JavaScript开发
465 周安装
Google Gemini API 完整指南:最新SDK迁移、模型对比与实战教程(2025版)
467 周安装