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

How to Integrate LinkedIn Ads with V0

To use LinkedIn Ads with V0, generate your B2B analytics dashboard UI in V0, then create a Next.js API route at app/api/linkedin/ads/route.ts that authenticates via OAuth2 and fetches campaign analytics from LinkedIn's Campaign Manager API. Store your LinkedIn App Client ID and Secret in Vercel environment variables — all API calls must be server-side to protect credentials.

What you'll learn

  • How to register a LinkedIn Developer app and configure Marketing API access
  • How to implement LinkedIn's OAuth2 Authorization Code flow in Next.js API routes
  • How to fetch campaign analytics data from LinkedIn's Campaign Manager API
  • How to build a B2B advertising performance dashboard with V0
  • How to store LinkedIn credentials securely in Vercel environment variables
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate13 min read50 minutesMarketingApril 2026RapidDev Engineering Team
TL;DR

To use LinkedIn Ads with V0, generate your B2B analytics dashboard UI in V0, then create a Next.js API route at app/api/linkedin/ads/route.ts that authenticates via OAuth2 and fetches campaign analytics from LinkedIn's Campaign Manager API. Store your LinkedIn App Client ID and Secret in Vercel environment variables — all API calls must be server-side to protect credentials.

Building a LinkedIn Ads Analytics Dashboard with V0

LinkedIn is the dominant platform for B2B advertising — sponsored content, lead generation forms, and InMail campaigns targeting decision-makers by job title, industry, company size, and seniority. For B2B founders and marketing teams, pulling LinkedIn campaign analytics into a custom dashboard provides better visibility than switching between Campaign Manager and other reporting tools.

LinkedIn's Marketing Developer Platform provides API access to campaign data including impressions, clicks, conversions, cost-per-click, and lead generation metrics. The integration requires OAuth2 authentication — users must authorize your app to access their Campaign Manager account. Your Next.js API routes handle the OAuth2 code exchange and token storage, then use the resulting access token to query the analytics API on the user's behalf.

Note that LinkedIn's Marketing API access requires applying for access through LinkedIn's Marketing Developer Program. Standard developer apps do not automatically have access to campaign analytics endpoints — you need to request and receive approval for the Advertising API product. This approval process can take days to weeks, so start the application early if you are building a production integration.

Integration method

Next.js API Route

V0 generates the B2B advertising dashboard UI while a Next.js API route handles LinkedIn's OAuth2 flow and Campaign Manager API calls server-side. LinkedIn requires OAuth2 for all API access — your API route manages token exchange, stores tokens securely in cookies, and proxies analytics queries back to your React components.

Prerequisites

  • A V0 account with a Next.js project at v0.dev
  • A LinkedIn account with Campaign Manager access and at least one active campaign
  • A LinkedIn Developer app created at developer.linkedin.com with Marketing API access approved
  • Your LinkedIn App Client ID and Client Secret
  • A Vercel deployment URL for configuring the OAuth redirect URI in your LinkedIn app settings

Step-by-step guide

1

Create a LinkedIn Developer App and Request Marketing API Access

LinkedIn's Marketing API requires creating a developer app and applying for access to the Advertising API product. This is a two-step process that takes longer than most API integrations because of LinkedIn's approval workflow. Go to developer.linkedin.com and sign in with your LinkedIn account. Click Create App. Fill in the app name, associate it with a LinkedIn Company Page (required), upload an app logo, and accept the legal agreement. After creation, you land on the app dashboard. In the app dashboard, go to the Products tab. Find Advertising API in the list of products and click Request Access. Fill out the access request form explaining your use case — be specific about which data you need and why. LinkedIn reviews applications manually. Standard developer access only gives you access to your own profile data; the Advertising API product unlocks access to Campaign Manager accounts the user authorizes. While waiting for approval, go to the Auth tab to copy your Client ID and Client Secret. Also add your redirect URI here — set it to http://localhost:3000/api/linkedin/callback for local development and add your production Vercel URL when ready (e.g., https://your-app.vercel.app/api/linkedin/callback). The redirect URI must exactly match what you use in the OAuth flow. LinkedIn's OAuth2 uses the standard Authorization Code flow. Required scopes for advertising analytics are r_ads and r_ads_reporting. You can also add rw_ads if you need to modify campaigns.

