Skip to main content
RapidDev - Software Development Agency
v0-integrationsNext.js API Route

How to Integrate Google Ads with V0

To integrate Google Ads with V0 by Vercel, generate a PPC dashboard UI with V0, create a Next.js API route that calls the Google Ads API using OAuth2 credentials, store tokens in Vercel environment variables, and deploy. Your app can display campaign performance, keyword data, and ad spend reports without exposing your credentials to the browser.

What you'll learn

  • How to set up Google Ads API access with a developer token and OAuth2 credentials
  • How to generate a PPC dashboard UI with V0 and connect it to the Google Ads API
  • How to create a Next.js API route that fetches campaign performance metrics
  • How to use GAQL (Google Ads Query Language) to query specific advertising data
  • How to display keyword performance, spend, and conversion data in charts and tables
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate15 min read45 minutesMarketingApril 2026RapidDev Engineering Team
TL;DR

To integrate Google Ads with V0 by Vercel, generate a PPC dashboard UI with V0, create a Next.js API route that calls the Google Ads API using OAuth2 credentials, store tokens in Vercel environment variables, and deploy. Your app can display campaign performance, keyword data, and ad spend reports without exposing your credentials to the browser.

Build Google Ads Performance Dashboards with V0 and Next.js

Google Ads manages billions in advertising spend, but the default reporting interface can be overwhelming — dozens of pre-built reports, complex segmentation options, and a UI designed for power users rather than quick decision-making. Many founders and marketing teams want a simplified dashboard showing exactly the metrics they care about: campaign spend vs. budget, top-performing keywords, conversion rates, and day-over-day trends. V0 can generate these dashboards quickly, and the Google Ads API provides the data to power them.

The Google Ads API is a mature but complex API that uses GAQL (Google Ads Query Language), a SQL-like query syntax for selecting metrics across your account. A typical GAQL query looks like: SELECT campaign.name, metrics.impressions, metrics.clicks, metrics.cost_micros FROM campaign WHERE segments.date DURING LAST_7_DAYS. The API returns results as structured JSON that your Next.js API route transforms into chart-ready data for your V0-generated frontend.

