Skip to main content
RapidDev - Software Development Agency
flutterflow-integrationsFlutterFlow API Call

Google Ads

Connect FlutterFlow to Google Ads by building a backend proxy — Firebase Cloud Function or Supabase Edge Function — that manages the triple-credential OAuth chain (Bearer token, developer token, customer ID) and executes GAQL queries against the Google Ads API. FlutterFlow calls your proxy with a GAQL string and date range; the proxy returns campaign data you parse into a dashboard. No credentials ever touch the Flutter client.

What you'll learn

  • Why Google Ads needs three separate credentials and why none of them can live in FlutterFlow
  • How to obtain an OAuth client, refresh token, and developer token for the Google Ads API
  • How to build a Cloud Function proxy that refreshes OAuth tokens automatically and runs GAQL queries
  • How to write GAQL queries to retrieve campaign and keyword performance metrics
  • How to parse the nested results array into FlutterFlow Data Types and bind them to dashboard widgets
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Advanced17 min read3 to 4 hoursMarketingLast updated July 2026RapidDev Engineering Team
TL;DR

Connect FlutterFlow to Google Ads by building a backend proxy — Firebase Cloud Function or Supabase Edge Function — that manages the triple-credential OAuth chain (Bearer token, developer token, customer ID) and executes GAQL queries against the Google Ads API. FlutterFlow calls your proxy with a GAQL string and date range; the proxy returns campaign data you parse into a dashboard. No credentials ever touch the Flutter client.

Quick facts about this guide
FactValue
ToolGoogle Ads
CategoryMarketing
MethodFlutterFlow API Call
DifficultyAdvanced
Time required3 to 4 hours
Last updatedJuly 2026

Build a Google Ads Reporting Dashboard in FlutterFlow

The Google Ads API is the most powerful — and most complex — advertising API available. It gives programmatic access to Search campaigns, Shopping campaigns, Display, YouTube, and Performance Max, with a SQL-like query language called GAQL (Google Ads Query Language) for reporting. For a FlutterFlow founder this means building an in-app dashboard showing keyword performance, campaign spend, Quality Scores, and conversion data without opening the Google Ads interface. The API itself is free; you pay only for actual ad spend billed by Google. Basic access is capped at 15,000 requests per day and 1,000 per minute per developer token.

The central challenge with Google Ads is its triple-credential auth model. Most APIs need one thing — an API key or a Bearer token. Google Ads needs three things at once: (1) An OAuth 2.0 Bearer token issued for a Google account that has access to the Ads customer account. This token expires every 60 minutes and must be refreshed using a refresh token stored server-side. (2) A developer token issued by Google's Ads API Center, which represents your application's identity and determines your API access tier. Developer tokens start in test-only mode and must be approved for Basic or Standard access before they work on real production accounts. (3) An optional but often necessary login-customer-id header that identifies which Manager (MCC) account you are acting through when querying customer accounts linked beneath it.

None of these three credentials can touch FlutterFlow. The compiled Flutter binary would expose all of them. The entire authentication flow must live in a Firebase Cloud Function or Supabase Edge Function. FlutterFlow sends only the customer_id, a GAQL query string, and a date range to your proxy. The proxy handles OAuth refresh, developer token injection, and the GAQL POST to the Google Ads API. Results come back as a nested JSON results array that you parse in FlutterFlow and bind to chart and list widgets.

Integration method

FlutterFlow API Call

The Google Ads API requires three credentials simultaneously: an OAuth 2.0 Bearer token (which expires and must be refreshed), a developer token issued from the Google Ads API Center, and optionally a login-customer-id header for Manager (MCC) account access. None of these can safely sit in FlutterFlow because the compiled Flutter app ships them to every device. The entire auth chain lives in a Firebase Cloud Function or Supabase Edge Function. FlutterFlow sends a GAQL query string and customer ID to your proxy; the proxy refreshes the OAuth token as needed, injects the developer token, and forwards the GAQL query to https://googleads.googleapis.com. Campaign data flows back through the proxy and gets parsed by FlutterFlow's JSON Path engine.