Pro tip: LinkedIn's Advertising API approval can take 5-10 business days. Submit the application with detailed information about your use case and the specific endpoints you need — vague applications are often rejected or take longer.

Expected result: You have a LinkedIn Developer app with Client ID and Client Secret, redirect URIs configured, and an Advertising API access request submitted.

2

Generate Your Ads Dashboard UI with V0

Use V0 to generate the front-end components for your LinkedIn Ads analytics dashboard. LinkedIn Ads analytics typically involve tables of campaign data, metric cards, date range selectors, and trend charts. When prompting V0, describe the data structure your API routes will return. Tell V0 that the campaigns endpoint returns an array of objects with campaignId, campaignName, status, impressions, clicks, conversions, and totalSpend. This allows V0 to generate correctly typed TypeScript interfaces and accurate data binding. For date range filtering — a common need in analytics dashboards — ask V0 to generate a date range picker using shadcn/ui's calendar component or a simple preset selector (Last 7 days, Last 30 days, Last 90 days, Custom range). The selected date range should be passed as query parameters to your API routes. LinkedIn campaign statuses have specific values: ACTIVE, PAUSED, ARCHIVED, DRAFT, CANCELED. Ask V0 to generate color-coded status badges: green for ACTIVE, yellow for PAUSED, gray for others. This visual clarity is important in a campaign dashboard where the user scans many campaigns at once.

V0 Prompt

Create a LinkedIn Ads analytics dashboard. At the top, show four summary metric cards: Total Spend ($), Total Impressions, Total Clicks, and Average CTR, loaded from /api/linkedin/ads/summary. Below, show a campaigns table with columns: Campaign Name, Status (color-coded badge), Impressions, Clicks, CTR (%), CPC ($), Conversions, Total Spend. Include a date range selector with presets: Last 7 days, Last 30 days, Last 90 days. Load table data from /api/linkedin/ads/campaigns?dateRange=LAST_30_DAYS.

Paste this in V0 chat

Pro tip: Ask V0 to include a column sorting feature on the campaigns table — users frequently want to sort by spend or impressions to find their top or bottom performers.

Expected result: V0 generates a professional analytics dashboard with metric summary cards, a sortable campaigns table, and date range selection wired to your API endpoints.

3

Create the LinkedIn OAuth2 Authentication Routes

LinkedIn's OAuth2 follows the standard Authorization Code flow. You need two API routes: one to redirect the user to LinkedIn's authorization page, and one to handle the callback and exchange the code for an access token. The login route (app/api/linkedin/login/route.ts) builds the LinkedIn authorization URL with your Client ID, the required scopes, and your redirect URI. LinkedIn's authorization endpoint is https://www.linkedin.com/oauth/v2/authorization. Include a state parameter (a random string) to prevent CSRF attacks — store it in a cookie and verify it matches in the callback. The callback route (app/api/linkedin/callback/route.ts) receives the code and state parameters from LinkedIn. Verify the state matches what you stored, then exchange the code for an access token by POSTing to https://www.linkedin.com/oauth/v2/accessToken with your Client ID, Client Secret, code, and redirect URI. LinkedIn returns an access_token and expires_in value. Store the access token in an HTTP-only cookie. LinkedIn tokens can last up to 60 days. Unlike Spotify's short-lived tokens, LinkedIn tokens are longer-lived, so a simple expiration check is often sufficient without implementing a full refresh flow. After storing the token, redirect the user back to your dashboard page. Your data-fetching API routes will read the token from the cookie for subsequent API calls.

V0 Prompt

Create two Next.js API routes: app/api/linkedin/login/route.ts that redirects to LinkedIn's OAuth authorization URL with scopes r_ads and r_ads_reporting using LINKEDIN_CLIENT_ID and LINKEDIN_REDIRECT_URI, and app/api/linkedin/callback/route.ts that exchanges the code for an access token by POSTing to https://www.linkedin.com/oauth/v2/accessToken with LINKEDIN_CLIENT_ID and LINKEDIN_CLIENT_SECRET, stores the token in an HTTP-only cookie, and redirects to /dashboard.

Paste this in V0 chat

