# How to Integrate Retool with LinkedIn Ads

- Tool: Retool
- Difficulty: Intermediate
- Time required: 30 minutes
- Last updated: April 2026

## TL;DR

Connect Retool to LinkedIn Ads by creating a REST API Resource using LinkedIn Marketing API with OAuth 2.0 authentication. Configure the base URL as https://api.linkedin.com and pass the OAuth access token in the Authorization header. Once configured, build queries to pull campaign analytics, manage ad accounts, and track lead gen form submissions from a centralized B2B marketing dashboard.

## Build a LinkedIn Ads B2B Marketing Dashboard in Retool

LinkedIn Ads' Campaign Manager provides detailed performance data, but B2B marketing operations teams often need to combine ad performance metrics with CRM data (Salesforce, HubSpot), correlate lead gen form submissions with pipeline stages, or build custom cross-campaign reporting views that LinkedIn's native UI doesn't support. Retool bridges this gap by pulling LinkedIn Ads data alongside CRM records into a unified internal dashboard.

LinkedIn's Marketing API provides access to ad accounts, campaigns, creatives, and analytics. The API uses URN (Uniform Resource Name) notation to reference all entities — for example, an ad account is referenced as urn:li:sponsoredAccount:123456. This URN system is LinkedIn's key differentiator from other ad platforms and requires URL-encoding when used as query parameters. Authentication uses OAuth 2.0 with 3-legged authorization, and all requests must include a LinkedIn-Version header specifying the API version.

Retool's REST API Resource handles LinkedIn's header-based auth and URN-based queries cleanly. Once configured, build dashboards where media buyers can monitor campaign KPIs, compare audience segment performance, review lead gen form submissions, and export data for client reporting — all without switching between Campaign Manager and other tools.

## Before you start

- A LinkedIn Ads account with active campaigns or an ad account with Marketing Developer Platform access
- A LinkedIn Developer application (created at developer.linkedin.com) with the Marketing Developer Platform product enabled
- An OAuth 2.0 access token with r_ads and r_ads_reporting scopes (plus rw_ads if you need write operations)
- Your LinkedIn Ad Account ID (visible in Campaign Manager URL as the number after /accounts/)
- A Retool account with permission to create Resources and Configuration Variables

## Step-by-step guide

### 1. Create a LinkedIn Developer app and obtain an access token

Navigate to developer.linkedin.com/apps and click Create App. Fill in the app name (e.g., 'Retool Marketing Dashboard'), associate it with your LinkedIn company page, and upload an app logo. Under Products, request access to Marketing Developer Platform — this may require approval from LinkedIn if your account isn't already approved.

Once the app is created, go to the Auth tab. Note the Client ID and Client Secret. Under OAuth 2.0 scopes, add: r_ads (read ad accounts), r_ads_reporting (read analytics), and rw_ads (read/write ad campaigns — only needed for write operations).

To generate an access token for testing: go to the OAuth 2.0 tools in LinkedIn's developer portal, select 3-legged OAuth, and generate a token with the required scopes. For production use, implement a proper OAuth flow to get a token with a longer expiry or use token refresh.

LinkedIn access tokens expire in 60 days. Set a calendar reminder to regenerate the token before expiry, or implement automatic refresh using Retool Workflows with the refresh_token endpoint.

Store the access token in Retool Settings → Configuration Variables as LINKEDIN_ADS_TOKEN marked as secret. Also store your Ad Account ID as LINKEDIN_AD_ACCOUNT_ID (plain string, not secret).

**Expected result:** You have a LinkedIn Developer app, an OAuth access token with the required scopes, and your ad account ID stored in Retool Configuration Variables.

### 2. Create the LinkedIn Marketing API REST Resource in Retool

In Retool, navigate to the Resources tab and click Add Resource. Select REST API.

Name: LinkedIn Marketing API

Base URL: https://api.linkedin.com

