LinkedIn Ads
Create and boost LinkedIn campaigns via Zernio API - No Marketing Developer Platform application required
Included with the Usage plan. No LinkedIn Marketing Developer Platform application needed. Zernio is an approved partner.
What's Supported
| Feature | Status |
|---|---|
| Boost Company Page posts | Yes |
Create Single Image / Single Video Ads from scratch (/v1/ads/create) | Yes |
| Campaign Group > Campaign > Creative hierarchy | Yes |
| Location + language targeting | Yes |
| Matched Audiences (read existing) | Yes |
| Real-time analytics (spend, CPC, CPM) | Yes |
| Conversions API (CAPI) | Yes |
| Conversion rule CRUD + campaign associations | Yes |
| Conversion attribution metrics readback | Yes |
| B2B targeting (job title, seniority, company size) | Roadmap |
| Lead Gen Forms | Roadmap |
| Other ad types from scratch (Carousel / Document / Text / Dynamic / Message) | Roadmap |
Boost a Company Page Post
const ad = await zernio.ads.boostPost({ body: {
postId: "POST_ID",
accountId: "ACCOUNT_ID",
adAccountId: "517258773", // numeric sponsored account ID (from /v1/ads/accounts)
name: "Boost product launch",
goal: "engagement",
budget: { amount: 50, type: "daily" },
schedule: { startDate: "2026-04-20", endDate: "2026-04-27" },
}});ad = client.ads.boost_post(
post_id="POST_ID",
account_id="ACCOUNT_ID",
ad_account_id="517258773", # numeric sponsored account ID (from /v1/ads/accounts)
name="Boost product launch",
goal="engagement",
budget={"amount": 50, "type": "daily"},
schedule={"startDate": "2026-04-20", "endDate": "2026-04-27"},
)curl -X POST "https://zernio.com/api/v1/ads/boost" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"postId": "POST_ID",
"accountId": "ACCOUNT_ID",
"adAccountId": "517258773",
"name": "Boost product launch",
"goal": "engagement",
"budget": { "amount": 50, "type": "daily" },
"schedule": { "startDate": "2026-04-20", "endDate": "2026-04-27" }
}'Create a Single Image or Video Ad
Build an ad from scratch, no existing post needed. Zernio creates a Direct Sponsored Content ("dark post") authored by your Company Page (it never appears on the Page's feed; it only runs as the ad), then wraps it in a campaign group, campaign, and creative.
const ad = await zernio.ads.createStandaloneAd({ body: {
accountId: "ACCOUNT_ID",
adAccountId: "517258773", // numeric sponsored account ID
organizationId: "107655573", // Company Page that authors the ad (or "urn:li:organization:107655573")
name: "Spring launch (single image)",
goal: "traffic", // engagement | traffic | awareness
budgetAmount: 50,
budgetType: "daily",
headline: "Schedule social posts in one API call",
body: "The social media API built for developers.", // post commentary (intro text above the ad)
imageUrl: "https://cdn.example.com/launch-1200x627.jpg",
linkUrl: "https://zernio.com", // destination, required when goal is "traffic"
callToAction: "LEARN_MORE", // defaults to LEARN_MORE when linkUrl is set
countries: ["US"],
}});ad = client.ads.create_standalone_ad(
account_id="ACCOUNT_ID",
ad_account_id="517258773",
organization_id="107655573",
name="Spring launch (single image)",
goal="traffic",
budget_amount=50,
budget_type="daily",
headline="Schedule social posts in one API call",
body="The social media API built for developers.",
image_url="https://cdn.example.com/launch-1200x627.jpg",
link_url="https://zernio.com",
call_to_action="LEARN_MORE",
countries=["US"],
)curl -X POST "https://zernio.com/api/v1/ads/create" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"accountId": "ACCOUNT_ID",
"adAccountId": "517258773",
"organizationId": "107655573",
"name": "Spring launch (single image)",
"goal": "traffic",
"budgetAmount": 50,
"budgetType": "daily",
"headline": "Schedule social posts in one API call",
"body": "The social media API built for developers.",
"imageUrl": "https://cdn.example.com/launch-1200x627.jpg",
"linkUrl": "https://zernio.com",
"callToAction": "LEARN_MORE",
"countries": ["US"]
}'Video ads
For a single video ad, swap imageUrl for video: { url } (the two are mutually exclusive). The clip is uploaded to LinkedIn under the Company Page, the campaign format becomes SINGLE_VIDEO, and the video_views goal becomes available (it requires a video). LinkedIn auto-generates the poster frame, so a thumbnail isn't required. The request blocks while LinkedIn transcodes the video (short clips take ~10-30s).
const ad = await zernio.ads.createStandaloneAd({ body: {
accountId: "ACCOUNT_ID",
adAccountId: "517258773",
organizationId: "107655573",
name: "Spring launch (video)",
goal: "video_views", // engagement | traffic | awareness | video_views
budgetAmount: 50,
budgetType: "daily",
headline: "See it in action",
body: "The social media API built for developers.",
video: { url: "https://cdn.example.com/launch.mp4" }, // MP4 H.264/AAC, 3s-30min, 75KB-500MB
linkUrl: "https://zernio.com", // optional unless goal is "traffic"
callToAction: "LEARN_MORE",
countries: ["US"],
}});The ad runs as a Direct Sponsored Content post authored by a Company Page. Pass that page via organizationId (a numeric organization ID or a full urn:li:organization:N URN). If you connected the page itself as a Zernio account, or your ad account is owned by an organization, it's inferred and organizationId is optional. The authenticated LinkedIn member must be an Administrator or Direct Sponsored Content Poster of that page, and the page must be associated with the ad account, or LinkedIn returns 403.
Supported goal values: engagement, traffic, awareness, video_views (video_views requires video). traffic requires linkUrl. headline is required; supply exactly one of imageUrl or video. body becomes the post's intro text; longHeadline (optional) becomes the secondary description line on image link ads. callToAction accepts LEARN_MORE, SIGN_UP, DOWNLOAD, SUBSCRIBE, REGISTER, JOIN, ATTEND, REQUEST_DEMO, VIEW_QUOTE, APPLY, SEE_MORE, SHOP_NOW, BUY_NOW. The image is uploaded to LinkedIn under the Company Page; recommended ratio 1.91:1 (e.g. 1200×627), JPEG/PNG/GIF.
Carousel, document, text, lead-gen, and conversion-optimized LinkedIn ads aren't supported via /v1/ads/create yet; use /v1/ads/boost to promote an existing post of any type in the meantime.
Budget Minimums
LinkedIn enforces a $10/day minimum for any ad format and $100 minimum lifetime budget for inactive campaigns.
Media Requirements
| Type | Format | Max Size | Notes |
|---|---|---|---|
| Single Image | JPEG, PNG, GIF | 5 MB | 1200x627 recommended |
| Video | MP4 | 200 MB | 3s-30min, 75 MB recommended |
| Carousel | JPEG, PNG | 10 MB/card | 2-10 cards, 1080x1080 |
Conversions API
Stream offline conversion events (deal closed, lead qualified, trial converted, purchase) back to LinkedIn through the same unified /v1/ads/conversions endpoint you already use for Meta and Google. Zernio uses the LinkedIn account you already connected, no separate Conversions API access token to generate from Campaign Manager. PII is SHA-256 hashed server-side per LinkedIn's spec before anything leaves your server (externalIds are passed through as plaintext per LinkedIn's requirement).
Reconnect required for LinkedIn accounts connected before Zernio shipped Conversions API support. LinkedIn does not silently upgrade existing OAuth grants with the new rw_conversions scope. Accounts that lack it return 403 with code linkedin_reconnect_required; pass the user back through the LinkedIn connect flow once and they're set.
Discover available conversion rules
const { data } = await zernio.ads.listConversionDestinations({
path: { accountId: 'ACCOUNT_ID' },
});data = client.ads.list_conversion_destinations(account_id="ACCOUNT_ID")curl "https://zernio.com/api/v1/accounts/ACCOUNT_ID/conversion-destinations" \
-H "Authorization: Bearer YOUR_API_KEY"Returns every CONVERSIONS_API rule across every sponsored ad account the connected token can access. Each destination carries an adAccountId you'll pass back on subsequent CRUD calls.
Create a new conversion rule
LinkedIn's conversion rule binds an event type (LEAD, PURCHASE, ADD_TO_CART, etc.) to the destination. By default the new rule is auto-associated with every campaign in the ad account; pass autoAssociationType: "NONE" to opt out and manage associations explicitly via /associations.
const dest = await zernio.ads.createConversionDestination({
path: { accountId: 'ACCOUNT_ID' },
body: {
adAccountId: '517258773', // numeric or "urn:li:sponsoredAccount:..."
name: 'Trial activation',
type: 'Lead', // unified name OR LinkedIn enum (e.g. "QUALIFIED_LEAD")
attributionType: 'LAST_TOUCH_BY_CAMPAIGN',
postClickAttributionWindowSize: 30,
viewThroughAttributionWindowSize: 7,
},
});dest = client.ads.create_conversion_destination(
account_id="ACCOUNT_ID",
ad_account_id="517258773",
name="Trial activation",
type="Lead",
attribution_type="LAST_TOUCH_BY_CAMPAIGN",
post_click_attribution_window_size=30,
view_through_attribution_window_size=7,
)curl -X POST "https://zernio.com/api/v1/accounts/ACCOUNT_ID/conversion-destinations" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"adAccountId": "517258773",
"name": "Trial activation",
"type": "Lead",
"attributionType": "LAST_TOUCH_BY_CAMPAIGN",
"postClickAttributionWindowSize": 30,
"viewThroughAttributionWindowSize": 7
}'Send a conversion event
const result = await zernio.ads.sendConversions({ body: {
accountId: 'ACCOUNT_ID',
destinationId: '25639412', // conversion rule id from listConversionDestinations
events: [{
eventName: 'Lead', // informational only — rule type is bound to destination
eventTime: Math.floor(Date.now() / 1000),
eventId: 'order_abc_123',
value: 42.50,
currency: 'USD',
user: {
email: 'customer@example.com',
firstName: 'Jane',
lastName: 'Doe',
country: 'US',
clickIds: {
// Optional: improves match rate. Capture from li_fat_id on landing-page URLs
// after enabling enhanced conversion tracking on the LinkedIn Insight Tag.
li_fat_id: 'AQH...',
},
},
}],
}});result = client.ads.send_conversions(
account_id="ACCOUNT_ID",
destination_id="25639412",
events=[{
"eventName": "Lead",
"eventTime": int(time.time()),
"eventId": "order_abc_123",
"value": 42.50,
"currency": "USD",
"user": {
"email": "customer@example.com",
"firstName": "Jane",
"lastName": "Doe",
"country": "US",
"clickIds": {"li_fat_id": "AQH..."},
},
}],
)curl -X POST "https://zernio.com/api/v1/ads/conversions" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"accountId": "ACCOUNT_ID",
"destinationId": "25639412",
"events": [{
"eventName": "Lead",
"eventTime": 1744732800,
"eventId": "order_abc_123",
"value": 42.50,
"currency": "USD",
"user": {
"email": "customer@example.com",
"firstName": "Jane",
"lastName": "Doe",
"country": "US",
"clickIds": { "li_fat_id": "AQH..." }
}
}]
}'Standard event names
Purchase, Lead, CompleteRegistration, AddToCart, InitiateCheckout, AddPaymentInfo, Subscribe, StartTrial, ViewContent, Search, Contact, SubmitApplication, Schedule. The unified name is informational on LinkedIn — the rule's type (set at create time) is what determines the LinkedIn-side conversion category.
Mapping unified names → LinkedIn type enum
| Unified | |
|---|---|
Purchase | PURCHASE |
Lead | LEAD |
CompleteRegistration | COMPLETE_SIGNUP |
AddToCart | ADD_TO_CART |
InitiateCheckout | START_CHECKOUT |
AddPaymentInfo | ADD_BILLING_INFO |
Subscribe | SUBSCRIBE |
StartTrial | START_TRIAL |
ViewContent | VIEW_CONTENT |
Search | SEARCH |
Contact | CONTACT |
SubmitApplication | SUBMIT_APPLICATION |
Schedule | SCHEDULE |
You can also pass a LinkedIn enum value directly (e.g. "QUALIFIED_LEAD", "OUTBOUND_CLICK") for types not in the unified set.
User identifiers
LinkedIn requires at least one of: SHA-256-hashed email, a LinkedIn first-party click ID (li_fat_id), an Acxiom or Oracle MOAT identifier, OR userInfo containing both firstName and lastName. Send as many as you have, hashed identifiers materially improve match rate.
Deduplication
Pass a stable eventId on every event. If you also fire the LinkedIn Insight Tag with the same event ID, LinkedIn discards the Conversions API copy and counts only the Insight Tag event. Aligns with LinkedIn's documented dedup behavior.
Campaign associations
By default createConversionDestination auto-associates the new rule with every active, paused, and draft campaign in the ad account (autoAssociationType: "ALL_CAMPAIGNS"). Auto-association is one-shot at create time, campaigns added later need explicit association:
const result = await zernio.ads.addConversionAssociations({
path: { accountId: 'ACCOUNT_ID', destinationId: '25639412' },
body: {
adAccountId: '517258773',
campaignIds: ['337643194', '345396555'],
},
});result = client.ads.add_conversion_associations(
account_id="ACCOUNT_ID",
destination_id="25639412",
ad_account_id="517258773",
campaign_ids=["337643194", "345396555"],
)curl -X POST "https://zernio.com/api/v1/accounts/ACCOUNT_ID/conversion-destinations/25639412/associations" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "adAccountId": "517258773", "campaignIds": ["337643194", "345396555"] }'Returns a per-campaign succeeded / failed map so you can retry only the rows that didn't take.
Attribution metrics
Read conversion attribution back from LinkedIn's adAnalytics endpoint, pivoted by date:
const { data } = await zernio.ads.getConversionMetrics({
path: { accountId: 'ACCOUNT_ID', destinationId: '25639412' },
query: {
adAccountId: '517258773',
startDate: '2026-04-01',
endDate: '2026-04-30',
granularity: 'DAILY',
},
});data = client.ads.get_conversion_metrics(
account_id="ACCOUNT_ID",
destination_id="25639412",
ad_account_id="517258773",
start_date="2026-04-01",
end_date="2026-04-30",
granularity="DAILY",
)curl "https://zernio.com/api/v1/accounts/ACCOUNT_ID/conversion-destinations/25639412/metrics?adAccountId=517258773&startDate=2026-04-01&endDate=2026-04-30&granularity=DAILY" \
-H "Authorization: Bearer YOUR_API_KEY"Returns externalWebsiteConversions, externalWebsitePostClickConversions, externalWebsitePostViewConversions, conversionValueInLocalCurrency, qualifiedLeads, costInLocalCurrency per date bucket.
LinkedIn's retention rules apply: granularity=DAILY covers the last ~6 months only; MONTHLY/YEARLY extends to 24 months; granularity=ALL with a range > 6 months auto-rounds to month boundaries.
Soft-delete
LinkedIn doesn't expose a hard-delete on conversion rules. DELETE /v1/accounts/{accountId}/conversion-destinations/{destinationId}?adAccountId=... flips enabled: false (the same operation Campaign Manager's UI delete performs); the rule remains fetchable as status: 'inactive'.
Limits
- 5,000 events per
sendConversionscall (LinkedIn'sBATCH_CREATEcap) - 600 requests/min and 300,000 requests/day per token (LinkedIn-side)
conversionHappenedAtmust be within the last 90 days- 365-day attribution windows are only valid for
LEAD,PURCHASE,ADD_TO_CART,QUALIFIED_LEAD,SUBMIT_APPLICATION