{
  "openapi": "3.0.1",
  "info": {
    "title": "LinkedIn Ad Library Scraper | from $1.00/1K Ads, No Login",
    "description": "Scrape the LinkedIn Ad Library without login: ad copy, creatives, payer, run dates, EU impressions and targeting as structured JSON. Search by advertiser, keyword, country or date. Pure HTTP, fast and cheap.",
    "version": "0.2",
    "x-build-id": "S4YaSWY6SUJinAMcE"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/alexmorain~linkedin-ad-library-scraper/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-alexmorain-linkedin-ad-library-scraper",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/acts/alexmorain~linkedin-ad-library-scraper/runs": {
      "post": {
        "operationId": "runs-sync-alexmorain-linkedin-ad-library-scraper",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor and returns information about the initiated run in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/runsResponseSchema"
                }
              }
            }
          }
        }
      }
    },
    "/acts/alexmorain~linkedin-ad-library-scraper/run-sync": {
      "post": {
        "operationId": "run-sync-alexmorain-linkedin-ad-library-scraper",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "inputSchema": {
        "type": "object",
        "properties": {
          "advertisers": {
            "title": "Advertisers",
            "type": "array",
            "description": "Company names or LinkedIn company slugs, one per line: <code>nvidia</code>, <code>doctolib</code>. Each one becomes its own search, and the results carry the advertiser name so you can tell them apart.<br><br><b>How to be sure of a name:</b> open the company LinkedIn page, the slug is the last part of the URL (<code>linkedin.com/company/<b>nvidia</b></code>). Some groups advertise under a legal entity rather than their brand; if a search comes back empty, that is usually why.<br><br>Leave empty if you are searching by keyword across the whole library instead.",
            "items": {
              "type": "string"
            }
          },
          "keyword": {
            "title": "Keyword",
            "type": "string",
            "description": "A word or phrase to search for in the ads themselves.<br><br>Used <b>alone</b>, it sweeps the entire Ad Library and finds every advertiser running that message: the way to discover competitors you had not listed. Used <b>together with advertisers</b>, it narrows each advertiser's ads to those mentioning it.<br><br>Leave empty to get all the ads of the advertisers above."
          },
          "searchUrls": {
            "title": "Search URLs",
            "type": "array",
            "description": "Full Ad Library search URLs, copied straight from your browser: <code>https://www.linkedin.com/ad-library/search?...</code><br><br>The escape hatch: build any filter combination by hand on the LinkedIn site, paste the resulting URL here, and the Actor reproduces it exactly. Useful for filters this input does not expose.<br><br><b>Single ads work too:</b> paste an ad URL such as <code>https://www.linkedin.com/ad-library/detail/1279725554</code> and the Actor fetches that ad directly. It is billed like any other ad: with details when <b>Include ad details</b> is on, at the listing rate when it is off. To fetch single ads with their details whatever that setting, use <b>Ad URLs</b> below.<br><br>One caveat, and it is the reason most competing tools return wrong data: LinkedIn <b>silently ignores</b> a start and end date in a URL unless <code>dateOption=custom-date-range</code> is present too. A URL without it returns the whole corpus while looking filtered. Prefer the date fields below, which always send the complete parameter set.",
            "items": {
              "type": "object",
              "required": [
                "url"
              ],
              "properties": {
                "url": {
                  "type": "string",
                  "title": "URL of a web page",
                  "format": "uri"
                }
              }
            }
          },
          "adUrls": {
            "title": "Ad URLs",
            "type": "array",
            "description": "Single ads to fetch directly, one per line: an ad URL such as <code>https://www.linkedin.com/ad-library/detail/1279725554</code>, or the numeric ID alone (<code>1279725554</code>).<br><br>Each ad comes back <b>with its details</b> (full copy, payer, run dates, and for EU-served ads impressions and targeting), whatever <b>Include ad details</b> says, and is billed at the with-details rate. One request per ad, no search.<br><br>The way to refresh a list of ads you already know, or to enrich the IDs a listing run returned. Max ads per query, Ad formats and the date fields do not apply to these ads: you named them one by one.",
            "items": {
              "type": "string"
            }
          },
          "countries": {
            "title": "Countries",
            "type": "array",
            "description": "Countries where the ads were shown, as ISO 3166-1 alpha-2 codes (<code>US</code>, <code>FR</code>, <code>DE</code>) or English names (<code>United States</code>, <code>France</code>). Names are converted to codes; an entry the Actor does not recognise stops the run with a message naming it, rather than returning nothing.<br><br>Leave empty for worldwide, which is the honest default when you do not know where a company advertises. Note that impressions and targeting data only exist for ads served in the <b>EU</b>, so an EU country here is what makes the richest fields appear.",
            "items": {
              "type": "string"
            }
          },
          "dateOption": {
            "title": "Date range",
            "enum": [
              "all-time",
              "last-30-days",
              "current-month",
              "current-year",
              "last-year"
            ],
            "type": "string",
            "description": "The period during which the ads ran. <b>The library only holds roughly the last 12 months</b>, whatever you ask for, so \"All time\" means \"the last twelve months\".<br><br>Ignored when you fill the two custom date fields below.",
            "default": "all-time"
          },
          "startDate": {
            "title": "Custom start date",
            "type": "string",
            "description": "<code>YYYY-MM-DD</code>, within the last 12 months. It overrides the dropdown above. Filled alone, the range runs from this date to yesterday: LinkedIn returns nothing for a window that ends today.<br><br>LinkedIn also returns nothing for a start date more than 12 months back, so an older date is moved forward to exactly 12 months back, with a warning in the log. A start date after the end date or after the last day LinkedIn serves (usually yesterday), or an end date more than 12 months back, stops the run with a clear error before any ad is fetched. With Search URLs or Ad URLs in the same input, the run goes on with those URLs only and says so in the log and the status message.<br><br>The custom dates apply to Advertisers, Keyword and Payer searches. Search URLs keep their own parameters and ignore them, and so do Ad URLs.<br><br>The Actor always sends the extra parameter LinkedIn requires for a custom range to actually apply, so the filter you set is the filter you get."
          },
          "endDate": {
            "title": "Custom end date",
            "type": "string",
            "description": "<code>YYYY-MM-DD</code>. Goes with the custom start date. Filled alone, the range starts from the oldest ads the library holds, about 12 months back.<br><br>LinkedIn returns nothing for a window that ends today, so an end date of today or later is moved back to yesterday.<br><br>An end date more than 12 months back stops the run with a clear error before any ad is fetched: the library holds nothing that old."
          },
          "payer": {
            "title": "Payer name",
            "type": "string",
            "description": "Filter on the entity that paid for the ad, the \"Paid for by\" line: often a legal entity or an agency rather than the brand.<br><br>Useful to map a group whose subsidiaries advertise separately, or to find every brand handled by one agency. Leave empty in normal use."
          },
          "adFormats": {
            "title": "Ad formats",
            "type": "array",
            "description": "Keep only these formats, one per line, as LinkedIn names them: <code>Single Image Ad</code>, <code>Video Ad</code>, <code>Carousel Ad</code>, <code>Message Ad</code>, <code>Document Ad</code>, <code>Job Ad</code>, <code>Follow Company Ad</code>. LinkedIn also lists <code>Text Ad</code>, <code>Event Ad</code>, <code>Spotlight Ad</code> and <code>Conversation Ad</code>, not yet seen by this Actor. Case does not matter and the final \"Ad\" is optional: <code>video</code> works.<br><br>LinkedIn has no format filter of its own, so the Actor reads the results and drops the other formats <b>before</b> they are returned: you are only charged for the ads you get. <b>Max ads per query</b> counts the ads kept, so a cap of 100 still aims at 100 ads of your formats.<br><br>To keep a rare format from scanning a whole library, a capped search reads at most 10 times the pages the cap needs, and never fewer than 20 pages (480 ads): 50 pages for a cap of 100. The status message says when a search stopped at that limit. With no cap, every page is read.<br><br>Leave empty for every format. Does not apply to Ad URLs.",
            "items": {
              "type": "string"
            }
          },
          "includeDetails": {
            "title": "Include ad details",
            "type": "boolean",
            "description": "Opens the detail page of every ad, which adds the <b>full ad copy</b>, the payer, the exact run dates and, for ads served in the EU, the <b>impressions</b> (total and per country) and the <b>targeting criteria</b> the advertiser selected.<br><br>Off by default because it costs one extra request per ad and is billed at a higher per-ad rate. The efficient pattern: sweep wide with it off to see who advertises and how much, then re-run with it on for the handful of advertisers you actually analyse.<br><br>Without it you still get advertiser, format, headline, creatives and a copy preview.",
            "default": false
          },
          "maxAdsPerQuery": {
            "title": "Max ads per query",
            "minimum": 0,
            "type": "integer",
            "description": "Stops each search after this many ads returned. The cap applies per search, so three advertisers with a cap of 100 can return up to 300 ads. Ads dropped by <b>Ad formats</b> or skipped by <b>New ads only</b> do not count, and Ad URLs are not capped.<br><br><b>Keep a cap on your first run.</b> A single large advertiser can be running several hundred ads, and you pay per ad returned. 100 is enough to see the shape of a competitor's advertising; raise it once you know what you are getting.<br><br>Set <code>0</code>, or leave the field empty, for no limit at all."
          },
          "mode": {
            "title": "Mode",
            "enum": [
              "all",
              "new-only"
            ],
            "type": "string",
            "description": "<b>All ads</b> returns every ad the search finds, on every run. Use it for a one-off export, or to build your first snapshot.<br><br><b>New ads only</b> keeps a persistent registry of every ad previous runs have returned and charged, and returns only ads never seen before. This turns the Actor into monitoring: schedule it daily and a quiet day costs you the start fee alone, since an ad you are not shown is an ad you are not charged for. The first run in this mode still returns everything, because nothing has been seen yet. Once a run has read a search to its very end, later runs stop that search after two pages in a row with no new ad, since LinkedIn lists the newest ads first: a quiet day is quick too. Until then, and again after any run that stopped early (cap, maximum charge, timeout, block), the next run reads the search to the end, so nothing is skipped.<br><br>An ad stopped by your maximum charge before being billed is not recorded, so it comes back on the next run.",
            "default": "all"
          },
          "registryKey": {
            "title": "Delta registry key",
            "type": "string",
            "description": "Only used by <b>New ads only</b> mode. Names the persistent registry of already-seen ads.<br><br>Leave empty and it is derived from what you search (advertisers, keyword, payer, countries, Search URLs, Ad URLs), which is what you want: the same search always finds its own registry again, and two different searches never pollute each other. Dates, formats, the cap and the details setting are not part of it. Set it by hand only to make several schedules share one memory, for example a daily run and a weekly catch-up over the same advertisers."
          },
          "webhookUrl": {
            "title": "Webhook URL",
            "type": "string",
            "description": "If set, the Actor POSTs a small JSON summary to this URL at the end of the run, <b>only when at least one ad was returned</b>: <code>actor</code>, <code>runId</code>, <code>mode</code>, <code>newAds</code>, <code>queries</code>. Combined with New ads only and a daily schedule, that is a push notification: silence when nothing moved, a call when a competitor launches ads.<br><br>Use the final URL: a redirect is not followed, except a 307 or 308, which gets the same POST once. Leave empty to skip it. An unreachable webhook is logged as a warning and never fails the run. Stored encrypted, like a password, since a webhook URL often carries its own access token."
          },
          "proxyConfiguration": {
            "title": "Proxy configuration",
            "type": "object",
            "description": "Leave as is. Residential proxies are pre-selected because LinkedIn's bot check now blocks most datacenter IPs.<br><br><b>If you run the Actor through the API with the default <code>{\"useApifyProxy\": true}</code></b>, it starts on datacenter proxies and <b>switches to residential by itself</b> at the first block, for the rest of the run. The per-ad price is the same either way.<br><br>If you pick a proxy group or your own proxy URLs here, the Actor keeps exactly what you chose and never switches.",
            "default": {
              "useApifyProxy": true
            }
          }
        }
      },
      "runsResponseSchema": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "actId": {
                "type": "string"
              },
              "userId": {
                "type": "string"
              },
              "startedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "finishedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "status": {
                "type": "string",
                "example": "READY"
              },
              "meta": {
                "type": "object",
                "properties": {
                  "origin": {
                    "type": "string",
                    "example": "API"
                  },
                  "userAgent": {
                    "type": "string"
                  }
                }
              },
              "stats": {
                "type": "object",
                "properties": {
                  "inputBodyLen": {
                    "type": "integer",
                    "example": 2000
                  },
                  "rebootCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "restartCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "resurrectCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "computeUnits": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "options": {
                "type": "object",
                "properties": {
                  "build": {
                    "type": "string",
                    "example": "latest"
                  },
                  "timeoutSecs": {
                    "type": "integer",
                    "example": 300
                  },
                  "memoryMbytes": {
                    "type": "integer",
                    "example": 1024
                  },
                  "diskMbytes": {
                    "type": "integer",
                    "example": 2048
                  }
                }
              },
              "buildId": {
                "type": "string"
              },
              "defaultKeyValueStoreId": {
                "type": "string"
              },
              "defaultDatasetId": {
                "type": "string"
              },
              "defaultRequestQueueId": {
                "type": "string"
              },
              "buildNumber": {
                "type": "string",
                "example": "1.0.0"
              },
              "containerUrl": {
                "type": "string"
              },
              "usage": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "integer",
                    "example": 1
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_UNBLOCKER_UNITS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "usageTotalUsd": {
                "type": "number",
                "example": 0.00005
              },
              "usageUsd": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "number",
                    "example": 0.00005
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_UNBLOCKER_UNITS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}