Prerequisites

  • A Google Ads account with active campaigns (test-only developer tokens only work against test accounts — you need Basic or Standard API access for production data)
  • A Google Cloud project with the Google Ads API enabled at console.cloud.google.com
  • An OAuth 2.0 client ID and client secret from Google Cloud Console (Web application type)
  • A developer token from the Google Ads API Center (ads.google.com/home/tools/manager-accounts) — apply for Basic or Standard access before production use
  • A Firebase project with Cloud Functions on Blaze plan or a Supabase project with Edge Functions enabled

Step-by-step guide

1

Obtain your three Google Ads API credentials

You need three separate things before writing any code. First, go to console.cloud.google.com, create or select a project, enable the Google Ads API, then go to APIs & Services → Credentials → Create Credentials → OAuth client ID. Choose Web application type and add https://developers.google.com/oauthplayground as an authorized redirect URI. Download the client credentials JSON — it contains your client_id and client_secret. Second, use the OAuth Playground at https://developers.google.com/oauthplayground to generate a refresh token: in the top-right gear icon, paste your client_id and client_secret and check 'Use your own OAuth credentials'. In Step 1, find the Google Ads API scope and select https://www.googleapis.com/auth/adwords. Authorize, exchange the code, and copy the refresh_token — this is the credential you will use to generate short-lived Bearer tokens inside your proxy. Third, obtain a developer token: log into the Google Ads API Center at https://developers.google.com/google-ads/api/docs/get-started/dev-token with a Google Ads Manager account, apply for a developer token, and wait for Basic access approval. Developer tokens start in test-only mode and cannot access real customer data until approved. All three values — client_id, client_secret, refresh_token, and developer_token — go into your backend environment variables only. The customer_id (your Google Ads account ID without hyphens) is not a secret and will be sent as a variable from FlutterFlow.

Pro tip: If you manage accounts under a Manager (MCC) account, also note the login_customer_id — the MCC account ID without hyphens. You will send it as a header in the proxy when querying linked client accounts.

Expected result: You have four values ready for environment variables: client_id, client_secret, refresh_token, and developer_token. The developer token has Basic or Standard access approved.

2

Deploy a Firebase Cloud Function proxy that refreshes OAuth tokens and runs GAQL queries

Create a Firebase Cloud Function that handles the entire Google Ads auth chain and executes GAQL queries on behalf of FlutterFlow. The function must do two things in sequence on every request: first, use the refresh token to obtain a fresh OAuth Bearer token by calling Google's token endpoint; then, use that Bearer token plus the developer token to POST a GAQL query to the Google Ads API. The Google Ads API endpoint format is https://googleads.googleapis.com/v18/customers/{customer_id}/googleAds:searchStream (or :search for paginated results). GAQL queries look like SQL: SELECT campaign.name, metrics.impressions, metrics.clicks, metrics.cost_micros, metrics.conversions FROM campaign WHERE segments.date DURING LAST_30_DAYS ORDER BY metrics.impressions DESC. The proxy must also include CORS headers for FlutterFlow web builds. If using MCC access, add the login-customer-id header alongside the developer token. The function must handle Google's 429 responses which include a Retry-After header — log the retry value and return a 429 upstream so the app can display an appropriate message. Deploy from your local machine using the Firebase CLI and copy the HTTPS trigger URL for use in FlutterFlow.

