{"id":10122,"date":"2026-09-27T08:05:00","date_gmt":"2026-09-27T06:05:00","guid":{"rendered":"https:\/\/www.lukaswojcik.com\/blog\/?p=10122"},"modified":"2026-09-25T10:49:11","modified_gmt":"2026-09-25T08:49:11","slug":"tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query","status":"publish","type":"post","link":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/tutorials\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\/","title":{"rendered":"Tutorial: Exporting GA4 Data to BigQuery and Writing the First SQL Query"},"content":{"rendered":"<p>The GA4 interface answers questions it was designed to answer. Anything outside that shape \u2014 a page sequence per session, a cohort defined by two conditions in a specific order, a metric the interface does not offer \u2014 runs into the limits of a reporting UI built on pre-aggregated data. The BigQuery export removes that ceiling by handing over the raw event stream, one row per event, queryable in SQL.<\/p>\n<p>The following walkthrough covers the three things needed to start: switching the export on, understanding what the raw data does and does not contain compared to the interface, and writing a first query that reconstructs user paths from the nested event schema.<\/p>\n<figure class=\"lw-diagram\">\n<img decoding=\"async\" src=\"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/diagrams\/ga4-bigquery-export-en.png\" width=\"1120\" height=\"580\" loading=\"lazy\" alt=\"Flow from the GA4 property through the BigQuery link and the daily events_YYYYMMDD tables to a first SQL query that reconstructs raw user paths per session\"><figcaption>Raw rows instead of aggregates &ndash; and for the same reason, the totals will not match the interface exactly.<\/figcaption><\/figure>\n<h2>Step 1: Switching the Export On<\/h2>\n<p>The link is created in GA4 under Admin, in the Product links section, via BigQuery links. Creating it requires edit rights on the GA4 property and owner rights on the destination Google Cloud project, with the BigQuery API enabled. The configuration asks for the Cloud project, a data location, which data streams to include, and an export frequency.<\/p>\n<p>Two frequencies are offered, and they behave differently. <strong>Daily<\/strong> writes one table per day, <code>events_YYYYMMDD<\/code>, usually available the following day; it is included at no cost for standard properties. <strong>Streaming<\/strong> writes to <code>events_intraday_YYYYMMDD<\/code> continuously and is billed at BigQuery&#8217;s streaming insert rates. For learning the data and for most analysis, daily alone is enough.<\/p>\n<p>Two constraints matter before relying on any of this. The export is <strong>not retroactive<\/strong> \u2014 it begins on the day the link is created, and no history is backfilled, which makes switching it on early worthwhile even without an immediate use for the data. And standard properties carry a documented daily export limit of one million events per day; a property exceeding it can have the export suspended, so the current volume is worth checking against the limit in Google&#8217;s documentation before the export becomes load-bearing.<\/p>\n<p>On the BigQuery side, the free tier covers 10 GiB of storage and 1 TiB of query processing per month, which is generous for a single property being explored by hand.<\/p>\n<h2>Step 2: What \u201cUnsampled\u201d Actually Means<\/h2>\n<p>Avoiding sampling in the export is not a technique \u2014 it follows from the export being the unaggregated event stream rather than a report. Three separate reductions the interface applies are simply absent:<\/p>\n<ul>\n<li><strong>Sampling in explorations.<\/strong> Standard GA4 reports are not sampled, but explorations apply sampling once a query exceeds the event limit for the property tier, and the result carries a notice saying so.<\/li>\n<li><strong>The <code>(other)<\/code> row.<\/strong> When a dimension produces more distinct values than a report can hold, the remainder is collapsed into a single <code>(other)<\/code> bucket \u2014 a real problem for page paths, product IDs, or search terms.<\/li>\n<li><strong>Thresholding.<\/strong> With Google signals active, rows representing very few users are withheld entirely so individuals cannot be identified, and the report shows a thresholding notice instead of the data.<\/li>\n<\/ul>\n<p>None of the three applies to the exported tables. The trade is that everything the interface computes on top of the raw data \u2014 modelled conversions, data-driven attribution, its own session definitions \u2014 is also absent, which is why totals from BigQuery and totals from the interface will not match exactly.<\/p>\n<h2>Step 3: The Shape of the Data<\/h2>\n<p>Each row is one event. What surprises anyone arriving from a conventional SQL background is that the interesting values are not columns: they sit inside <code>event_params<\/code>, a repeated record of key\/value pairs, where the value itself is a struct with a separate field per type.<\/p>\n<table style=\"width:100%;border-collapse:collapse\">\n<thead>\n<tr>\n<th style=\"white-space:nowrap;vertical-align:top\">Field<\/th>\n<th>Type<\/th>\n<th>What it holds<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td style=\"white-space:nowrap;vertical-align:top\"><code>event_date<\/code><\/td>\n<td>STRING<\/td>\n<td>The day as <code>YYYYMMDD<\/code>, matching the table suffix.<\/td>\n<\/tr>\n<tr>\n<td style=\"white-space:nowrap;vertical-align:top\"><code>event_timestamp<\/code><\/td>\n<td>INT64<\/td>\n<td>Microseconds since epoch \u2014 the ordering key within a session.<\/td>\n<\/tr>\n<tr>\n<td style=\"white-space:nowrap;vertical-align:top\"><code>event_name<\/code><\/td>\n<td>STRING<\/td>\n<td><code>page_view<\/code>, <code>session_start<\/code>, <code>purchase<\/code>, and custom events.<\/td>\n<\/tr>\n<tr>\n<td style=\"white-space:nowrap;vertical-align:top\"><code>user_pseudo_id<\/code><\/td>\n<td>STRING<\/td>\n<td>The client identifier \u2014 a device or browser, not a person.<\/td>\n<\/tr>\n<tr>\n<td style=\"white-space:nowrap;vertical-align:top\"><code>event_params<\/code><\/td>\n<td>REPEATED RECORD<\/td>\n<td>Key\/value pairs: <code>ga_session_id<\/code>, <code>page_location<\/code>, <code>page_title<\/code> and the rest.<\/td>\n<\/tr>\n<tr>\n<td style=\"white-space:nowrap;vertical-align:top\"><code>items<\/code><\/td>\n<td>REPEATED RECORD<\/td>\n<td>E-commerce products attached to the event.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>A session has no column of its own. It is reconstructed by pairing <code>user_pseudo_id<\/code> with the <code>ga_session_id<\/code> parameter, since session ids are only unique within a single client.<\/p>\n<h2>Step 4: A First Query to Confirm the Export Works<\/h2>\n<p>Before anything analytical, one query establishes that data is arriving and that the table reference is correct:<\/p>\n<pre class=\"wp-block-kevinbatdorf-code-block-pro\"><code>SELECT\n  event_date,\n  COUNT(*) AS events,\n  COUNT(DISTINCT user_pseudo_id) AS users\nFROM `project.analytics_XXXXXXXXX.events_*`\nWHERE _TABLE_SUFFIX BETWEEN '20260920' AND '20260926'\nGROUP BY event_date\nORDER BY event_date;<\/code><\/pre>\n<p>The <code>_TABLE_SUFFIX<\/code> filter is not optional housekeeping. The wildcard <code>events_*<\/code> addresses every daily table in the dataset at once, and without a suffix filter the query scans the entire export history \u2014 which is billed by bytes read.<\/p>\n<h2>Step 5: Extracting Raw User Paths<\/h2>\n<p>The query the interface cannot produce is a full page sequence per session. Reading a value out of <code>event_params<\/code> is done with a scalar subquery over <code>UNNEST<\/code>, which is the idiom worth memorising \u2014 it recurs in every GA4 query:<\/p>\n<pre class=\"wp-block-kevinbatdorf-code-block-pro\"><code>WITH page_views AS (\n  SELECT\n    user_pseudo_id,\n    (SELECT value.int_value FROM UNNEST(event_params)\n      WHERE key = 'ga_session_id') AS session_id,\n    event_timestamp,\n    (SELECT value.string_value FROM UNNEST(event_params)\n      WHERE key = 'page_location') AS page_location\n  FROM `project.analytics_XXXXXXXXX.events_*`\n  WHERE _TABLE_SUFFIX BETWEEN '20260920' AND '20260926'\n    AND event_name = 'page_view'\n)\nSELECT\n  CONCAT(user_pseudo_id, '-', CAST(session_id AS STRING)) AS session_key,\n  COUNT(*) AS page_views,\n  STRING_AGG(\n    REGEXP_EXTRACT(page_location, r'^https?:\/\/[^\/]+([^?#]*)'),\n    ' &gt; ' ORDER BY event_timestamp\n  ) AS path\nFROM page_views\nWHERE session_id IS NOT NULL\nGROUP BY session_key\nHAVING page_views &gt; 1\nORDER BY page_views DESC\nLIMIT 100;<\/code><\/pre>\n<p>Each row is one session&#8217;s journey in order, with the domain and query string stripped so that identical pages group together. From here the same structure answers more specific questions: filtering to sessions that contain a particular event, counting how often one page precedes another, or measuring how many steps precede a purchase.<\/p>\n<h2>Staying Inside the Guardrails<\/h2>\n<p>Two expectations are worth setting before this data reaches a report. Numbers from the export will not reconcile exactly with the GA4 interface, and that is by design rather than a fault to be chased: the interface adds modelling and its own session logic that the raw rows do not contain. And the export carries event-level data covering identifiable devices, so the same consent basis and retention discipline that applies to analytics data applies to the dataset in Cloud \u2014 including who is granted access to the project.<\/p>\n<div class=\"lw-faq\">\n<h2>Questions and answers<\/h2>\n<h3>Does events_* count events twice when the streaming export is switched on?<\/h3>\n<p>With the suffix filter from step 4, no; without it, possibly. The wildcard events_* also matches the events_intraday_YYYYMMDD tables, because their names start with events_ as well. For them, _TABLE_SUFFIX is then not 20260926 but intraday_20260926.<\/p>\n<p>The condition <code>_TABLE_SUFFIX BETWEEN '20260920' AND '20260926'<\/code> excludes those tables, because the comparison is made character by character and the letter i sorts after every digit. Anyone who needs the current day&#8217;s data from the intraday table therefore has to include it explicitly, for example with an additional condition <code>_TABLE_SUFFIX = 'intraday_20260927'<\/code>.<\/p>\n<p>A query can only count twice if, without a suffix filter or with a pattern that is too broad, it picks up both kinds of table and both exist for the same day. A query that mixes the two sources should therefore count only one of them per day.<\/p>\n<h3>Does LIMIT 100 reduce the cost of a query?<\/h3>\n<p>No. BigQuery bills by the bytes a query has to read, and LIMIT only caps the output after the data has been read. Because BigQuery stores data by column, two other measures do cut costs: selecting only the columns that are needed instead of SELECT *, and the filter on _TABLE_SUFFIX, which takes whole daily tables out of the bill.<\/p>\n<h3>Does the retention period set in GA4 also apply to the tables in BigQuery?<\/h3>\n<p>No. The retention setting in GA4 covers the data in GA4 itself. Whatever has been exported to BigQuery sits there as a separate copy in the Cloud project and stays until it is deleted there. Left alone, the dataset therefore grows day by day, and with it the storage, which can eventually exceed the free 10 GiB.<\/p>\n<p>The retention discipline the article also calls for on the dataset therefore has to be set up in BigQuery. A default expiration for tables can be set on the dataset for this (in the API, the field defaultTableExpirationMs); new daily tables are then deleted automatically once that period has passed. It only applies to tables created after the change; older tables need an expiration of their own or have to be removed by hand.<\/p>\n<h3>Does the export also work without a billing account in Google Cloud?<\/h3>\n<p>Yes; BigQuery then runs as a sandbox, but with two restrictions that matter for GA4. Tables in the sandbox expire after 60 days, so the daily export never holds more than about two months. And the streaming export is not available there. That is enough for getting to know the data; building a long time series needs a billing account, even if usage stays within the free tier.<\/p>\n<\/div>\n<div class=\"lw-quellen\">\n<h2>Sources<\/h2>\n<ul>\n<li><a href=\"https:\/\/developers.google.com\/analytics\/devguides\/collection\/ga4\" target=\"_blank\" rel=\"noopener noreferrer\">Google Analytics 4 developer guide<\/a><\/li>\n<\/ul>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Enabling the free daily GA4 export to BigQuery, understanding why the exported rows carry none of the sampling, cardinality bucketing or thresholding the interface applies, and writing a first query that reconstructs raw user paths from the nested event schema.<\/p>\n","protected":false},"author":1,"featured_media":13830,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[113],"tags":[91333,91258,91407,91219,91252],"class_list":["post-10122","post","type-post","status-publish","format-standard","hentry","category-tutorials","tag-bigquery","tag-google-analytics-4","tag-google-cloud","tag-tutorial","tag-web-analytics"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Tutorial: Exporting GA4 Data to BigQuery and Writing the First SQL Query | Lukas Wojcik<\/title>\n<meta name=\"description\" content=\"Enabling the free daily GA4 export to BigQuery, seeing what the interface does to the numbers, and a first query on the nested event schema.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/tutorials\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Tutorial: Exporting GA4 Data to BigQuery and Writing the First SQL Query | Lukas Wojcik\" \/>\n<meta property=\"og:description\" content=\"Enabling the free daily GA4 export to BigQuery, seeing what the interface does to the numbers, and a first query on the nested event schema.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/tutorials\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\/\" \/>\n<meta property=\"og:site_name\" content=\"Lukas Wojcik - Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-09-27T06:05:00+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/09\/hero-10122-tutorial-exporting-ga4-data-to-bigqu-g.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"630\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Lukas Wojcik\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Lukas Wojcik\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"8 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-analytics\\\/tutorials\\\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-analytics\\\/tutorials\\\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\\\/\"},\"author\":{\"name\":\"Lukas Wojcik\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"headline\":\"Tutorial: Exporting GA4 Data to BigQuery and Writing the First SQL Query\",\"datePublished\":\"2026-09-27T06:05:00+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-analytics\\\/tutorials\\\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\\\/\"},\"wordCount\":1373,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"image\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-analytics\\\/tutorials\\\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/hero-10122-tutorial-exporting-ga4-data-to-bigqu-g.png\",\"keywords\":[\"BigQuery\",\"Google Analytics 4\",\"Google Cloud\",\"Tutorial\",\"Web Analytics\"],\"articleSection\":[\"Tutorials\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-analytics\\\/tutorials\\\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-analytics\\\/tutorials\\\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\\\/\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-analytics\\\/tutorials\\\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\\\/\",\"name\":\"Tutorial: Exporting GA4 Data to BigQuery and Writing the First SQL Query | Lukas Wojcik\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-analytics\\\/tutorials\\\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-analytics\\\/tutorials\\\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/hero-10122-tutorial-exporting-ga4-data-to-bigqu-g.png\",\"datePublished\":\"2026-09-27T06:05:00+00:00\",\"description\":\"Enabling the free daily GA4 export to BigQuery, seeing what the interface does to the numbers, and a first query on the nested event schema.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-analytics\\\/tutorials\\\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-analytics\\\/tutorials\\\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-analytics\\\/tutorials\\\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\\\/#primaryimage\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/hero-10122-tutorial-exporting-ga4-data-to-bigqu-g.png\",\"contentUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/hero-10122-tutorial-exporting-ga4-data-to-bigqu-g.png\",\"width\":1200,\"height\":630,\"caption\":\"Tutorial: Exporting GA4 Data to BigQuery and Writing the First SQL Query\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/digital-analytics\\\/tutorials\\\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Tutorial: Exporting GA4 Data to BigQuery and Writing the First SQL Query\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\",\"name\":\"Lukas Wojcik - Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\",\"name\":\"Lukas Wojcik\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"contentUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"width\":424,\"height\":636,\"caption\":\"Lukas Wojcik\"},\"logo\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\"},\"sameAs\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Tutorial: Exporting GA4 Data to BigQuery and Writing the First SQL Query | Lukas Wojcik","description":"Enabling the free daily GA4 export to BigQuery, seeing what the interface does to the numbers, and a first query on the nested event schema.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/tutorials\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\/","og_locale":"en_US","og_type":"article","og_title":"Tutorial: Exporting GA4 Data to BigQuery and Writing the First SQL Query | Lukas Wojcik","og_description":"Enabling the free daily GA4 export to BigQuery, seeing what the interface does to the numbers, and a first query on the nested event schema.","og_url":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/tutorials\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\/","og_site_name":"Lukas Wojcik - Blog","article_published_time":"2026-09-27T06:05:00+00:00","og_image":[{"width":1200,"height":630,"url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/09\/hero-10122-tutorial-exporting-ga4-data-to-bigqu-g.png","type":"image\/png"}],"author":"Lukas Wojcik","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Lukas Wojcik","Est. reading time":"8 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/tutorials\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\/#article","isPartOf":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/tutorials\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\/"},"author":{"name":"Lukas Wojcik","@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"headline":"Tutorial: Exporting GA4 Data to BigQuery and Writing the First SQL Query","datePublished":"2026-09-27T06:05:00+00:00","mainEntityOfPage":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/tutorials\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\/"},"wordCount":1373,"commentCount":0,"publisher":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"image":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/tutorials\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\/#primaryimage"},"thumbnailUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/09\/hero-10122-tutorial-exporting-ga4-data-to-bigqu-g.png","keywords":["BigQuery","Google Analytics 4","Google Cloud","Tutorial","Web Analytics"],"articleSection":["Tutorials"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/tutorials\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/tutorials\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\/","url":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/tutorials\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\/","name":"Tutorial: Exporting GA4 Data to BigQuery and Writing the First SQL Query | Lukas Wojcik","isPartOf":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/tutorials\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\/#primaryimage"},"image":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/tutorials\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\/#primaryimage"},"thumbnailUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/09\/hero-10122-tutorial-exporting-ga4-data-to-bigqu-g.png","datePublished":"2026-09-27T06:05:00+00:00","description":"Enabling the free daily GA4 export to BigQuery, seeing what the interface does to the numbers, and a first query on the nested event schema.","breadcrumb":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/tutorials\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/tutorials\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/tutorials\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\/#primaryimage","url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/09\/hero-10122-tutorial-exporting-ga4-data-to-bigqu-g.png","contentUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/09\/hero-10122-tutorial-exporting-ga4-data-to-bigqu-g.png","width":1200,"height":630,"caption":"Tutorial: Exporting GA4 Data to BigQuery and Writing the First SQL Query"},{"@type":"BreadcrumbList","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/digital-analytics\/tutorials\/tutorial-exporting-ga4-data-to-bigquery-and-writing-the-first-sql-query\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.lukaswojcik.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Tutorial: Exporting GA4 Data to BigQuery and Writing the First SQL Query"}]},{"@type":"WebSite","@id":"https:\/\/www.lukaswojcik.com\/blog\/#website","url":"https:\/\/www.lukaswojcik.com\/blog\/","name":"Lukas Wojcik - Blog","description":"","publisher":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.lukaswojcik.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9","name":"Lukas Wojcik","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","contentUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","width":424,"height":636,"caption":"Lukas Wojcik"},"logo":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg"},"sameAs":["https:\/\/www.lukaswojcik.com\/blog"]}]}},"_links":{"self":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts\/10122","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/comments?post=10122"}],"version-history":[{"count":3,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts\/10122\/revisions"}],"predecessor-version":[{"id":20685,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts\/10122\/revisions\/20685"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/media\/13830"}],"wp:attachment":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/media?parent=10122"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/categories?post=10122"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/tags?post=10122"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}