Headers (add all three):
- Key: Authorization | Value: Bearer {{ retoolContext.configVars.LINKEDIN_ADS_TOKEN }}
- Key: LinkedIn-Version | Value: 202401 (or the current version — see LinkedIn's API changelog)
- Key: Content-Type | Value: application/json
- Key: X-Restli-Protocol-Version | Value: 2.0.0

The LinkedIn-Version header is mandatory and controls which API version your requests target. LinkedIn releases quarterly API versions (format: YYYYMM). Using an outdated version may return deprecated field names or missing data. Check LinkedIn's API changelog at developer.linkedin.com/docs/guide/v2/changelog for the current recommended version.

The X-Restli-Protocol-Version header enables LinkedIn's REST.li 2.0 protocol, which simplifies response structure for collection endpoints. Without it, some endpoints use a less convenient response format.

Click Create Resource. Test with a GET to /v2/adAccountsV2?q=search&search.type.values[0]=BUSINESS — if it returns a JSON object with elements array containing your ad account(s), the connection is working.

**Expected result:** The LinkedIn Marketing API resource is created and a test GET /v2/adAccountsV2 returns your ad accounts in JSON format with their URN identifiers.

### 3. Fetch ad accounts and campaigns

Create a query named getAdAccounts. Set method to GET and path to /v2/adAccountsV2. Add query parameters:
- q: search
- search.type.values[0]: BUSINESS
- fields: id,name,currency,status,totalBudget,reference

The response elements array contains your ad accounts. Each account's id field contains a URN like urn:li:sponsoredAccount:123456. This URN is required for all subsequent queries.

Create a Select component named select_account. Bind its options to getAdAccounts.data.elements.map(a => ({ label: a.name, value: a.id })).

Now create getCampaigns: GET /v2/adCampaignsV2 with parameters:
- q: search
- search.account.values[0]: {{ select_account.value }} (the URN of the selected account)
- fields: id,name,status,type,totalBudget,unitCost,targetingCriteria,createdAt

Note that the URN must be URL-encoded when used as a query parameter value. Retool handles this automatically when you use {{ }} expressions in query parameter fields.

Transform the campaigns response to flatten the nested budget, cost, and status fields for the Table component.

```
// Transformer for LinkedIn campaigns
const campaigns = data.elements || [];
return campaigns.map(c => ({
  id: c.id,
  name: c.name || '(Unnamed)',
  status: c.status || 'UNKNOWN',
  type: c.type || '',
  total_budget: c.totalBudget?.amount
    ? `${c.totalBudget.currencyCode} ${parseFloat(c.totalBudget.amount).toFixed(2)}`
    : 'No budget',
  cost_type: c.unitCost?.costType || '',
  bid_amount: c.unitCost?.amount?.amount
    ? `${c.unitCost.amount.currencyCode} ${parseFloat(c.unitCost.amount.amount).toFixed(2)}`
    : '',
  created: c.createdAt ? new Date(c.createdAt).toLocaleDateString() : ''
}));
```

**Expected result:** The dashboard shows a dropdown of ad accounts, and selecting one populates a Table with active campaigns showing budget, status, and campaign type.

### 4. Fetch campaign analytics and performance metrics

LinkedIn analytics use a dedicated reporting endpoint separate from the campaign endpoint. Create a query named getCampaignAnalytics: GET /v2/adAnalyticsV2.

Required query parameters:
- q: analytics
- pivot: CAMPAIGN
- dateRange.start.day: {{ dateRange.start.getDate() || 1 }}
- dateRange.start.month: {{ dateRange.start.getMonth() + 1 || 1 }}
- dateRange.start.year: {{ dateRange.start.getFullYear() || 2024 }}
- dateRange.end.day: {{ dateRange.end.getDate() }}
- dateRange.end.month: {{ dateRange.end.getMonth() + 1 }}
- dateRange.end.year: {{ dateRange.end.getFullYear() }}
- campaigns[0]: {{ table_campaigns.selectedRow?.data?.id || select_account.value }}
- fields: impressions,clicks,costInLocalCurrency,conversions,leads,dateRange,pivotValues

The analytics endpoint returns aggregated metrics for the selected campaigns and date range. The response includes impressions, clicks, costInLocalCurrency (total spend), and lead counts.

Create a transformer to compute derived metrics: CTR = clicks / impressions, CPL = costInLocalCurrency / leads, CPC = costInLocalCurrency / clicks. Display these as formatted percentages and currency values in the Table.

For time-series analytics (daily breakdown), add timeGranularity: DAILY to the query. The response will include a dateRange object per row showing the specific day for that data point.

```
// Transformer for LinkedIn analytics metrics
const elements = data.elements || [];
return elements.map(el => {
  const impressions = el.impressions || 0;
  const clicks = el.clicks || 0;
  const spend = parseFloat(el.costInLocalCurrency || 0);
  const leads = el.leads || 0;
  const conversions = el.conversions || 0;
  return {
    campaign_id: (el.pivotValues || [])[0] || '',
    impressions,
    clicks,
    ctr: impressions > 0 ? `${((clicks / impressions) * 100).toFixed(2)}%` : '0%',
    spend: `$${spend.toFixed(2)}`,
    cpc: clicks > 0 ? `$${(spend / clicks).toFixed(2)}` : 'N/A',
    leads,
    cpl: leads > 0 ? `$${(spend / leads).toFixed(2)}` : 'N/A',
    conversions,
    cpa: conversions > 0 ? `$${(spend / conversions).toFixed(2)}` : 'N/A'
  };
});
```

**Expected result:** The analytics query returns campaign performance metrics (CTR, CPC, CPL) for the selected date range, displayed in a sortable Table alongside the campaign list.

### 5. Fetch lead gen form submissions

LinkedIn Lead Gen Forms collect professional profile data directly on LinkedIn. Access submissions via: GET /v2/leadGenerationFormResponses.

Required parameters:
- q: owner
- owner: {{ select_account.value }} (the ad account URN)
- fields: leadGenerationForm,submittedAt,localizedFirstName,localizedLastName,formResponse

Optionally filter by specific form: add formId: urn:li:leadGenerationForm:YOUR_FORM_ID.

The formResponse object contains an array of question-answer pairs. LinkedIn forms collect standard fields (first name, last name, email, company, job title, phone) plus any custom questions you configured. The field names in formResponse vary based on how the form was configured.

Create a transformer that extracts the core profile fields from the formResponse array by matching question keys. LinkedIn question IDs have standard formats for built-in fields:
- 'firstName' → lead's first name
- 'lastName' → lead's last name  
- 'emailAddress' → lead's email
- 'company' → current company
- 'title' → job title

Display submissions in a Table with columns for name, email, company, job title, form submission time, and the originating form/campaign. Add a 'Mark as Contacted' toggle column that updates a Retool Database table tracking lead follow-up status.

```
// Transformer for LinkedIn lead gen form responses
const responses = data.elements || [];
return responses.map(r => {
  const answers = {};
  (r.formResponse?.answers || []).forEach(a => {
    answers[a.questionId] = a.answerDetails?.textAnswerValue || '';
  });
  return {
    form_id: r.leadGenerationForm || '',
    submitted_at: r.submittedAt ? new Date(r.submittedAt).toLocaleString() : '',
    first_name: r.localizedFirstName || answers['firstName'] || '',
    last_name: r.localizedLastName || answers['lastName'] || '',
    email: answers['emailAddress'] || '',
    company: answers['company'] || '',
    job_title: answers['title'] || '',
    full_name: `${r.localizedFirstName || ''} ${r.localizedLastName || ''}`.trim()
  };
});
```

**Expected result:** The lead gen form submissions Table shows recent leads with contact details, submission time, and the form they came from — ready for CRM follow-up workflows.

## Best practices

- Store the LinkedIn OAuth access token in Retool Configuration Variables (Settings → Configuration Variables) marked as secret — never hardcode tokens in resource headers or query configurations.
- Set a reminder to regenerate the OAuth access token before the 60-day expiry — LinkedIn does not send expiry warnings, and expired tokens cause all dashboard queries to silently fail.
- Always include the LinkedIn-Version header with a specific API version date — without it, LinkedIn may default to an older version with different field names, and updating the version later can break existing transformers.
- Use URL-encoded URN strings in query parameters — Retool handles encoding automatically in the query parameter fields, but manually constructed URL paths may break if you include raw URN characters.
- Limit date range queries to 90 days at a time — LinkedIn's analytics API may time out or return partial data for very large date ranges. For longer historical analysis, use a Retool Workflow to fetch data in monthly batches.
- Combine LinkedIn lead data with CRM records using a JavaScript query that joins on email address — this gives your team a unified view without requiring LinkedIn CRM integration configuration.
- Export lead gen form submissions to Retool Database daily using a Retool Workflow — LinkedIn only retains form response data for 90 days via the API, so automated export prevents data loss.

## Use cases

### Build a B2B campaign performance dashboard

Create a Retool dashboard pulling LinkedIn Ads campaign analytics: impressions, clicks, CTR, cost per click, conversions, and cost per lead. Marketing teams can compare performance across campaigns and audience segments, identify underperforming ad sets, and track budget spend against targets.

Prompt example:

```
Build a Retool LinkedIn Ads dashboard showing all active campaigns in a Table with impressions, clicks, CTR, spend, conversions, and CPL. Add a date range filter and a Chart showing daily spend and conversion trend. Include stat components for total spend, total leads, and average CPL for the selected period.
```

### Build a lead gen form submission tracker

Create a Retool panel that pulls LinkedIn Lead Gen Form submissions in real time and cross-references them with CRM records. When a lead submits a LinkedIn form, the panel shows their profile data, the campaign that drove the submission, and whether they already exist in Salesforce — enabling fast follow-up routing.

Prompt example:

```
Build a Retool lead gen form dashboard showing recent LinkedIn form submissions in a Table: lead name, email, company, job title, submission timestamp, and originating campaign. Add a CRM lookup button that searches Salesforce for matching contacts by email. Include a status column showing if the lead has been claimed for follow-up.
```

### Build a cross-platform ad spend comparison dashboard

Create a unified Retool dashboard combining LinkedIn Ads spend and performance data with Facebook Ads, Google Ads, or other ad platforms. Marketing directors can see total B2B marketing spend across all channels, compare CPL by platform, and allocate budgets based on performance without manually exporting from multiple ad dashboards.

Prompt example:

```
Build a Retool multi-channel ad performance dashboard with a Table showing LinkedIn Ads campaigns alongside metrics. Add a Chart comparing LinkedIn CPL vs budget targets over the last 30 days. Include a platform selector to switch between LinkedIn, Facebook Ads, and Google Ads views using the same Table component.
```

## Troubleshooting

### API returns 401 Unauthorized with 'ACCESS_TOKEN_EXPIRED'

Cause: LinkedIn OAuth access tokens expire after 60 days. Once expired, all API requests fail until the token is refreshed or regenerated.

Solution: Regenerate the OAuth access token in the LinkedIn Developer Portal using the OAuth 2.0 tools, or implement token refresh using the refresh_token endpoint (POST https://www.linkedin.com/oauth/v2/accessToken with grant_type=refresh_token). Update the LINKEDIN_ADS_TOKEN Configuration Variable in Retool Settings with the new token. Consider setting a Retool Workflow to alert you 7 days before expiry.

### Campaign analytics endpoint returns 403 Forbidden or empty results

Cause: The OAuth token is missing the r_ads_reporting scope, or the ad account URN being passed as the campaigns filter parameter is not accessible by the authenticated user.

Solution: Regenerate your OAuth token and ensure r_ads and r_ads_reporting scopes are both selected during the OAuth flow. Verify the ad account URN format is correct (urn:li:sponsoredAccount:NUMERIC_ID). Confirm that the LinkedIn user who authorized the token has access to the ad account in Campaign Manager.

### Analytics query returns no data even for date ranges with known spend

Cause: The dateRange parameters use separate year/month/day fields rather than a single timestamp. If any of these fields are null, undefined, or formatted incorrectly, LinkedIn returns an empty result rather than an error.

Solution: Ensure all six dateRange parameters are present and populated: dateRange.start.year, .month, .day and dateRange.end.year, .month, .day. Month is 1-indexed (January = 1). Test the query with hardcoded date values first to confirm the analytics endpoint is working, then add the dynamic DateRange component bindings.

```
// Hardcoded date range for testing (January 2024)
// dateRange.start.year: 2024
// dateRange.start.month: 1
// dateRange.start.day: 1
// dateRange.end.year: 2024
// dateRange.end.month: 1
// dateRange.end.day: 31
```

### Lead gen form responses endpoint returns 400 with 'INVALID_URN'

Cause: The owner parameter must be the full ad account URN string (urn:li:sponsoredAccount:123456), not just the numeric account ID. Passing only the number causes an INVALID_URN error.

Solution: Check that the select_account dropdown is binding the full URN value from the getAdAccounts query response (the id field which contains the full urn:li:sponsoredAccount:XXXXXX string). Do not strip the URN prefix — pass the complete string in the owner parameter.

## Frequently asked questions

### Does Retool have a native LinkedIn Ads connector?

No, Retool does not have a native LinkedIn Ads connector. You connect via a REST API Resource using a LinkedIn OAuth 2.0 access token. The setup involves creating a LinkedIn Developer application, generating an access token with the appropriate scopes, and configuring the token as a header in the Retool resource. This gives full access to the LinkedIn Marketing API.

### What is a LinkedIn URN and why does it appear in API responses?

URN stands for Uniform Resource Name — LinkedIn's system for uniquely identifying all entities (ad accounts, campaigns, creatives, audiences). URNs look like urn:li:sponsoredAccount:123456. When building queries in Retool, you pass these full URN strings as parameter values. LinkedIn's API uses URNs instead of simple numeric IDs to provide namespaced, globally unique identifiers across their platform.

### How long do LinkedIn access tokens last and how do I refresh them?

LinkedIn OAuth 2.0 access tokens expire after 60 days. Refresh tokens (if your app uses the refreshToken grant) last 365 days. To refresh an access token, POST to https://www.linkedin.com/oauth/v2/accessToken with grant_type=refresh_token and your refresh_token value. The response includes a new access_token and new refresh_token. For simplicity, many teams regenerate access tokens manually every 60 days using the LinkedIn Developer Portal's OAuth testing tools.

### Can I create or update LinkedIn Ads campaigns from Retool?

Yes, with the rw_ads OAuth scope. The Marketing API supports creating campaigns (POST /v2/adCampaignsV2), updating campaign status (PATCH /v2/adCampaignsV2/{id}), and managing budgets. However, LinkedIn's ad creation API requires complex request bodies including targeting criteria, creative references, and bid configurations. Most teams use Retool for read-only reporting and manage campaign creation in LinkedIn Campaign Manager.

### How do I export LinkedIn lead gen form submissions to my CRM?

Build a Retool Workflow that runs every few hours to fetch new lead gen form responses using GET /v2/leadGenerationFormResponses. For each new submission, create or update a contact in your CRM (Salesforce, HubSpot) using that CRM's API Resource. Store the latest submission timestamp in a Retool Database or Configuration Variable to avoid reprocessing old submissions. LinkedIn retains form data for 90 days, so run this workflow regularly.

---

Source: https://www.rapidevelopers.com/retool-integrations/linkedin-ads
© RapidDev — https://www.rapidevelopers.com/retool-integrations/linkedin-ads