index.js
1// functions/index.js (Firebase Cloud Functions)
2const functions = require('firebase-functions');
3const fetch = require('node-fetch');
4
5const { CLIENT_ID, CLIENT_SECRET, REFRESH_TOKEN, DEVELOPER_TOKEN } = process.env;
6
7async function getAccessToken() {
8 const res = await fetch('https://oauth2.googleapis.com/token', {
9 method: 'POST',
10 headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
11 body: new URLSearchParams({
12 client_id: CLIENT_ID,
13 client_secret: CLIENT_SECRET,
14 refresh_token: REFRESH_TOKEN,
15 grant_type: 'refresh_token'
16 })
17 });
18 const { access_token } = await res.json();
19 return access_token;
20}
21
22exports.googleAdsProxy = functions.https.onRequest(async (req, res) => {
23 res.set('Access-Control-Allow-Origin', '*');
24 res.set('Access-Control-Allow-Methods', 'POST, OPTIONS');
25 res.set('Access-Control-Allow-Headers', 'Content-Type');
26 if (req.method === 'OPTIONS') { res.status(204).send(''); return; }
27
28 const { customer_id, gaql_query, login_customer_id } = req.body;
29 if (!customer_id || !gaql_query) {
30 res.status(400).json({ error: 'customer_id and gaql_query required' });
31 return;
32 }
33
34 const accessToken = await getAccessToken();
35 const headers = {
36 'Authorization': `Bearer ${accessToken}`,
37 'developer-token': DEVELOPER_TOKEN,
38 'Content-Type': 'application/json'
39 };
40 if (login_customer_id) headers['login-customer-id'] = login_customer_id;
41
42 const gaRes = await fetch(
43 `https://googleads.googleapis.com/v18/customers/${customer_id}/googleAds:search`,
44 { method: 'POST', headers, body: JSON.stringify({ query: gaql_query }) }
45 );
46 const data = await gaRes.json();
47 if (gaRes.status === 429) {
48 const retryAfter = gaRes.headers.get('Retry-After');
49 res.set('Retry-After', retryAfter || '60');
50 }
51 res.status(gaRes.status).json(data);
52});
53

Pro tip: Cache the OAuth access token in a module-level variable inside the Cloud Function and only refresh it when it is close to expiry (tokens last 60 minutes) — this avoids an extra network round-trip to Google's token endpoint on every FlutterFlow request.

Expected result: The Cloud Function is deployed, returns Google Ads campaign results when given a valid customer_id and GAQL query, and handles 429 rate limit responses with a Retry-After header.

3

Create a FlutterFlow API Group pointing at your proxy

In FlutterFlow, click API Calls in the left navigation panel, then click + Add → Create API Group. Name it GoogleAdsProxy and paste your Firebase Cloud Function HTTPS trigger URL as the Base URL. No headers are needed at the group level — all credentials are handled server-side. Create an API Call inside the group named getCampaignMetrics. Set the HTTP method to POST and leave the endpoint path blank. In the Body tab, select JSON and add three variables: customer_id (String), gaql_query (String), and login_customer_id (String, optional — pass an empty string if not needed). Your JSON body template: { "customer_id": "{{customer_id}}", "gaql_query": "{{gaql_query}}", "login_customer_id": "{{login_customer_id}}" }. In Response & Test, fill in a real customer_id (digits only, no hyphens like 1234567890) and a GAQL query string. Click Test. A successful response has a results array with objects containing campaign.name, metrics.impressions, metrics.clicks, and metrics.cost_micros (spend in micros — divide by 1,000,000 for dollars). Click Generate from Response to build JSON Path expressions automatically. The nested path structure in the Google Ads API response is: $.results[*].campaign.name, $.results[*].metrics.impressions, $.results[*].metrics.cost_micros, and so on.

api_call_body.json
1{
2 "customer_id": "{{customer_id}}",
3 "gaql_query": "{{gaql_query}}",
4 "login_customer_id": "{{login_customer_id}}"
5}

Pro tip: Store your GAQL query strings as App Values constants in FlutterFlow (App Settings → App Values) so you can update the queries without rebuilding the app — just keep in mind App Values are not secrets and compile into the binary.

Expected result: The getCampaignMetrics test call returns a results array with campaign performance data and auto-generated JSON Paths visible in the FlutterFlow test panel.

4

Write GAQL queries and map results to a Data Type