API access requires a Google Ads developer token (applied for through Google's developer program) and OAuth2 credentials from Google Cloud Console. The setup has more steps than simpler APIs, but once configured, you get access to comprehensive performance data across all your campaigns, ad groups, keywords, and ads. For agencies managing multiple client accounts, the API also supports manager account (MCC) access to query across all linked accounts.

Integration method

Next.js API Route

Google Ads integrates with V0-generated Next.js apps through server-side API routes that query the Google Ads API using OAuth2 credentials and a developer token. Your client ID, client secret, refresh token, and developer token are stored as server-only Vercel environment variables. The V0-generated dashboard components fetch campaign metrics, keyword performance, and spend data through your Next.js API routes, which proxy requests to the Google Ads API and return structured reporting data.

Prerequisites

  • A Google Ads account with at least one active campaign — the API requires an account that has been approved for advertising
  • A Google Ads developer token — apply at developers.google.com/google-ads/api/docs/get-started/dev-token (takes 1-3 business days for basic access approval)
  • A Google Cloud project with the Google Ads API enabled, and OAuth2 credentials (client ID and client secret) created at console.cloud.google.com
  • A refresh token generated by completing the OAuth2 authorization flow for your Google Ads account — use the Google Ads API OAuth2 playground or oauth2l tool
  • Your Google Ads customer ID (the 10-digit number shown at the top right of your Google Ads interface, formatted as XXX-XXX-XXXX)

Step-by-step guide

1

Generate the PPC Dashboard UI with V0

Open V0 at v0.dev and describe the advertising performance dashboard you want to build. Google Ads dashboards typically combine metric summary cards at the top (total spend, clicks, conversions, ROAS) with charts for trend visualization and a detailed data table for campaign or keyword breakdowns. Recharts (included in V0's default stack via shadcn/ui) handles the line charts and bar charts well. When prompting V0, be specific about the data shape your components expect. Specify that campaign data comes from /api/google-ads/campaigns and includes name, status, budget_micros (Google Ads represents all monetary values in micros — millionths of the currency unit — so divide by 1,000,000 for display), impressions, clicks, cost_micros, and conversions. Plan for the data transformation in your API routes so the frontend receives dollar values rather than micros. For date range selection, ask V0 to generate a date range picker that passes a range parameter to the API (like ?range=LAST_7_DAYS or ?range=LAST_30_DAYS). Google Ads API supports several predefined date ranges in GAQL via the DURING keyword, which is simpler than constructing custom date filters. After generating and reviewing in V0's preview, push to GitHub via V0's Git panel.

V0 Prompt

Create a PPC analytics dashboard with a header showing account name and date range selector (Last 7 days / Last 30 days / Last 90 days). Show 4 metric cards: Total Impressions, Total Clicks, Total Spend (formatted as $X,XXX.XX), and Total Conversions. Below, show a dual-axis line chart with clicks on the left axis and spend on the right axis over the selected date range. At the bottom, show a sortable campaigns table with columns: Campaign Name, Status (Active/Paused badge), Budget, Spend, Impressions, Clicks, CTR, Avg CPC, Conversions. All data from /api/google-ads/campaigns?range={selectedRange}.

Paste this in V0 chat

Pro tip: Remember that Google Ads API returns monetary values in micros (millionths of the currency unit). Always divide cost_micros by 1,000,000 in your API route before sending data to the frontend. Displaying $25,430,000 instead of $25.43 is a very common and embarrassing bug.

Expected result: A Google Ads analytics dashboard renders in V0's preview with date range controls, metric summary cards, a trend chart, and a campaigns table with proper column headers. Components call /api/google-ads/campaigns with a range query parameter.

2

Set Up Google Ads API Access

Before writing any API route code, you need three credentials: a developer token, OAuth2 credentials, and a refresh token. This setup takes more effort than most API integrations but is a one-time process. First, the developer token: apply at developers.google.com/google-ads/api/docs/get-started/dev-token. During development, you'll receive a test developer token that can only access test accounts, not live account data. Apply for basic access to use real account data — approval typically takes 1-3 business days. Second, Google Cloud OAuth2: create a project at console.cloud.google.com, enable the Google Ads API under APIs & Services → Enable APIs, create OAuth2 credentials under APIs & Services → Credentials → Create Credentials → OAuth client ID (type: Web application). Note the client ID and client secret. Add https://developers.google.com/oauthplayground as an authorized redirect URI. Third, the refresh token: go to developers.google.com/oauthplayground, click the settings gear, check 'Use your own OAuth credentials', enter your client ID and secret. In the scope box, enter https://www.googleapis.com/auth/adwords. Click 'Authorize APIs', complete the Google sign-in for the Google Ads account you want to access, then click 'Exchange authorization code for tokens'. Copy the refresh_token value — this is what allows your server to get new access tokens without user interaction. The developer token goes in the developer-token header on API requests. The OAuth2 credentials are used to obtain short-lived access tokens from Google's token endpoint, which are then passed as Bearer tokens in API requests.

Pro tip: The Google Ads API uses a versioned endpoint (currently v18 or later). Always check the current stable version at developers.google.com/google-ads/api/docs/release-notes and use that version number in your API URLs. Outdated version numbers cause 404 errors that look like credential issues.

Expected result: You have four credential values: GOOGLE_ADS_DEVELOPER_TOKEN, GOOGLE_ADS_CLIENT_ID, GOOGLE_ADS_CLIENT_SECRET, and GOOGLE_ADS_REFRESH_TOKEN, plus GOOGLE_ADS_CUSTOMER_ID. These are the environment variables needed for the next step.

3

Create the Google Ads API Route

Create the Next.js API route that fetches campaign performance data. The route needs to do two things: first, exchange your refresh token for a short-lived access token using Google's OAuth2 token endpoint; second, use that access token plus your developer token to query the Google Ads API using GAQL. The token endpoint is https://oauth2.googleapis.com/token. POST to it with grant_type=refresh_token, client_id, client_secret, and refresh_token to receive an access_token valid for one hour. Cache this access token in memory or a short-lived store rather than fetching a new one on every request. The Google Ads API endpoint for search queries is https://googleads.googleapis.com/v18/customers/{customerId}/googleAds:search (replace v18 with the current version). POST to it with a JSON body containing a query field with your GAQL string. Include three headers: Authorization: Bearer {access_token}, developer-token: {your_developer_token}, and Content-Type: application/json. The GAQL query for campaign performance looks like: SELECT campaign.id, campaign.name, campaign.status, campaign.advertising_channel_type, campaign_budget.amount_micros, metrics.impressions, metrics.clicks, metrics.cost_micros, metrics.conversions FROM campaign WHERE campaign.status IN ('ENABLED', 'PAUSED') AND segments.date DURING LAST_7_DAYS ORDER BY metrics.cost_micros DESC. Transform the response to divide cost_micros values by 1,000,000 before returning to the frontend.

app/api/google-ads/campaigns/route.ts
1// app/api/google-ads/campaigns/route.ts
2import { NextRequest, NextResponse } from 'next/server';
3
4const GOOGLE_ADS_API_VERSION = 'v18';
5
6async function getAccessToken(): Promise<string> {
7 const response = await fetch('https://oauth2.googleapis.com/token', {
8 method: 'POST',
9 headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
10 body: new URLSearchParams({
11 grant_type: 'refresh_token',
12 client_id: process.env.GOOGLE_ADS_CLIENT_ID!,
13 client_secret: process.env.GOOGLE_ADS_CLIENT_SECRET!,
14 refresh_token: process.env.GOOGLE_ADS_REFRESH_TOKEN!,
15 }),
16 });
17
18 const data = await response.json();
19 if (!data.access_token) {
20 throw new Error('Failed to obtain access token from Google OAuth2');
21 }
22 return data.access_token;
23}
24
25const DATE_RANGES: Record<string, string> = {
26 LAST_7_DAYS: 'LAST_7_DAYS',
27 LAST_30_DAYS: 'LAST_30_DAYS',
28 LAST_90_DAYS: 'LAST_90_DAYS',
29 THIS_MONTH: 'THIS_MONTH',
30};
31
32export async function GET(request: NextRequest) {
33 const { searchParams } = new URL(request.url);
34 const rangeParam = searchParams.get('range') ?? 'LAST_7_DAYS';
35 const dateRange = DATE_RANGES[rangeParam] ?? 'LAST_7_DAYS';
36
37 const customerId = process.env.GOOGLE_ADS_CUSTOMER_ID?.replace(/-/g, '');
38 const developerToken = process.env.GOOGLE_ADS_DEVELOPER_TOKEN;
39
40 if (!customerId || !developerToken) {
41 return NextResponse.json(
42 { error: 'Google Ads credentials not configured' },
43 { status: 500 }
44 );
45 }
46
47 try {
48 const accessToken = await getAccessToken();
49
50 const query = `
51 SELECT
52 campaign.id,
53 campaign.name,
54 campaign.status,
55 campaign_budget.amount_micros,
56 metrics.impressions,
57 metrics.clicks,
58 metrics.cost_micros,
59 metrics.conversions,
60 metrics.average_cpc
61 FROM campaign
62 WHERE campaign.status IN ('ENABLED', 'PAUSED')
63 AND segments.date DURING ${dateRange}
64 ORDER BY metrics.cost_micros DESC
65 `;
66
67 const response = await fetch(
68 `https://googleads.googleapis.com/${GOOGLE_ADS_API_VERSION}/customers/${customerId}/googleAds:search`,
69 {
70 method: 'POST',
71 headers: {
72 Authorization: `Bearer ${accessToken}`,
73 'developer-token': developerToken,
74 'Content-Type': 'application/json',
75 },
76 body: JSON.stringify({ query }),
77 }
78 );
79
80 if (!response.ok) {
81 const error = await response.json();
82 return NextResponse.json(
83 { error: error.error?.message ?? 'Google Ads API error' },
84 { status: response.status }
85 );
86 }
87
88 const data = await response.json();
89 const rows = data.results ?? [];
90
91 const campaigns = rows.map((row: {
92 campaign: { id: string; name: string; status: string };
93 campaignBudget?: { amountMicros: string };
94 metrics: {
95 impressions: string;
96 clicks: string;
97 costMicros: string;
98 conversions: string;
99 averageCpc: string;
100 };
101 }) => ({
102 id: row.campaign.id,
103 name: row.campaign.name,
104 status: row.campaign.status,
105 budgetDollars: row.campaignBudget?.amountMicros
106 ? Number(row.campaignBudget.amountMicros) / 1_000_000
107 : null,
108 impressions: Number(row.metrics.impressions),
109 clicks: Number(row.metrics.clicks),
110 spendDollars: Number(row.metrics.costMicros) / 1_000_000,
111 conversions: Number(row.metrics.conversions),
112 avgCpcDollars: Number(row.metrics.averageCpc) / 1_000_000,
113 ctr: row.metrics.clicks && row.metrics.impressions
114 ? Number(row.metrics.clicks) / Number(row.metrics.impressions)
115 : 0,
116 }));
117
118 return NextResponse.json({ campaigns, dateRange });
119 } catch (error) {
120 const msg = error instanceof Error ? error.message : 'Unknown error';
121 return NextResponse.json({ error: msg }, { status: 500 });
122 }
123}

Pro tip: Google Ads API returns numeric values as strings in the JSON response (e.g., impressions: '12345' not impressions: 12345). Always use Number() to convert before doing math. Also, the customer ID in the API URL must have dashes removed — 123-456-7890 becomes 1234567890.

Expected result: GET /api/google-ads/campaigns?range=LAST_7_DAYS returns an array of campaign objects with converted dollar values (not micros), click/impression counts, and status. The dashboard components receive clean, display-ready data.

4

Add Environment Variables and Deploy

Push your code to GitHub and add all five Google Ads credentials to Vercel. Open the Vercel Dashboard, select your project, go to Settings → Environment Variables. Add: GOOGLE_ADS_DEVELOPER_TOKEN (your developer token from the Google Ads API Center), GOOGLE_ADS_CLIENT_ID (from Google Cloud Console OAuth2 credentials), GOOGLE_ADS_CLIENT_SECRET (from the same OAuth2 credential), GOOGLE_ADS_REFRESH_TOKEN (obtained via the OAuth2 playground), and GOOGLE_ADS_CUSTOMER_ID (your 10-digit Google Ads account ID, with or without dashes — the route strips them). None of these should have the NEXT_PUBLIC_ prefix — all Google Ads API calls must happen server-side. Add all variables for Production, Preview, and Development environments. For local development, add them to .env.local and run npm run dev to test your dashboard with real data before deploying. After deployment, test the dashboard by opening your Vercel URL and verifying campaigns appear with real performance data. If you see 'test account' errors, your developer token is in test mode and can only access Google Ads test accounts — apply for basic access at developers.google.com to unlock real account data. The approval process is straightforward for standard account access.

Pro tip: Add a GOOGLE_ADS_LOGIN_CUSTOMER_ID environment variable if you're accessing a Google Ads account through a manager (MCC) account. Include this as a login-customer-id header in your API requests to specify which manager account the developer token is associated with.

Expected result: The deployed Vercel app displays real Google Ads campaign data including campaign names, spend, clicks, and conversions. The developer token and OAuth credentials are never exposed in browser network traffic.

Common use cases

Campaign Performance Dashboard

Build a daily performance dashboard showing key PPC metrics across all active campaigns: impressions, clicks, CTR, average CPC, total spend, and conversions. Replace the complex Google Ads interface with a focused view of the metrics that matter most to your business.

V0 Prompt

Create a Google Ads performance dashboard with a date range selector (Last 7 days, Last 30 days, This month). Show metric cards at the top for total impressions, clicks, spend (formatted as currency), and conversions. Below, show a line chart of daily clicks and spend over the selected period. At the bottom, show a table of campaigns with columns for name, status, budget, spend, impressions, clicks, CTR, and CPC. Data from /api/google-ads/campaigns. Use a clean analytics dashboard style.

Copy this prompt to try it in V0

Keyword Performance Analyzer

Analyze which keywords are driving the best return on ad spend. Show keyword-level data including Quality Score, average position, bid, and actual CPC alongside conversion data. Identify underperforming keywords by their high cost and low conversion rate.

V0 Prompt

Build a keyword performance table fetched from /api/google-ads/keywords with columns for keyword text, match type, campaign, quality score (shown as colored badge 1-10), impressions, clicks, average CPC, spend, conversions, and cost per conversion. Add column sorting and a search filter. Highlight keywords where cost per conversion exceeds $50 in red. Include a CSV export button. Use a data-dense table layout suitable for performance marketers.

Copy this prompt to try it in V0

Ad Spend vs. Budget Tracker

Monitor daily budget pacing across campaigns to catch overspend or underpacing before it affects performance. Show each campaign's daily budget, current day's spend, and pacing percentage, plus a monthly projection based on current daily spend rate.

V0 Prompt

Design a budget pacing dashboard with a card per active campaign showing the campaign name, daily budget, today's spend, pacing percentage as a progress bar (green if 80-120%, yellow if 60-80%, red if over 120%), and projected monthly spend. Data fetched from /api/google-ads/budget-pacing. Sort campaigns by those with the most budget deviation at the top. Add a total across all campaigns at the bottom.

Copy this prompt to try it in V0

Troubleshooting

API returns 'USER_PERMISSION_DENIED' or 'CUSTOMER_NOT_FOUND' error

Cause: The Google account that authorized the OAuth2 refresh token does not have access to the specified customer account, or the customer ID is incorrect.

Solution: Verify the GOOGLE_ADS_CUSTOMER_ID matches the account you authorized during the OAuth2 flow. The customer ID is visible in the top-right of Google Ads (format XXX-XXX-XXXX). In your API route, strip the dashes before including it in the URL. Confirm the authorizing Google account has at least Read-only access to that account.

typescript
1// Strip dashes from customer ID
2const customerId = process.env.GOOGLE_ADS_CUSTOMER_ID?.replace(/-/g, '');

API returns 401 Unauthorized even though credentials look correct

Cause: The access token obtained from the refresh token exchange has expired (access tokens last 1 hour), or the refresh token has been revoked by re-authorizing the OAuth2 app.

Solution: Access tokens are short-lived — your API route must request a fresh one on each request or implement token caching. Verify the refresh token is still valid by testing the token exchange manually with the OAuth2 playground. If revoked, repeat the OAuth2 authorization flow to generate a new refresh token.

GAQL query returns 'INVALID_FIELD' or 'UNDEFINED_FIELD' error

Cause: The GAQL field name is incorrect, or you're combining fields from incompatible resources (each GAQL query is scoped to one main resource).

Solution: Use the Google Ads Query Builder at developers.google.com/google-ads/api/fields to build and validate GAQL queries. Each query uses FROM {resource} — campaign, ad_group, keyword_view, etc. — and can only select fields from that resource and its related entities. Check the field compatibility matrix for the resource you're querying.

Monetary values display as huge numbers like 25430000 instead of $25.43

Cause: Google Ads API returns all currency values in micros (millionths of the currency unit). The raw value was passed directly to the frontend without dividing by 1,000,000.

Solution: Divide all _micros fields by 1,000,000 in your API route before returning data to the frontend. Never pass raw micros values to the client — always transform them to dollar amounts in the server-side route.

typescript
1const spendDollars = Number(row.metrics.costMicros) / 1_000_000;
2const avgCpcDollars = Number(row.metrics.averageCpc) / 1_000_000;

Best practices

  • Always divide micros values by 1,000,000 in your API route before sending to the frontend — raw micros values displayed as dollars cause major confusion and loss of trust in your dashboard
  • Cache the OAuth2 access token for up to 55 minutes (slightly under the 60-minute expiry) to avoid unnecessary token exchange requests on every API call
  • Use GAQL's DURING keyword with predefined date ranges (LAST_7_DAYS, LAST_30_DAYS) rather than custom date literals — it's simpler and automatically handles timezone differences
  • Request only the fields you need in each GAQL query — the more fields you request, the slower the query runs and the larger the response payload
  • Store the customer ID as an environment variable rather than hardcoding it — this makes it easy to switch between accounts or support multiple accounts in the future
  • Implement proper error handling for the OAuth2 token exchange — if the refresh token is revoked or expired, return a clear 503 error rather than crashing the dashboard
  • For agencies managing multiple client accounts, use manager account (MCC) access with the login-customer-id header to query across all linked accounts from a single API integration

Alternatives

Frequently asked questions

How long does it take to get a Google Ads developer token?

Applying for a developer token takes a few minutes, but approval for basic access (required for real account data) typically takes 1-3 business days. During development, you receive a test developer token immediately that only works with Google Ads test accounts. Plan for this delay in your project timeline — start the application process before writing any code.

Can I use the Google Ads API without a Google Ads account?

No — you need an active Google Ads account that has been approved for advertising (not just created) to get API access. You also need to have spent money on Google Ads at some point, as the API is designed for advertisers rather than pure API developers. If you're building for a client, you can use your client's account credentials with their permission.

What is GAQL and how does it differ from SQL?

GAQL (Google Ads Query Language) is a SQL-like query language specific to the Google Ads API. Like SQL it has SELECT, FROM, WHERE, ORDER BY, and LIMIT clauses, but it operates on Google Ads resources (campaign, ad_group, keyword_view) rather than database tables. Each query has a main resource in the FROM clause, and you can only combine fields from that resource and its directly related entities. The Google Ads documentation provides a field reference and an interactive query builder at developers.google.com/google-ads/api/fields.

Can I write data back to Google Ads (create campaigns, update bids)?

Yes — the Google Ads API supports mutate operations for creating campaigns, ad groups, ads, keywords, and adjusting bids. These use POST requests to the /googleAds:mutate endpoint with an array of operation objects. Writing operations require the same credentials as reading, but you should be careful — automated bid and budget changes can spend real money. Implement confirmation dialogs and sanity-check limits before allowing write operations from your dashboard.

Why does my dashboard show different numbers than the Google Ads interface?

Discrepancies between API data and the Google Ads interface are common and have several causes: different date ranges, different time zones (API defaults to the account timezone), attribution model differences, or the interface showing data updated in near-real-time while the API data lags by a few hours. Always specify the same date range in your GAQL query as you're viewing in the interface to compare accurately.

Is there a free tier for the Google Ads API?

The Google Ads API itself is free — you pay for the ads you run, not for API access. However, to get basic access (required for real account data), you must apply and be approved, which involves demonstrating a legitimate business use case. The test token (available immediately) is free and unlimited but only works with test accounts created specifically for development purposes.

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.