How to Integrate LinkedIn Ads with Your Headless CMS
Connect LinkedIn Ads to your headless CMS so approved campaign copy, landing page URLs, images, UTMs, and disclaimers can move from editorial workflow to paid B2B campaigns without copy-paste errors.
What is LinkedIn Ads?
LinkedIn Ads is LinkedIn’s advertising platform for running Sponsored Content, Message Ads, Dynamic Ads, lead generation forms, and matched audience campaigns across LinkedIn’s professional network. It’s commonly used by B2B marketing teams that target by company, job title, seniority, industry, skills, and account lists. Campaign Manager handles budgeting, targeting, creative review, reporting, and lead capture, while the LinkedIn Marketing API lets approved apps create and update campaign assets programmatically.
Why integrate LinkedIn Ads with a headless CMS?
If your team runs 20 LinkedIn campaigns a quarter, the slow part usually isn’t buying media. It’s keeping campaign copy, landing page URLs, hero images, UTM parameters, legal disclaimers, and regional variations aligned across your website, ad creative, and reporting spreadsheets. A disconnected workflow means someone publishes a landing page, someone else copies text into Campaign Manager, another person updates UTMs, and a fourth person checks whether the ad still matches the page. That works until a headline changes after launch or a compliance note gets missed.
Architecture overview
A typical flow starts when an editor publishes a `linkedinAd` document in Sanity Studio. A webhook filtered with GROQ, for example `_type == "linkedinAd" && defined(linkedin.campaignUrn)`, sends the document ID to a Sanity Function or your own webhook endpoint. The Function uses `@sanity/client` to fetch the latest approved fields from the Content Lake, including referenced landing page data, image asset URLs, UTM values, and the LinkedIn sponsored campaign URN. It then calls LinkedIn’s REST Marketing API with OAuth, usually `POST /rest/posts` to create Direct Sponsored Content for the organization and `POST /rest/adCreatives` to attach that post to a sponsored campaign. The response IDs, such as the post URN and creative URN, can be written back to Sanity for audit and reporting. The end user sees the approved Sponsored Content in their LinkedIn feed, clicks through to the landing page, and lands on a web experience powered by the same structured content.
Common use cases
Launch Sponsored Content from approved campaign pages
Publish a campaign page in Sanity, then create the matching LinkedIn Sponsored Content with the same headline, intro text, image, URL, and UTMs.
Keep regulated ad copy consistent
Sync approved disclaimers, product claims, and regional variants into LinkedIn creative payloads instead of relying on manual edits in Campaign Manager.
Run structured creative variants
Model A/B headline and intro text variants in Sanity, then push each approved version to a LinkedIn campaign for controlled testing.
Connect lead gen campaigns to content context
Tie LinkedIn Lead Gen Form campaigns back to the exact offer, landing page, audience, and content version that produced the lead.
Step-by-step integration
- 1
Set up LinkedIn Ads API access
Create or use a LinkedIn Campaign Manager account, create a LinkedIn Developer app, associate it with your company page, and request access to the Marketing Developer Platform if you don’t already have it. Configure OAuth 2.0 and request the scopes your integration needs, commonly `r_ads`, `rw_ads`, `r_organization_social`, and `w_organization_social`. You’ll also need the sponsored account URN, campaign URN, and organization URN.
- 2
Install the integration dependencies
For a TypeScript webhook or Sanity Function, install `@sanity/client`. You can call LinkedIn’s Rest.li Marketing API with `fetch`, as shown below, or use LinkedIn’s Rest.li client package if that fits your stack. Store the LinkedIn access token, organization URN, Sanity project ID, dataset, and Sanity token in environment variables.
- 3
Model LinkedIn ad content in Sanity Studio
Create a schema such as `linkedinAd` with fields for `title`, `introText`, `description`, `landingPage`, `heroImage`, `utmCampaign`, `linkedinCampaignUrn`, `targetStatus`, and `linkedinCreativeUrn`. Keep LinkedIn-specific fields separate from your landing page content, but reference the landing page so GROQ can join them when syncing.
- 4
Create the publish trigger
Add a Sanity webhook or Function trigger for publish events on `linkedinAd` documents. Use a GROQ filter so only ad-ready documents trigger the sync, for example documents where the campaign URN exists and the status is `approved`.
- 5
Call the LinkedIn Marketing API
In the handler, fetch the current document from the Content Lake with GROQ, validate required fields, create a Direct Sponsored Content post through `POST /rest/posts`, then create an ad creative through `POST /rest/adCreatives`. Save the LinkedIn IDs back to Sanity so editors can see what was created.
- 6
Test the full path before activating spend
Start with a test campaign or set new creatives to `PAUSED`. Confirm the LinkedIn post preview, landing page URL, UTM parameters, image crop, and legal text. Then test the frontend page, analytics tags, and lead form routing before switching creatives to active.
Code example
import {createClient} from '@sanity/client'
const sanity = createClient({
projectId: process.env.SANITY_PROJECT_ID!,
dataset: process.env.SANITY_DATASET!,
apiVersion: '2025-01-01',
token: process.env.SANITY_WRITE_TOKEN!,
useCdn: false,
})
async function linkedInPost(path: string, body: unknown) {
const res = await fetch(`https://api.linkedin.com${path}`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.LINKEDIN_ACCESS_TOKEN}`,
'Content-Type': 'application/json',
'LinkedIn-Version': '202501',
'X-Restli-Protocol-Version': '2.0.0',
},
body: JSON.stringify(body),
})
if (!res.ok) throw new Error(`${path} failed: ${res.status} ${await res.text()}`)
return {id: res.headers.get('x-restli-id'), body: await res.json().catch(() => null)}
}
export async function POST(req: Request) {
const {_id} = await req.json()
const ad = await sanity.fetch(`*[_id == $id][0]{
title,
introText,
description,
linkedinCampaignUrn,
"url": landingPage->url,
"imageUrl": heroImage.asset->url
}`, {id: _id})
if (!ad?.linkedinCampaignUrn) return Response.json({skipped: true})
const post = await linkedInPost('/rest/posts', {
author: process.env.LINKEDIN_ORGANIZATION_URN,
commentary: ad.introText,
visibility: 'PUBLIC',
distribution: {feedDistribution: 'NONE', targetEntities: [], thirdPartyDistributionChannels: []},
content: {article: {source: ad.url, title: ad.title, description: ad.description}},
lifecycleState: 'PUBLISHED',
})
const creative = await linkedInPost('/rest/adCreatives', {
campaign: ad.linkedinCampaignUrn,
intendedStatus: 'PAUSED',
reference: post.id,
})
await sanity.patch(_id).set({linkedinPostUrn: post.id, linkedinCreativeUrn: creative.id}).commit()
return Response.json({postUrn: post.id, creativeUrn: creative.id})
}How Sanity + LinkedIn Ads works
Build your LinkedIn Ads integration on Sanity
Sanity gives you the structured content foundation, real-time event system, and flexible APIs you need to connect approved campaign content with LinkedIn Ads.
Start building free →CMS approaches to LinkedIn Ads
| Capability | Traditional CMS | Sanity |
|---|---|---|
| Ad creative source data | Schema-as-code lets you model LinkedIn-specific fields, referenced landing pages, regional disclaimers, and approval status as structured data. | |
| Sync on publish | Webhooks and Functions can trigger from content mutations and call LinkedIn’s API without separate infrastructure for the first sync path. | |
| Field-level API payloads | GROQ can select, filter, project, and join exactly the fields needed for LinkedIn posts and ad creatives. | |
| Editorial review before spend | Sanity Studio can include campaign status, required-field validation, Comments, Tasks, and Content Releases before a webhook creates LinkedIn assets. | |
| Multi-channel campaign reuse | One structured back end can feed LinkedIn Ads, landing pages, email, mobile, and AI agents through APIs and Agent Context. |
Keep building
Explore related integrations to complete your content stack.
Sanity + Google Ads
Sync approved landing page content, ad copy, URLs, and tracking fields into search and display campaign workflows.
Sanity + Meta Ads
Use structured campaign content, images, and landing page references across Facebook and Instagram ad variants.
Sanity + HubSpot
Connect campaign content with forms, lead routing, email follow-up, and CRM context after LinkedIn leads convert.