GAQL (Google Ads Query Language) looks like SQL but targets Google Ads resources. Every GAQL query has a FROM clause that names the primary resource (campaign, ad_group, keyword_view, search_term_view) and a SELECT clause listing the fields you want. Metrics like impressions, clicks, and conversions always live under metrics.*. Resource attributes live under the resource name — campaign.name, ad_group.name, keyword.text. Segments like date, day_of_week, and device live under segments.*. A useful starting query for campaign-level performance: SELECT campaign.name, campaign.status, metrics.impressions, metrics.clicks, metrics.cost_micros, metrics.conversions, metrics.average_cpc FROM campaign WHERE campaign.status = 'ENABLED' AND segments.date DURING LAST_30_DAYS ORDER BY metrics.cost_micros DESC. In FlutterFlow, create a Data Type called CampaignResult with fields: campaignName (String), status (String), impressions (Integer), clicks (Integer), costMicros (Integer), conversions (Double), averageCpc (Integer). Map the JSON Paths from your test response to these fields. Note that cost_micros is an integer representing millionths of currency units — divide by 1000000 in FlutterFlow using a computed property or format it in the proxy before returning. Build a dashboard screen with a ListView bound to the getCampaignMetrics Backend Query, passing the customer_id from the user's app profile. Inside each list item, show campaignName, formatted spend (costMicros / 1000000), clicks, and conversions in a Card widget.

gaql_queries.sql
1-- Example GAQL queries for common use cases
2
3-- Campaign performance last 30 days
4SELECT campaign.name, campaign.status,
5 metrics.impressions, metrics.clicks,
6 metrics.cost_micros, metrics.conversions,
7 metrics.average_cpc
8FROM campaign
9WHERE campaign.status = 'ENABLED'
10 AND segments.date DURING LAST_30_DAYS
11ORDER BY metrics.cost_micros DESC
12
13-- Keyword performance with Quality Score
14SELECT
15 ad_group_criterion.keyword.text,
16 ad_group_criterion.quality_info.quality_score,
17 metrics.impressions, metrics.clicks,
18 metrics.average_cpc
19FROM keyword_view
20WHERE campaign.status = 'ENABLED'
21 AND ad_group.status = 'ENABLED'
22 AND ad_group_criterion.status = 'ENABLED'
23 AND segments.date DURING LAST_30_DAYS
24ORDER BY metrics.clicks DESC
25LIMIT 50
26

Pro tip: Use the Google Ads API Query Builder at https://developers.google.com/google-ads/api/docs/query/overview to visually construct and validate GAQL queries before hardcoding them in your app.

Expected result: Your Data Type fields are mapped to the Google Ads API response paths and the dashboard ListView shows real campaign data with correctly formatted spend values.

5

Add caching and handle the 429 Retry-After rate limit response

The Google Ads API allows 15,000 requests per day and 1,000 per minute per developer token. These limits apply across all customer accounts your token can access — not per account. If you query many customer accounts in parallel or on every screen open, you can exhaust the per-minute limit quickly. The proxy should serialize requests (not fire parallel GAQL calls simultaneously) and cache results in Firestore for at least 15 minutes so concurrent app sessions share a single upstream call. When the API returns a 429 response, it includes a Retry-After header telling you how many seconds to wait. Your proxy should pass this value through to FlutterFlow in a Retry-After response header, and FlutterFlow's action flow should check the API Call's response status and show a 'Data temporarily unavailable — please try again in X seconds' message rather than displaying empty results. For multi-account MCC dashboards, serialize queries per account and add a short delay between each to stay well under the per-minute ceiling. Add the Firestore caching pattern inside your Cloud Function: check for a cached document keyed by customer_id + query hash before calling Google, and write the fresh response back to Firestore after every upstream call. If you need help building a multi-account caching proxy or the MCC query serialization logic, RapidDev's team builds FlutterFlow integrations like this every week — free scoping call at rapidevelopers.com/contact.

index.js
1// Caching and rate-limit handling inside googleAdsProxy function
2const crypto = require('crypto');
3const admin = require('firebase-admin');
4if (!admin.apps.length) admin.initializeApp();
5const db = admin.firestore();
6
7const queryHash = crypto
8 .createHash('md5')
9 .update(customer_id + gaql_query)
10 .digest('hex');
11const cacheRef = db.collection('gads_cache').doc(queryHash);
12const cached = await cacheRef.get();
13
14if (cached.exists && !req.body.force_refresh) {
15 const { payload, cached_at } = cached.data();
16 if (Date.now() - cached_at < 15 * 60 * 1000) {
17 res.json({ ...payload, from_cache: true });
18 return;
19 }
20}
21
22// ... GAQL fetch to Google Ads API ...
23// After receiving data:
24await cacheRef.set({ payload: data, cached_at: Date.now() });
25res.json({ ...data, from_cache: false });
26

