Skip to content

REST API v1 · Resource

LinkedIn ads

Read LinkedIn ad results and audiences, search the Ad Library, and boost posts.

API reference · LinkedIn ads

Last updated

Endpoints

Paths are relative to https://fliok.com/api/v1. Each endpoint names the scope and the lowest key role it needs; a key without them gets 403 (see Authentication).

LinkedIn is a paid add-on on the Basic plan and up. Until your workspace has it, these endpoints answer 402 linkedin_addon_required (or 403 linkedin_unavailable when nothing is on offer), exactly as the dashboard does. Connect LinkedIn in Integrations first. Every endpoint runs the dashboard’s own code: the same roles, checks, limits and audit log.

  • Each part needs its LinkedIn product. Ad results and drafts need ads, the Ad Library adlibrary, boosts boost. Lead Sync, the Conversions API and Matched Audiences need LinkedIn’s leads, conversions and audiences products, which are not switched on yet: until they are, those endpoints answer feature: "off" or the dashboard’s refusal.
  • Nothing launches by itself. Message Ads, Conversation Ads and Sponsored Content are made as drafts with no bid. You review and launch them in LinkedIn Campaign Manager. Pausing a campaign needs ads:write.
  • Spending needs ads:spend. Starting a boost (LinkedIn has no paused boost: it can serve at once) and resuming a campaign also need the ads:spend scope. A key must be made with ads:spend named; no existing key has it. Without it you get 403 insufficient_scope before anything is read. A boost then takes the same three steps as the dashboard: init, preview with your total budget, and the start with confirmSpend: true and the exact goal, dates, amount and currency of that preview.
  • No body: POST /linkedin/ads/refresh, POST …/campaigns/{campaignId}/pause and /resume, and POST /linkedin/audiences/{id}/sync.
  • Not in the API: raising a campaign’s budget or bid (Fliok never changes what a LinkedIn campaign may spend; do it in Campaign Manager), Fliok AI’s outreach planner (a dashboard helper that bills the AI wallet; make the draft with POST /linkedin/ads/message-ads), and connecting LinkedIn (a person signs in to LinkedIn in Integrations). Posts and events are on the LinkedIn page.

LinkedIn ads

