# LinkedIn Ads Tracker — New & Changed Ads (`agency-shift/linkedin-ads-competitor-tracker`) Actor

Observe public LinkedIn Ad Library searches and compare saved runs. Export verified ad IDs, advertiser identity, available ad copy and source links; receive new and changed observations with before-and-after values. Bounded searches never infer stopped ads.

- **URL**: https://apify.com/agency-shift/linkedin-ads-competitor-tracker.md
- **Developed by:** [Mako](https://apify.com/agency-shift) (community)
- **Categories:** Automation, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.38 / 1,000 delivered ad records

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## LinkedIn Ads Tracker — New & Changed Ads

Observe selected public LinkedIn Ad Library searches, then compare later runs to receive newly observed ads and changes in available ad copy, advertiser metadata and creative fields. Export JSON, CSV or Excel through your Apify dataset.

**Validated on 1 October 2026:** cloud tests retrieved 24 listing observations in one source request and 30 unique ads across two pages. Separate tests checked detail-page advertiser filtering and a genuine empty search. Public access uses Apify Unblocker; direct and residential connections returned HTTP 403 during testing. Source availability can change.

### Pricing: service fees plus Apify usage

**$0.38 per 1,000 delivered ad records, plus startup and separate Apify platform usage.** The ad-record service fee is $0.00038 for each dataset record delivered. The same event rates apply to every Apify plan.

Each run also has an Actor-start fee of **$0.00005 per allocated GB of memory, with a minimum of one startup event**. At the supported 256 MB or 512 MB allocations, that is one $0.00005 event. Apify Unblocker, compute, data transfer and storage are billed separately at your Apify plan's rates. This is not an all-inclusive per-record price.

The default Unblocker connection uses **10 Unblocker units per successful source request**. Listing mode can return multiple ads from one request. Detail mode adds a request for every examined ad, including ads later excluded by an exact advertiser filter. Failed runs, empty searches, unchanged monitoring runs and filter exclusions can consume platform usage without producing billable ad records. Startup fees still apply.

For example, 24 delivered listing records have a **$0.00912 ad-record fee**, plus **$0.00005 startup** at 256 MB or 512 MB, plus the run's actual platform usage. This example is service-fee arithmetic, not an estimate of the total bill.

**The maximum Actor charge limits event fees, not total platform usage.** The lowest permitted maximum Actor charge is $0.01; this is a configurable ceiling, **not a $0.01 minimum fee**. Bound source work with `maxRequests`, `maxPagesPerSearch`, `maxAdsPerSearch` and a run timeout. `maxResults` alone does not bound source work in Changes mode. Start with a small run and inspect its actual cost before scheduling it.

### Start with a small search

Open [LinkedIn Ad Library](https://www.linkedin.com/ad-library/home), make a filtered search and copy its search URL. For example:

```json
{
  "searchUrls": ["https://www.linkedin.com/ad-library/search?accountOwner=Microsoft&countries=US"],
  "country": "US",
  "mode": "snapshot",
  "includeDetails": false,
  "maxResults": 24,
  "maxAdsPerSearch": 24,
  "maxPagesPerSearch": 1,
  "maxRequests": 1,
  "proxyConfiguration": {"useApifyProxy": true, "apifyProxyGroups": ["UNBLOCKER"]}
}
```

This reads at most one search page and returns up to 24 listing observations without fetching detail pages. If LinkedIn blocks access, the run fails with the source error rather than pretending there are no ads. Apify Unblocker is selected by default. It has platform usage costs: the documented rate at publication is 10 Unblocker units per successful request. A successful listing page uses 10 units and can provide multiple ads. Setting `includeDetails: true` adds a detail request for each examined ad: one search page plus three detail pages uses up to 40 successful-request units. You can explicitly choose another proxy or disable it; there is no automatic escalation, account login, session-cookie input or paid data-provider dependency. See [Apify Unblocker documentation](https://docs.apify.com/proxy/unblocker).

**Default listing mode** returns ad ID, advertiser name, available preview copy, headline, first creative image and source link in one listing request. Preview copy may be shortened, and advertiser company URL, payer, CTA and dates can be unavailable. Every record states `detailLevel: "listing"`. Set `includeDetails: true` to request public detail-page metadata; those records state `detailLevel: "detail"`.

Advertiser-name searches can match multiple organizations. Check `advertiserName` and `advertiserUrl`, or set `includeDetails: true` together with `exactAdvertiserUrl` to keep only exact company URL matches. Numeric company URLs and named company URLs are not resolved as equivalent. The country in the copied URL must match the `country` input.

### Monitor changes

Set `mode` to `changes`, choose a stable `monitorName`, and rerun or schedule that input in Apify. History is isolated by user, Actor, monitor name, normalized search URL, country, detail mode and exact advertiser filter.

- `initial`: first baseline observation, unless `emitInitialSnapshot` is false.
- `new`: an ad first observed by that saved monitor after its baseline.
- `changed`: a recognized creative or advertiser field changed, with `changedFields`, `before` and `after`.
- Unchanged observations create no dataset record. Source retrieval and proxy usage can still cost money.

`firstObservedAt` is our first observation, not a claimed campaign launch date. Ads absent from a later scan remain in history. We do not infer that a campaign stopped, especially when results are limited or a request failed. Missing source fields do not erase earlier values in Changes mode.

Changed fields: advertiser name and URL, format, copy, headline, destination URL, image URL, CTA label and paying entity. Source date-label updates alone do not create a creative-change event. Expiring image signatures are ignored in comparisons.

### Output

Each record includes `adId`, advertiser identity, `sourceUrl`, `searchUrl`, `observedAt`, `firstObservedAt`, `eventType`, and `eventId`. Recognized copy, headline, image, destination, CTA label, ad-type, payer and source run-date fields are nullable. A linked landing page is never fetched. Available fields depend on LinkedIn's public HTML and ad format.

The **Coverage and errors** output opens `SUMMARY`, including requests made, pages examined, detail requests, exact-filter exclusions, failed searches, partial coverage, output counts and remaining searches. A partially successful scan can preserve verified ads while reporting incomplete coverage. If every search fails and no records were delivered, the run is marked failed; resumed saved output followed by new source failures is reported as partial.

### Bounds and reliability

`maxRequests` is a hard ceiling across listing and detail requests, including unsuccessful requests. `maxPagesPerSearch` and `maxAdsPerSearch` cap discovery and optional detail work before further source requests are made. There are no automatic retries. `maxResults` caps output and may be lower than the number of details examined in Changes mode.

History is bounded to 10,000 ads per search and 16 MB per checkpoint. A monitor lock prevents concurrent history writes. Pending output is saved before delivery and resumed later with the same event IDs. Delivery is at least once: interruption after dataset writing but before a checkpoint can repeat a record and, under per-event pricing, its charge. Consumers should deduplicate `eventId`.

Changing the search filters creates a separate baseline. Use a new monitor name to deliberately reset history. Saved history uses Apify key-value storage and may incur storage costs. Use a sensible schedule such as once daily after validating a small run.

### Limits

This is a bounded observation tool, not an official LinkedIn API or a complete advertiser inventory. No performance conclusions, campaign budgets, lead enrichment, OCR, creative downloading, automatic campaign-stop detection or LinkedIn publishing are provided. LinkedIn can restrict access, change markup or omit fields. No LinkedIn username, password or cookies are requested.

### Development validation

Run `npm test`. Parser tests include clearly labeled excerpts of actual anonymous cloud search and detail HTML, plus synthetic failure, pagination and history scenarios. The release validation record separately documents end-to-end cloud runs and output comparisons.

# Actor input Schema

## `searchUrls` (type: `array`):

Copy 1–5 search URLs from LinkedIn Ad Library. Include an advertiser or keyword filter. Advertiser-name searches can match several companies; use the exact advertiser URL filter when attribution matters.

## `country` (type: `string`):

Two-letter uppercase country code. Any countries parameter in a search URL must match this value. This is a source search filter, not proof of every country an ad targets.

## `exactAdvertiserUrl` (type: `string`):

Only retain details whose public advertiser company URL exactly matches this URL. LinkedIn numeric company IDs and slugs are not automatically resolved as aliases. Nonmatching ads still consume bounded detail requests. Requires Fetch each ad detail page.

## `mode` (type: `string`):

Snapshot exports observed ads each run. Changes compares stored history for the same user, Actor, monitor, search URL, country and exact advertiser filter. Detail mode is a separate history scope.

## `maxResults` (type: `integer`):

Caps delivered dataset records charged at $0.00038 each. This does not cap source requests in Changes mode or separate platform usage. The maximum Actor charge is an event-fee ceiling, not a total usage budget.

## `maxAdsPerSearch` (type: `integer`):

Caps distinct listing ads examined. In detail mode this also caps ad detail requests including failures and exact-filter exclusions.

## `maxPagesPerSearch` (type: `integer`):

Caps source search pages before further requests are made.

## `maxRequests` (type: `integer`):

Hard ceiling on combined listing and detail source requests, including unsuccessful requests. Bounds source work before another request; no automatic retries or proxy upgrades. Separate platform usage can accrue even if no records are delivered.

## `monitorName` (type: `string`):

Reuse this name in Changes mode for persistent history. A new name creates a separate baseline.

## `emitInitialSnapshot` (type: `boolean`):

When disabled, seed the first Changes baseline without initial ad-record fees. Startup and platform usage still apply. Later newly observed and changed records incur the ad-record service fee.

## `proxyConfiguration` (type: `object`):

Apify Unblocker is selected by default because direct and residential requests were blocked in validation. Usage is billed separately from Actor fees at your plan rates: 10 Unblocker units per successful source request, plus compute and storage. Disable or choose another proxy explicitly if appropriate; no automatic upgrade.

## `includeDetails` (type: `boolean`):

Default false: collect listing previews with fewer requests; copy may be shortened and company URL, payer, CTA or dates may be absent. True adds one source request per examined ad, increasing separate platform usage even for ads excluded by the exact advertiser filter. Required for exact advertiser URL filtering. Changing this setting creates a separate baseline.

## Actor input object example

```json
{
  "searchUrls": [
    "https://www.linkedin.com/ad-library/search?accountOwner=Microsoft&countries=US"
  ],
  "country": "US",
  "mode": "snapshot",
  "maxResults": 25,
  "maxAdsPerSearch": 25,
  "maxPagesPerSearch": 3,
  "maxRequests": 100,
  "monitorName": "default",
  "emitInitialSnapshot": true,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "UNBLOCKER"
    ]
  },
  "includeDetails": false
}
```

# Actor output Schema

## `records` (type: `string`):

No description

## `summary` (type: `string`):

No description

## `error` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "searchUrls": [
        "https://www.linkedin.com/ad-library/search?accountOwner=Microsoft&countries=US"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "UNBLOCKER"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("agency-shift/linkedin-ads-competitor-tracker").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "searchUrls": ["https://www.linkedin.com/ad-library/search?accountOwner=Microsoft&countries=US"],
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["UNBLOCKER"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("agency-shift/linkedin-ads-competitor-tracker").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "searchUrls": [
    "https://www.linkedin.com/ad-library/search?accountOwner=Microsoft&countries=US"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "UNBLOCKER"
    ]
  }
}' |
apify call agency-shift/linkedin-ads-competitor-tracker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,agency-shift/linkedin-ads-competitor-tracker"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/RPUi0PbfvUxLBBIew/builds/uVfombhYb0bD7o5Kf/openapi.json
