How to load LinkedIn Ads data into Snowflake with OWOX Data Marts
To get LinkedIn Ads data into Snowflake, create a connector Data Mart, sign in with LinkedIn, enter the ad account, choose the Ad Analytics endpoint and publish. The analytics table identifies each row by URNs, not names, so the useful report comes from joining it to the Campaigns table. Both queries are in this guide.
LinkedIn is usually the most expensive click a B2B team buys, and the one it can say least about. Campaign Manager shows clicks and leads. Whether those leads became pipeline is in the CRM, and the two only meet in a spreadsheet somebody maintains by hand.
Putting LinkedIn Ads data into Snowflake is the first step to joining them properly. This guide sets it up with the LinkedIn Ads connector in OWOX Data Marts, and then deals with the part most guides skip: the analytics table that arrives has no campaign names in it.
What you end up with
Two tables in your own Snowflake account.
linkedin_ads_ad_analytics: daily performance. Impressions, clicks, cost and engagement for each creative on each day.linkedin_ads_ad_campaigns: one row per campaign with its name, status, objective and type.
Each is a separate connector Data Mart. The connector reads the LinkedIn API, writes the tables and refreshes them on a schedule. It does not model the data. Joining the two tables into a report is SQL you write, shown below.
Before you start
In LinkedIn. A LinkedIn user with access to the ad account in Campaign Manager, and the account’s ID, which Campaign Manager shows next to the account name.
In Snowflake. A storage of type Snowflake connected to your project, as described in the storage guide: account identifier, warehouse, and a key pair or a username with a programmatic access token. The role must be able to create a database, a schema and tables. A small dedicated warehouse with auto-suspend is enough, and Snowflake pricing explained shows what a daily load costs.
OWOX Data Marts
See your first report built in real time. 15 minutes.
- Connect your data warehouse
- Pick your metrics
- Get a live Google Sheets report
In the time it takes to write a ticket. Then imagine never writing that ticket again.
Book a DemoWe'll use your actual use case
Step 1: create the Data Mart
Click New Data Mart, enter a title such as “LinkedIn Ads Analytics”, select the Snowflake storage and click Create Data Mart. On the Data Setup tab, set Definition Type to Connector and choose LinkedIn Ads.
Step 2: sign in and enter the account
Step 2: sign in and enter the account
Continue with LinkedIn is the short way: sign in as the user who has access to the ad account and allow access.
Manually means creating your own LinkedIn app. The app has to be verified by an administrator of your company page and approved for the Advertising API, which can take up to a day. From it you take a client ID, a primary client secret and a refresh token generated with the r_ads and r_ads_reporting scopes. The credentials guide has each step. The sign-in button avoids all of it.

Then fill in Account URNs with the ID of the ad account.
Step 3: choose the endpoint and the fields
Step 3: choose the endpoint and the fields
Each Data Mart loads one endpoint. LinkedIn Ads has five.
- Ad Analytics. Daily performance metrics. The one for reporting.
- Campaigns. Targeting, format, bid strategy, budget and, most usefully, the campaign name.
- Campaign Groups. The level above campaigns, with shared objectives and budgets.
- Creatives. The ads themselves.
- Ad Accounts. Account settings, currency and status.
Create the Ad Analytics Data Mart first and the Campaigns one right after it. You will want both.
Then select fields. Ad Analytics offers a long list, most of it engagement detail: video views, document completions, lead form opens, job applications.

Take impressions, clicks, costInUsd and the conversion or lead fields you report on. For accounts billed in another currency there is also costInLocalCurrency.
The last screen asks for the database, schema and table. The setup proposes a database named after the source, the schema PUBLIC and a table named after the endpoint.
Step 4: publish and run
Click Publish & Run Data Mart. Publishing starts the first import. When it finishes, the Data Setup tab shows the source, the Snowflake table and its columns.

Step 5: load history and set the schedule
Step 5: load history and set the schedule
History. Click Manual Run and choose Backfill (custom period). One backfill run covers at most 31 days. For a longer history, run consecutive periods, each after the previous one has finished.

Schedule. A Data Mart without a trigger does not run again. On the Triggers tab, add a trigger of type Connector Run: daily, weekly, monthly or at an interval.

