Complete Guide to Building a Firebase × BigQuery × Looker Studio Revenue Analytics Dashboard with Antigravity
A complete guide to connecting Firebase event data to BigQuery and building a real-time revenue dashboard in Looker Studio using Antigravity AI IDE. Learn how to consolidate AdMob, subscription, and in-app purchase revenue into a single view with step-by-step implementation.
Setup and context: Why Revenue Analytics Dashboards Matter for Independent Developers
If you're running a mobile app independently, you've probably experienced the frustration of scattered revenue data. AdMob earnings live in one console, subscription revenue in another (App Store Connect or RevenueCat), and in-app purchase data somewhere else entirely. Answering a simple question like "what was my total revenue last month?" requires jumping between three or four different tools.
The Firebase × BigQuery × Looker Studio stack solves this problem elegantly. Firebase Analytics exports your event data to BigQuery automatically, you write custom SQL to aggregate all revenue sources, and Looker Studio turns that data into a visual dashboard you can share with anyone. The whole pipeline runs without manual intervention once it's set up.
The challenge is getting it all connected correctly. That's where Antigravity AI IDE becomes invaluable. Its agent mode and code generation capabilities mean you can build the entire pipeline—BigQuery SQL queries, Cloud Functions for data ingestion, automated report delivery scripts—without leaving your editor.
This guide is written for:
Developers who have Firebase Analytics integrated and want deeper revenue insights
Anyone who wants to visualize AdMob, subscription, and IAP trends in one place
Developers curious about BigQuery or Looker Studio but unsure where to start
Anyone who wants to use Antigravity agents for data pipeline automation
Architecture Overview: Understanding the Data Flow
Before writing any code, let's map out how data flows through the system.
Firebase Analytics (event collection): Captures user behavior and purchase events inside your app
BigQuery Export (automatic sync): Configured once in Firebase Console; events flow to BigQuery daily after that
BigQuery (data warehouse): Google's managed warehouse where you write SQL to aggregate and filter data any way you need
Cloud Functions for Firebase (data ingestion): Small glue functions that pull AdMob Reporting API data or RevenueCat webhooks into BigQuery
Looker Studio (BI dashboard): Google's free BI tool that connects directly to BigQuery and turns data into charts and scorecards
Antigravity accelerates every implementation phase of this pipeline. It generates BigQuery SQL from natural-language requirements, scaffolds Cloud Functions boilerplate, advises on schema design, and pinpoints the root cause when errors surface—all without leaving the editor.
✦
Thank you for reading this far.
Continue Reading
What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.
WHAT YOU'LL LEARN
✦Automate your revenue reporting by building a Firebase-to-BigQuery pipeline with Antigravity, eliminating the manual work of piecing together revenue data from multiple dashboards
✦Consolidate AdMob, subscription, and in-app purchase revenue into a single BigQuery view and visualize everything in real time through one Looker Studio dashboard
✦Implement a weekly automated report delivery pipeline using Antigravity agents so you can make data-driven decisions and continuously optimize your monetization strategy
Secure payment via Stripe · Cancel anytime
✦
Unlock This Article
Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.
Step 1: Connecting Firebase to BigQuery with Antigravity
1-1. Enabling Firebase BigQuery Export
In the Firebase Console, navigate to Project Settings → Integrations tab → BigQuery → Link. This one-time setup triggers a daily automatic export of all Firebase Analytics event data to BigQuery.
The real challenge is understanding the exported schema. Firebase uses deeply nested STRUCT types, which can be confusing if you're not familiar with BigQuery's UNNEST function.
Open Antigravity and type this prompt:
Explain the Firebase BigQuery export table schema.
Then write a query to extract in_app_purchase events from
events_YYYYMMDD tables and calculate daily revenue totals.
Antigravity generates the following query alongside an explanation of the schema:
-- Calculate daily IAP revenue from Firebase BigQuery export-- The * wildcard targets all date-suffix tables at onceSELECT -- Convert event timestamp from UTC to JST DATE(TIMESTAMP_MICROS(event_timestamp), 'Asia/Tokyo') AS event_date, -- Currency code (JPY, USD, etc.) (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'currency') AS currency, -- Revenue total (convert from micros to standard unit) ROUND(SUM( (SELECT value.int_value FROM UNNEST(event_params) WHERE key = 'value') / 1000000.0 ), 2) AS total_revenue, -- Transaction count COUNT(*) AS purchase_countFROM -- Replace with your actual project ID and analytics dataset `YOUR_PROJECT_ID.analytics_XXXXXXXXX.events_*`WHERE event_name = 'in_app_purchase' -- Limit to last 90 days to control query costs AND _TABLE_SUFFIX >= FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 90 DAY))GROUP BY event_date, currencyORDER BY event_date DESC
Paste this directly into the BigQuery Query Editor to verify your IAP data is flowing correctly.
1-2. Verifying Data Arrival
In BigQuery, you should see an analytics_XXXXXXXXX dataset with events_YYYYMMDD tables (one per day) and an events_intraday_YYYYMMDD table for same-day data. If tables appear empty, note that it can take up to 24 hours after enabling the export for data to appear. Ask Antigravity to generate a quick diagnostic query to confirm data is arriving before proceeding.
Step 2: Building Custom Revenue Queries in BigQuery
2-1. Ingesting AdMob Revenue into BigQuery
AdMob revenue data isn't included in the Firebase BigQuery export—you need to fetch it separately via the AdMob Reporting API and write it to BigQuery using a Cloud Function.
Prompt Antigravity with this:
Write a Node.js Cloud Function that fetches daily revenue
from the AdMob Reporting API and writes it to a BigQuery
admob_revenue table. Include Cloud Scheduler configuration
to run every morning at 8am.
Here's the core structure Antigravity generates:
// functions/src/syncAdMobRevenue.jsconst { google } = require('googleapis');const { BigQuery } = require('@google-cloud/bigquery');const bigquery = new BigQuery();const DATASET_ID = 'revenue_analytics'; // Your BigQuery dataset nameconst TABLE_ID = 'admob_revenue'; // Destination table/** * Fetches yesterday's AdMob revenue data and writes it to BigQuery. * Triggered by Cloud Scheduler via HTTP POST. */exports.syncAdMobRevenue = async (req, res) => { try { // Authenticate with service account for AdMob API access const auth = await google.auth.getClient({ scopes: ['https://www.googleapis.com/auth/admob.readonly'], }); const admob = google.admob({ version: 'v1', auth }); // Calculate yesterday's date const yesterday = new Date(); yesterday.setDate(yesterday.getDate() - 1); const dateStr = yesterday.toISOString().split('T')[0]; // YYYY-MM-DD const [year, month, day] = dateStr.split('-').map(Number); // Request network report from AdMob const publisherId = process.env.ADMOB_PUBLISHER_ID; const report = await admob.accounts.networkReport.generate({ parent: `accounts/${publisherId}`, requestBody: { reportSpec: { dateRange: { startDate: { year, month, day }, endDate: { year, month, day }, }, dimensions: ['APP', 'AD_UNIT', 'COUNTRY'], metrics: ['ESTIMATED_EARNINGS', 'IMPRESSIONS', 'CLICKS'], }, }, }); // Transform API response to BigQuery row format const rows = (report.data || []) .filter(row => row.row) .map(row => ({ date: dateStr, app_id: row.row.dimensionValues?.APP?.value || '', ad_unit: row.row.dimensionValues?.AD_UNIT?.value || '', country: row.row.dimensionValues?.COUNTRY?.value || '', // AdMob returns earnings in micros (divide by 1M for USD) estimated_earnings_usd: (row.row.metricValues?.ESTIMATED_EARNINGS?.microsValue || 0) / 1000000, impressions: parseInt(row.row.metricValues?.IMPRESSIONS?.integerValue || 0), clicks: parseInt(row.row.metricValues?.CLICKS?.integerValue || 0), synced_at: new Date().toISOString(), })); if (rows.length > 0) { await bigquery.dataset(DATASET_ID).table(TABLE_ID).insert(rows); console.log(`✅ Wrote ${rows.length} AdMob rows to BigQuery`); } res.status(200).json({ success: true, rows: rows.length }); } catch (error) { console.error('AdMob sync error:', error); res.status(500).json({ error: error.message }); }};
After Antigravity generates this code, use Cmd+I to open the inline chat and refine it: "Strengthen the error handling," "Migrate to Cloud Functions v2," or "Add retry logic for rate limit errors."
2-2. Creating a Unified Revenue View
Once IAP data is in Firebase's BigQuery export and AdMob data is flowing via Cloud Function, create a BigQuery view that merges all three revenue streams into a single table.
-- Unified revenue view combining all monetization sources-- Looker Studio connects to this single view as its data sourceCREATE OR REPLACE VIEW `YOUR_PROJECT_ID.revenue_analytics.unified_revenue` AS-- Source 1: In-App Purchases from Firebase AnalyticsSELECT DATE(TIMESTAMP_MICROS(event_timestamp), 'Asia/Tokyo') AS date, 'iap' AS revenue_type, app_info.id AS app_id, geo.country AS country, ROUND( (SELECT value.int_value FROM UNNEST(event_params) WHERE key = 'value') / 1000000.0, 4 ) AS revenue_usd, user_pseudo_id AS user_idFROM `YOUR_PROJECT_ID.analytics_XXXXXXXXX.events_*`WHERE event_name = 'in_app_purchase' AND _TABLE_SUFFIX >= FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 365 DAY))UNION ALL-- Source 2: AdMob Advertising Revenue (synced via Cloud Function)SELECT PARSE_DATE('%Y-%m-%d', date) AS date, 'admob' AS revenue_type, app_id, country, estimated_earnings_usd AS revenue_usd, NULL AS user_idFROM `YOUR_PROJECT_ID.revenue_analytics.admob_revenue`WHERE PARSE_DATE('%Y-%m-%d', date) >= DATE_SUB(CURRENT_DATE(), INTERVAL 365 DAY)UNION ALL-- Source 3: Subscription Revenue (RevenueCat webhook to BigQuery)SELECT DATE(purchased_at) AS date, 'subscription' AS revenue_type, app_id, country_code AS country, price_in_purchased_currency / exchange_rate AS revenue_usd, subscriber_attributes.user_id AS user_idFROM `YOUR_PROJECT_ID.revenue_analytics.revenuecat_events`WHERE event_type = 'INITIAL_PURCHASE' AND DATE(purchased_at) >= DATE_SUB(CURRENT_DATE(), INTERVAL 365 DAY)
This single view becomes the canonical source of truth for all revenue metrics in Looker Studio.
Step 3: Building the Looker Studio Dashboard
3-1. Connecting BigQuery as a Data Source
Go to Looker Studio, click Create → Report → Add data → BigQuery, and select the unified_revenue view you just created. Looker Studio handles authentication and you're ready to start building immediately.
3-2. Recommended Dashboard Layout
Ask Antigravity what metrics and chart types to include for an independent developer monetization dashboard. A typical recommended layout includes:
Looker Studio's calculated fields let you define metrics without writing BigQuery SQL each time. For example, ARPU looks like this:
// ARPU Calculated Field (Looker Studio formula syntax)
SUM(revenue_usd) / COUNT_DISTINCT(user_id)
Ask Antigravity "How do I write a calculated field in Looker Studio for 30-day retention rate?" and it produces the correct syntax for Looker Studio's formula engine, which differs from standard SQL.
Step 4: Automating Weekly Reports with Antigravity Agents
A dashboard is only valuable if you look at it. A better approach is to have insights delivered to you automatically. Use Antigravity's agent mode to build an automated weekly report pipeline.
Open the Agent Panel with Cmd+Shift+A and provide this instruction:
Implement the following pipeline:
1. Cloud Scheduler triggers every Monday at 8am
2. Fetch last week's revenue summary from BigQuery
3. Format an HTML email report with SendGrid
4. Post the same summary to the #revenue Slack channel
5. Host on Cloud Run with minimum-instances=0
Antigravity decomposes this into concrete subtasks and generates the scaffolding. Here's the core reporting service:
// services/weeklyReport.jsconst { BigQuery } = require('@google-cloud/bigquery');const sgMail = require('@sendgrid/mail');const { WebClient } = require('@slack/web-api');const bigquery = new BigQuery();const slack = new WebClient(process.env.SLACK_BOT_TOKEN);/** * Fetches last week's aggregated revenue from BigQuery. */async function fetchWeeklyRevenue() { const query = ` SELECT revenue_type, ROUND(SUM(revenue_usd), 2) AS total_revenue, COUNT(DISTINCT user_id) AS unique_buyers, COUNT(*) AS transaction_count FROM \`YOUR_PROJECT_ID.revenue_analytics.unified_revenue\` WHERE date BETWEEN DATE_SUB(DATE_TRUNC(CURRENT_DATE(), WEEK(MONDAY)), INTERVAL 7 DAY) AND DATE_SUB(DATE_TRUNC(CURRENT_DATE(), WEEK(MONDAY)), INTERVAL 1 DAY) GROUP BY revenue_type ORDER BY total_revenue DESC `; const [rows] = await bigquery.query({ query }); return rows;}/** * Sends the weekly revenue summary as an HTML email via SendGrid. */async function sendRevenueEmail(revenueData) { sgMail.setApiKey(process.env.SENDGRID_API_KEY); const total = revenueData.reduce((s, r) => s + r.total_revenue, 0).toFixed(2); await sgMail.send({ to: process.env.REPORT_EMAIL, from: process.env.FROM_EMAIL, subject: `📊 Weekly Revenue Report — ${new Date().toDateString()}`, html: ` <h2>📊 Last Week Revenue Summary</h2> <p>Total: <strong>$${total}</strong></p> ${revenueData.map(r => `<p>${r.revenue_type}: $${r.total_revenue} (${r.unique_buyers ?? '—'} buyers)</p>` ).join('')} <p><a href="${process.env.LOOKER_DASHBOARD_URL}">→ View Full Dashboard</a></p> `, }); console.log('✅ Weekly report email sent');}/** * Posts a concise summary to the #revenue Slack channel. */async function postSlackSummary(revenueData) { const total = revenueData.reduce((s, r) => s + r.total_revenue, 0).toFixed(2); const lines = revenueData.map(r => `• ${r.revenue_type}: $${r.total_revenue}`).join('\n'); await slack.chat.postMessage({ channel: '#revenue', text: `📊 *Weekly Revenue Report*\nTotal: *$${total}*\n\n${lines}\n\n<${process.env.LOOKER_DASHBOARD_URL}|View Dashboard>`, }); console.log('✅ Slack summary posted');}module.exports = { fetchWeeklyRevenue, sendRevenueEmail, postSlackSummary };
After Antigravity generates the code, use inline chat (Cmd+I) to iterate: "Switch SendGrid to Resend," "Add a week-over-week comparison section," or "Attach a Chart.js revenue graph image."
Step 5: Turning Data Insights into Product Decisions
Building the dashboard is only the first step. The real value comes from acting on what you see. Here are common patterns to watch for:
AdMob eCPM varies significantly by country: If certain markets show unusually high eCPM, consider producing localized content for those audiences or adjusting ad formats. Ask Antigravity "What ad formats maximize eCPM for [country] in 2026?" to get a prioritized list of tactics.
Low IAP conversion rate: Join Firebase Analytics funnel data (funnel_progression events) with purchase events in BigQuery to identify the exact drop-off step. Antigravity can generate this join query from a plain-language description.
High subscription churn: Join RevenueCat CANCELLATION events with Firebase engagement metrics in BigQuery to identify behavioral patterns before cancellation. This kind of cohort analysis is where the unified view pays for itself most clearly.
For a deeper dive into AdMob-specific optimization, see the Antigravity × AdMob × Firebase Analytics Revenue Optimization Master Guide. For subscription strategy, the Advanced StoreKit 2 Subscription Revenue Optimization with Antigravity guide covers churn prevention, price testing, and AI-assisted analysis in detail.
Common Errors and How to Fix Them
Firebase data isn't appearing in BigQuery: Check the BigQuery link status in Firebase Console. Even when the link shows "Active," new app installs can take up to 24 hours to start appearing. Check the events_intraday_YYYYMMDD table for same-day data to confirm the export is working.
No matching signature error in BigQuery: This typically means you're trying to access a nested STRUCT field without UNNEST. Paste the full error into Antigravity and it will rewrite the query with the correct UNNEST syntax.
Cloud Function deployment fails: If you see ENOSPC errors, Cloud Build's cache may be bloated. Cancel in-progress builds with gcloud builds cancel and clear cached images before redeploying.
Looker Studio shows a data source authentication error: Ensure Looker Studio's service account (service-PROJECT_ID@gcp-sa-datastudio.iam.gserviceaccount.com) has the BigQuery Data Viewer role on your dataset.
AdMob API rate limit errors: The AdMob Reporting API allows up to 200 requests per day. Reduce Cloud Scheduler trigger frequency or cache previously fetched data. Ask Antigravity to implement an exponential-backoff retry wrapper.
Summary
In this guide, we walked through building a complete Firebase × BigQuery × Looker Studio revenue analytics pipeline using Antigravity AI IDE. Here's a quick recap of everything we covered:
Enabling Firebase BigQuery Export to start automatically accumulating event data
Writing BigQuery queries to extract IAP revenue and building a Cloud Function to ingest AdMob data
Creating a unified_revenue view that merges all three revenue streams into one place
Building a Looker Studio dashboard with scorecards, time-series charts, and country breakdowns
Implementing an automated weekly report delivery pipeline with Antigravity agents
Once this pipeline is live, revenue data accumulates automatically every day. You'll have a single dashboard showing your complete financial picture in real time—making it far easier to spot trends, allocate development effort to high-ROI features, and grow your app business with confidence.
Share
Thank You for Reading
Antigravity Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.