Pro tip: The Google Ads developer token access tier matters: a test-only token silently returns empty results for real accounts instead of an error. If your proxy returns data in the OAuth Playground but not in production, your developer token has not yet received Basic access approval.

Expected result: Cached responses serve concurrent sessions without hitting the Google Ads API, and 429 responses propagate a Retry-After value to the FlutterFlow app for user-friendly error messaging.

Common use cases

Search campaign keyword performance dashboard for an agency

A PPC agency builds a FlutterFlow app where clients can see their Google Search campaign keyword-level data — impressions, clicks, average CPC, Quality Score, and conversions — updated daily. The proxy holds all three Google credentials and filters by the client's customer ID stored in the app's user profile.

FlutterFlow Prompt

Build a screen showing a sortable list of keywords with clicks, average CPC, and Quality Score for the last 30 days, fetched from a Google Ads proxy using GAQL.

Copy this prompt to try it in FlutterFlow

E-commerce Shopping campaign monitoring app

An online retailer builds a FlutterFlow dashboard showing Google Shopping campaign performance by product group — spend, ROAS, conversion value — alongside inventory levels from their own backend, giving buyers a combined view without opening Ads Manager.

FlutterFlow Prompt

Show a list of Shopping campaigns with spend, conversions, and conversion value for the current month, with a ROAS metric card at the top, loaded from a Google Ads proxy.

Copy this prompt to try it in FlutterFlow

Multi-account MCC dashboard for a Google Ads manager

A Google Ads consultant manages 20 client accounts under one MCC. They build a FlutterFlow app that lists all client accounts with their monthly spend and impressions, using the login-customer-id header in the proxy to query each sub-account through the MCC.

FlutterFlow Prompt

Create a list of all Google Ads customer accounts under our MCC with their monthly spend, sorted highest to lowest, loaded from a proxy that uses the login-customer-id header.

Copy this prompt to try it in FlutterFlow

Troubleshooting

The proxy returns an empty results array even though campaigns exist and have spend

Cause: The developer token is still in test-only access mode. Test-only tokens can only query test accounts, not real production Google Ads accounts. They silently return empty results rather than throwing an error.

Solution: Go to https://ads.google.com/home/tools/manager-accounts with your Manager account and apply for Basic access in the API Center. Google typically approves Basic access within a few business days. Until approved, use a test account to verify the rest of the integration. You can check your current access level in the Google Ads API Console under 'API Center'.

Google Ads API returns 401 Unauthorized with 'Request had invalid authentication credentials'

Cause: The OAuth access token in the proxy has expired (tokens last 60 minutes) and the token refresh step failed, or the refresh token itself has been invalidated by a password change or permission revocation.

Solution: Add error checking around the getAccessToken() call in your proxy: if the token endpoint returns an error, log the error response body and return a 500 with a descriptive message rather than proceeding with an invalid token. If the refresh token has been invalidated, re-run the OAuth Playground flow to generate a new one and update your environment variable. Also confirm the Google Cloud project has the Google Ads API enabled.

Manager account queries return data for the wrong customer or 'Customer not found'

Cause: The login-customer-id header is missing or set to the wrong MCC account ID when querying a linked sub-account. Without this header, the API uses the customer_id as both the queried account and the access account, which fails if the OAuth credentials belong to the MCC and not the sub-account directly.

Solution: In your proxy, set the login-customer-id header to your MCC account ID (digits only, no hyphens) when the request body includes a login_customer_id value. The customer_id in the GAQL :search endpoint URL should be the sub-account you are querying. Test this combination in the Google Ads REST API explorer at https://developers.google.com/google-ads/api/rest/auth before deploying.

GAQL query returns a 400 error with 'UNRECOGNIZED_FIELD' or 'INVALID_QUERY'

Cause: The GAQL query references a field that does not exist on the queried resource, or uses incompatible field combinations. Some metrics are only available with specific segmentation and some segment-metric combinations are incompatible.