app/api/linkedin/login/route.ts
1// app/api/linkedin/login/route.ts
2import { NextResponse } from 'next/server';
3
4export async function GET() {
5 const state = Math.random().toString(36).slice(2);
6
7 const params = new URLSearchParams({
8 response_type: 'code',
9 client_id: process.env.LINKEDIN_CLIENT_ID!,
10 redirect_uri: process.env.LINKEDIN_REDIRECT_URI!,
11 state,
12 scope: 'r_ads r_ads_reporting',
13 });
14
15 const response = NextResponse.redirect(
16 `https://www.linkedin.com/oauth/v2/authorization?${params.toString()}`
17 );
18
19 response.cookies.set('linkedin_oauth_state', state, {
20 httpOnly: true,
21 secure: process.env.NODE_ENV === 'production',
22 maxAge: 600,
23 sameSite: 'lax',
24 });
25
26 return response;
27}

Pro tip: LinkedIn access tokens last 60 days by default. Store the token expiry time alongside the token so your app can detect when to require re-authentication.

Expected result: Clicking the LinkedIn login button redirects to LinkedIn's authorization page. After approving, the user is redirected back to your app with an access token stored in a cookie.

4

Create the Campaign Analytics API Route

With authentication working, create the API route that fetches campaign analytics from LinkedIn's API. LinkedIn's Analytics Finder API endpoint is at https://api.linkedin.com/v2/adAnalytics — it uses a complex query parameter syntax based on LinkedIn's Rest.li framework. The LinkedIn API uses a pivot + time range + date range query structure. To get campaign-level analytics, you set pivot=CAMPAIGN, specify a dateRange.start and dateRange.end, and pass the account ID (adAccountId). The account ID is the Campaign Manager account number, typically in the format urn:li:sponsoredAccount:XXXXXXXXXX. First, you need the user's ad account ID. Call https://api.linkedin.com/v2/adAccountsV2?q=search to list the accounts the authenticated user has access to. Extract the account ID from the response and use it in subsequent analytics calls. LinkedIn's Analytics API returns metrics like impressionCount, clickCount, conversionValueInLocalCurrency, costInLocalCurrency, and externalWebsiteConversions. Map these to the field names your V0 components expect. The LinkedIn API uses URN (Uniform Resource Name) format for IDs throughout — campaigns are identified as urn:li:sponsoredCampaign:12345 rather than just 12345. Your API route must handle these URN formats when filtering analytics by campaign.

V0 Prompt

Create a Next.js API route at app/api/linkedin/ads/campaigns/route.ts that reads the linkedin_access_token from cookies, fetches the user's ad accounts from https://api.linkedin.com/v2/adAccountsV2?q=search, then fetches analytics from https://api.linkedin.com/v2/adAnalytics with pivot=CAMPAIGN and a LAST_30_DAYS date range. Return an array of campaigns with campaignId, impressions, clicks, conversions, and totalSpend.

Paste this in V0 chat

app/api/linkedin/ads/campaigns/route.ts
1// app/api/linkedin/ads/campaigns/route.ts
2import { NextRequest, NextResponse } from 'next/server';
3import { cookies } from 'next/headers';
4
5export async function GET(request: NextRequest) {
6 const cookieStore = await cookies();
7 const accessToken = cookieStore.get('linkedin_access_token')?.value;
8
9 if (!accessToken) {
10 return NextResponse.json({ error: 'Not authenticated' }, { status: 401 });
11 }
12
13 const headers = {
14 Authorization: `Bearer ${accessToken}`,
15 'LinkedIn-Version': '202401',
16 };
17
18 // Get ad accounts
19 const accountsRes = await fetch(
20 'https://api.linkedin.com/v2/adAccountsV2?q=search&search.status.values[0]=ACTIVE',
21 { headers }
22 );
23
24 if (!accountsRes.ok) {
25 return NextResponse.json(
26 { error: 'Failed to fetch ad accounts' },
27 { status: accountsRes.status }
28 );
29 }
30
31 const accountsData = await accountsRes.json();
32 const accountId = accountsData.elements?.[0]?.id;
33
34 if (!accountId) {
35 return NextResponse.json({ error: 'No ad accounts found' }, { status: 404 });
36 }
37
38 // Get analytics
39 const today = new Date();
40 const thirtyDaysAgo = new Date(today.getTime() - 30 * 24 * 60 * 60 * 1000);
41
42 const analyticsParams = new URLSearchParams({
43 q: 'analytics',
44 pivot: 'CAMPAIGN',
45 'dateRange.start.day': thirtyDaysAgo.getDate().toString(),
46 'dateRange.start.month': (thirtyDaysAgo.getMonth() + 1).toString(),
47 'dateRange.start.year': thirtyDaysAgo.getFullYear().toString(),
48 'dateRange.end.day': today.getDate().toString(),
49 'dateRange.end.month': (today.getMonth() + 1).toString(),
50 'dateRange.end.year': today.getFullYear().toString(),
51 'accounts[0]': `urn:li:sponsoredAccount:${accountId}`,
52 timeGranularity: 'ALL',
53 });
54
55 const analyticsRes = await fetch(
56 `https://api.linkedin.com/v2/adAnalytics?${analyticsParams}`,
57 { headers }
58 );
59
60 if (!analyticsRes.ok) {
61 return NextResponse.json(
62 { error: 'Failed to fetch analytics' },
63 { status: analyticsRes.status }
64 );
65 }
66
67 const analyticsData = await analyticsRes.json();
68 return NextResponse.json(analyticsData);
69}

