Skip to content

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

FeatureStatus
Boost Company Page postsYes
Create Single Image / Single Video Ads from scratch (/v1/ads/create)Yes
Campaign Group > Campaign > Creative hierarchyYes
Location + language targetingYes
Matched Audiences (read existing)Yes
Real-time analytics (spend, CPC, CPM)Yes
Conversions API (CAPI)Yes
Conversion rule CRUD + campaign associationsYes
Conversion attribution metrics readbackYes
B2B targeting (job title, seniority, company size)Roadmap
Lead Gen FormsRoadmap
Other ad types from scratch (Carousel / Document / Text / Dynamic / Message)Roadmap

Boost a Company Page Post

typescript
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" },
}});
python
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"},
)
bash
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.

typescript
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"],
}});
python
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"],
)
bash
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).

typescript
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

TypeFormatMax SizeNotes
Single ImageJPEG, PNG, GIF5 MB1200x627 recommended
VideoMP4200 MB3s-30min, 75 MB recommended
CarouselJPEG, PNG10 MB/card2-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

typescript
const { data } = await zernio.ads.listConversionDestinations({
  path: { accountId: 'ACCOUNT_ID' },
});
python
data = client.ads.list_conversion_destinations(account_id="ACCOUNT_ID")
bash
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.

typescript
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,
  },
});
python
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,
)
bash
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

typescript
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...',
      },
    },
  }],
}});
python
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..."},
        },
    }],
)
bash
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

UnifiedLinkedIn
PurchasePURCHASE
LeadLEAD
CompleteRegistrationCOMPLETE_SIGNUP
AddToCartADD_TO_CART
InitiateCheckoutSTART_CHECKOUT
AddPaymentInfoADD_BILLING_INFO
SubscribeSUBSCRIBE
StartTrialSTART_TRIAL
ViewContentVIEW_CONTENT
SearchSEARCH
ContactCONTACT
SubmitApplicationSUBMIT_APPLICATION
ScheduleSCHEDULE

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:

typescript
const result = await zernio.ads.addConversionAssociations({
  path: { accountId: 'ACCOUNT_ID', destinationId: '25639412' },
  body: {
    adAccountId: '517258773',
    campaignIds: ['337643194', '345396555'],
  },
});
python
result = client.ads.add_conversion_associations(
    account_id="ACCOUNT_ID",
    destination_id="25639412",
    ad_account_id="517258773",
    campaign_ids=["337643194", "345396555"],
)
bash
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:

typescript
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',
  },
});
python
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",
)
bash
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 sendConversions call (LinkedIn's BATCH_CREATE cap)
  • 600 requests/min and 300,000 requests/day per token (LinkedIn-side)
  • conversionHappenedAt must be within the last 90 days
  • 365-day attribution windows are only valid for LEAD, PURCHASE, ADD_TO_CART, QUALIFIED_LEAD, SUBMIT_APPLICATION