Solution: Use the Google Ads API Query Builder at https://developers.google.com/google-ads/api/docs/query/overview to validate the query. Common mistakes: requesting metrics.impressions FROM keyword_view (use ad_group_criterion joined query instead), or combining date segments with metrics that do not support daily breakdown. The error message body from the Google Ads API usually includes the specific field name that caused the issue.

Best practices

  • Never put any of the three Google Ads credentials — OAuth client secret, refresh token, or developer token — in a FlutterFlow API Call header or body. All three must live exclusively in backend environment variables.
  • Cache every GAQL query result for at least 15 minutes in Firestore. The 1,000-requests-per-minute developer token limit is shared across all customer accounts, and uncached parallel queries from concurrent users exhaust it quickly.
  • Apply for Basic or Standard developer token access as early as possible — test-only tokens silently return empty results for real accounts, making the integration look broken when it is actually a tier issue.
  • Cache the OAuth access token in your Cloud Function's module scope and only refresh it when it is within 5 minutes of expiry to avoid an extra network call to Google's token endpoint on every request.
  • Use the Google Ads API Query Builder to validate all GAQL queries before hardcoding them in your app — invalid field combinations return 400 errors that are hard to diagnose without knowing which field is incompatible.
  • Serialize multi-account MCC queries rather than firing them in parallel — parallel queries consume your per-minute rate limit much faster than sequential ones.
  • Divide cost_micros by 1,000,000 either in the proxy (before returning the response) or using FlutterFlow's number formatting in the widget binding — raw micros displayed in the UI confuse users.
  • Include the Google Ads API version in your endpoint URL (e.g., /v18/) and update it when Google releases new API versions, as older versions are periodically deprecated.

Alternatives

Frequently asked questions

Why does Google Ads need three credentials when most APIs only need one?

Google Ads separates concerns across three layers: the OAuth token proves which Google account is acting (user identity), the developer token identifies your application and determines your API quota tier, and the login-customer-id specifies which Manager account you are acting through when querying linked client accounts. All three are required for production access, which is why the integration must live entirely in a backend proxy.

My developer token was approved for test access only — can I still build the integration?

Yes, but you can only query test accounts until you receive Basic or Standard access approval. Use a Google Ads test account to build and validate the entire proxy, FlutterFlow API Group, Data Type, and dashboard layout. When Basic access is approved, switch the customer_id to your production account — no other changes are needed in your code or FlutterFlow configuration.

What is GAQL and how does it differ from SQL?

GAQL (Google Ads Query Language) is Google's SQL-like query language for the Ads API. It uses SELECT and FROM like SQL, but the FROM clause names a Google Ads resource (campaign, ad_group, keyword_view) rather than a database table. Fields use dot notation to reference nested properties (metrics.impressions, campaign.name). There are no JOINs — resource relationships are traversed via pre-defined implicit joins in the API. WHERE clauses support DURING DATE_RANGE keywords like LAST_30_DAYS, LAST_MONTH, and THIS_YEAR.

How do I display spend in dollars when the API returns cost_micros?

The Google Ads API returns monetary values in micros — millionths of the account currency — to avoid floating point precision issues. Divide cost_micros by 1,000,000 to get the dollar amount (e.g., 1,500,000 micros = $1.50). You can do this division in the Cloud Function proxy before returning the data, or use FlutterFlow's custom function to compute the formatted value in the widget binding.

Can I use this integration to create or pause Google Ads campaigns from FlutterFlow?

Yes — the Google Ads API supports mutate operations (creating, updating, and removing campaigns, ad groups, and ads) through the googleAds:mutate endpoint. You would add a separate POST endpoint to your proxy that accepts the mutate operations and forwards them with the same three-credential auth chain. Campaign creation in particular requires building the full campaign → ad group → ad → creative hierarchy through API calls, which is a significant addition beyond reporting.

RapidDev

Talk to an Expert

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

Book a free consultation

Integrations are where projects stall

We wire FlutterFlow integrations like this one every week — auth, webhooks, edge cases included. Fixed-price builds from $13K, delivered in 6–10 weeks.

Talk to an integration engineer

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.