Google Data Studio was renamed to Looker Studio in October 2022. Embed Looker Studio reports in Bolt apps using iframe embeds — no API key required, works in the WebContainer preview. For custom analytics dashboards, use the Google Analytics Data API as the programmatic alternative. Configure report sharing for public or link-based access, then use the embed URL in a React iframe component.
Embed Looker Studio Reports or Build Custom Analytics in Bolt.new
Google Data Studio was rebranded as Looker Studio in October 2022, but the product and its embed capabilities remain the same. Looker Studio is a free business intelligence tool from Google that connects to dozens of data sources — Google Analytics, Google Ads, BigQuery, Google Sheets, and third-party services — and displays them as interactive charts, tables, and scorecards in shareable reports.
For Bolt developers, there are two integration approaches with different complexity trade-offs. The first is iframe embedding: every Looker Studio report has an embed URL that can be placed inside an HTML iframe. When sharing settings allow it, the report renders inside the iframe with full interactivity — date range pickers, filter dropdowns, and cross-chart filtering all function within the embed. This approach requires no API, no credentials, and no backend code. It works in Bolt's WebContainer preview and in production. The main limitation is styling: you cannot customize the report's visual design from your application — the report looks like Looker Studio, not your brand.
The second approach is building custom dashboards using the Google Analytics Data API (GA4). Instead of embedding a Looker Studio report, you fetch raw metrics from Google Analytics and render them with your own charts, matching your application's design system exactly. This gives complete visual control and lets you combine GA data with other data sources in ways Looker Studio's connectors cannot. The trade-off is authentication complexity: GA4 API requires a Google Cloud service account or OAuth, server-side API routes, and more development work.
Integration method
Looker Studio integration uses two approaches. The simpler path embeds existing Looker Studio reports via iframe using the report's embed URL — this requires no API, no credentials, and works directly in Bolt's WebContainer preview. The more powerful path uses the Google Analytics Data API (GA4) to fetch raw analytics data and build custom visualizations in React using Recharts or similar. The GA4 API requires a service account or OAuth and API routes for server-side access.
Prerequisites
- A Bolt.new account with a Next.js project
- For embedding: a Looker Studio report with sharing enabled (View access for anyone with the link)
- For GA4 API: a Google Analytics 4 property with existing data
- For GA4 API: a Google Cloud project with Google Analytics Data API enabled
- For GA4 API: a Google Cloud service account with Viewer role in your GA4 property
Step-by-step guide
Embed a Looker Studio Report via Iframe
Embed a Looker Studio Report via Iframe
The simplest Looker Studio integration requires no API calls, no credentials, and no backend code. Every Looker Studio report can be embedded via iframe if sharing is configured correctly. To get the embed URL, open your Looker Studio report and click the Share button in the top-right corner. In the sharing dialog, click 'Schedule email delivery' — no, that is not right. Instead, click the three-dot menu (⋮) in the top right and select 'Embed report.' This opens the embed code dialog showing the full iframe HTML code. The embed URL follows the format: `https://lookerstudio.google.com/embed/reporting/{reportId}/page/{pageId}`. For the embed to work for anyone (not just Google account holders with explicit access), the report must be shared with view access. In the Share dialog, change access to 'Anyone with the link can view.' Without this setting, viewers who are not explicitly added to the report will see an access error inside the iframe. The iframe dimensions in Looker Studio's generated embed code default to 600x450 pixels — adjust these to fit your layout. The report is responsive within those dimensions. Using a percentage-based width (100%) with a fixed height or an aspect-ratio CSS property gives better responsiveness. URL parameters let you pre-filter the report or set date ranges. Append `params={%22df80%22:%22include%25EE%2580%2580IN%25EE%2580%8010%22}` style parameters for built-in filters, or configure your Looker Studio report to accept URL parameters using the 'URL Parameters' data source feature. For date range control, the `df_date_range` parameter sets the report's date range. Security note: embedded Looker Studio reports are only as secure as your sharing settings. If the report contains sensitive business data, use explicit user access rather than link-based sharing, and consider whether embedding is appropriate for your use case.
Create a LookerStudioEmbed React component that accepts props: reportUrl (string), title (string, optional), height (number, default 600). The component should render a responsive iframe with the reportUrl, a loading skeleton while the iframe loads, and an onLoad handler that hides the skeleton. Add a fullscreen toggle button that opens the report URL in a new tab. The iframe should have allowFullScreen and sandbox attributes. Show an error state if the iframe fails to load with a link to open the report directly. No API key needed.
Paste this in Bolt.new chat
1// components/LookerStudioEmbed.tsx2'use client';3import { useState } from 'react';45interface LookerStudioEmbedProps {6 reportUrl: string;7 title?: string;8 height?: number;9 className?: string;10}1112export function LookerStudioEmbed({13 reportUrl,14 title = 'Analytics Report',15 height = 600,16 className = '',17}: LookerStudioEmbedProps) {18 const [loading, setLoading] = useState(true);19 const [error, setError] = useState(false);2021 // Ensure the URL uses the embed format22 const embedUrl = reportUrl.includes('/embed/')23 ? reportUrl24 : reportUrl.replace('/reporting/', '/embed/reporting/');2526 const handleLoad = () => setLoading(false);27 const handleError = () => {28 setLoading(false);29 setError(true);30 };3132 return (33 <div className={`relative w-full rounded-lg overflow-hidden border border-gray-200 ${className}`}>34 {/* Header */}35 <div className="flex items-center justify-between px-4 py-3 bg-white border-b border-gray-200">36 <h3 className="text-sm font-medium text-gray-700">{title}</h3>37 <a38 href={reportUrl}39 target="_blank"40 rel="noreferrer"41 className="text-xs text-blue-600 hover:text-blue-800 flex items-center gap-1"42 >43 Open in Looker Studio ↗44 </a>45 </div>4647 {/* Loading skeleton */}48 {loading && !error && (49 <div50 className="absolute inset-0 top-10 bg-gray-50 flex items-center justify-center"51 style={{ height }}52 >53 <div className="flex flex-col items-center gap-3">54 <div className="w-8 h-8 border-2 border-blue-600 border-t-transparent rounded-full animate-spin" />55 <span className="text-sm text-gray-500">Loading report...</span>56 </div>57 </div>58 )}5960 {/* Error state */}61 {error && (62 <div63 className="flex flex-col items-center justify-center bg-gray-50 gap-3"64 style={{ height }}65 >66 <p className="text-sm text-gray-600">Report could not load.</p>67 <p className="text-xs text-gray-500">Ensure the report is shared as "Anyone with the link can view"</p>68 <a69 href={reportUrl}70 target="_blank"71 rel="noreferrer"72 className="text-sm text-blue-600 hover:underline"73 >74 Open report directly75 </a>76 </div>77 )}7879 {/* Iframe */}80 {!error && (81 <iframe82 src={embedUrl}83 style={{ height, display: loading ? 'none' : 'block' }}84 className="w-full"85 title={title}86 allowFullScreen87 onLoad={handleLoad}88 onError={handleError}89 sandbox="allow-same-origin allow-scripts allow-popups allow-forms"90 />91 )}92 </div>93 );94}Pro tip: The Looker Studio embed URL uses the /embed/ path, not the /reporting/ path from the browser URL bar. If you use the wrong URL format, the embed shows an error. The component above automatically converts /reporting/ to /embed/reporting/ if needed.
Expected result: The LookerStudioEmbed component renders your Looker Studio report inside an iframe. The loading skeleton shows while the report loads and disappears when the content renders. The 'Open in Looker Studio' link opens the full report in a new tab.
Set Up Google Analytics Data API Authentication
Set Up Google Analytics Data API Authentication
For building custom dashboards that bypass Looker Studio entirely and fetch raw GA4 data, you need the Google Analytics Data API (v1). Authentication uses a Google Cloud service account — a server identity that can be granted viewer access to your GA4 property without requiring user login. Step 1 — Enable the API: Go to console.cloud.google.com. Create a new project or select an existing one. Search for 'Google Analytics Data API' in the API library and click 'Enable.' This must be done before any API calls will succeed. Step 2 — Create a service account: In the Google Cloud Console, go to IAM & Admin → Service Accounts. Click 'Create Service Account.' Give it a name (e.g., 'bolt-analytics-reader'), click 'Create and Continue,' and skip the optional role grant at the Google Cloud level (you will grant GA access separately). Click 'Done.' Step 3 — Download the key: Click on your new service account in the list. Go to the 'Keys' tab. Click 'Add Key' → 'Create new key' → 'JSON.' This downloads a JSON file containing the service account credentials. This file contains a private key — keep it secure and never commit it to version control. Step 4 — Grant GA4 access: Go to analytics.google.com. Open Admin → Property → Property Access Management. Click the '+' button to add a user. Enter the service account email (it ends in @your-project.iam.gserviceaccount.com). Select 'Viewer' role. Click 'Add.' The service account can now read your GA4 data. Step 5 — Add to .env: The service account key JSON is a multi-line file. Convert it to a single-line string by stringifying it: `JSON.stringify(keyFileContent)`. Store the entire stringified JSON as `GOOGLE_SERVICE_ACCOUNT_KEY` in your .env file. Your API route parses it back to JSON at runtime.
Add GOOGLE_SERVICE_ACCOUNT_KEY and GA4_PROPERTY_ID to .env. Create a lib/ga4.ts file that exports a getGA4Client() function. It should parse GOOGLE_SERVICE_ACCOUNT_KEY from process.env as JSON, create a JWT-signed bearer token using the service account's private key and client_email for the scope https://www.googleapis.com/auth/analytics.readonly, and return a fetch helper that includes the bearer token. Cache the JWT token for 55 minutes. Export a ga4Query function that accepts a GA4 runReport request body and returns the report response.
Paste this in Bolt.new chat
1// lib/ga4.ts2import crypto from 'crypto';34const GA4_API_BASE = 'https://analyticsdata.googleapis.com/v1beta';56let tokenCache: { token: string; expiresAt: number } | null = null;78async function getServiceAccountToken(): Promise<string> {9 if (tokenCache && Date.now() < tokenCache.expiresAt - 60_000) {10 return tokenCache.token;11 }1213 const keyRaw = process.env.GOOGLE_SERVICE_ACCOUNT_KEY;14 if (!keyRaw) throw new Error('GOOGLE_SERVICE_ACCOUNT_KEY is not configured');1516 const serviceAccount = JSON.parse(keyRaw) as {17 client_email: string;18 private_key: string;19 token_uri: string;20 };2122 const now = Math.floor(Date.now() / 1000);23 const header = Buffer.from(JSON.stringify({ alg: 'RS256', typ: 'JWT' })).toString('base64url');24 const payload = Buffer.from(JSON.stringify({25 iss: serviceAccount.client_email,26 scope: 'https://www.googleapis.com/auth/analytics.readonly',27 aud: serviceAccount.token_uri || 'https://oauth2.googleapis.com/token',28 exp: now + 3600,29 iat: now,30 })).toString('base64url');3132 const signingInput = `${header}.${payload}`;33 const sign = crypto.createSign('RSA-SHA256');34 sign.update(signingInput);35 const signature = sign.sign(36 serviceAccount.private_key.replace(/\\n/g, '\n'),37 'base64url'38 );39 const jwt = `${signingInput}.${signature}`;4041 const tokenResponse = await fetch('https://oauth2.googleapis.com/token', {42 method: 'POST',43 headers: { 'Content-Type': 'application/x-www-form-urlencoded' },44 body: new URLSearchParams({45 grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',46 assertion: jwt,47 }),48 });4950 const tokens = await tokenResponse.json() as { access_token?: string; error?: string };51 if (!tokens.access_token) {52 throw new Error(`GA4 auth failed: ${tokens.error || 'unknown error'}`);53 }5455 tokenCache = { token: tokens.access_token, expiresAt: Date.now() + 3600 * 1000 };56 return tokens.access_token;57}5859export async function ga4Query<T = unknown>(body: Record<string, unknown>): Promise<T> {60 const accessToken = await getServiceAccountToken();61 const propertyId = process.env.GA4_PROPERTY_ID;62 if (!propertyId) throw new Error('GA4_PROPERTY_ID is not configured');6364 const response = await fetch(65 `${GA4_API_BASE}/properties/${propertyId}:runReport`,66 {67 method: 'POST',68 headers: {69 Authorization: `Bearer ${accessToken}`,70 'Content-Type': 'application/json',71 },72 body: JSON.stringify(body),73 }74 );7576 if (!response.ok) {77 const error = await response.json().catch(() => ({})) as { error?: { message: string } };78 throw new Error(error.error?.message || `GA4 API error ${response.status}`);79 }8081 return response.json() as Promise<T>;82}Pro tip: Store the service account JSON as a single-line string in .env by running: node -e "process.stdout.write(JSON.stringify(require('./service-account.json')))" and copying the output. The JSON.stringify removes all newlines that would break the .env file format. Never commit the original JSON key file.
Expected result: The GA4 authentication helper successfully obtains access tokens. A test call to ga4Query with a simple metrics request (activeUsers) returns data from your GA4 property without authentication errors.
Build the GA4 Analytics Dashboard
Build the GA4 Analytics Dashboard
With authentication working, build the analytics API routes and dashboard components. The GA4 Data API uses a `runReport` request that specifies dimensions (what to group by), metrics (what to measure), and a date range. The response returns rows with dimension values and metric values matched by index to the header rows. Key GA4 metrics for a standard analytics dashboard: `sessions`, `screenPageViews`, `totalUsers`, `newUsers`, `bounceRate`, `averageSessionDuration`, `engagementRate`, `conversions`. Dimensions for breakdown: `date` (YYYY-MM-DD), `pagePath`, `deviceCategory`, `country`, `city`, `sessionSource`, `sessionMedium`. For a 30-day session trend, use dimension `date` and metric `sessions` with `dateRange: {startDate: '30daysAgo', endDate: 'today'}`. GA4 returns one row per day with the date string and session count. Sort by date ascending for a time-series chart. For top pages, use dimension `pagePath` and metric `screenPageViews` sorted by screenPageViews descending with limit 10. This returns the most visited pages in your property. GA4's response format is verbose — dimensions and metrics are returned as separate arrays, with rows referencing them by index. A helper function that transforms the GA4 response format into flat key-value objects significantly simplifies working with the data in React components. GA4 API calls are outbound HTTPS requests and work in Bolt's WebContainer during development. You can build the full analytics dashboard and see real data from your GA4 property without deploying.
Create Next.js API routes for GA4 analytics. Build /api/analytics/overview that fetches: total sessions, pageviews, users, bounce rate for 'last30days'; daily session breakdown (date + sessions) for a line chart; and top 10 pages by pageviews. Use the ga4Query helper from lib/ga4.ts. Parse GA4's dimension/metric response format into flat objects. Build a React AnalyticsDashboard page with: 4 stat cards (sessions, pageviews, users, bounce rate), a Recharts LineChart of daily sessions, and a table of top pages with pageview count and percentage of total.
Paste this in Bolt.new chat
1// app/api/analytics/overview/route.ts2import { NextResponse } from 'next/server';3import { ga4Query } from '@/lib/ga4';45interface GA4Row {6 dimensionValues: Array<{ value: string }>;7 metricValues: Array<{ value: string }>;8}910interface GA4ReportResponse {11 rows?: GA4Row[];12 rowCount?: number;13 dimensionHeaders?: Array<{ name: string }>;14 metricHeaders?: Array<{ name: string; type: string }>;15}1617function parseGA4Rows(18 report: GA4ReportResponse19): Array<Record<string, string>> {20 if (!report.rows) return [];21 const dimHeaders = (report.dimensionHeaders || []).map((h) => h.name);22 const metHeaders = (report.metricHeaders || []).map((h) => h.name);2324 return report.rows.map((row) => {25 const obj: Record<string, string> = {};26 row.dimensionValues.forEach((d, i) => { obj[dimHeaders[i]] = d.value; });27 row.metricValues.forEach((m, i) => { obj[metHeaders[i]] = m.value; });28 return obj;29 });30}3132export async function GET() {33 try {34 const [summaryReport, trendsReport, pagesReport] = await Promise.all([35 // Summary metrics36 ga4Query<GA4ReportResponse>({37 dateRanges: [{ startDate: '30daysAgo', endDate: 'today' }],38 metrics: [39 { name: 'sessions' },40 { name: 'screenPageViews' },41 { name: 'totalUsers' },42 { name: 'bounceRate' },43 { name: 'averageSessionDuration' },44 ],45 }),46 // Daily trend47 ga4Query<GA4ReportResponse>({48 dateRanges: [{ startDate: '30daysAgo', endDate: 'today' }],49 dimensions: [{ name: 'date' }],50 metrics: [{ name: 'sessions' }, { name: 'totalUsers' }],51 orderBys: [{ dimension: { dimensionName: 'date' } }],52 }),53 // Top pages54 ga4Query<GA4ReportResponse>({55 dateRanges: [{ startDate: '30daysAgo', endDate: 'today' }],56 dimensions: [{ name: 'pagePath' }],57 metrics: [{ name: 'screenPageViews' }, { name: 'sessions' }],58 orderBys: [{ metric: { metricName: 'screenPageViews' }, desc: true }],59 limit: 10,60 }),61 ]);6263 const summaryRows = parseGA4Rows(summaryReport);64 const summary = summaryRows[0] || {};6566 const trends = parseGA4Rows(trendsReport).map((row) => ({67 date: row.date,68 sessions: parseInt(row.sessions || '0', 10),69 users: parseInt(row.totalUsers || '0', 10),70 }));7172 const pages = parseGA4Rows(pagesReport).map((row) => ({73 path: row.pagePath,74 pageviews: parseInt(row.screenPageViews || '0', 10),75 sessions: parseInt(row.sessions || '0', 10),76 }));7778 return NextResponse.json({79 summary: {80 sessions: parseInt(summary.sessions || '0', 10),81 pageviews: parseInt(summary.screenPageViews || '0', 10),82 users: parseInt(summary.totalUsers || '0', 10),83 bounceRate: parseFloat(summary.bounceRate || '0'),84 avgDuration: parseFloat(summary.averageSessionDuration || '0'),85 },86 trends,87 pages,88 });89 } catch (err) {90 const message = err instanceof Error ? err.message : 'Analytics fetch failed';91 return NextResponse.json({ error: message }, { status: 500 });92 }93}Pro tip: GA4 data processing can take up to 24-48 hours for some metrics (especially 'yesterday' data). For real-time analytics, use the GA4 Realtime API (runRealtimeReport) which shows data from the last 30 minutes. The realtime API uses different metrics like activeUsers and screenPageViewsPerUser.
Expected result: The analytics overview route returns real GA4 data including session counts, pageviews, and top pages. The React dashboard displays a line chart of daily sessions and a table of top pages. All data is fetched from your real Google Analytics property.
Common use cases
Embedded Analytics Dashboard for Clients
Build a client portal where customers can view their analytics reports without accessing Google Data Studio directly. Embed the Looker Studio report in a React component with your brand's layout surrounding it. Use URL parameters to pre-filter the report by client-specific dimensions. This gives clients a polished, branded analytics experience while you maintain the reports in Looker Studio's familiar interface.
Build an embedded analytics page for my Bolt app. Create a LookerStudioEmbed React component that accepts a reportUrl prop, an optional dateRange prop ('last7days', 'last30days', 'last90days'), and a height prop (default 600px). The component should append the dateRange as a URL parameter to the report URL and render it in a responsive iframe with loading state. Add a toolbar above the iframe with the date range selector buttons. Handle the iframe load event to hide the loading spinner. Show the iframe in a card container with a 'Powered by Looker Studio' footer.
Copy this prompt to try it in Bolt.new
Custom Google Analytics Dashboard with GA4 API
Build a branded analytics dashboard that fetches sessions, pageviews, users, bounce rate, and top pages directly from the GA4 API. Render the data with your own Recharts charts instead of Looker Studio's default styling. Combine GA data with your own app metrics for a unified reporting view that Looker Studio's connectors cannot provide.
Build a custom Google Analytics dashboard using GA4 Data API. Create a Next.js API route at /api/analytics/overview that authenticates with a Google service account (GOOGLE_SERVICE_ACCOUNT_KEY from process.env) and calls the GA4 Data API to fetch: sessions, pageviews, users, bounce rate, and top 10 pages for a given date range. Return formatted metrics and a pages array sorted by pageviews. Build a React dashboard with a metrics summary row (4 stat cards), a line chart of daily sessions over the date range using Recharts, and a sortable table of top pages.
Copy this prompt to try it in Bolt.new
GA4 Event Tracking Dashboard
Pull custom event data from GA4 to build a conversion funnel visualization or event frequency dashboard. Track specific user actions (button clicks, form submissions, video plays) and display them as a funnel chart or time-series graph. Useful for product teams monitoring feature adoption and conversion rates.
Create a GA4 event tracking dashboard. Build /api/analytics/events that fetches event data from GA4 Data API for a specific event name (passed as query param). Use GOOGLE_SERVICE_ACCOUNT_KEY for auth. Return event count by day, total count, unique users who triggered it, and top associated pages. Build a React EventDashboard component that shows total event count, a line chart of daily events over the last 30 days, a breakdown by device category (mobile/desktop/tablet), and a table of top pages where the event occurred.
Copy this prompt to try it in Bolt.new
Troubleshooting
Looker Studio report shows 'You need access' inside the iframe
Cause: The report is not shared with public access. When an unauthenticated user views the embed, Looker Studio checks sharing permissions and shows an access error if they are not authorized.
Solution: Open the Looker Studio report, click Share → Change to 'Anyone with the link' → Viewer. Save the sharing settings. The embed will now load for anyone who has the URL, without requiring Google account sign-in.
GA4 API returns 403 Forbidden — The caller does not have permission
Cause: The service account has not been granted viewer access to the GA4 property. Creating the service account in Google Cloud does not automatically grant it access to Google Analytics.
Solution: Go to analytics.google.com → Admin → Property → Property Access Management. Click '+' to add a user. Enter the service account email (format: name@project.iam.gserviceaccount.com). Select 'Viewer' role. Click 'Add.' Wait 2-3 minutes for permissions to propagate.
GA4 API JWT authentication fails with 'invalid_grant' or 'invalid JWT'
Cause: The service account key JSON was not properly formatted when stored in the .env file. Multi-line private keys in the JSON get corrupted when stored as environment variables without proper escaping.
Solution: Convert the service account JSON to a single-line string: node -e "process.stdout.write(JSON.stringify(require('./key.json')))" and use the output as GOOGLE_SERVICE_ACCOUNT_KEY. The JSON.stringify escapes all newlines in the private key to \n literal characters.
1// In your API route, restore the newlines after parsing:2const serviceAccount = JSON.parse(process.env.GOOGLE_SERVICE_ACCOUNT_KEY || '{}');3// The private_key field will have \n literals from JSON.stringify4// Restore them for the crypto module:5const privateKey = serviceAccount.private_key.replace(/\\n/g, '\n');GA4 runReport returns empty rows array even though the property has data
Cause: The GA4_PROPERTY_ID is incorrect, using the old Universal Analytics property ID instead of the GA4 property ID, or the date range has no data.
Solution: Verify the GA4 property ID in Google Analytics Admin → Property → Property Details. The GA4 property ID is a numeric string (not the tracking ID starting with UA-). Also check the date range — data from today may not be fully processed yet; use 'yesterday' as the end date for reliable data.
1// GA4 property IDs are numeric: '123456789'2// NOT the old UA-XXXXXXXXX format3// Find it in GA Admin → Property Settings → Property ID4GA4_PROPERTY_ID=123456789Best practices
- For simple reporting use cases, Looker Studio iframe embedding is significantly faster to implement than building a custom GA4 API dashboard — use the embed approach unless you specifically need custom styling or data combination.
- Store GOOGLE_SERVICE_ACCOUNT_KEY as a server-side environment variable without NEXT_PUBLIC_ prefix — the service account key is a private key that grants access to your analytics data.
- Cache GA4 API responses for 5-15 minutes in your API routes — analytics data does not update in real time and repeated calls waste API quota while adding latency.
- Always convert the service account JSON to a single-line string with JSON.stringify before storing in .env — multi-line private keys get corrupted in standard .env file format.
- Grant service accounts only 'Viewer' access to GA4 properties — they do not need to create, edit, or delete data, and minimum-privilege access reduces the impact of credential exposure.
- Use GA4's date range shortcuts (today, yesterday, 7daysAgo, 30daysAgo) rather than hardcoded dates in your API routes so reports always show fresh data relative to the current date.
- When the Looker Studio embed shows an access error, check both the report's sharing settings and whether it is embedded from the correct /embed/ URL path rather than the /reporting/ browser URL.
- Note the naming difference for documentation and user communication: the product is 'Looker Studio' (since October 2022) even though many users still call it 'Google Data Studio.' Both names refer to the same product.
Alternatives
Looker (distinct from Looker Studio) is the enterprise BI platform with LookML modeling and API access, suited for large organizations with complex data infrastructure.
The Google Analytics API is the direct source of data that Looker Studio visualizes — use it for custom dashboards when Looker Studio's default visualizations do not meet design requirements.
Tableau offers more advanced visualization types and data blending capabilities than Looker Studio, though at enterprise pricing and with similar iframe embedding options.
Power BI is the Microsoft equivalent of Looker Studio with similar embed capabilities and deeper integration with Azure and Microsoft 365 data sources.
Frequently asked questions
Is Google Data Studio the same as Looker Studio?
Yes — Google renamed Google Data Studio to Looker Studio in October 2022. The product, its features, and its API are identical. The URL changed from datastudio.google.com to lookerstudio.google.com. Existing reports continued to work without any changes required. Some documentation and forum posts still use the old name.
Do I need an API key to embed a Looker Studio report?
No — Looker Studio iframe embedding requires only a sharing setting change, not an API key. Set the report to 'Anyone with the link can view,' get the embed URL from the report's embed dialog, and use it in an iframe. No credentials, no backend code, and no rate limits for basic embed use.
Can I embed a Looker Studio report in Bolt's WebContainer preview?
Yes — iframe embedding works in Bolt's WebContainer preview since it is a standard HTML feature supported by all browsers. The GA4 Data API calls also work in the preview since they are outbound HTTPS requests. The only limitation is if Looker Studio's CSP (Content Security Policy) headers prevent iframe embedding on certain origins — most reports embed fine.
What is the difference between using Looker Studio embed and the GA4 Data API?
Looker Studio embed shows an existing pre-built report inside an iframe — the report is managed in Looker Studio's interface, non-technical users can build and maintain it, and you have no control over its visual design. GA4 Data API fetches raw analytics data that you render with your own charts — full visual control, ability to combine with other data, but requires development work and authentication setup.
How do I find my GA4 property ID?
In Google Analytics, click Admin (the gear icon) in the bottom left. In the Property column, click 'Property Settings.' The Property ID is shown at the top of the settings page as a numeric string like 123456789 — this is different from the old UA-XXXXXXXXX tracking ID format. Copy the numeric property ID into your GA4_PROPERTY_ID environment variable.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation