Skip to main content
RapidDev - Software Development Agency
lovable-integrationsEdge Function Integration

How to Integrate Lovable with Google Search Console

To integrate Google Search Console with Lovable, create a Supabase Edge Function that authenticates with the Google Search Analytics API using a service account JSON key stored in Cloud Secrets. The Edge Function fetches clicks, impressions, CTR, and average position data by query, page, device, or country dimension. Your Lovable frontend calls this Edge Function to power custom search performance dashboards.

What you'll learn

  • How to create a Google Cloud service account and grant it Search Console property access
  • How to store the service account JSON key securely in Lovable Cloud Secrets
  • How to write a Deno Edge Function that uses Google OAuth2 JWT authentication
  • How to query the Search Analytics API for clicks, impressions, CTR, and position by dimension
  • How to build a search performance dashboard with date range filtering in Lovable
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate13 min read60 minutesMarketingMarch 2026RapidDev Engineering Team
TL;DR

To integrate Google Search Console with Lovable, create a Supabase Edge Function that authenticates with the Google Search Analytics API using a service account JSON key stored in Cloud Secrets. The Edge Function fetches clicks, impressions, CTR, and average position data by query, page, device, or country dimension. Your Lovable frontend calls this Edge Function to power custom search performance dashboards.

Build Custom Search Performance Dashboards with Google Search Console and Lovable

Google Search Console is the authoritative source for understanding how your site appears in Google organic search results. Unlike third-party SEO tools that estimate traffic, GSC provides exact data directly from Google: the precise number of clicks, impressions, average position, and CTR for every query and page on your site. Most teams only access this data through Google's own interface or manual data studio reports. Building a custom Lovable dashboard lets you surface exactly the metrics your team cares about, combine them with other data sources, and create automated reporting that runs on your schedule.

The Search Analytics API is part of the Google Search Console API and is accessed via OAuth2 authentication. For server-side applications like Lovable Edge Functions, the recommended approach is a service account — a non-human Google account that your application authenticates as using a cryptographic JSON key. You grant this service account read-only access to your Search Console property, store the JSON key securely in Cloud Secrets, and the Edge Function uses it to generate short-lived access tokens at runtime.

The Search Analytics API supports flexible querying with dimensions (query, page, device, country, date), date ranges, filters, and row limits up to 25,000 per request. This makes it possible to build highly specific reports: top pages by clicks for mobile users in a specific country, trending queries in the last 7 days compared to the previous 7 days, or pages that have lost significant impressions over a month. These use cases are difficult to build in the GSC interface but straightforward to implement with the API and a Lovable dashboard.

Integration method

Edge Function Integration

Google Search Console's Search Analytics API requires OAuth2 authentication. The recommended server-side approach for Lovable is a service account: you create a Google Cloud service account, grant it access to your Search Console property, and store the service account JSON key in Cloud Secrets. A Supabase Edge Function uses this key to mint access tokens and fetch search performance data on behalf of your app.

Prerequisites

  • A Lovable project with Lovable Cloud enabled
  • A Google account with a verified Search Console property (your site must be verified in Google Search Console)
  • Access to Google Cloud Console (console.cloud.google.com) to create a service account
  • The Search Console API enabled in your Google Cloud project
  • Familiarity with Google Cloud's IAM console — no coding experience required, just UI navigation

Step-by-step guide

1

Create a Google Cloud service account and enable the API

Go to console.cloud.google.com and select or create a project. In the left sidebar, navigate to APIs & Services → Library and search for 'Google Search Console API'. Click Enable. Next, go to APIs & Services → Credentials and click Create Credentials → Service Account. Give it a name like 'Lovable GSC Reader' and click Create and Continue. For the role, select Basic → Viewer (the service account only needs read access). Click Done. You should now see your service account listed. Click on it, navigate to the Keys tab, and click Add Key → Create New Key. Choose JSON format and click Create. A JSON file will download to your computer — this is your service account key. Keep this file safe; you will paste its contents into Lovable Secrets. The JSON contains a private_key field which is a long RSA key used to sign JWT authentication requests.

Pro tip: When you download the JSON key file, save it securely immediately. Google only allows you to download it once — if you lose it, you must create a new key.

Expected result: You have downloaded a service account JSON key file containing client_email, private_key, and other fields.

2

Grant the service account access to your Search Console property

The service account exists in Google Cloud, but it does not yet have access to your Search Console property data. You need to add it as a user in Search Console. Go to search.google.com/search-console, select your property, and click Settings in the left sidebar. Click Users and permissions. Click Add User. In the email field, paste the service account email address (it looks like service-account-name@your-project.iam.gserviceaccount.com — you can find it in the JSON key file under the client_email field). Set the permission to Full (or Restricted for read-only access — read-only is sufficient and more secure). Click Add. The service account can now read your Search Console data. Note: the Search Console API only works with verified properties, so make sure your site is properly verified before proceeding.

Pro tip: Grant the service account Restricted (read-only) permission in Search Console — it does not need Full access to fetch analytics data, and least-privilege is always the safer choice.

Expected result: The service account email appears in your Search Console property's Users and permissions list with Restricted access.

3

Store the service account JSON key in Cloud Secrets

In your Lovable project, open the Cloud tab by clicking the plus (+) icon next to the preview window. Navigate to the Secrets section and click Add Secret. For the name, enter GOOGLE_SERVICE_ACCOUNT_JSON. For the value, open the JSON key file you downloaded, select all its contents, and paste it into the value field. The JSON is a single-line or multi-line object — paste it exactly as downloaded. Click Save. Lovable encrypts this value and makes it available only in Edge Functions via Deno.env.get('GOOGLE_SERVICE_ACCOUNT_JSON'). Never paste this JSON into Lovable's chat interface, as it contains a private cryptographic key. If you are concerned about security, Lovable holds SOC 2 Type II and ISO 27001:2022 certifications, and its security infrastructure actively blocks hardcoded secrets from appearing in application code.

Pro tip: The service account JSON is a multi-line string when stored in an environment variable. Parse it with JSON.parse(Deno.env.get('GOOGLE_SERVICE_ACCOUNT_JSON')!) in your Edge Function.

Expected result: GOOGLE_SERVICE_ACCOUNT_JSON appears in your Cloud Secrets panel with its value masked.

4

Create the Google Search Console proxy Edge Function

Ask Lovable to create the Edge Function that authenticates with Google's OAuth2 server using a JWT signed with your service account private key, obtains an access token, and then calls the Search Analytics API. The function accepts POST requests with a JSON body containing siteUrl (your verified property URL, e.g. 'https://example.com/'), startDate, endDate, dimensions (an array like ['query', 'page']), and an optional rowLimit. The tricky part of Google service account authentication on Deno is signing a JWT with the RS256 algorithm using the private key from your JSON. Deno's Web Crypto API supports this natively. The function extracts the private key from the stored JSON, signs a JWT, exchanges it for an access token, and then uses that token to call the Google Search Analytics API endpoint. The code below shows the complete implementation.

Lovable Prompt

Create a Supabase Edge Function at supabase/functions/gsc-proxy/index.ts that accepts POST requests with body fields: siteUrl, startDate, endDate, dimensions (array), and rowLimit. It should authenticate with Google's OAuth2 API using a service account JWT from the GOOGLE_SERVICE_ACCOUNT_JSON secret, then call the Search Analytics API at https://searchconsole.googleapis.com/webmasters/v3/sites/{siteUrl}/searchAnalytics/query and return the results with CORS headers.

Paste this in Lovable chat

