Overview
Connecting LinkedIn Ads lets the dashboard:- Create, update, pause, resume, and archive campaign groups (the budget container) and campaigns (targeting + delivery units).
- Create creatives bound to a campaign and run a 2-step asset upload flow for images, videos, and documents.
- Pull spend, impressions, clicks, conversions, and cost-in-USD analytics.
- Run creative experiments by rotating multiple campaigns under one campaign group.
Prerequisites
Before connecting, make sure you have:- A LinkedIn Business account with at least one sponsored ad account.
- A user role on the sponsored account that allows campaign management.
- Marketing Developer Platform access. LinkedIn requires partner review of the connecting application before live writes are allowed; review can take 1 to 6 weeks. Until approval lands, the integration runs in dry-run mode and write operations return synthetic responses without touching the upstream API. Set the platform-side flag
LINKEDIN_ADS_LIVE_MODE=trueto flip writes live once approval is granted. - An OAuth 2.0 application configured for the dashboard’s redirect URI. The
client_idandclient_secretare set on the platform side asLINKEDIN_ADS_CLIENT_IDandLINKEDIN_ADS_CLIENT_SECRET.
OAuth scopes
The connection requests the following scopes:r_ads and r_ads_reporting cover list and analytics calls; rw_ads covers all create / update operations on campaign groups, campaigns, and creatives.
Connect
- Open the Paid UA → Integrations page in the dashboard.
- Click Connect on the LinkedIn Ads tile.
- Sign in with the LinkedIn user that has access to your sponsored account.
- Approve the requested scopes.
- After redirect, the dashboard exchanges the auth code for tokens and probes
/adAccounts?q=searchto confirm access and capture the connected sponsored accounts.
Naming map
LinkedIn’s domain model uses different terms from the dashboard’s cross-platform DTO. Internally the integration maps:| Dashboard DTO | LinkedIn resource | Notes |
|---|---|---|
campaign | campaignGroup | The budget container |
adSet | campaign | The targeting + delivery unit |
ad | creative | Bound to a campaign by URN |
adAccount | sponsoredAccount | Referenced by URN: urn:li:sponsoredAccount:{id} |
What happens behind the scenes
| Operation | API endpoint |
|---|---|
| List sponsored accounts | GET /adAccounts?q=search |
| List / create campaign groups | GET / POST /adAccounts/{id}/adCampaignGroups |
| Update / pause / resume / archive campaign group | POST /adAccounts/{id}/adCampaignGroups/{id} (Rest.li patch.$set) |
| List / create campaigns (= ad sets) | GET / POST /adAccounts/{id}/adCampaigns |
| Update / pause / resume / archive campaign | POST /adAccounts/{id}/adCampaigns/{id} (Rest.li patch.$set) |
| Create / update creative | POST /creatives[/{id}] |
| Register asset upload | POST /assets?action=registerUpload |
| Pull analytics | GET /adAnalytics?q=analytics&pivot=CAMPAIGN&... |
status field (ACTIVE, PAUSED, ARCHIVED). LinkedIn does not hard-delete campaign groups or campaigns — the cancel verb is ARCHIVED.
Every REST request carries the required versioning headers:
Idempotency-Key HTTP header so retries do not produce duplicate resources, even if the connection drops mid-request.
Creatives & asset uploads
LinkedIn’s asset upload is a two-step flow:- The dashboard calls
POST /assets?action=registerUploadwith the recipe URN (urn:li:digitalmediaRecipe:feedshare-imageorurn:li:digitalmediaRecipe:feedshare-video) and the owner URN of the entity that will own the asset (typically the connected organization). - The dashboard receives an
uploadUrlplus an asset URN, and the bytes of the image / video are PUT directly to the returned URL.
POST /creatives with the asset URN bound into the creative payload.
Budget conversion
LinkedIn budgets are stored as{ amount: "<whole-currency>", currencyCode: "USD" }. The dashboard sends USD amounts in its DTOs and serialises them into the LinkedIn currency-amount shape on the request.
dailyBudget and totalBudget map to the DTO’s daily and lifetime budget types respectively.
Rate limiting
Requests are throttled per(client_id, ad_account_id) using a sliding window built on Redis. LinkedIn’s limits are partner-specific — 100 requests per day for unreviewed apps and up to several thousand per second for approved partners. If the platform reports HTTP 429, the request is automatically retried with exponential backoff.
If Redis is unavailable, the rate limiter fails closed and the request is reported as a quota error to the autonomous-growth runner — preventing accidental over-quota fan-out under outage.
Reporting
Analytics are pulled hourly (or on demand) from/adAnalytics?q=analytics. The dashboard requests the CAMPAIGN_GROUP (or CAMPAIGN) pivot with DAILY granularity by default. Default fields include externalWebsiteConversions, impressions, clicks, and costInUsd. Spend is reported in USD natively for USD-denominated accounts; non-USD ad accounts have local-currency spend converted to USD on ingestion.
Experiments (multi-campaign rotation)
LinkedIn does not expose a native split-test API. The dashboard maps experiment variants onto multiple campaigns under a single campaign group, with each variant carrying its own targeting and creative. The control variant ships active; treatments ship paused until the experiment runner toggles them. Per-variant performance is read back from the analytics endpoint scoped to the parent campaign group.Disconnect
Click Disconnect on the integration tile. The OAuth refresh token is removed from the dashboard. To fully revoke access, also remove the third-party app authorisation from your LinkedIn account’s connected-apps list.Troubleshooting
- “LinkedIn Ads /adAccounts returned 401” — the OAuth token has been revoked or has expired without a valid refresh token. Re-connect the integration.
- “LinkedIn Ads create_campaign_group: 429 — …” — your app’s rate budget was breached. The dashboard backs off automatically; if the error persists, request a higher Marketing Developer Platform throughput tier.
- Write returns
dryRun: true— the integration is running in dry-run mode (default until Marketing Developer Platform review lands). SetLINKEDIN_ADS_LIVE_MODE=trueon the platform side to flip writes live. - “webhooks not supported on linkedin_ads” — the Marketing API does not expose webhooks. State changes (campaign disapproved, daily cap hit) are detected via the hourly metrics-sync job.
registerUploadreturns 400 withownerempty — the upload owner URN was not derivable from the supplied DTO. Pass the organisation URN (e.g.urn:li:organization:1234) in the DTO’sasset_idfield, or call theregister_uploadadapter operation directly with the full body.- Update returns 405 Method Not Allowed — LinkedIn’s update verb is POST with a
patch.$setenvelope, not PATCH or PUT. The integration handles this automatically; if you see this error, please report it.