35
  • GET/linkedin/adsLinkedIn ad results for ?days=7|30|90: each campaign’s spend, impressions, clicks, leads and cost per lead, totals per currency, and the CRM’s LinkedIn leads and winsScope linkedin:read · viewer role or higher
  • GET/linkedin/ads/accountsYour LinkedIn ad accounts, whether Fliok reports on each, and the last sync. Never a tokenScope linkedin:read · viewer role or higher
  • PATCH/linkedin/ads/accountsReport on one ad account, or stop (`{ account, report }`). Switching one on needs the LinkedIn add-on; switching off never doesScope linkedin:write · admin role or higher
  • POST/linkedin/ads/refreshRead the ad accounts, campaigns and daily results from LinkedIn now instead of at the next 6-hourly sync. Three a quarter-hourScope linkedin:write · admin role or higher
  • GET/linkedin/ads/demographicsWho saw your ads (job titles, seniority, industries, company sizes, locations) for an ?account= or ?campaign=, read live and kept an hourScope linkedin:read · viewer role or higher
  • POST/linkedin/ads/campaigns/{campaignId}/pausePause one campaign Fliok has read (its numeric id). Stops spend at once, so it needs no add-on and no ads:spend. AuditedScope ads:write · admin role or higher
  • POST/linkedin/ads/campaigns/{campaignId}/resumeResume one paused campaign: it SPENDS again at its budget. Also needs ads:spend (no key has it unless made with it named) and the add-onScope ads:write · admin role or higher
  • GET/linkedin/ad-librarySearch LinkedIn’s Ad Library: ?kind=ads|jobs|partnerships with a keyword or advertiser name, country and days. Kept an hour per searchScope linkedin:read · viewer role or higher
  • GET/linkedin/ads/message-ads/optionsWhat a message ad can use: the ad accounts you may create campaigns in, the sender and the languagesScope linkedin:read · admin role or higher
  • GET/linkedin/ads/message-ads/locationsLinkedIn locations to target, by name (?q=). Returns the urn:li:geo ids a message ad’s `locations` takesScope linkedin:read · admin role or higher
  • GET/linkedin/ads/audienceLinkedIn’s values for one targeting facet (?facet=titles|seniorities|industries|… and ?q=), with the URNs an `audience` takesScope linkedin:read · admin role or higher
  • POST/linkedin/ads/audience/countHow many LinkedIn members an audience reaches, and whether that meets LinkedIn’s minimum. Read-only: nothing is madeScope ads:write · admin role or higher
  • POST/linkedin/ads/message-adsMake a Message Ad or Conversation Ad as a DRAFT campaign in your ad account. Nothing launches and no bid is set. Optional WhatsApp buttonScope ads:write · admin role or higher
  • GET/linkedin/ads/sponsored-content/optionsWhat Sponsored Content can use: the ad accounts you may create campaigns in and the company pages you administerScope linkedin:read · admin role or higher
  • POST/linkedin/ads/sponsored-contentQueue a Sponsored Content ad (image, video, document or lead gen form) as a DRAFT from an uploaded file. 202 with a job to followScope ads:write · admin role or higher
  • GET/linkedin/ads/sponsored-content/{jobId}How a Sponsored Content draft is getting on: working (with the step), done (what was made) or failed (why, and what exists)Scope linkedin:read · admin role or higher
  • GET/linkedin/boostBoost status of up to 100 of your published page posts (?posts=id,id), with results for boosted ones. `feature` says off or reconnectScope linkedin:read · viewer role or higher
  • POST/linkedin/boost/{id}/initStep 1 of a boost: is the post eligible, the ad account, billing, currency, goals, default dates and the most it may cost. Spends nothingScope ads:write · admin role or higher
  • POST/linkedin/boost/{id}/previewStep 2: LinkedIn’s forecast for a goal, dates and a total budget. The last preview with `amount` is what step 3 must echo. Spends nothingScope ads:write · admin role or higher
  • POST/linkedin/boost/{id}Step 3: start the boost — it SPENDS at once. Also needs ads:spend, `confirmSpend: true` and the previewed goal, dates, amount and currencyScope ads:write · admin role or higher
  • DELETE/linkedin/boost/{id}Stop a running boost (`{ confirmStop: true }`). LinkedIn deletes its campaign; it is not a pauseScope ads:write · admin role or higher
  • GET/linkedin/ads/outreachYour LinkedIn outreach: Conversation Ads with a WhatsApp button, the chats and leads each brought, and its Fliok AI playbookScope linkedin:read · admin role or higher
  • PATCH/linkedin/ads/outreach/{id}Switch one outreach’s Fliok AI playbook on or off, or change its instructions (`{ on?, instructions? }`). AuditedScope ads:write · admin role or higher
  • GET/linkedin/leadsLead Sync: on or off, the LinkedIn lead gen forms found and each one’s WhatsApp follow-up. Leads arrive in the CRM as contactsScope linkedin:read · viewer role or higher
  • PATCH/linkedin/leadsSwitch Lead Sync on or off (`{ leadSync }`). On needs the LinkedIn add-on and LinkedIn’s Lead Sync productScope linkedin:write · admin role or higher
  • PATCH/linkedin/leads/forms/{formId}Set one lead gen form’s WhatsApp follow-up: on or off, the approved template and language, and the consent question it needsScope linkedin:write · admin role or higher
  • GET/linkedin/conversionsThe Conversions API setup: mode, ad account, the rule for each CRM event, on or off, and recent sends. Never the tokenScope linkedin:read · viewer role or higher
  • PUT/linkedin/conversionsSave the Conversions API setup: token or OAuth mode, ad account, a rule per event, on or off. A token is stored encrypted, never returnedScope linkedin:write · admin role or higher
  • DELETE/linkedin/conversionsRemove the Conversions API setup and its stored token. Nothing more is sent to LinkedInScope linkedin:write · admin role or higher
  • GET/linkedin/conversions/rulesThe conversion rules in the saved ad account, read from LinkedIn, to pick one per CRM eventScope linkedin:read · admin role or higher
  • POST/linkedin/conversions/rulesMake a conversion rule at LinkedIn for one CRM event (lead, qualified, won, payment) in the saved ad accountScope linkedin:write · admin role or higher
  • GET/linkedin/audiencesYour Matched Audiences synced from CRM contacts: status, contacts sent and matched, the ad accounts and what can go in oneScope linkedin:read · viewer role or higher
  • POST/linkedin/audiencesA new Matched Audience from CRM tags and stages. Its contacts’ hashed emails go to LinkedIn from the next syncScope linkedin:write · admin role or higher
  • DELETE/linkedin/audiences/{id}Delete one audience in Fliok and at LinkedInScope linkedin:write · admin role or higher
  • POST/linkedin/audiences/{id}/syncSync one audience now instead of at the next hourly run. Six an hourScope linkedin:write · admin role or higher

Request bodies

What these endpoints take, field by field. A body that doesn’t match answers 400 naming the field.

PATCH /linkedin/ads/accounts

JSON body. Admin keys. Switching reporting on needs the LinkedIn add-on; switching it off never does.

FieldTypeWhat it is
accountrequiredstringThe ad account, "urn:li:sponsoredAccount:<number>", from GET /linkedin/ads/accounts.
reportrequiredbooleantrue to read this account's results, false to stop.

Returns 200 { ok, account, report }

POST /linkedin/ads/audience/count

JSON body. Read-only: nothing is made at LinkedIn. Facets: locations, titles, seniorities, industries, functions, skills, companySizes, companies (URNs from GET /linkedin/ads/audience).

FieldTypeWhat it is
audiencerequiredobjectFacet → array of up to 30 URNs, e.g. { "locations": ["urn:li:geo:102713980"], "titles": ["urn:li:title:1"] }. Locations are required.

Returns 200 { count, meetsMinimum, … }

POST /linkedin/ads/message-ads

JSON body. Admin keys with ads:write. Makes a DRAFT campaign group, campaign, message and ad; nothing launches and no bid is set. Send an `Idempotency-Key` so a retry never makes a second draft.

FieldTypeWhat it is
kindrequired"message" | "conversation"A Message Ad (one message, one button) or a Conversation Ad (an opening and up to five buttons).
accountrequiredstringThe ad account URN, from GET /linkedin/ads/message-ads/options.
namerequiredstringThe campaign name, up to 200 characters.
subjectrequiredstringThe message subject, up to 60 characters.
bodystringMessage Ad text, up to 1,500 characters.
ctaTextstringMessage Ad button text, up to 20 characters.
ctaUrlstringMessage Ad button link (https).
openingstringConversation Ad opening message, up to 500 characters.
buttonsobject[]Conversation Ad buttons, up to 5: { text, action: "url", url } | { text, action: "reply", reply } | { text, action: "whatsapp", message }. One WhatsApp button at most; it opens a tracked link to your WhatsApp number with `message` typed.
locationsrequiredstring[]1–20 "urn:li:geo:<number>" from GET /linkedin/ads/message-ads/locations.
audienceobjectExtra targeting with names: facet → [{ urn, name }], up to 30 each. Facets: locations, titles, seniorities, industries, functions, skills, companySizes, companies (URNs from GET /linkedin/ads/audience).
playbookstringWith a WhatsApp button: how Fliok AI answers the chats this ad opens. The draft is kept as an outreach.
localerequiredstringLanguage, like "en_US".
dailyBudgetrequiredstringDaily budget in the account currency, like "500". Saved on the draft; nothing spends until you launch it.
startAtrequirednumberStart, Unix milliseconds. A past start moves to now.
endAtrequirednumber | nullEnd, Unix milliseconds, or null to run until stopped.
notPoliticalrequiredtrueConfirms this is not political advertising.

Returns 200 { ok, kind, name, accountUrn, accountName, created: { campaignGroupUrn, campaignUrn, … }, campaignManagerUrl, startAt, outreach? }

POST /linkedin/ads/sponsored-content

JSON body. Admin keys with ads:write. Queues a DRAFT Sponsored Content ad; follow it at GET /linkedin/ads/sponsored-content/{jobId}. Upload the file first, as for a LinkedIn post. Send an `Idempotency-Key`.

FieldTypeWhat it is
formatrequired"image" | "video" | "document" | "leadgen"The ad format. "leadgen" needs `leadForm`.
accountrequiredstringThe ad account URN, from GET /linkedin/ads/sponsored-content/options.
pagerequiredstringThe company page it runs as, "urn:li:organization:<number>".
mediaKeyrequiredstringThe uploaded file's key (the LinkedIn post media upload).
namerequiredstringThe campaign name.
introTextrequiredstringThe text above the ad, up to 3,000 characters.
headlinerequiredstringThe headline, up to 200 characters.
ctaLabelstringButton label, like "LEARN_MORE".
landingUrlstringWhere the ad leads (https). Not for lead gen.
leadFormobjectFor "leadgen": { headline, description?, privacyPolicyUrl, thankYouMessage, thankYouUrl }.
locationsrequiredstring[]1–20 "urn:li:geo:<number>".
localerequiredstringLanguage, like "en_US".
dailyBudgetrequiredstringDaily budget in the account currency. Nothing spends until you launch it.
startAtrequirednumberStart, Unix milliseconds.
endAtrequirednumber | nullEnd, Unix milliseconds, or null.
notPoliticalrequiredtrueConfirms this is not political advertising.

Returns 202 { ok, jobId, format, name, accountUrn }

PATCH /linkedin/ads/outreach/{id}

JSON body, at least one field. Admin keys with ads:write.

