# LinkedIn Ad Library Scraper — Ads, Impressions & Targeting (`memo23/linkedin-ads-scraper`) Actor

Scrape the LinkedIn Ad Library by company, keyword, payer, or country. Full ad copy, headlines, CTAs, destination URLs, payer entity, run dates, total impressions, impressions by country, targeting parameters, and raw media links (images, video streams, documents). Export JSON/CSV.

- **URL**: https://apify.com/memo23/linkedin-ads-scraper.md
- **Developed by:** [Muhamed Didovic](https://apify.com/memo23) (community)
- **Categories:** Social media, Lead generation, Automation
- **Stats:** 117 total users, 55 monthly users, 96.4% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $0.40 / 1,000 ad scraped (full details included)s

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## LinkedIn Ad Library Scraper — Ads, Impressions & Targeting

Turn any LinkedIn Ad Library search into a structured stream of sponsored ads.
Start from a company, keyword, payer, country, or a pasted Ad Library URL, and get full ad copy, creative links, run dates, impression ranges, per-country impression splits, and targeting parameters as clean rows.

***

### Why use this scraper?

- **Impressions and targeting, not just creatives** — impression ranges, per-country splits, and the targeting facets an advertiser used, parsed into typed fields.
- **Filter before you pay** — narrow by impression volume and by targeting category server-side, so you only get charged for rows you actually want.
- **Many searches, one run** — each entry in `searches` carries its own company, country, date window, and filters.
- **Numeric impressions** — `totalImpressionsMin` / `totalImpressionsMax` alongside the raw `"5k-10k"` string, so you can sort and threshold without parsing text.
- **No browser** — every page is fetched over plain HTTP, which is why runs are fast and cheap.
- **You are not billed for failures** — an ad whose detail page fails is logged and skipped, never pushed and never charged.

***

### Overview

The LinkedIn Ad Library publishes every sponsored ad running on LinkedIn. For ads shown to EU audiences it additionally publishes impression volumes and targeting parameters under DSA transparency rules.

This actor turns that library into a dataset: **one row per ad**. It's built for competitive-intelligence teams tracking a rival's creatives and spend signals, agencies auditing a category, and researchers studying commercial advertising.

Ads outside the EU still return the full creative, copy, payer, and run dates — but LinkedIn does not publish impressions or targeting for them, so those fields are absent. That's a limit of the source, not the scraper.

***

### Supported inputs

You can start a run four ways, and mix them freely:

**1. Company, keyword, or payer**

```json
{ "companies": ["HubSpot"], "keywords": ["AI agents"], "payers": ["HubSpot, Inc."] }
```

**2. Independent searches, each with its own filters**

```json
{
  "searches": [
    { "company": "HubSpot", "countries": ["DE"], "dateOption": "last-30-days" },
    { "keyword": "AI agents", "includedTargetingFacetCategories": ["JOB"] }
  ]
}
```

**3. A pasted Ad Library URL** — search pages or a single ad detail page

```json
{
  "startUrls": [
    "https://www.linkedin.com/ad-library/search?accountOwner=HubSpot&countries=DE",
    "https://www.linkedin.com/ad-library/detail/1441861963"
  ]
}
```

**4. Filters on top of any of the above** — country, date window, impression range, targeting categories.

**Not supported:** LinkedIn feed posts, organic company posts, Campaign Manager data, ad spend in currency (LinkedIn publishes impression ranges, never cost), and any page behind a login wall.

***

### Use cases

| Audience | What they use it for |
|---|---|
| Competitive intelligence | Track a competitor's live creatives, messaging shifts, and relative impression volume |
| Performance marketing | Mine winning hooks, headlines, and CTAs across a category before writing new ads |
| Agencies | Audit a prospect's current advertising and build the pitch deck from real data |
| Market research | Measure category-level advertising pressure and geographic mix over time |
| Policy & academic research | Study DSA-disclosed targeting parameters and impression distribution |
| Brand protection | Find who is running ads that name your brand, and which entity paid for them |

***

### How it works

1. Your companies, keywords, payers, and `searches` entries are turned into Ad Library search URLs with your filters applied.
2. Each search page is fetched over HTTP and read directly from the server-rendered markup.
3. Pagination follows the Ad Library's own pagination endpoint until `maxItems` is reached, backing off automatically if LinkedIn rate-limits.
4. When `scrapeAdDetails` is on, each ad's detail page is fetched for payer, run dates, impressions, and targeting.
5. Rows are pushed to the dataset as they complete, ready to export as JSON, CSV, or Excel.

***

### Input configuration

| Field | Type | Default | Description |
|---|---|---|---|
| `companies` | array | `[]` | Advertiser/company names — one search each |
| `keywords` | array | `[]` | Free-text keyword searches |
| `payers` | array | `[]` | Paying-entity names |
| `countries` | array | `[]` | ISO country codes, e.g. `["DE","FR"]` |
| `dateOption` | string | `all-time` | `all-time`, `last-30-days`, `current-month`, `current-year`, `last-year`. For a custom window set `startDate`/`endDate` instead; the actor switches to a custom range on its own |
| `startDate` / `endDate` | string | – | `YYYY-MM-DD`; when set they take priority over `dateOption` |
| `impressionsMinValue` | integer | – | Lower impression bound, combined with its unit |
| `impressionsMinUnit` | string | `thousand` | `none`, `thousand`, or `million` |
| `impressionsMaxValue` | integer | – | Upper impression bound |
| `impressionsMaxUnit` | string | `thousand` | `none`, `thousand`, or `million` |
| `includedTargetingFacetCategories` | array | `[]` | Ad targeting must use these: `LANGUAGE`, `LOCATION`, `AUDIENCE`, `DEMOGRAPHIC`, `COMPANY`, `EDUCATION`, `JOB`, `INTERESTS_AND_TRAITS` |
| `excludedTargetingFacetCategories` | array | `[]` | Ad targeting must NOT use these; same value list |
| `searches` | array | `[]` | Independent searches; each entry may override any filter above |
| `startUrls` | array | `[]` | Ad Library search or detail URLs |
| `maxItems` | integer | `100` | Maximum ads for the whole run, spent in URL order. Capped at 30 on the free plan |
| `maxItemsPerSearch` | integer | – | Ceiling per individual search. Set this whenever a run contains more than one company |
| `scrapeAdDetails` | boolean | `true` | Fetch each ad's detail page for impressions, targeting, and payer |
| `includeEmployerBranding` | boolean | `false` | Include employer-brand promos alongside sponsored ads |
| `sessionCookie` / `sessionCookies` | string / array | – | Optional `li_at` cookie(s) if a search returns only employer-brand results |
| `proxy` | object | residential, with an Apify Unblocker fallback | Overrides the built-in routing. Apify datacenter proxy does not get through LinkedIn's Cloudflare and should not be used here |
| `maxConcurrency` | integer | `3` | Parallel detail fetches |
| `minDelay` / `maxDelay` | integer | `1` / `3` | Delay range in seconds between requests |
| `debug` | boolean | `false` | Verbose per-request logging |

**Note on impression filters:** LinkedIn requires *both* bounds. If you set only one, the actor fills the other in automatically so your search still runs.

#### Example — a competitor's German ads above 100k impressions

```json
{
  "companies": ["HubSpot"],
  "countries": ["DE"],
  "impressionsMinValue": 100,
  "impressionsMinUnit": "thousand",
  "maxItems": 100
}
```

#### Example — two unrelated searches in one run

```json
{
  "searches": [
    { "company": "HubSpot", "countries": ["DE"], "dateOption": "last-30-days" },
    { "keyword": "AI agents", "includedTargetingFacetCategories": ["JOB"] }
  ],
  "maxItems": 200
}
```

***

### Output overview

One row per ad. Every row carries identity (`adId`, `adLibraryUrl`), the advertiser, the creative and copy, and the CTA destination. Rows for EU-facing ads additionally carry the payer, run window, impression range, per-country impression split, and targeting facets.

***

### Output sample

Real row, trimmed — produced by `{ "companies": ["HubSpot"], "countries": ["DE"] }`:

```json
{
  "adId": "1441861963",
  "adLibraryUrl": "https://www.linkedin.com/ad-library/detail/1441861963",
  "advertiserName": "HubSpot",
  "advertiserBio": "Promoted",
  "advertiserUrl": "https://www.linkedin.com/company/68529",
  "isPromoted": true,
  "adStatus": "ACTIVE",
  "format": "VIDEO",
  "headline": "Routine-Anfragen erledigt? Gute Arbeit.",
  "body": "HubSpot's Customer Agent erledigt Routineanfragen von allein und holt dich nur dazu, wenn es zählt. …",
  "ctas": ["Mehr erfahren"],
  "clickUrl": "http://hubspot.de/",
  "imageUrl": "https://media.licdn.com/dms/image/v2/D4D10AQH3o7vyy-TVcA/videocover-high/%E2%80%A6",
  "paidBy": "HubSpot, Inc.",
  "availabilityRaw": "Ran from Jul 1, 2026 to Jul 25, 2026",
  "firstShownAt": "2026-07-01",
  "lastShownAt": "2026-07-25",
  "totalImpressionsRaw": "5k-10k",
  "totalImpressionsMin": 5000,
  "totalImpressionsMax": 10000,
  "impressionsByCountry": [{ "country": "Germany", "percentage": 100 }],
  "targeting": [
    { "category": "Language", "included": ["Targeting includes Deutsch"], "excluded": [] },
    { "category": "Job", "included": ["Member"], "excluded": [] }
  ],
  "scrapedAt": "2026-07-25T19:48:25.821Z"
}
```

***

### Key output fields

**Identity** — `adId`, `adLibraryUrl`, `startUrl`, `scrapedAt`

**Advertiser** — `advertiserName`, `advertiserBio`, `advertiserUrl`, `advertiserLogo`, `paidBy`, `isPromoted`

**Creative** — `format` (`SINGLE_IMAGE`, `VIDEO`, `CAROUSEL`, `DOCUMENT`, `EVENT`, `TEXT`), `headline`, `body`, `imageUrl`, `imageUrls`, `videoUrl`, `videoPosterUrl`, `documentUrl`

**Destination** — `ctas`, `clickUrl`, `externalLinksInText`

**Run window** — `availabilityRaw`, `firstShownAt`, `lastShownAt`, `adStatus`

**Reach (EU-facing ads)** — `totalImpressionsRaw`, `totalImpressionsMin`, `totalImpressionsMax`, `impressionsByCountry[]`

**Targeting (EU-facing ads)** — `targeting[]` with `category`, `included[]`, `excluded[]`, `raw`

***

### Pricing

Pay per event, tiered by your Apify plan:

| Plan tier | Per ad |
|---|---|
| Free | $0.0015 |
| Bronze | $0.0009 |
| Silver | $0.0007 |
| Gold and above | $0.0004 |

Plus $0.00005 per actor start. Ads that fail detail extraction are never pushed and never charged.

***

### FAQ

**Do I need a LinkedIn account or cookie?**
No. The actor reads the public Ad Library anonymously. `sessionCookie` exists only as a fallback if a particular search returns nothing but employer-brand promos.

**Why are impressions and targeting missing on some ads?**
LinkedIn publishes them only for ads shown to EU audiences, under DSA transparency rules. A US-only campaign returns creatives and run dates but no impressions. Filtering by an EU country is the reliable way to get rows that always have them.

**Can I get ad spend in dollars?**
No. LinkedIn publishes impression *ranges*, never cost. Any tool claiming a spend figure is estimating it.

**Why did I get fewer ads than `maxItems`?**
Either the search genuinely has fewer matches, or LinkedIn stopped serving more results. The run's status message says which, so a short result never looks like a complete one.

**I put 50 companies in one run and only the first few returned anything.**
`maxItems` is a budget for the whole run and it is spent in URL order, so one company with a large ad library can absorb it before the later URLs are ever requested. Those companies then look identical to companies that never advertised. Set `maxItemsPerSearch` as the per-company ceiling and leave `maxItems` as the overall cap:

```json
{
  "companies": ["HubSpot", "NVIDIA", "Salesforce"],
  "countries": ["DE"],
  "maxItemsPerSearch": 100,
  "maxItems": 5000
}
```

**Can I filter by impressions with only a minimum?**
Yes. LinkedIn itself requires both bounds, so the actor fills the missing side in for you.

**Does it scrape organic LinkedIn posts?**
No — sponsored ads from the Ad Library only.

***

### Support

Found a bug or need a field that isn't here? Open an issue on the actor's Issues tab and it will be looked at.

***

### Additional services

Need a tailored export, a different schema, a scheduled feed into your warehouse, or a scraper for another site? Custom work is available — get in touch through the Apify profile.

***

### Explore more scrapers

Browse the full collection at [apify.com/memo23](https://apify.com/memo23) — scrapers for job boards, real-estate portals, review sites, social platforms, and more.

***

### 🤖 For AI Agents & LLM Apps

Compact reference for AI agents calling this actor via the [Apify MCP server](https://mcp.apify.com) or the Apify API (actor: `memo23/linkedin-ads-scraper`).

**Purpose:** Scrapes the LinkedIn Ad Library — one row per sponsored ad, including DSA-disclosed impression ranges and targeting facets for EU-facing ads.

**Minimal input:**

```json
{ "companies": ["HubSpot"], "countries": ["DE"], "maxItems": 25 }
```

**Output:** one dataset row per ad — `adId`, `adLibraryUrl`, `advertiserName`, `advertiserBio`, `advertiserUrl`, `isPromoted`, `adStatus`, `format`, `headline`, `body`, `ctas[]`, `clickUrl`, `externalLinksInText[]`, `imageUrl`, `imageUrls[]`, `videoUrl`, `documentUrl`, `paidBy`, `availabilityRaw`, `firstShownAt`, `lastShownAt`, `totalImpressionsRaw`, `totalImpressionsMin`, `totalImpressionsMax`, `impressionsByCountry[{country, percentage}]`, `targeting[{category, included[], excluded[]}]`, `scrapedAt`.

**Behaviors an agent should know:**

- Always set `maxItems`; a broad keyword can match thousands of ads.
- Set `maxItemsPerSearch` too whenever the run holds more than one company, otherwise the first busy advertiser spends the whole `maxItems` budget and the rest are skipped without being fetched.
- `impressionsMinValue` requires a unit (`none`/`thousand`/`million`); a one-sided range is completed automatically.
- `includedTargetingFacetCategories` accepts only: `LANGUAGE`, `LOCATION`, `AUDIENCE`, `DEMOGRAPHIC`, `COMPANY`, `EDUCATION`, `JOB`, `INTERESTS_AND_TRAITS`.
- Impressions and targeting are absent for non-EU ads — don't treat that as a scrape failure.
- Billing is per dataset item plus a per-run start fee; failed ads are not pushed and not charged.
- Set `scrapeAdDetails: false` for a cheap, fast listing-only pass without impressions or targeting.

***

### ⚠️ Disclaimer

This Actor is an independent tool and is not affiliated with, endorsed by, or sponsored by LinkedIn Corporation, Microsoft Corporation, or any of their subsidiaries. All trademarks mentioned are the property of their respective owners.

The scraper accesses only the publicly available LinkedIn Ad Library, which LinkedIn publishes for advertising transparency — no authenticated endpoints, paid features, or member content behind the linkedin.com login wall. The optional `sessionCookie` input is supplied by the user, applies only to those same public Ad Library pages, and is never required. Users are responsible for ensuring their use complies with LinkedIn's Terms of Service, applicable data-protection law (GDPR, CCPA, etc.), and any contractual obligations of their own organization.

***

### SEO Keywords

linkedin ads scraper, scrape linkedin ads, linkedin ad library scraper, linkedin ads api, linkedin.com ad library scraper, Apify linkedin ads, ad library scraper, ad intelligence scraper, competitor ads scraper, ad creative scraper, ad impressions data, ad targeting data, competitive ad intelligence, ad spend research, b2b advertising data, DSA ad transparency data, marketing intelligence scraper, paid social data, adtech data extraction, b2b marketing research

# Actor input Schema

## `companies` (type: `array`):

Company or advertiser names to search for (e.g. `HubSpot`, `Microsoft`). Each name runs as its own search.

## `keywords` (type: `array`):

Words or phrases to search ad copy for. Each keyword runs as its own search.

## `payers` (type: `array`):

Entities that paid for the ads (can differ from the advertiser). Each payer runs as its own search.

## `countries` (type: `array`):

Only include ads shown in these countries. Use 2-letter ISO codes (e.g. `US`, `GB`, `DE`). Leave empty for worldwide.

## `dateOption` (type: `string`):

Restrict results by when the ads ran. For an exact window, use the custom start/end dates below instead.

## `startDate` (type: `string`):

Only ads that ran on or after this date (YYYY-MM-DD). Overrides the date range preset when set.

## `endDate` (type: `string`):

Only ads that ran on or before this date (YYYY-MM-DD). Overrides the date range preset when set.

## `impressionsMinValue` (type: `integer`):

Only return ads with at least this many impressions. Combined with the unit below (e.g. 100 + thousand = 100,000). LinkedIn requires both a min and a max — if you set only one side, the other is opened up automatically.

## `impressionsMinUnit` (type: `string`):

Multiplier for the minimum impressions value.

## `impressionsMaxValue` (type: `integer`):

Only return ads with at most this many impressions, combined with the unit below.

## `impressionsMaxUnit` (type: `string`):

Multiplier for the maximum impressions value.

## `includedTargetingFacetCategories` (type: `array`):

Only return ads whose targeting uses these categories.

## `excludedTargetingFacetCategories` (type: `array`):

Only return ads whose targeting does NOT use these categories. A category may appear in both lists — that is LinkedIn's "Both" option.

## `searches` (type: `array`):

Run several unrelated searches in one go. Each entry takes its own company / payer / keyword plus optional countries, dateOption, startDate, endDate, impressions and targeting filters — anything left out falls back to the settings above. Example: \[{"company":"Nike","countries":\["DE"],"dateOption":"last-30-days"},{"keyword":"AI","impressionsMinValue":100,"impressionsMinUnit":"thousand"}]

## `maxItems` (type: `integer`):

Global cap on the number of ads scraped across all searches. On the free plan this is capped at 30.

## `maxItemsPerSearch` (type: `integer`):

Ceiling per individual search URL or search entry. maxItems is a budget for the whole run and is spent in order, so when you batch many companies into one run set this too — otherwise one company with many ads consumes the budget and the rest are skipped without being fetched. Example: 60 companies, maxItemsPerSearch 100, maxItems 6000.

## `scrapeAdDetails` (type: `boolean`):

Visit each ad's detail page to capture the complete record: full ad copy, headline, CTA, destination URL, payer entity, run dates, total impressions, impressions by country, targeting parameters, and raw media links. Same price per ad either way.

## `includeEmployerBranding` (type: `boolean`):

LinkedIn mixes promoted employer-brand pages into some searches. Enable to include them as items (marked `type: EMPLOYER_BRAND`).

## `sessionCookie` (type: `string`):

Paste your LinkedIn cookies. Easiest: install the EditThisCookie extension, log into LinkedIn, and paste the whole `name=value; name2=value2` cookie string here — at minimum `li_at` and `JSESSIONID` (JSESSIONID enables authenticated pagination). You can also paste just the `li_at` value. Stored encrypted; a throwaway account works fine. Leave blank to use the built-in session pool where available.

## `sessionCookies` (type: `array`):

Optional. A JSON array of LinkedIn cookies (each a bare li\_at value or a full cookie string) to rotate through — the actor spreads load across them and retires any that get logged out or challenged. Example: \["li\_at=AQ...", "li\_at=BR..."]. Use for higher-volume runs.

## `startUrls` (type: `array`):

Paste LinkedIn Ad Library URLs directly: search URLs (`https://www.linkedin.com/ad-library/search?...`) or single ad detail URLs (`https://www.linkedin.com/ad-library/detail/<id>`). Runs in addition to the searches configured above.

## `proxy` (type: `object`):

Proxy configuration. Leave unset: the actor then tries a cheap residential route first and falls back to Apify Unblocker only when that is refused, which is far cheaper than sending every page through Unblocker. Setting a value here replaces the cheap tier; the Unblocker fallback still applies.

## `maxConcurrency` (type: `integer`):

How many ad detail pages to load in parallel (1–5).

## `minDelay` (type: `integer`):

Minimum pause between page actions.

## `maxDelay` (type: `integer`):

Maximum pause between page actions.

## `debug` (type: `boolean`):

Verbose logs for troubleshooting.

## Actor input object example

```json
{
  "companies": [
    "HubSpot"
  ],
  "dateOption": "all-time",
  "impressionsMinUnit": "thousand",
  "impressionsMaxUnit": "thousand",
  "maxItems": 100,
  "scrapeAdDetails": true,
  "includeEmployerBranding": false,
  "maxConcurrency": 3,
  "minDelay": 1,
  "maxDelay": 3,
  "debug": false
}
```

# Actor output Schema

## `results` (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 = {
    "companies": [
        "HubSpot"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("memo23/linkedin-ads-scraper").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 = { "companies": ["HubSpot"] }

# Run the Actor and wait for it to finish
run = client.actor("memo23/linkedin-ads-scraper").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 '{
  "companies": [
    "HubSpot"
  ]
}' |
apify call memo23/linkedin-ads-scraper --silent --output-dataset

```

## MCP server setup

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

```

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/xOYxOUTQFBfKzkdSW/builds/pgk1uKUrsZKHUmSGg/openapi.json