Scheduled runs are incremental. Each one loads the current day plus the days set by the Reimport Lookback Window, two by default. LinkedIn conversions can be attributed days after the click, so raise the lookback if your reports depend on them. Rows are merged on the keys, so a re-imported day replaces the earlier version.
If a run ends with a warning
LinkedIn’s analytics endpoint returns at most 15,000 rows per request and has no paging. The connector requests one day at a time to stay under that limit. An account with more than 15,000 active creatives on a single day can still exceed it. The run then finishes with a Warning status and lists the affected days, and data for those days may be incomplete.
What the tables look like in Snowflake
What the tables look like in Snowflake
The identifiers are in pivotValues. The connector requests analytics by creative, campaign, campaign group and account at once. LinkedIn returns those four identifiers as URNs, and they arrive together in one text column, in a form like this:
urn:li:sponsoredCreative:…, urn:li:sponsoredCampaign:…,
urn:li:sponsoredCampaignGroup:…, urn:li:sponsoredAccount:…There is no campaign_name column in this table. You extract the campaign ID from pivotValues and join to the Campaigns table for the name.
Names are case-sensitive, and in camel case. The connector creates the schema, table and column names quoted, exactly as LinkedIn spells them: dateRangeStart, costInUsd. In Snowflake a quoted name is case-sensitive and an unquoted one is read as upper case, so SELECT costInUsd fails. Quote every column, the schema and the table: ads_owox."PUBLIC"."linkedin_ads_ad_analytics". The database name is not quoted.
The day is dateRangeStart. With daily data, dateRangeStart and dateRangeEnd are the same day.
Query the data
Both queries were validated on Snowflake against tables loaded by the connector. The examples use a shared database, ads_owox, for all ad platforms.
Cost, clicks and impressions by campaign ID and day:
SELECT
"dateRangeStart" AS date,
REGEXP_SUBSTR(
"pivotValues",
'sponsoredCampaign:([0-9]+)', 1, 1, 'e'
) AS campaign_id,
SUM("impressions") AS impressions,
SUM("clicks") AS clicks,
SUM("costInUsd") AS cost_usd
FROM ads_owox."PUBLIC"."linkedin_ads_ad_analytics"
GROUP BY 1, 2
ORDER BY date, cost_usd DESC;REGEXP_SUBSTR with the 'e' parameter returns the part in parentheses: the digits after sponsoredCampaign:. Swap in sponsoredCreative or sponsoredCampaignGroup to report at another level.
The same report with campaign names, joined to the Campaigns table:
WITH a AS (
SELECT
"dateRangeStart" AS date,
REGEXP_SUBSTR(
"pivotValues",
'sponsoredCampaign:([0-9]+)', 1, 1, 'e'
) AS campaign_id,
"impressions",
"clicks",
"costInUsd"
FROM ads_owox."PUBLIC"."linkedin_ads_ad_analytics"
)
SELECT
a.date,
c."name" AS campaign,
SUM(a."impressions") AS impressions,
SUM(a."clicks") AS clicks,
SUM(a."costInUsd") AS cost_usd
FROM a
LEFT JOIN ads_owox."PUBLIC"."linkedin_ads_ad_campaigns" c
ON a.campaign_id = c."id"
GROUP BY a.date, c."name"
ORDER BY a.date, cost_usd DESC;This second query is the one worth publishing. Save it as a SQL Data Mart, and the people who read the report see campaign names and never meet a URN.
From table to report
On a Data Mart’s Destinations tab you attach reports to Google Sheets, Data Studio, Slack or email, each with its own columns and schedule. Do that on the Data Mart built from the joined query, not on the raw analytics table. Snowflake reports in Google Sheets covers the delivery.
Two directions from here matter for B2B.
LinkedIn next to the other channels. One row per day and channel, with LinkedIn’s costInUsd mapped to the same spend column as the rest. Self-service marketing analytics on Snowflake has that query.
LinkedIn next to pipeline. If CRM data is in Snowflake, the join key is whatever your campaigns write into the lead: a UTM campaign value or the campaign ID. Decide that convention first. The SQL is the easy part.
Where to go next
The same setup loads the other sources into the same Snowflake account: Google Ads, Facebook Ads, Microsoft Ads, TikTok Ads, Reddit Ads and Shopify.
On Databricks the steps are the same and the table names have three parts: see LinkedIn Ads to Databricks. For the layer you build on top, see Snowflake as a reusable reporting layer.
Turn your data into decisions.
Governed data marts give you the clean foundation ML needs to actually work.
- No AI hallucinations
- Analyst-governed definitions
- Every number traces to SQL
Frequently Asked Questions
How do I deliver LinkedIn Ads reports from Snowflake to the team?
Publish the query that joins analytics to campaign names as a SQL Data Mart. On its Destinations tab, create reports to Google Sheets, Data Studio, Slack or email, each with its own columns and schedule. Readers get campaign names and current numbers without a Snowflake login.
Why is there no campaign name in the LinkedIn Ads analytics table?
LinkedIn identifies each analytics row by URNs. The connector requests data by creative, campaign, campaign group and account, and the four URNs arrive together in the pivotValues column. Extract the campaign ID with REGEXP_SUBSTR and join to the table loaded from the Campaigns endpoint to get the name. Publishing that joined query as a Data Mart gives readers names directly.
How should I schedule LinkedIn Ads loads?
A daily Connector Run trigger suits most reporting. Each scheduled run is incremental: it loads the current day plus the days set by the Reimport Lookback Window, two by default. Raise the lookback if you report on conversions that LinkedIn attributes several days after the click. Rows are merged on the keys, so re-imported days replace the earlier version.
How can I connect LinkedIn Ads spend to CRM and pipeline data in Snowflake?
Load LinkedIn Ads with the connector and bring CRM data into the same Snowflake account. The join needs a shared key that your campaigns write into the lead record, such as a UTM campaign value or the LinkedIn campaign ID. Agree that convention first, then write the join in SQL and publish it as a Data Mart so the definition is reused.
Which LinkedIn Ads endpoints should I load for reporting?
Ad Analytics for daily performance: impressions, clicks, cost and conversions per creative. Campaigns for the campaign name, status, objective and type. Each endpoint is its own Data Mart and its own table. Campaign Groups, Creatives and Ad Accounts are available when you need those levels or their settings.
Why did my LinkedIn Ads run finish with a warning?
LinkedIn's analytics endpoint returns at most 15,000 rows per request and does not support paging. The connector requests one day at a time to stay under the limit. If a single day still exceeds it, for example in an account with more than 15,000 active creatives, the run ends with a Warning status and lists the affected days, for which data may be incomplete.
How do I load LinkedIn Ads data into Snowflake automatically?
Create a Data Mart in OWOX Data Marts on your Snowflake storage, set its definition type to Connector and choose LinkedIn Ads. Sign in with LinkedIn, enter the ad account ID, pick the Ad Analytics endpoint and the fields you need, then publish. A Connector Run trigger on the Triggers tab repeats the load daily, weekly, monthly or at an interval.