Pro tip: LinkedIn's API versioning uses the LinkedIn-Version header with a date like 202401 — always include this header to use a stable API version rather than the default.

Expected result: GET /api/linkedin/ads/campaigns returns campaign analytics data from LinkedIn for the authenticated user's ad account.

5

Add Environment Variables in Vercel

Your LinkedIn API routes need three environment variables. Add them in Vercel Dashboard → Settings → Environment Variables with scope set to Production, Preview, and Development. LINKEDIN_CLIENT_ID: your LinkedIn app's Client ID from developer.linkedin.com. This appears in OAuth authorization URLs and is not a strict secret, but storing it as an env var keeps your code portable. LINKEDIN_CLIENT_SECRET: your LinkedIn app's Client Secret. This authorizes the token exchange step. Never add NEXT_PUBLIC_ prefix and never commit to Git. LINKEDIN_REDIRECT_URI: for production, set to https://your-app.vercel.app/api/linkedin/callback. This must exactly match one of the redirect URIs registered in your LinkedIn app's Auth settings. The mismatch between registered URI and actual redirect URI is the most common LinkedIn OAuth error. For local development, use http://localhost:3000/api/linkedin/callback and add the same value to your .env.local file. Add both URLs to your LinkedIn app's Authorized Redirect URLs list on the Auth tab — LinkedIn allows multiple redirect URIs per app. After saving variables and redeploying, test the full OAuth flow by clicking your dashboard's Connect LinkedIn button, authorizing the app, and verifying the dashboard loads campaign data.

Pro tip: LinkedIn requires your app's Privacy Policy URL to be set in the developer portal before OAuth flows will work for production users. Add a privacy policy page to your V0 app and link it in the LinkedIn app settings.

Expected result: Vercel shows all three LinkedIn environment variables. The OAuth flow completes successfully and the dashboard displays real campaign analytics.

Common use cases

B2B Campaign Performance Dashboard

A B2B SaaS company runs multiple LinkedIn sponsored content campaigns and wants a unified performance view beyond Campaign Manager's built-in reports. V0 generates a dashboard with campaign cards showing key metrics (impressions, clicks, CTR, conversions, spend) with trend arrows. API routes fetch the data from LinkedIn's analytics endpoint.

V0 Prompt

Create a LinkedIn Ads performance dashboard with a metrics summary row showing total spend, impressions, clicks, and conversions for the selected date range. Below, show a table of campaigns with columns for campaign name, status badge, impressions, clicks, CTR, conversions, CPC, and total spend. Include a date range picker at the top. Load data from /api/linkedin/ads/analytics?dateRange=LAST_30_DAYS.

Copy this prompt to try it in V0

Lead Generation Report

A marketing agency needs to report on LinkedIn Lead Gen Form performance for multiple clients. V0 generates a lead quality report showing form submissions, lead counts, and cost per lead by campaign. The data is fetched from LinkedIn's Lead Generation API endpoints.

V0 Prompt

Build a lead generation report page with cards for each LinkedIn Lead Gen campaign showing campaign name, total leads collected, cost per lead, completion rate, and a sparkline of daily leads over the past 30 days. Include an Export to CSV button. Load lead data from /api/linkedin/ads/leads.

Copy this prompt to try it in V0

Audience Targeting Insights