supabase/functions/gsc-proxy/index.ts
1import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'
2
3const corsHeaders = {
4 'Access-Control-Allow-Origin': '*',
5 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
6}
7
8async function getGoogleAccessToken(serviceAccountJson: any): Promise<string> {
9 const now = Math.floor(Date.now() / 1000)
10 const payload = {
11 iss: serviceAccountJson.client_email,
12 scope: 'https://www.googleapis.com/auth/webmasters.readonly',
13 aud: 'https://oauth2.googleapis.com/token',
14 exp: now + 3600,
15 iat: now,
16 }
17
18 const header = { alg: 'RS256', typ: 'JWT' }
19 const encodeBase64Url = (obj: object) =>
20 btoa(JSON.stringify(obj)).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '')
21
22 const signingInput = `${encodeBase64Url(header)}.${encodeBase64Url(payload)}`
23
24 // Import the private key for RS256 signing
25 const privateKeyPem = serviceAccountJson.private_key
26 const pemBody = privateKeyPem.replace(/-----[^-]+-----/g, '').replace(/\n/g, '')
27 const keyBuffer = Uint8Array.from(atob(pemBody), c => c.charCodeAt(0))
28
29 const key = await crypto.subtle.importKey(
30 'pkcs8', keyBuffer.buffer,
31 { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' },
32 false, ['sign']
33 )
34
35 const signature = await crypto.subtle.sign(
36 'RSASSA-PKCS1-v1_5', key,
37 new TextEncoder().encode(signingInput)
38 )
39
40 const jwt = `${signingInput}.${btoa(String.fromCharCode(...new Uint8Array(signature)))
41 .replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '')}`
42
43 const tokenResponse = await fetch('https://oauth2.googleapis.com/token', {
44 method: 'POST',
45 headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
46 body: `grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=${jwt}`,
47 })
48
49 const tokenData = await tokenResponse.json()
50 if (!tokenData.access_token) throw new Error(`Token error: ${JSON.stringify(tokenData)}`)
51 return tokenData.access_token
52}
53
54serve(async (req) => {
55 if (req.method === 'OPTIONS') return new Response('ok', { headers: corsHeaders })
56
57 try {
58 const serviceAccountJson = JSON.parse(Deno.env.get('GOOGLE_SERVICE_ACCOUNT_JSON')!)
59 const { siteUrl, startDate, endDate, dimensions = ['query'], rowLimit = 1000 } = await req.json()
60
61 const accessToken = await getGoogleAccessToken(serviceAccountJson)
62
63 const apiUrl = `https://searchconsole.googleapis.com/webmasters/v3/sites/${encodeURIComponent(siteUrl)}/searchAnalytics/query`
64 const response = await fetch(apiUrl, {
65 method: 'POST',
66 headers: {
67 'Authorization': `Bearer ${accessToken}`,
68 'Content-Type': 'application/json',
69 },
70 body: JSON.stringify({ startDate, endDate, dimensions, rowLimit }),
71 })
72
73 const data = await response.json()
74 return new Response(JSON.stringify(data), {
75 headers: { ...corsHeaders, 'Content-Type': 'application/json' },
76 })
77 } catch (error) {
78 return new Response(
79 JSON.stringify({ error: error.message }),
80 { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
81 )
82 }
83})

Pro tip: Google access tokens expire after 1 hour. For dashboards with frequent refreshes, cache the access token in a Supabase table keyed by service account email with an expiry timestamp, and only re-mint it when expired.

Expected result: The gsc-proxy Edge Function is deployed and can be tested by sending a POST request with siteUrl, startDate, endDate, and dimensions. It returns Search Analytics data from Google.

5

Build the search analytics dashboard frontend

With the Edge Function deployed, use Lovable's chat to build the dashboard UI. The core interaction is: user selects a date range and dimension (queries, pages, devices, or countries), the frontend calls the gsc-proxy Edge Function via a POST request with the appropriate parameters, and the results are displayed in a sortable table with metric cards for totals. Ask Lovable to create a dashboard page with a date range picker (using shadcn/ui's DatePicker or a simple two-input date range), dimension tabs (Queries, Pages, Devices, Countries), a summary bar with total clicks, impressions, average CTR, and average position, and a data table with sortable columns. The table rows come from the Search Analytics API response's rows array, where each row has keys (the dimension values) and clicks, impressions, ctr, and position fields. The siteUrl parameter should come from a configuration field or environment variable so the dashboard works for any verified property.

