[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6DVRDMYMBCN7F5PCQ0QDB66":3,"skill-same:01M6DVRDMYMBCN7F5PCQ0QDB66":82},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":28,"publishedAt":49,"linkOnly":50,"markdownContent":51,"markdownBody":52,"frontMatter":53,"refName":54,"commitSha":55,"skillDirectory":56,"forksCount":57,"repositoryHtmlUrl":58,"repositoryHomepageUrl":6,"ownerHtmlUrl":59,"ownerBlogUrl":60,"ownerDescription":6,"updatedAt":61,"install":62,"resources":75},"01M6DVRDMYMBCN7F5PCQ0QDB66","google-analytics",null,"ACTIVATE when the user wants to PULL\u002FREPORT GA4 data via the Google Analytics Data API — traffic reports, user behavior, acquisition channels, conversions, audience segments. Trigger phrases: 'google analytics', 'GA4', 'traffic report', 'analytics data', 'user acquisition', 'engagement metrics', 'audience segments', 'page views', 'sessions'. For SETTING UP tracking (events, GTM, UTMs), see analytics-tracking. For search-side data (queries, rankings, CTR, impressions), see search-console.","FabienSalles\u002Fclaude-marketplace","FabienSalles","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F937901?v=4","01M6DVJBD31CJKRB382FQFZ5FH","Fabien Salles","01M6DVJBQNY8Q18KJD1ESAD6TX",9,"MIT","https:\u002F\u002Fgithub.com\u002FFabienSalles\u002Fclaude-marketplace\u002Fblob\u002Fmain\u002Fplugins\u002Fmarketing-analytics\u002Fskills\u002Fgoogle-analytics\u002FSKILL.md",[18,23],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000013","分析与可视化","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":26,"taxonomyTypeDesc":27},"01JBSEEDCAT000000000000033","数据分析师","PROFESSION","适用职业",[29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48],"analytics","google analytics","google","up","via","trigger","ctr","gtm","ga4","activate","search-console","queries","reports","engagement","acquisition","page","api","search","pull","sessions","2026-11-10 10:12:30",false,"---\nname: google-analytics\ndescription: \"ACTIVATE when the user wants to PULL\u002FREPORT GA4 data via the Google Analytics Data API — traffic reports, user behavior, acquisition channels, conversions, audience segments. Trigger phrases: 'google analytics', 'GA4', 'traffic report', 'analytics data', 'user acquisition', 'engagement metrics', 'audience segments', 'page views', 'sessions'. For SETTING UP tracking (events, GTM, UTMs), see analytics-tracking. For search-side data (queries, rankings, CTR, impressions), see search-console.\"\n---\n\n# Google Analytics (GA4)\n\nPull reports and insights from GA4 using the Google Analytics Data API.\n\n## Prerequisites\n\nRequires Google OAuth credentials:\n- `GOOGLE_CLIENT_ID`\n- `GOOGLE_CLIENT_SECRET`\n- A valid OAuth access token (refreshed as needed)\n\nSet credentials in `.env`, `.env.local`, or `~\u002F.claude\u002F.env.global`.\n\n### Getting an Access Token\n\n```bash\n# Step 1: Visit this URL in a browser; Google redirects to the loopback address with the code\necho \"https:\u002F\u002Faccounts.google.com\u002Fo\u002Foauth2\u002Fv2\u002Fauth?client_id=${GOOGLE_CLIENT_ID}&redirect_uri=http:\u002F\u002Flocalhost:8085\u002F&scope=https:\u002F\u002Fwww.googleapis.com\u002Fauth\u002Fanalytics.readonly&response_type=code&access_type=offline\"\n\n# Step 2: Copy the code= value from the browser's address bar after the redirect, then exchange it for tokens\ncurl -s -X POST \"https:\u002F\u002Foauth2.googleapis.com\u002Ftoken\" \\\n  -d \"code={AUTH_CODE}\" \\\n  -d \"client_id=${GOOGLE_CLIENT_ID}\" \\\n  -d \"client_secret=${GOOGLE_CLIENT_SECRET}\" \\\n  -d \"redirect_uri=http:\u002F\u002Flocalhost:8085\u002F\" \\\n  -d \"grant_type=authorization_code\"\n\n# Step 3: Refresh an expired token\ncurl -s -X POST \"https:\u002F\u002Foauth2.googleapis.com\u002Ftoken\" \\\n  -d \"refresh_token={REFRESH_TOKEN}\" \\\n  -d \"client_id=${GOOGLE_CLIENT_ID}\" \\\n  -d \"client_secret=${GOOGLE_CLIENT_SECRET}\" \\\n  -d \"grant_type=refresh_token\"\n```\n\nStore the refresh token securely. The access token expires after 1 hour.\n\n### Finding Your GA4 Property ID\n\n```bash\ncurl -s -H \"Authorization: Bearer ${GA_ACCESS_TOKEN}\" \\\n  \"https:\u002F\u002Fanalyticsadmin.googleapis.com\u002Fv1beta\u002FaccountSummaries\" \\\n  | python3 -c \"\nimport json, sys\ndata = json.load(sys.stdin)\nfor acct in data.get('accountSummaries', []):\n    for prop in acct.get('propertySummaries', []):\n        print(f\\\"{prop['property']}  |  {prop.get('displayName','')}  |  Account: {acct.get('displayName','')}\\\")\n\"\n```\n\nThe property ID format is `properties\u002FXXXXXXXXX`.\n\n---\n\n## API Base\n\n```\nPOST https:\u002F\u002Fanalyticsdata.googleapis.com\u002Fv1beta\u002F{property_id}:runReport\n```\n\nAll report requests use POST with a JSON body. Always include `Authorization: Bearer {ACCESS_TOKEN}`.\n\n---\n\n## 1. Traffic Overview Report\n\nGet sessions, users, page views, and engagement rate over a date range.\n\n### Example curl\n\n```bash\ncurl -s -X POST \\\n  \"https:\u002F\u002Fanalyticsdata.googleapis.com\u002Fv1beta\u002Fproperties\u002F{PROPERTY_ID}:runReport\" \\\n  -H \"Authorization: Bearer ${GA_ACCESS_TOKEN}\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"dateRanges\": [{\"startDate\": \"30daysAgo\", \"endDate\": \"today\"}],\n    \"metrics\": [\n      {\"name\": \"sessions\"},\n      {\"name\": \"totalUsers\"},\n      {\"name\": \"newUsers\"},\n      {\"name\": \"screenPageViews\"},\n      {\"name\": \"engagementRate\"},\n      {\"name\": \"averageSessionDuration\"},\n      {\"name\": \"bounceRate\"}\n    ]\n  }'\n```\n\n### Date Range Shortcuts\n\n- `today`, `yesterday`\n- `7daysAgo`, `14daysAgo`, `28daysAgo`, `30daysAgo`, `90daysAgo`\n- Specific dates: `2024-01-01`\n- Compare periods by passing two dateRanges\n\n---\n\n## 2. User Acquisition Report\n\nSee where users come from (channels, sources, campaigns).\n\n```bash\ncurl -s -X POST \\\n  \"https:\u002F\u002Fanalyticsdata.googleapis.com\u002Fv1beta\u002Fproperties\u002F{PROPERTY_ID}:runReport\" \\\n  -H \"Authorization: Bearer ${GA_ACCESS_TOKEN}\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"dateRanges\": [{\"startDate\": \"30daysAgo\", \"endDate\": \"today\"}],\n    \"dimensions\": [\n      {\"name\": \"sessionDefaultChannelGroup\"}\n    ],\n    \"metrics\": [\n      {\"name\": \"sessions\"},\n      {\"name\": \"totalUsers\"},\n      {\"name\": \"engagementRate\"},\n      {\"name\": \"conversions\"}\n    ],\n    \"orderBys\": [{\"metric\": {\"metricName\": \"sessions\"}, \"desc\": true}],\n    \"limit\": 20\n  }'\n```\n\n### Useful Acquisition Dimensions\n\n| Dimension | Description |\n|-----------|-------------|\n| `sessionDefaultChannelGroup` | Channel grouping (Organic, Paid, Social, etc.) |\n| `sessionSource` | Traffic source (google, facebook, etc.) |\n| `sessionMedium` | Medium (organic, cpc, referral, etc.) |\n| `sessionCampaignName` | UTM campaign name |\n| `firstUserSource` | First-touch attribution source |\n\n---\n\n## 3. Top Pages Report\n\nFind the highest-traffic pages on the site.\n\n```bash\ncurl -s -X POST \\\n  \"https:\u002F\u002Fanalyticsdata.googleapis.com\u002Fv1beta\u002Fproperties\u002F{PROPERTY_ID}:runReport\" \\\n  -H \"Authorization: Bearer ${GA_ACCESS_TOKEN}\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"dateRanges\": [{\"startDate\": \"30daysAgo\", \"endDate\": \"today\"}],\n    \"dimensions\": [\n      {\"name\": \"pagePath\"}\n    ],\n    \"metrics\": [\n      {\"name\": \"screenPageViews\"},\n      {\"name\": \"totalUsers\"},\n      {\"name\": \"engagementRate\"},\n      {\"name\": \"averageSessionDuration\"}\n    ],\n    \"orderBys\": [{\"metric\": {\"metricName\": \"screenPageViews\"}, \"desc\": true}],\n    \"limit\": 25\n  }'\n```\n\n---\n\n## 4. Engagement Metrics\n\nUnderstand how users interact with your content.\n\n```bash\ncurl -s -X POST \\\n  \"https:\u002F\u002Fanalyticsdata.googleapis.com\u002Fv1beta\u002Fproperties\u002F{PROPERTY_ID}:runReport\" \\\n  -H \"Authorization: Bearer ${GA_ACCESS_TOKEN}\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"dateRanges\": [{\"startDate\": \"30daysAgo\", \"endDate\": \"today\"}],\n    \"dimensions\": [\n      {\"name\": \"pagePath\"}\n    ],\n    \"metrics\": [\n      {\"name\": \"engagedSessions\"},\n      {\"name\": \"engagementRate\"},\n      {\"name\": \"averageSessionDuration\"},\n      {\"name\": \"screenPageViewsPerSession\"},\n      {\"name\": \"eventCount\"}\n    ],\n    \"orderBys\": [{\"metric\": {\"metricName\": \"engagedSessions\"}, \"desc\": true}],\n    \"limit\": 20\n  }'\n```\n\n### Key Engagement Metrics\n\n| Metric | What It Measures |\n|--------|-----------------|\n| `engagementRate` | % of sessions that were engaged (>10s, 2+ pages, or conversion) |\n| `averageSessionDuration` | Mean session length in seconds |\n| `screenPageViewsPerSession` | Pages per session |\n| `bounceRate` | % of sessions with no engagement |\n| `eventCount` | Total events fired |\n\n---\n\n## 5. Conversion Tracking\n\nReport on conversion events (purchases, signups, etc.).\n\n```bash\ncurl -s -X POST \\\n  \"https:\u002F\u002Fanalyticsdata.googleapis.com\u002Fv1beta\u002Fproperties\u002F{PROPERTY_ID}:runReport\" \\\n  -H \"Authorization: Bearer ${GA_ACCESS_TOKEN}\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"dateRanges\": [{\"startDate\": \"30daysAgo\", \"endDate\": \"today\"}],\n    \"dimensions\": [\n      {\"name\": \"eventName\"}\n    ],\n    \"metrics\": [\n      {\"name\": \"eventCount\"},\n      {\"name\": \"totalUsers\"},\n      {\"name\": \"eventValue\"}\n    ],\n    \"dimensionFilter\": {\n      \"filter\": {\n        \"fieldName\": \"eventName\",\n        \"inListFilter\": {\n          \"values\": [\"purchase\", \"sign_up\", \"generate_lead\", \"begin_checkout\"]\n        }\n      }\n    }\n  }'\n```\n\n### Conversion by Channel\n\n```bash\ncurl -s -X POST \\\n  \"https:\u002F\u002Fanalyticsdata.googleapis.com\u002Fv1beta\u002Fproperties\u002F{PROPERTY_ID}:runReport\" \\\n  -H \"Authorization: Bearer ${GA_ACCESS_TOKEN}\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"dateRanges\": [{\"startDate\": \"30daysAgo\", \"endDate\": \"today\"}],\n    \"dimensions\": [\n      {\"name\": \"sessionDefaultChannelGroup\"}\n    ],\n    \"metrics\": [\n      {\"name\": \"sessions\"},\n      {\"name\": \"conversions\"},\n      {\"name\": \"totalRevenue\"}\n    ],\n    \"orderBys\": [{\"metric\": {\"metricName\": \"conversions\"}, \"desc\": true}]\n  }'\n```\n\n---\n\n## 6. Audience Segments\n\nBreak down traffic by device, geography, and demographics.\n\n### By Device Category\n\n```bash\ncurl -s -X POST \\\n  \"https:\u002F\u002Fanalyticsdata.googleapis.com\u002Fv1beta\u002Fproperties\u002F{PROPERTY_ID}:runReport\" \\\n  -H \"Authorization: Bearer ${GA_ACCESS_TOKEN}\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"dateRanges\": [{\"startDate\": \"30daysAgo\", \"endDate\": \"today\"}],\n    \"dimensions\": [{\"name\": \"deviceCategory\"}],\n    \"metrics\": [\n      {\"name\": \"sessions\"},\n      {\"name\": \"totalUsers\"},\n      {\"name\": \"engagementRate\"},\n      {\"name\": \"conversions\"}\n    ]\n  }'\n```\n\n### By Country\n\nReplace `deviceCategory` with `country` in the dimensions.\n\n### By Landing Page + Source\n\n```bash\ncurl -s -X POST \\\n  \"https:\u002F\u002Fanalyticsdata.googleapis.com\u002Fv1beta\u002Fproperties\u002F{PROPERTY_ID}:runReport\" \\\n  -H \"Authorization: Bearer ${GA_ACCESS_TOKEN}\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"dateRanges\": [{\"startDate\": \"30daysAgo\", \"endDate\": \"today\"}],\n    \"dimensions\": [\n      {\"name\": \"landingPage\"},\n      {\"name\": \"sessionSource\"}\n    ],\n    \"metrics\": [\n      {\"name\": \"sessions\"},\n      {\"name\": \"engagementRate\"},\n      {\"name\": \"conversions\"}\n    ],\n    \"orderBys\": [{\"metric\": {\"metricName\": \"sessions\"}, \"desc\": true}],\n    \"limit\": 30\n  }'\n```\n\n---\n\n## 7. Period Comparison\n\nCompare two time periods to identify trends.\n\n```bash\ncurl -s -X POST \\\n  \"https:\u002F\u002Fanalyticsdata.googleapis.com\u002Fv1beta\u002Fproperties\u002F{PROPERTY_ID}:runReport\" \\\n  -H \"Authorization: Bearer ${GA_ACCESS_TOKEN}\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"dateRanges\": [\n      {\"startDate\": \"30daysAgo\", \"endDate\": \"today\", \"name\": \"current\"},\n      {\"startDate\": \"60daysAgo\", \"endDate\": \"31daysAgo\", \"name\": \"previous\"}\n    ],\n    \"metrics\": [\n      {\"name\": \"sessions\"},\n      {\"name\": \"totalUsers\"},\n      {\"name\": \"conversions\"},\n      {\"name\": \"engagementRate\"}\n    ]\n  }'\n```\n\n---\n\n## Response Parsing\n\nGA4 API returns JSON. Parse with python3 or jq:\n\n```bash\n# Parse report into a table\ncurl -s -X POST \"...\" | python3 -c \"\nimport json, sys\ndata = json.load(sys.stdin)\nheaders = [h['name'] for h in data.get('dimensionHeaders',[])] + [m['name'] for m in data.get('metricHeaders',[])]\nprint(' | '.join(headers))\nprint('-' * (len(headers) * 20))\nfor row in data.get('rows', []):\n    dims = [d['value'] for d in row.get('dimensionValues',[])]\n    mets = [m['value'] for m in row.get('metricValues',[])]\n    print(' | '.join(dims + mets))\n\"\n```\n\n---\n\n## Workflow: Monthly Analytics Report\n\nWhen asked for a monthly report:\n\n1. Pull traffic overview (sessions, users, page views) with period comparison\n2. Pull acquisition breakdown by channel\n3. Pull top 20 pages by page views\n4. Pull conversion summary by channel\n5. Pull device and country breakdown\n\nPresent as a structured report with tables, trends (up\u002Fdown arrows), and recommendations:\n\n```\n## Monthly Analytics Report: {Property Name}\n### Period: {date range} vs {previous period}\n\n### Traffic Summary\n| Metric | Current | Previous | Change |\n|--------|---------|----------|--------|\n| Sessions | X | Y | +Z% |\n| ...\n\n### Top Channels\n...\n\n### Top Pages\n...\n\n### Conversion Summary\n...\n\n### Recommendations\n- [Based on data patterns]\n```\n\n## Common Issues\n\n- **403 Forbidden**: User lacks access to the GA4 property\n- **Empty rows**: No data for the requested date range or filters\n- **Quota exceeded**: GA4 API has daily quotas; reduce date ranges or batch requests\n- **Property not found**: Verify the property ID format (`properties\u002FXXXXXXXXX`)\n","# Google Analytics (GA4)\n\nPull reports and insights from GA4 using the Google Analytics Data API.\n\n## Prerequisites\n\nRequires Google OAuth credentials:\n- `GOOGLE_CLIENT_ID`\n- `GOOGLE_CLIENT_SECRET`\n- A valid OAuth access token (refreshed as needed)\n\nSet credentials in `.env`, `.env.local`, or `~\u002F.claude\u002F.env.global`.\n\n### Getting an Access Token\n\n```bash\n# Step 1: Visit this URL in a browser; Google redirects to the loopback address with the code\necho \"https:\u002F\u002Faccounts.google.com\u002Fo\u002Foauth2\u002Fv2\u002Fauth?client_id=${GOOGLE_CLIENT_ID}&redirect_uri=http:\u002F\u002Flocalhost:8085\u002F&scope=https:\u002F\u002Fwww.googleapis.com\u002Fauth\u002Fanalytics.readonly&response_type=code&access_type=offline\"\n\n# Step 2: Copy the code= value from the browser's address bar after the redirect, then exchange it for tokens\ncurl -s -X POST \"https:\u002F\u002Foauth2.googleapis.com\u002Ftoken\" \\\n  -d \"code={AUTH_CODE}\" \\\n  -d \"client_id=${GOOGLE_CLIENT_ID}\" \\\n  -d \"client_secret=${GOOGLE_CLIENT_SECRET}\" \\\n  -d \"redirect_uri=http:\u002F\u002Flocalhost:8085\u002F\" \\\n  -d \"grant_type=authorization_code\"\n\n# Step 3: Refresh an expired token\ncurl -s -X POST \"https:\u002F\u002Foauth2.googleapis.com\u002Ftoken\" \\\n  -d \"refresh_token={REFRESH_TOKEN}\" \\\n  -d \"client_id=${GOOGLE_CLIENT_ID}\" \\\n  -d \"client_secret=${GOOGLE_CLIENT_SECRET}\" \\\n  -d \"grant_type=refresh_token\"\n```\n\nStore the refresh token securely. The access token expires after 1 hour.\n\n### Finding Your GA4 Property ID\n\n```bash\ncurl -s -H \"Authorization: Bearer ${GA_ACCESS_TOKEN}\" \\\n  \"https:\u002F\u002Fanalyticsadmin.googleapis.com\u002Fv1beta\u002FaccountSummaries\" \\\n  | python3 -c \"\nimport json, sys\ndata = json.load(sys.stdin)\nfor acct in data.get('accountSummaries', []):\n    for prop in acct.get('propertySummaries', []):\n        print(f\\\"{prop['property']}  |  {prop.get('displayName','')}  |  Account: {acct.get('displayName','')}\\\")\n\"\n```\n\nThe property ID format is `properties\u002FXXXXXXXXX`.\n\n---\n\n## API Base\n\n```\nPOST https:\u002F\u002Fanalyticsdata.googleapis.com\u002Fv1beta\u002F{property_id}:runReport\n```\n\nAll report requests use POST with a JSON body. Always include `Authorization: Bearer {ACCESS_TOKEN}`.\n\n---\n\n## 1. Traffic Overview Report\n\nGet sessions, users, page views, and engagement rate over a date range.\n\n### Example curl\n\n```bash\ncurl -s -X POST \\\n  \"https:\u002F\u002Fanalyticsdata.googleapis.com\u002Fv1beta\u002Fproperties\u002F{PROPERTY_ID}:runReport\" \\\n  -H \"Authorization: Bearer ${GA_ACCESS_TOKEN}\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"dateRanges\": [{\"startDate\": \"30daysAgo\", \"endDate\": \"today\"}],\n    \"metrics\": [\n      {\"name\": \"sessions\"},\n      {\"name\": \"totalUsers\"},\n      {\"name\": \"newUsers\"},\n      {\"name\": \"screenPageViews\"},\n      {\"name\": \"engagementRate\"},\n      {\"name\": \"averageSessionDuration\"},\n      {\"name\": \"bounceRate\"}\n    ]\n  }'\n```\n\n### Date Range Shortcuts\n\n- `today`, `yesterday`\n- `7daysAgo`, `14daysAgo`, `28daysAgo`, `30daysAgo`, `90daysAgo`\n- Specific dates: `2024-01-01`\n- Compare periods by passing two dateRanges\n\n---\n\n## 2. User Acquisition Report\n\nSee where users come from (channels, sources, campaigns).\n\n```bash\ncurl -s -X POST \\\n  \"https:\u002F\u002Fanalyticsdata.googleapis.com\u002Fv1beta\u002Fproperties\u002F{PROPERTY_ID}:runReport\" \\\n  -H \"Authorization: Bearer ${GA_ACCESS_TOKEN}\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"dateRanges\": [{\"startDate\": \"30daysAgo\", \"endDate\": \"today\"}],\n    \"dimensions\": [\n      {\"name\": \"sessionDefaultChannelGroup\"}\n    ],\n    \"metrics\": [\n      {\"name\": \"sessions\"},\n      {\"name\": \"totalUsers\"},\n      {\"name\": \"engagementRate\"},\n      {\"name\": \"conversions\"}\n    ],\n    \"orderBys\": [{\"metric\": {\"metricName\": \"sessions\"}, \"desc\": true}],\n    \"limit\": 20\n  }'\n```\n\n### Useful Acquisition Dimensions\n\n| Dimension | Description |\n|-----------|-------------|\n| `sessionDefaultChannelGroup` | Channel grouping (Organic, Paid, Social, etc.) |\n| `sessionSource` | Traffic source (google, facebook, etc.) |\n| `sessionMedium` | Medium (organic, cpc, referral, etc.) |\n| `sessionCampaignName` | UTM campaign name |\n| `firstUserSource` | First-touch attribution source |\n\n---\n\n## 3. Top Pages Report\n\nFind the highest-traffic pages on the site.\n\n```bash\ncurl -s -X POST \\\n  \"https:\u002F\u002Fanalyticsdata.googleapis.com\u002Fv1beta\u002Fproperties\u002F{PROPERTY_ID}:runReport\" \\\n  -H \"Authorization: Bearer ${GA_ACCESS_TOKEN}\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"dateRanges\": [{\"startDate\": \"30daysAgo\", \"endDate\": \"today\"}],\n    \"dimensions\": [\n      {\"name\": \"pagePath\"}\n    ],\n    \"metrics\": [\n      {\"name\": \"screenPageViews\"},\n      {\"name\": \"totalUsers\"},\n      {\"name\": \"engagementRate\"},\n      {\"name\": \"averageSessionDuration\"}\n    ],\n    \"orderBys\": [{\"metric\": {\"metricName\": \"screenPageViews\"}, \"desc\": true}],\n    \"limit\": 25\n  }'\n```\n\n---\n\n## 4. Engagement Metrics\n\nUnderstand how users interact with your content.\n\n```bash\ncurl -s -X POST \\\n  \"https:\u002F\u002Fanalyticsdata.googleapis.com\u002Fv1beta\u002Fproperties\u002F{PROPERTY_ID}:runReport\" \\\n  -H \"Authorization: Bearer ${GA_ACCESS_TOKEN}\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"dateRanges\": [{\"startDate\": \"30daysAgo\", \"endDate\": \"today\"}],\n    \"dimensions\": [\n      {\"name\": \"pagePath\"}\n    ],\n    \"metrics\": [\n      {\"name\": \"engagedSessions\"},\n      {\"name\": \"engagementRate\"},\n      {\"name\": \"averageSessionDuration\"},\n      {\"name\": \"screenPageViewsPerSession\"},\n      {\"name\": \"eventCount\"}\n    ],\n    \"orderBys\": [{\"metric\": {\"metricName\": \"engagedSessions\"}, \"desc\": true}],\n    \"limit\": 20\n  }'\n```\n\n### Key Engagement Metrics\n\n| Metric | What It Measures |\n|--------|-----------------|\n| `engagementRate` | % of sessions that were engaged (>10s, 2+ pages, or conversion) |\n| `averageSessionDuration` | Mean session length in seconds |\n| `screenPageViewsPerSession` | Pages per session |\n| `bounceRate` | % of sessions with no engagement |\n| `eventCount` | Total events fired |\n\n---\n\n## 5. Conversion Tracking\n\nReport on conversion events (purchases, signups, etc.).\n\n```bash\ncurl -s -X POST \\\n  \"https:\u002F\u002Fanalyticsdata.googleapis.com\u002Fv1beta\u002Fproperties\u002F{PROPERTY_ID}:runReport\" \\\n  -H \"Authorization: Bearer ${GA_ACCESS_TOKEN}\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"dateRanges\": [{\"startDate\": \"30daysAgo\", \"endDate\": \"today\"}],\n    \"dimensions\": [\n      {\"name\": \"eventName\"}\n    ],\n    \"metrics\": [\n      {\"name\": \"eventCount\"},\n      {\"name\": \"totalUsers\"},\n      {\"name\": \"eventValue\"}\n    ],\n    \"dimensionFilter\": {\n      \"filter\": {\n        \"fieldName\": \"eventName\",\n        \"inListFilter\": {\n          \"values\": [\"purchase\", \"sign_up\", \"generate_lead\", \"begin_checkout\"]\n        }\n      }\n    }\n  }'\n```\n\n### Conversion by Channel\n\n```bash\ncurl -s -X POST \\\n  \"https:\u002F\u002Fanalyticsdata.googleapis.com\u002Fv1beta\u002Fproperties\u002F{PROPERTY_ID}:runReport\" \\\n  -H \"Authorization: Bearer ${GA_ACCESS_TOKEN}\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"dateRanges\": [{\"startDate\": \"30daysAgo\", \"endDate\": \"today\"}],\n    \"dimensions\": [\n      {\"name\": \"sessionDefaultChannelGroup\"}\n    ],\n    \"metrics\": [\n      {\"name\": \"sessions\"},\n      {\"name\": \"conversions\"},\n      {\"name\": \"totalRevenue\"}\n    ],\n    \"orderBys\": [{\"metric\": {\"metricName\": \"conversions\"}, \"desc\": true}]\n  }'\n```\n\n---\n\n## 6. Audience Segments\n\nBreak down traffic by device, geography, and demographics.\n\n### By Device Category\n\n```bash\ncurl -s -X POST \\\n  \"https:\u002F\u002Fanalyticsdata.googleapis.com\u002Fv1beta\u002Fproperties\u002F{PROPERTY_ID}:runReport\" \\\n  -H \"Authorization: Bearer ${GA_ACCESS_TOKEN}\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"dateRanges\": [{\"startDate\": \"30daysAgo\", \"endDate\": \"today\"}],\n    \"dimensions\": [{\"name\": \"deviceCategory\"}],\n    \"metrics\": [\n      {\"name\": \"sessions\"},\n      {\"name\": \"totalUsers\"},\n      {\"name\": \"engagementRate\"},\n      {\"name\": \"conversions\"}\n    ]\n  }'\n```\n\n### By Country\n\nReplace `deviceCategory` with `country` in the dimensions.\n\n### By Landing Page + Source\n\n```bash\ncurl -s -X POST \\\n  \"https:\u002F\u002Fanalyticsdata.googleapis.com\u002Fv1beta\u002Fproperties\u002F{PROPERTY_ID}:runReport\" \\\n  -H \"Authorization: Bearer ${GA_ACCESS_TOKEN}\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"dateRanges\": [{\"startDate\": \"30daysAgo\", \"endDate\": \"today\"}],\n    \"dimensions\": [\n      {\"name\": \"landingPage\"},\n      {\"name\": \"sessionSource\"}\n    ],\n    \"metrics\": [\n      {\"name\": \"sessions\"},\n      {\"name\": \"engagementRate\"},\n      {\"name\": \"conversions\"}\n    ],\n    \"orderBys\": [{\"metric\": {\"metricName\": \"sessions\"}, \"desc\": true}],\n    \"limit\": 30\n  }'\n```\n\n---\n\n## 7. Period Comparison\n\nCompare two time periods to identify trends.\n\n```bash\ncurl -s -X POST \\\n  \"https:\u002F\u002Fanalyticsdata.googleapis.com\u002Fv1beta\u002Fproperties\u002F{PROPERTY_ID}:runReport\" \\\n  -H \"Authorization: Bearer ${GA_ACCESS_TOKEN}\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"dateRanges\": [\n      {\"startDate\": \"30daysAgo\", \"endDate\": \"today\", \"name\": \"current\"},\n      {\"startDate\": \"60daysAgo\", \"endDate\": \"31daysAgo\", \"name\": \"previous\"}\n    ],\n    \"metrics\": [\n      {\"name\": \"sessions\"},\n      {\"name\": \"totalUsers\"},\n      {\"name\": \"conversions\"},\n      {\"name\": \"engagementRate\"}\n    ]\n  }'\n```\n\n---\n\n## Response Parsing\n\nGA4 API returns JSON. Parse with python3 or jq:\n\n```bash\n# Parse report into a table\ncurl -s -X POST \"...\" | python3 -c \"\nimport json, sys\ndata = json.load(sys.stdin)\nheaders = [h['name'] for h in data.get('dimensionHeaders',[])] + [m['name'] for m in data.get('metricHeaders',[])]\nprint(' | '.join(headers))\nprint('-' * (len(headers) * 20))\nfor row in data.get('rows', []):\n    dims = [d['value'] for d in row.get('dimensionValues',[])]\n    mets = [m['value'] for m in row.get('metricValues',[])]\n    print(' | '.join(dims + mets))\n\"\n```\n\n---\n\n## Workflow: Monthly Analytics Report\n\nWhen asked for a monthly report:\n\n1. Pull traffic overview (sessions, users, page views) with period comparison\n2. Pull acquisition breakdown by channel\n3. Pull top 20 pages by page views\n4. Pull conversion summary by channel\n5. Pull device and country breakdown\n\nPresent as a structured report with tables, trends (up\u002Fdown arrows), and recommendations:\n\n```\n## Monthly Analytics Report: {Property Name}\n### Period: {date range} vs {previous period}\n\n### Traffic Summary\n| Metric | Current | Previous | Change |\n|--------|---------|----------|--------|\n| Sessions | X | Y | +Z% |\n| ...\n\n### Top Channels\n...\n\n### Top Pages\n...\n\n### Conversion Summary\n...\n\n### Recommendations\n- [Based on data patterns]\n```\n\n## Common Issues\n\n- **403 Forbidden**: User lacks access to the GA4 property\n- **Empty rows**: No data for the requested date range or filters\n- **Quota exceeded**: GA4 API has daily quotas; reduce date ranges or batch requests\n- **Property not found**: Verify the property ID format (`properties\u002FXXXXXXXXX`)\n",{"name":5,"description":7},"main","479b264ec17cc5fafbf36287a1087472d3a386d3","plugins\u002Fmarketing-analytics\u002Fskills\u002Fgoogle-analytics",2,"https:\u002F\u002Fgithub.com\u002FFabienSalles\u002Fclaude-marketplace","https:\u002F\u002Fgithub.com\u002FFabienSalles","https:\u002F\u002Ffabiensalles.com","2026-10-13 04:33:22",{"source":8,"skillName":5,"commands":63,"prompt":73,"zipAvailable":74},[64,67,70],{"tool":65,"command":66},"npx","npx skills add FabienSalles\u002Fclaude-marketplace --skill google-analytics",{"tool":68,"command":69},"bunx","bunx skills add FabienSalles\u002Fclaude-marketplace --skill google-analytics",{"tool":71,"command":72},"pnpm","pnpm dlx skills add FabienSalles\u002Fclaude-marketplace --skill google-analytics","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FFabienSalles\u002Fclaude-marketplace\u002Fblob\u002Fmain\u002Fplugins\u002Fmarketing-analytics\u002Fskills\u002Fgoogle-analytics\u002FSKILL.md",true,[76],{"path":77,"resourceType":78,"resourceTypeDesc":79,"sizeBytes":80,"sourceHtmlUrl":81},".skillkit.json","REFERENCE","参考文档",222,"https:\u002F\u002Fgithub.com\u002FFabienSalles\u002Fclaude-marketplace\u002Fblob\u002Fmain\u002Fplugins\u002Fmarketing-analytics\u002Fskills\u002Fgoogle-analytics\u002F.skillkit.json",[83,112,131,151,174,191],{"skillNo":84,"name":85,"nameZh":6,"description":86,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":87,"categories":88,"keywords":92,"publishedAt":49},"01M6DW2YS1MGF65JNS1QQ15QHS","vitest-test-conventions","ACTIVATE when writing Vitest tests, creating test files, using vi.fn()\u002Fvi.mock()\u002Fvi.spyOn(), or test factories. ACTIVATE for 'Vitest', 'it.each', 'NestJS testing module', 'supertest', 'vi.fn', 'vi.mock', 'spyOn'. Provides Vitest\u002FNestJS-specific testing patterns; cross-language testing principles (DAMP, AAA, spy over mock, what NOT to test) live in craft:testing-principles. DO NOT use for: node:test files (a file importing node:test) or a project whose tests run under node --test (see node-test:node-test-conventions), TDD workflow\u002Fiterations (see vitest:vitest-tdd-workflow), PHP\u002FPHPUnit tests (see phpunit:php-test-conventions).","https:\u002F\u002Fgithub.com\u002FFabienSalles\u002Fclaude-marketplace\u002Fblob\u002Fmain\u002Fplugins\u002Fvitest\u002Fskills\u002Fvitest-test-conventions\u002FSKILL.md",[89],{"categoryNo":90,"categoryName":91,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量",[93,94,95,96,97,98,99,100,101,102,103,104,105,38,106,107,108,109,110,111],"test","vitest","conventions","vitest test conventions","testing","writing","tdd","workflow","file","testing patterns","use","files","nestjs","do","project","tests","module","fn","testing principles",{"skillNo":113,"name":114,"nameZh":6,"description":115,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":116,"categories":117,"keywords":121,"publishedAt":49},"01M6DW2VFE6NPFCGQKQ68P1MVZ","vitest-tdd-workflow","ACTIVATE when building new features, endpoints, or user stories using TDD with Vitest in a TypeScript\u002FNestJS context. ACTIVATE for '\u002Ffeature-tdd-dev', 'TDD', 'red-green-refactor' in TS context. Provides Vitest\u002FNestJS-specific TDD examples and commands; cross-language TDD workflow (cross-layer iterations, RED-GREEN-REFACTOR, working app, bug-fix-first-test, mocks-hiding-bugs) lives in craft:tdd-workflow-principles. DO NOT use for: node:test files (a file importing node:test) or a project whose tests run under node --test (see node-test:node-test-conventions), test writing conventions (see vitest:vitest-test-conventions), PHP TDD (see phpunit:php-tdd-workflow).","https:\u002F\u002Fgithub.com\u002FFabienSalles\u002Fclaude-marketplace\u002Fblob\u002Fmain\u002Fplugins\u002Fvitest\u002Fskills\u002Fvitest-tdd-workflow\u002FSKILL.md",[118],{"categoryNo":119,"categoryName":120,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",[99,100,122,94,123,98,124,101,125,126,103,127,104,38,106,128,107,108,129,130],"tdd workflow","vitest tdd workflow","dev","examples","fix","red-green-refactor","green","feature","app",{"skillNo":132,"name":133,"nameZh":6,"description":134,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":135,"categories":136,"keywords":137,"publishedAt":49},"01M6DW2RGMQE10D8C5Z5MA2RMY","ts-refactoring","ACTIVATE when refactoring TypeScript code. ACTIVATE for 'refactor', 'extract', 'redesign', 'simplify', or 'clean up' in a TypeScript \u002F Node \u002F NestJS context. This skill provides TS-specific examples for the cross-language refactoring rules defined in craft:refactoring-principles. DO NOT use for: writing new features from scratch, general OOP patterns (see ts-oop).","https:\u002F\u002Fgithub.com\u002FFabienSalles\u002Fclaude-marketplace\u002Fblob\u002Fmain\u002Fplugins\u002Ftypescript\u002Fskills\u002Fts-refactoring\u002FSKILL.md",[],[138,139,98,140,141,125,142,143,144,145,146,103,32,147,148,105,149,38,106,150],"refactoring","ts refactoring","typescript","code","refactor","simplify","Rules","node","redesign","general","clean","craft","patterns",{"skillNo":152,"name":153,"nameZh":6,"description":154,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":155,"categories":156,"keywords":158,"publishedAt":49},"01M6DW2MHYXNBBVYEGB058Y48J","ts-ports-adapters","ACTIVATE when a TypeScript codebase with no dependency-injection container has to declare a port and wire its adapter — a repository, a clock, an id generator, an event dispatcher the domain depends on. ACTIVATE for 'port', 'adapter', 'SPI', 'dependency inversion' or 'composition root' in a codebase that wires its dependencies by hand. Covers: the port as a type of functions in the domain layer, the adapter as an object literal annotated with it, the handler that takes its dependencies first and its data second, the composition root in the infrastructure layer, and the in-memory stub that substitutes for the adapter in a unit test. DO NOT use for: NestJS, or any container-based DI with decorators and tokens (see nest-ddd-conventions), aggregate operations and smart constructors (see ddd-ts-fp), the Result type itself (see ts-functional).","https:\u002F\u002Fgithub.com\u002FFabienSalles\u002Fclaude-marketplace\u002Fblob\u002Fmain\u002Fplugins\u002Ftypescript\u002Fskills\u002Fts-ports-adapters\u002FSKILL.md",[157],{"categoryNo":90,"categoryName":91,"taxonomyType":21,"taxonomyTypeDesc":22},[159,160,161,93,162,163,103,164,165,105,166,38,106,167,168,169,170,171,172,173],"ports","ts ports adapters","memory","ddd","Composition","container","functions","Literal","dependency-injection","domain","tokens","as","type","id","adapter",{"skillNo":175,"name":176,"nameZh":6,"description":177,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":178,"categories":179,"keywords":180,"publishedAt":49},"01M6DW2GRXZFYT05HEDB26C9B8","ts-oop","ACTIVATE when designing TypeScript classes, and the value objects or collections modelled as classes, or when the user asks about object design, encapsulation, Tell Don't Ask, or Symbol.iterator. Provides TS-specific examples for the cross-language OOP rules defined in craft:oop-principles, plus TS-specific patterns (Symbol.iterator, branded types for primitive identifiers). DO NOT use for: a value object or an aggregate modelled as an immutable record rather than a class (see ddd-ts-fp), refactoring methodology (see ts-refactoring).","https:\u002F\u002Fgithub.com\u002FFabienSalles\u002Fclaude-marketplace\u002Fblob\u002Fmain\u002Fplugins\u002Ftypescript\u002Fskills\u002Fts-oop\u002FSKILL.md",[],[181,182,183,140,162,125,184,185,138,144,186,103,187,149,38,106,188,189,190,170],"oop","ts oop","design","methodology","immutable","ask","refactoring methodology","objects","record","collections",{"skillNo":192,"name":193,"nameZh":6,"description":194,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":195,"categories":196,"keywords":197,"publishedAt":49},"01M6DW2D26XG5MJMTP9MSE43S2","ts-layer-boundaries","ACTIVATE when wiring a Worker entry point, a Router, a controller, or a domain event consumer in a TypeScript CQRS codebase, or when deciding where a port belongs. ACTIVATE for 'composition root', 'layer boundary', 'ESLint zones', 'domain must not import infrastructure'. Covers: the *Worker.ts composition root, the curried consumer, Router wiring of command endpoints, the five-gesture controller, the domain-to-infrastructure import boundary enforced by ESLint, infrastructure-only ports, in-memory doubles for every port, the read-can-reach-write asymmetry, and read endpoints going through a domain handler. DO NOT use for: the port's own shape and adapter binding (see ts-ports-adapters), the Result type (see ts-functional), domain event structure (see ts-ddd-events).","https:\u002F\u002Fgithub.com\u002FFabienSalles\u002Fclaude-marketplace\u002Fblob\u002Fmain\u002Fplugins\u002Ftypescript\u002Fskills\u002Fts-layer-boundaries\u002FSKILL.md",[],[198,161,162,199,200,163,159,103,201,202,203,38,106,204,168,205,206,171,173,207],"ts layer boundaries","controller","boundary","router","only","endpoints","structure","cqrs","worker","import"]