A demand generation team wants to understand which audience segments perform best across their LinkedIn campaigns. V0 generates a breakdown view showing performance metrics segmented by job function, seniority, or industry from LinkedIn's demographic analytics.

V0 Prompt

Create an audience insights panel with a segmentation selector (Job Function, Seniority, Industry, Company Size). For the selected segment, show a horizontal bar chart comparing click rates across segment values, with a table below showing full metrics. Load segment data from /api/linkedin/ads/demographics?segment=JOB_FUNCTION&campaignId=ID.

Copy this prompt to try it in V0

Troubleshooting

LinkedIn OAuth returns error=unauthorized_scope_error

Cause: Your LinkedIn app has not been granted access to the r_ads or r_ads_reporting scopes. These require the Advertising API product to be approved.

Solution: Go to your LinkedIn Developer app → Products tab. Request access to the Advertising API product if not already done, or check the approval status. Only approved scopes can be requested in the OAuth flow.

OAuth callback fails with error=invalid_redirect_uri

Cause: The redirect URI in your OAuth authorization URL does not exactly match one of the registered redirect URIs in your LinkedIn app's Auth settings.

Solution: Open LinkedIn Developer Portal → your app → Auth tab. Verify the LINKEDIN_REDIRECT_URI in your environment exactly matches one of the listed Authorized Redirect URLs — including protocol (http vs https), domain, port, and path.

Analytics API returns 403 Forbidden even with a valid access token

Cause: The authenticated LinkedIn user does not have access to the Campaign Manager account, or the Advertising API product has not been approved for your app.

Solution: Verify the user logging in has Campaign Manager access to at least one ad account. Confirm the Advertising API product is approved in your LinkedIn Developer app (Products tab should show Approved status).

adAnalytics endpoint returns empty elements array

Cause: The date range specified has no campaign activity, the account ID is incorrect, or the campaign URNs are malformed.

Solution: Verify the ad account ID is correct by checking it in Campaign Manager. Test with a broader date range to confirm data exists. Log the full request URL to verify the query parameters are formatted correctly.

Best practices

  • Cache LinkedIn analytics responses for at least 15 minutes since the data does not change in real-time and API rate limits are strict
  • Store the access token expiry time and proactively redirect users to re-authenticate before the token expires rather than showing an error
  • Request only the scopes your app actually uses — users see the scope list during OAuth authorization and unnecessary permissions reduce conversion
  • Handle LinkedIn's eventual consistency — campaign data may have a delay of several hours before reflecting in analytics API responses
  • Add a clear 'Connect LinkedIn Account' flow to your dashboard with explanation of what access is being requested and why
  • Test the full OAuth flow on both localhost and Vercel preview before going to production — different redirect URIs are involved
  • Monitor LinkedIn API deprecation notices — LinkedIn's API versioning uses date-based versions and older versions are periodically sunset

Alternatives

Frequently asked questions

Does LinkedIn require official approval to access the Advertising API?

Yes. Standard LinkedIn Developer apps only have access to basic profile APIs. To access campaign analytics and ad management endpoints, you must apply for the Advertising API product in your app's Products tab. LinkedIn reviews applications manually, and approval can take several days to weeks depending on your use case and company size.

Can I access LinkedIn Ads data for multiple clients' accounts?

Yes. When a user authenticates via OAuth, they authorize your app to access the Campaign Manager accounts they have access to. If they manage multiple client accounts, you can list all accessible accounts using the adAccountsV2 endpoint and let the user select which account's data to view.

What analytics metrics are available from LinkedIn's API?

LinkedIn's Analytics API returns impressionCount, clickCount, videoViews, videoCompletions, costInLocalCurrency, conversionValueInLocalCurrency, externalWebsiteConversions, and several more metrics. The available metrics depend on the campaign type (Sponsored Content, Lead Gen, InMail, etc.).

How long do LinkedIn OAuth access tokens last?

LinkedIn access tokens last for 60 days. LinkedIn also provides a refresh token that can be used to get new access tokens without requiring the user to re-authenticate, as long as the refresh token has not expired. Refresh tokens are valid for one year.

Can I use LinkedIn's API to create or modify campaigns?

Yes. LinkedIn's Marketing API includes endpoints for creating, updating, and managing campaigns, ad groups, and creatives. These require the rw_ads scope in addition to the read scopes. Campaign creation requires the same Advertising API product approval.

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.