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
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
Create a LinkedIn Developer App and Request Marketing API Access
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.
Generate Your Ads Dashboard UI with V0
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.
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.
Create the LinkedIn OAuth2 Authentication Routes
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.
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
1// app/api/linkedin/login/route.ts2import { NextResponse } from 'next/server';34export async function GET() {5 const state = Math.random().toString(36).slice(2);67 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 });1415 const response = NextResponse.redirect(16 `https://www.linkedin.com/oauth/v2/authorization?${params.toString()}`17 );1819 response.cookies.set('linkedin_oauth_state', state, {20 httpOnly: true,21 secure: process.env.NODE_ENV === 'production',22 maxAge: 600,23 sameSite: 'lax',24 });2526 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.
Create the Campaign Analytics API Route
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.
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
1// app/api/linkedin/ads/campaigns/route.ts2import { NextRequest, NextResponse } from 'next/server';3import { cookies } from 'next/headers';45export async function GET(request: NextRequest) {6 const cookieStore = await cookies();7 const accessToken = cookieStore.get('linkedin_access_token')?.value;89 if (!accessToken) {10 return NextResponse.json({ error: 'Not authenticated' }, { status: 401 });11 }1213 const headers = {14 Authorization: `Bearer ${accessToken}`,15 'LinkedIn-Version': '202401',16 };1718 // Get ad accounts19 const accountsRes = await fetch(20 'https://api.linkedin.com/v2/adAccountsV2?q=search&search.status.values[0]=ACTIVE',21 { headers }22 );2324 if (!accountsRes.ok) {25 return NextResponse.json(26 { error: 'Failed to fetch ad accounts' },27 { status: accountsRes.status }28 );29 }3031 const accountsData = await accountsRes.json();32 const accountId = accountsData.elements?.[0]?.id;3334 if (!accountId) {35 return NextResponse.json({ error: 'No ad accounts found' }, { status: 404 });36 }3738 // Get analytics39 const today = new Date();40 const thirtyDaysAgo = new Date(today.getTime() - 30 * 24 * 60 * 60 * 1000);4142 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 });5455 const analyticsRes = await fetch(56 `https://api.linkedin.com/v2/adAnalytics?${analyticsParams}`,57 { headers }58 );5960 if (!analyticsRes.ok) {61 return NextResponse.json(62 { error: 'Failed to fetch analytics' },63 { status: analyticsRes.status }64 );65 }6667 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.
Add Environment Variables in Vercel
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.
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.
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.
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
Twitter Ads API provides consumer-audience ad analytics for engagement-focused campaigns, contrasting with LinkedIn's B2B professional targeting.
Google Ads API provides search and display advertising analytics with broader reach and a more mature API developer experience.
Facebook Ads (Meta Ads) API covers consumer social advertising with advanced audience targeting and has a similar OAuth2 integration pattern.
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.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation