LinkedIn Ads
The linkedin_ads connector reads ad accounts, campaign groups, campaigns, creatives, and ad analytics from the LinkedIn Marketing Developer Platform via Rest.li v2 REST endpoints. It runs on StreamWright's declarative HTTP Transport (transport = "http").
Auth & HTTP Configuration
LinkedIn Ads uses OAuth Bearer tokens, API version headers (LinkedIn-Version), Rest.li 2.0 protocol headers, dotted parameter encoding, and offset-based pagination (start/count → paging.total):
kind: source
name: linkedin_ads_demo
spec:
config:
account_ids: { type: list, items: string, description: "Sponsored Ad Account IDs" }
api_version: { type: string, default: "202401", description: "LinkedIn Version header (YYYYMM)" }
start_date: { type: date, required: false, description: "Start date for performance metrics" }
secrets:
linkedin_access_token: { type: string }
auth:
provider: linkedin_ads
type: bearer
token: "{{ secrets.linkedin_access_token }}"
http:
base_url: https://api.linkedin.com
headers:
LinkedIn-Version: "{{ config.api_version }}"
X-Restli-Protocol-Version: "2.0.0"
params_encoding: dotted
paginator:
type: offset
offset_param: start
limit_param: count
page_size: 100
total_path: paging.total
records:
path: elements
Access tokens are registered as secrets at connection time and automatically redacted as *** in every log message.
Reading data
Each stream queries its corresponding Rest.li endpoint, inheriting authentication, headers, and pagination:
partitions:
- { name: account_id, values: "{{ config.account_ids }}" }
retry: { max_attempts: 3, max_delay: 2m }
requests:
- name: raw_campaigns
http:
path: /rest/adCampaigns
method: GET
params:
q: search
search.account.values[0]: "urn:li:sponsoredAccount:{{ partition.account_id }}"
transform:
mode: page
steps:
- name: campaigns
select: |
SELECT partition->>'account_id' AS account_id,
(record->>'id')::BIGINT AS campaign_id,
record->>'name' AS campaign_name,
record->>'status' AS status,
record->>'type' AS campaign_type,
record->>'costType' AS cost_type
FROM raw_campaigns
export:
campaigns:
step: campaigns
primary_key: [account_id, campaign_id]
Analytics metrics
The campaign_performance stream queries /rest/adAnalytics using LinkedIn's structured parameters, converted automatically via params_encoding: dotted:
requests:
- name: raw_campaign_performance
http:
path: /rest/adAnalytics
method: GET
params:
q: analytics
pivot: CAMPAIGN
timeGranularity: DAILY
accounts:
- "urn:li:sponsoredAccount:{{ partition.account_id }}"
dateRange:
start:
year: "{{ window.start | date('%Y') | int }}"
month: "{{ window.start | date('%m') | int }}"
day: "{{ window.start | date('%d') | int }}"
end:
year: "{{ window.end | date('%Y') | int }}"
month: "{{ window.end | date('%m') | int }}"
day: "{{ window.end | date('%d') | int }}"
Reference streams (examples)
The starter template (examples/sources/ads/linkedin_ads) includes the following reference streams:
| Stream | Description | Endpoint |
|---|---|---|
ad_accounts | Sponsored ad accounts and organization references | /rest/adAccounts |
campaign_groups | Campaign groups, budgets, and run schedules | /rest/adCampaignGroups |
campaigns | Individual campaigns, budgets, and cost types | /rest/adCampaigns |
creatives | Creatives, creative types, and content references | /rest/adCreatives |
campaign_performance | Daily impressions, clicks, spend, conversions, and leads | /rest/adAnalytics |
These streams are reference examples. Because streams in StreamWright are declarative, you can customize columns, adjust DuckDB queries, or add new endpoints to match your upstream reporting needs.
Shared vocabulary
linkedin_ads maps to the same canonical nodes as Google,
Microsoft, and Meta Ads, allowing unified multi-channel reporting pipelines.