FieldTypeWhat it is
onbooleanFliok AI answers this outreach's chats by its playbook, or not.
instructionsstringThe playbook's new instructions.

Returns 200 { ok, on, … }

POST /linkedin/boost/{id}/init

Empty JSON body `{}`. Step 1 of a boost: `{id}` is a Fliok LinkedIn post id, a published post made as your company page. LinkedIn may create a sponsored account for the page. Spends nothing.

FieldTypeWhat it is

Returns 200 { billingConfigured, billingUrl, currencyCode, goals, defaultGoal, defaultSchedule, maxTotalBudget }

POST /linkedin/boost/{id}/preview

JSON body. Step 2. Spends nothing. The last preview WITH `amount` is what step 3 must echo.

FieldTypeWhat it is
goalrequiredstringOne of the goals from step 1 (e.g. "ENGAGEMENT", "WEBSITE_VISIT", "LEAD_GENERATION").
schedulerequiredobject{ start, end }, Unix milliseconds.
amountstringTotal budget in the account currency, like "5000". Leave it out for LinkedIn's suggestions only.

Returns 200 { currencyCode, audienceName, audienceCount, forecast, suggestedBudgets, maxTotalBudget, minDailyBudget, chosen }

POST /linkedin/boost/{id}

JSON body. Step 3: starts the boost, which SPENDS. Needs ads:spend as well as ads:write. Every field must equal the last preview with a budget. Send an `Idempotency-Key`: Fliok sends the create to LinkedIn once and never retries it.

FieldTypeWhat it is
confirmSpendrequiredtrueYou confirm this spends money.
goalrequiredstringThe previewed goal.
schedulerequiredobjectThe previewed { start, end }.
amountrequiredstringThe previewed total budget.
currencyCoderequiredstringThe ad account currency, like "INR".
callToActionobjectLead generation only: { label, target } with an https target.

Returns 200 { ok, status: "IN_REVIEW" } · 409 start_again / preview_first / billing_required / boost_in_progress · 502 outcome_unknown (check the status before trying again)

DELETE /linkedin/boost/{id}

JSON body. Stops a running boost: LinkedIn deletes its campaign (it is not a pause).

FieldTypeWhat it is
confirmStoprequiredtrueYou confirm the boost stops for good.

Returns 200 { ok } · 409 not_running

PATCH /linkedin/leads

JSON body. Admin keys. Switching Lead Sync on needs the add-on and LinkedIn's Lead Sync product.

FieldTypeWhat it is
leadSyncrequiredbooleanBring LinkedIn lead gen form leads into the CRM, or stop.

Returns 200 { ok, leadSync, … }

PATCH /linkedin/leads/forms/{formId}

JSON body. Admin keys. `{formId}` is the form's id from GET /linkedin/leads.

FieldTypeWhat it is
followUp.enabledrequiredbooleanSend a WhatsApp template to each new lead from this form.
followUp.templateNamerequiredstring | nullAn approved template's name.
followUp.languagerequiredstring | nullThe template's language, like "en".
followUp.consentQuestionIdrequiredstring | nullThe form question where the lead agreed to WhatsApp messages.

Returns 200 { ok }

PUT /linkedin/conversions

JSON body. Admin keys. Turning it on needs the add-on. A token sent here is stored encrypted and never returned.

FieldTypeWhat it is
moderequired"token" | "oauth"Send with a Conversions API token you paste, or with the connected LinkedIn account.
tokenstring | nullThe Conversions API token (mode "token"). Leave it out to keep the saved one.
adAccountrequiredstringThe ad account URN conversions belong to.
rulesobjectCRM event → conversion rule URN or null: lead, qualified, won, payment.
enabledrequiredbooleanSend conversions, or not.

Returns 200 { configured, mode, tokenSaved, tokenHint, adAccountUrn, rules, enabled, … }

POST /linkedin/conversions/rules

JSON body. Admin keys. Makes a conversion rule at LinkedIn in the saved ad account. Send an `Idempotency-Key`.

FieldTypeWhat it is
eventrequired"lead" | "qualified" | "won" | "payment"The CRM event the rule counts.

Returns 201 { rule: { urn, name, type } }

POST /linkedin/audiences

JSON body. Admin keys. Contacts with any of the tags or in any of the stages go to LinkedIn as hashed emails from the next sync. Send an `Idempotency-Key`.

FieldTypeWhat it is
namerequiredstringThe audience name, up to 100 characters.
adAccountUrnrequiredstringThe ad account it is made in.
tagsstring[]Up to 50 contact tags.
stageIdsstring[]Up to 20 lead stage ids.

Returns 201 { audience: { id, name, adAccountUrn, tags, stageIds } }