Lovable Prompt

Create a Google Search Console dashboard page with date range pickers (defaulting to last 28 days), dimension tabs for Queries/Pages/Devices/Countries, and a data table. When the user submits, POST to our /functions/v1/gsc-proxy with siteUrl, startDate, endDate, and the selected dimension array. Show metric cards for total clicks, impressions, average CTR, and average position above the table. Table columns: dimension value, clicks, impressions, CTR (formatted as %), position (1 decimal place).

Paste this in Lovable chat

Pro tip: The Search Analytics API returns position as a float (e.g., 4.7). Display it with one decimal place. CTR is returned as a decimal (0.034 = 3.4%) — multiply by 100 and format as a percentage for readability.

Expected result: The dashboard displays Search Console data with date range filtering, dimension switching, and a sortable results table showing clicks, impressions, CTR, and position for the selected property.

Common use cases

Weekly SEO performance report dashboard

Build a dashboard that shows week-over-week changes in clicks, impressions, and average position for your top 50 pages. Highlight pages that have gained or lost significantly, enabling quick identification of content that needs attention.

Lovable Prompt

Create an SEO performance dashboard that fetches data from our Google Search Console Edge Function. Show a table of top pages by clicks for the last 7 days compared to the previous 7 days. Highlight rows where clicks changed by more than 20% in either direction. Include metric cards for total clicks, impressions, average CTR, and average position.

Copy this prompt to try it in Lovable

Keyword cannibalization detector

Query the Search Analytics API for pages grouped by query to find cases where multiple pages on your site rank for the same keyword. Display a table showing queries where more than one URL is appearing in Google search, helping your content team identify and resolve cannibalization issues.

Lovable Prompt

Build a keyword cannibalization report page that calls our /functions/v1/gsc-proxy Edge Function with dimensions=['query', 'page'] and shows a table grouped by query where more than one URL has impressions. Sort by total impressions descending so high-priority cannibalization issues appear first.

Copy this prompt to try it in Lovable

CTR opportunity finder

Find queries where your site ranks in positions 4–20 but has a CTR well below the expected rate for those positions. These are pages that rank but don't get clicked, meaning a title tag or meta description improvement could yield quick traffic wins.

Lovable Prompt

Create a CTR opportunity page that calls our GSC Edge Function and fetches the last 28 days of data by query with position between 4 and 20. Show a table sorted by impressions descending with columns for query, position, CTR, impressions, and an estimated traffic gain if CTR reached the position average.

Copy this prompt to try it in Lovable

Troubleshooting

Edge Function returns 'Token error: {"error": "unauthorized_client"}' when trying to get a Google access token

Cause: The service account has not been granted access to the Search Console property, or the service account email in the JSON key does not match the one added in Search Console settings.

Solution: Go to search.google.com/search-console → Settings → Users and permissions and verify that the exact email from the client_email field of your service account JSON is listed. If not, add it with Restricted permission. Wait 2–3 minutes for permissions to propagate before testing again.

Search Analytics API returns 'Access Not Configured' or 403 Forbidden

Cause: The Google Search Console API is not enabled in the Google Cloud project associated with the service account.

Solution: Go to console.cloud.google.com → APIs & Services → Library. Search for 'Google Search Console API' and click Enable. Also enable the 'Web Search Analytics' API if listed separately. It can take a few minutes after enabling for API calls to succeed.

The Edge Function deploys successfully but the dashboard shows no data rows, only empty arrays

