HookGet Start free

Google Analytics 4 → HookGet

HookGet reads Google Analytics 4's reporting API on a schedule and turns it into normalised events on the same pipeline as everything else you deliver.

In short

  • Reads the GA4 Data API (runReport) with a service account — grant it Viewer once per Analytics account and paste its JSON key. No OAuth dance.
  • One paste, every property: POST /v1/sources/discover lists each GA4 property the key can read, across all your Analytics accounts, as a ready-to-create source already labelled with its brand.
  • One event per settled day — or per day × channel × device when you ask for dimensions: web.traffic_reported with sessions, active_users, screen_page_views, or up to 8 metrics you choose (keyEvents, engagementRate, …).
  • Labels on the source ({"brand":"wizlu"}) become attributes on every event, so "sessions by brand" across eleven sites is one widget, not eleven dashboards.
  • Days are read once, two days after they close — GA4 finalises numbers for up to 48 hours, and an event that changes value after delivery is worse than one that is late.
  • Daily aggregates only: no user-level data leaves the property, and the events honestly carry containsPii: false.

What it produces

Read from the APIBecomesData
One settled day of the property — or one row per day × dimension valuesweb.traffic_reportedactive_users, sessions, screen_page_views (default) — or your own metric list; attributes channel, device, country, new_vs_returning, medium, platform, host when requested; plus your labels

Set up

# 1. Google Cloud console: create a service account, download its JSON key
# 2. GA4 Admin -> Account access management: add the service account email as Viewer
#    (once per Analytics account — every property under it is covered)
# 3. Ask what the key can read: every property, across every account, labelled by brand
curl -X POST https://api.hookget.com/v1/sources/discover \
  -H "authorization: Bearer $HOOKGET_KEY" \
  -d '{"provider":"google-analytics","secret":"<the JSON key file, pasted whole>"}'
# 4. Create the ones you want — the discover response is already in this shape
curl -X POST https://api.hookget.com/v1/sources \
  -H "authorization: Bearer $HOOKGET_KEY" \
  -d '{"provider":"google-analytics",
       "name":"wizlu.co.il",
       "labels":{"brand":"wizlu.co.il"},
       "secret":"<the same JSON key>",
       "config":{"property_id":"123456789",
                 "dimensions":["sessionDefaultChannelGroup","deviceCategory"],
                 "metrics":["sessions","activeUsers","keyEvents","engagementRate"]},
       "backfill_from":"2026-06-01T00:00:00Z"}'
One scheduled sync of a pull source The scheduler claims a due source, fetches from the provider's API with the saved cursor, normalises the response into events, and stores them. Only when the sync succeeds does the cursor advance. A failure keeps the old cursor and retries with a growing backoff; after twenty consecutive failures the source pauses and reports its health. Due interval passed Fetch from the saved cursor Normalise dedupe on provider ids Stored, cursor advances only on success — never mid-failure Failure: old cursor kept backoff doubles · 20 strikes → paused, health says why
The two rules that make polling safe: the cursor advances only on success (a partial read never silently skips events — re-reads deduplicate for free), and a failing credential backs off and eventually pauses instead of hammering the provider in your name.

Stated plainly

GA4 keeps refining a day for 24–48 hours, so each day is read once, two days after it closes. This connector is for the durable daily record — spend joins, weekly reports, anomaly alerts — not for the realtime card in the GA4 UI, which their API exposes separately and less stably.

Questions

Why a service account and not my Google login?

A service account is a credential you can scope to one property and revoke in one place, without touching your own account. Granting it Viewer on the property is one step in GA4 Admin, and the JSON key is stored encrypted — no route ever returns it.

Can I choose which metrics arrive?

Yes — config.metrics takes up to eight GA4 metric API names (activeUsers, keyEvents, engagementRate, totalRevenue…, or keyEvents:purchase for one key event). They arrive snake_cased as event metrics, validated against the Data API schema before the source is created — conversions left that schema in 2024 and is refused, rather than failing on every sync.

Which breakdowns can I ask for, and why not landing pages?

config.dimensions takes up to two of: sessionDefaultChannelGroup (→ channel), deviceCategory (→ device), country, newVsReturning, sessionMedium, platform, hostName. Each row of the day becomes one event with those values as attributes, so a widget can group sessions by channel per brand. landingPage, pagePath and city are refused on purpose: thousands of distinct values a day per property is an identifier, not a category, and attributes are what dashboards GROUP BY.

I have eleven websites in eleven Analytics accounts. Eleven setups?

One. Create one service account, add its email as Viewer on each Analytics account (that is the one manual step Google requires), then POST /v1/sources/discover with the key: HookGet asks the Admin API which properties the key can read and returns each as a ready-to-create source, already carrying labels.brand from the property's name. Create the ones you want, then the Traffic across brands template renders sessions, channel mix, devices and key events per brand — one dashboard for all eleven.

Start delivering webhooks today

Point your webhooks at HookGet and watch the first delivery arrive, signed, in under a minute.

Create a free account Try the free webhook tester

10,000 deliveries a month free, no credit card. The free tier blocks rather than bills, so trying it cannot produce an invoice.