Examples

Read your LinkedIn ad results

curl "https://fliok.com/api/v1/linkedin/ads?days=30" \
  -H "Authorization: Bearer wa_YOUR_API_KEY"

Search LinkedIn’s Ad Library

curl "https://fliok.com/api/v1/linkedin/ad-library?kind=ads&keyword=saree&country=IN&days=30" \
  -H "Authorization: Bearer wa_YOUR_API_KEY"

Make a Conversation Ad as a draft, with a WhatsApp button

curl -X POST "https://fliok.com/api/v1/linkedin/ads/message-ads" \
  -H "Authorization: Bearer wa_YOUR_API_KEY" \
  -H "Idempotency-Key: diwali-founders-1" \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "conversation",
    "account": "urn:li:sponsoredAccount:512830044",
    "name": "Diwali offer — founders, Mumbai",
    "subject": "A Diwali gift for your team",
    "opening": "Hi! We make handwoven sarees for corporate gifting. Want the Diwali catalogue?",
    "buttons": [{ "text": "Chat on WhatsApp", "action": "whatsapp", "message": "Hi, please send the Diwali catalogue" }],
    "locations": ["urn:li:geo:106164952"],
    "playbook": "Share the catalogue, ask team size and delivery date, offer a call.",
    "locale": "en_US",
    "dailyBudget": "1500",
    "startAt": 1761955200000,
    "endAt": null,
    "notPolitical": true
  }'

Boost a page post (needs ads:spend)

# 1. Is it eligible, and what may it cost? (spends nothing)
curl -X POST "https://fliok.com/api/v1/linkedin/boost/7d0e5a52-6b1f-4c1e-8a3e-0c9d2f4b7a10/init" \
  -H "Authorization: Bearer wa_YOUR_SPEND_KEY" -H "Content-Type: application/json" -d '{}'

# 2. Preview with your total budget (spends nothing)
curl -X POST "https://fliok.com/api/v1/linkedin/boost/7d0e5a52-6b1f-4c1e-8a3e-0c9d2f4b7a10/preview" \
  -H "Authorization: Bearer wa_YOUR_SPEND_KEY" -H "Content-Type: application/json" \
  -d '{ "goal": "ENGAGEMENT", "schedule": { "start": 1761955200000, "end": 1762560000000 }, "amount": "5000" }'

# 3. Start it: echo the preview exactly. This spends.
curl -X POST "https://fliok.com/api/v1/linkedin/boost/7d0e5a52-6b1f-4c1e-8a3e-0c9d2f4b7a10" \
  -H "Authorization: Bearer wa_YOUR_SPEND_KEY" -H "Idempotency-Key: boost-diwali-1" -H "Content-Type: application/json" \
  -d '{ "confirmSpend": true, "goal": "ENGAGEMENT", "schedule": { "start": 1761955200000, "end": 1762560000000 }, "amount": "5000", "currencyCode": "INR" }'

Sync CRM contacts to a LinkedIn Matched Audience

curl -X POST "https://fliok.com/api/v1/linkedin/audiences" \
  -H "Authorization: Bearer wa_YOUR_API_KEY" \
  -H "Idempotency-Key: aud-hot-leads" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Hot leads — Mumbai", "adAccountUrn": "urn:li:sponsoredAccount:512830044", "tags": ["hot", "mumbai"] }'

Rate limits

The buckets these endpoints use. Burst is how many requests you can make back-to-back from a full bucket; sustained is how fast it refills.

BucketEndpointsBurstSustained
Reads/messages · /conversations · /contacts · /templates · /phone-numbers · GET /webhooks · Instagram, LinkedIn, ads and catalog reads · GET /payment-requests/{id}60060 / sec
LinkedIn ad results refreshPOST /linkedin/ads/refresh33 / 15 min
LinkedIn campaign pause and resumePOST /linkedin/ads/campaigns/{campaignId}/pause · /resume2020 / 10 min
LinkedIn draft adsPOST /linkedin/ads/message-ads · POST /linkedin/ads/sponsored-content (each its own bucket)1010 / hour
LinkedIn Ad LibraryGET /linkedin/ad-library2020 / min
LinkedIn boostsPOST /linkedin/boost/{id} · DELETE /linkedin/boost/{id} (preview and init: 30, then 1 / 2 sec)201 / 5 sec
LinkedIn audiencesPOST /linkedin/audiences (10 / 10 min) · DELETE /linkedin/audiences/{id} (20 / 10 min) · POST /linkedin/audiences/{id}/sync66 / hour

Every limit, the RateLimit-* headers and what to do on a 429: Rate limits.