Automatically collect Google Analytics 4 traffic data and deliver reports to Slack.
한국어 README | Contributing | Changelog
Features:
- Multi-property support
- Customizable report periods (daily / weekly / biweekly / monthly / custom)
- Customizable comparison baselines (previous period / same period last week / custom)
- Per-section toggle (summary, traffic sources, pages, devices)
- Slack Webhook + OAuth dual delivery
- Fully customizable message layout via Jinja2 templates
- Automated via GitHub Actions
git clone https://github.com/ShinChanU/ga4-slack-report.git
cd ga4-slack-report
pip install -r requirements.txt- Go to Google Cloud Console
- APIs & Services > Library > search "Google Analytics Data API" > Enable
- APIs & Services > Credentials > Create Service Account
- Service Account > Keys > Add Key > JSON > download
- Go to Google Analytics
- Admin > Property > Property Access Management
- Add users > enter service account email (permission: Viewer or above)
Webhook (simple):
- Slack API > Create New App > From scratch
- Incoming Webhooks > Activate > Add New Webhook to Workspace
- Select channel > copy Webhook URL
OAuth (flexible):
- Create Slack App, then OAuth & Permissions > add scopes:
chat:write,chat:write.public - Install to workspace > copy Bot User OAuth Token
- Note the target channel ID
cp config.example.yaml config.yaml
cp .env.example .envconfig.yaml— edit property IDs, report period, etc..env— fill in your service account JSON, Slack Webhook URL, etc.
Fill in the values in your .env file, or export them directly in the terminal.
Using mise (recommended):
mise run reportmise automatically loads
.envand installs the correct Python version.
Without mise:
# Option 1: Use .env file
set -a && source .env && set +a && python main.py config.yaml
# Option 2: Export directly
export GOOGLE_SERVICE_ACCOUNT_JSON='{"type":"service_account","project_id":"...","private_key":"...",...}'
export SLACK_WEBHOOK_URL='https://hooks.slack.com/services/T.../B.../xxxx'
python main.py config.yamlNote:
GOOGLE_SERVICE_ACCOUNT_JSONmust contain the entire service account JSON key as a single line. Copy the full contents of the downloaded JSON file.
| Key | Type | Default | Description |
|---|---|---|---|
period |
string | daily |
daily weekly biweekly monthly custom |
compare |
string | previous_period |
previous_period same_period_last_week custom |
data_delay_days |
int | 1 |
Days to offset for GA4 data delay |
custom.current_days |
int | 7 |
Current period length when period: custom |
custom.previous_days |
int | 7 |
Previous period length |
Date range examples (data_delay_days=1, today=4/11):
| period | Current | Previous (previous_period) |
|---|---|---|
| daily | 4/10 | 4/9 |
| weekly | 4/4 ~ 4/10 | 3/28 ~ 4/3 |
| biweekly | 3/28 ~ 4/10 | 3/14 ~ 3/27 |
| monthly | 3/12 ~ 4/10 | 2/10 ~ 3/11 |
properties:
- name: "My Website"
property_id: "123456789"
- name: "Blog"
property_id: "987654321"Finding your Property ID: Google Analytics > Admin > Property Settings > Property ID
| Key | Description |
|---|---|
credentials_env |
Env var name containing the full service account JSON |
| Key | Type | Default | Description |
|---|---|---|---|
method |
string | webhook |
webhook or oauth |
webhook_url_env |
string | Env var name for Webhook URL | |
token_env |
string | Env var name for OAuth token | |
channel_id |
string | Required for oauth, Slack channel ID |
| Key | Type | Default | Description |
|---|---|---|---|
sections.summary |
bool | true |
Overall summary (users, sessions, pageviews, duration, bounce rate) |
sections.traffic_sources |
bool | true |
Top traffic sources |
sections.top_pages |
bool | true |
Top pages by pageviews |
sections.top_devices |
bool | false |
Device category breakdown |
top_n |
int | 10 |
Number of top items |
multi_site_mode |
string | separate |
separate (one message per property) / combined |
custom_template |
string | null |
Path to custom Jinja2 template |
Use your own Slack Block Kit layout instead of the default.
templates/my-custom.json.j2:
[
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*{{ property_name }}* Report\nUsers: {{ current.active_users }} ({{ trend(diffs.active_users_pct, '%') }})"
}
}
]report:
custom_template: templates/my-custom.json.j2| Variable | Type | Description |
|---|---|---|
property_name |
string | Property name |
property_id |
string | GA4 Property ID |
current_range[0] / current_range[1] |
date | Current period start/end |
previous_range[0] / previous_range[1] |
date | Previous period start/end |
current.active_users / .sessions / .pageviews |
int | Current metrics |
current.avg_session_duration |
float | Average session duration (seconds) |
current.bounce_rate |
float | Bounce rate (0-1) |
previous.* |
number | Previous period metrics |
diffs.active_users_pct / .sessions_pct / .pageviews_pct |
float | Change rate (%) |
diffs.avg_session_duration_pct |
float | Duration change (%) |
diffs.bounce_rate_diff |
float | Bounce rate change (%p) |
traffic_sources |
list | Top sources [{source, medium, sessions}] |
pages |
list | Top pages [{path, pageviews, active_users}] |
devices |
list | Device breakdown [{category, sessions}] |
| Function | Description | Example |
|---|---|---|
trend(diff, unit) |
Change arrow | trend(diffs.active_users_pct, "%") -> (▲ +25.0%) |
format_duration(sec) |
Format seconds | format_duration(125.5) -> 2m 5s |
format_bounce_rate(rate) |
Format bounce rate | format_bounce_rate(0.45) -> 45.0% |
Repo Settings > Secrets and variables > Actions:
| Secret | Required | Description |
|---|---|---|
GOOGLE_SERVICE_ACCOUNT_JSON |
Yes | Full service account JSON key |
SLACK_WEBHOOK_URL |
Conditional | Required for webhook method |
SLACK_USER_OAUTH_TOKEN |
Conditional | Required for oauth method |
Edit the cron value in .github/workflows/ga4-slack-report.yml (UTC):
schedule:
- cron: "0 0 * * 1" # Weekly Mon 09:00 KST
- cron: "0 0 * * *" # Daily at 09:00 KST
- cron: "0 0 * * 1-5" # Weekdays 09:00 KSTActions tab > "GA4 Report -> Slack" > Run workflow
ga4-slack-report/
├── ga4_report/ # Core package
│ ├── config.py # YAML config loader + validation
│ ├── ga4_client.py # GA4 Data API client + date range calculation
│ ├── report.py # Data collection + diff calculation
│ ├── renderer.py # Slack Block Kit renderer
│ ├── slack.py # Webhook / OAuth delivery
│ └── templates/
│ ├── default.json.j2 # Default English template
│ └── default_ko.json.j2 # Korean template
├── tests/ # pytest tests
├── config.example.yaml # Example config
├── .env.example # Example environment variables
├── main.py # Entrypoint
├── mise.toml # mise task runner config
└── requirements.txt # Dependencies
pip install -r requirements-dev.txt
pytest tests/ -vContributions are welcome! Please read the Contributing Guide and our Code of Conduct before submitting a PR.