Cause: The siteUrl parameter must exactly match how your property is registered in Search Console, including protocol and trailing slash. Domain properties and URL-prefix properties have different formats.

Solution: For URL-prefix properties, use the exact URL including trailing slash, e.g. 'https://www.example.com/'. For domain properties (sc-domain: format), use 'sc-domain:example.com'. You can verify the exact property URL format in your Search Console property selector — it shows the canonical URL you verified.

typescript
1// Encode siteUrl correctly for the API endpoint
2const apiUrl = `https://searchconsole.googleapis.com/webmasters/v3/sites/${encodeURIComponent(siteUrl)}/searchAnalytics/query`

JSON.parse error when the Edge Function tries to read GOOGLE_SERVICE_ACCOUNT_JSON from secrets

Cause: The JSON was pasted into Lovable Secrets with extra whitespace, line breaks got corrupted, or the JSON is malformed.

Solution: Open the service account JSON file in a text editor, copy all contents, and paste directly into the secret value field without any modifications. The JSON can be multi-line — Lovable Secrets handles multi-line values. If the issue persists, try minifying the JSON by removing all whitespace before storing it.

typescript
1// Safe parsing with error handling
2try {
3 const serviceAccountJson = JSON.parse(Deno.env.get('GOOGLE_SERVICE_ACCOUNT_JSON')!)
4} catch (e) {
5 return new Response(JSON.stringify({ error: 'Invalid GOOGLE_SERVICE_ACCOUNT_JSON format' }), { status: 500 })
6}

Best practices

  • Use a dedicated service account with Restricted (read-only) permission in Search Console — never use a personal Google account's OAuth token for server-side access
  • Store the entire service account JSON as a single secret in Cloud Secrets — never split it across multiple secrets or commit it to git
  • Always URL-encode the siteUrl when constructing the Search Analytics API endpoint URL to handle slashes and colons correctly
  • Cache Search Analytics API responses in Supabase for at least 1 hour — GSC data is only updated once or twice daily anyway, so frequent API calls waste quota
  • Use the rowLimit parameter conservatively (1,000–5,000 rows) and implement pagination with startRow if you need to fetch more than 25,000 rows
  • Add date range validation in your Edge Function to prevent queries for future dates or ranges longer than 16 months (the maximum GSC data retention period)
  • Separate your production and staging Lovable projects with different service accounts so that staging testing does not count against your production API quota
  • For complex SEO reports that compare multiple properties, RapidDev's team can help design a multi-property architecture with efficient shared caching

Alternatives

Frequently asked questions

Do I need a paid Google account or plan to use the Search Console API?

No. The Google Search Console API is free to use. You need a free Google account, a verified property in Search Console, and a Google Cloud project (also free within the free tier limits). There are no per-query charges for the Search Analytics API.

Why use a service account instead of OAuth2 for end users?

A service account authenticates as a fixed identity (your application) without requiring a user to sign in with their personal Google account each time. This is the right approach for server-side dashboards where the data belongs to your organization. OAuth2 user-based authentication would make more sense if you wanted each visitor to see their own GSC data from properties they own.

How fresh is the data returned by the Search Analytics API?

Google Search Console data typically has a 2–3 day delay. If today is March 30, the most recent data available is usually March 27–28. The API will return an empty result or error if you request dates within the last 2 days. Always default your dashboards to end dates at least 3 days in the past.

Can I query data for multiple GSC properties in one Lovable app?

Yes. Add the service account email to each Search Console property you want to access. The Edge Function can accept the siteUrl as a parameter, so your frontend can include a property selector and pass the appropriate URL to the Edge Function. All properties accessed by the same service account require only one GOOGLE_SERVICE_ACCOUNT_JSON secret.

What is the maximum date range I can query with the Search Analytics API?

The Search Analytics API returns data for up to 16 months. Queries for date ranges longer than 16 months will return an error. For trend analysis over longer periods, you should store API results in Supabase on a regular schedule and query your database for historical comparisons.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.