# Competitor Ads Checker: Meta, LinkedIn & Google Ad Library (`doesaiknow/competitor-ads-checker`) Actor

Check if companies run ads on Google, Meta (Facebook & Instagram) and LinkedIn. Paste domains, get one row per domain: active, not found or unknown per platform, ad count, first/last seen, ad IDs, ad library links and a company profile. Qualify leads and track competitor ads. MCP-ready.

- **URL**: https://apify.com/doesaiknow/competitor-ads-checker.md
- **Developed by:** [David S](https://apify.com/doesaiknow) (community)
- **Categories:** Marketing, Lead generation, Social media
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.05 / domain checked

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?

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

## Competitor Ads Checker — Google, Meta & LinkedIn Ad Library by domain

Is this company running ads? Paste a list of company domains and get **one row per domain** with a clear answer for each ad library: **Google** (ads shown in the last 7 days) and **Meta (Facebook & Instagram)** (ads with status ACTIVE on the check date). **LinkedIn (beta) — add to platforms** to also check ads that ran in the last 30 days. Every positive answer comes with evidence: an ad ID, a link to the public ad library, dates and the scope that was checked.

Built for agencies that qualify leads, sales teams that enrich a B2B prospect list and marketers who track competitors' ads for competitive intelligence. It is MCP-ready, so AI agents can call it as a tool, and it plugs into Clay, Zapier, Make, n8n or the Apify API.

### What does Competitor Ads Checker do?

It answers one question per company domain: **are ads pointing to this domain in the public ad libraries, within a stated time window?**

For every domain you get:

- a status per platform: `active`, `not_found` or `unknown` (never a bare yes/no);
- the number of ads (Google gives an estimate), first and last seen dates;
- one evidence ad with its ID and ad library link, plus up to 10 sample ads;
- the Google ad account behind the ads, the matched Facebook page and the LinkedIn company page;
- a light company profile: name, website, LinkedIn page, industry, employee range, HQ country.

`not_found` always means "not found in this window and scope" — not "this company never advertises". `unknown` means the check could not be completed reliably, and it is free.

### Who uses it

- **PPC and social media agencies** — qualify leads before outreach: who already runs Google or Meta ads, and who does not.
- **Sales teams** — enrich a B2B prospect list or CRM with "running ads" flags and a company profile, one row per company.
- **Marketers and founders** — track competitors and see which ad libraries they appear in, with links to their ads.

### Google Ads Library check

The Google check searches the **Google Ads Transparency Center** by the domain you entered, for ads shown in the **last 7 days, any region**.

You get the ad count estimate, first and last seen dates (UTC; the website may show one day earlier), the advertiser ID, a verified flag and the **ad account name**. The ad account is often an agency or reseller, not the company itself, so the row adds `isThirdPartyAccount` with the reason (`known_agency`, `no_shared_token`, `generic_token_only`, `shared_token`) and `advertisersCount` when several accounts advertise for the same domain. An account carrying the brand's name is preferred; other accounts are listed in `thirdPartyAdvertisers`. If the domain redirects elsewhere and only other companies advertise on it (for example landing pages hosted on a platform's domain), Google is `unknown` (`third_party_advertisers_only`, free).

### Meta Ad Library check (Facebook & Instagram)

The Meta check looks for ads with status **ACTIVE** on the check date, in all countries. Finding the right Facebook page is the hard part, so the page match is explicit:

- `high` — the page is linked from the company website (Facebook or Instagram link) or its alias equals the domain name;
- `legal_name` — the page name equals the full legal company name on the website (for example "… GmbH");
- `ad_domain` — the active ads link to the domain, so the page that publishes them is used;
- `medium` — the page only matches by name; a name match alone never produces `active`;
- `alias_no_ad_history` — the linked page has no ads in the library.

**`alias_no_ad_history`** (`not_found`, `confidence: "medium"`, charged): the linked Facebook page is absent from the library's page search, which lists only pages that have run ads, and no ad links to the domain. When the page publishing ads that link to the domain does not share the brand's name (a sponsor, agency or franchisee), the row says `publisherIsThirdParty: true`.

### LinkedIn Ad Library check (beta)

LinkedIn (beta) — add to platforms: it is off by default and runs only when `platforms` includes `"linkedin"`. LinkedIn is checked when the company website links its LinkedIn page and that page's website points back to the domain. A link with a numeric company ID only, or a website that redirects to another domain, gives `unknown` (free). It returns ads that ran in the **last 30 days (ending yesterday)**, the ad count, dates and the LinkedIn company ID. Without a confirmed company page the LinkedIn answer is `unknown` (free) — there is no guessing by company name.

LinkedIn is marked `beta: true`. Some LinkedIn ads come without dates; then `firstSeen` is `null` and the evidence says `dateSource: "query_window"`. When a library returns more pages than the Actor reads and no evidence was found, the answer is `unknown` (`pagination_unresolved`), never `not_found`.

### Domains that redirect to another domain

Results always describe **the domain you entered**. A redirect can lead to another company (acquisition, expired domain bought by someone else), so status and the charge use only the entered domain: names and links from the destination website are ignored, Meta counts only ads linking to the entered domain, and LinkedIn is `unknown` (`cross_domain_redirect`, free). The destination's Google result is shown in `redirectTarget` — **for information only**, it never changes status or billing.

### Input

```json
{
    "domains": ["reliantplumbing.com", "tayloremmet.co.uk", "https://www.simplygreenpest.com/"],
    "platforms": ["google", "meta", "linkedin"],
    "includeSampleAds": true,
    "maxSampleAdsPerPlatform": 3,
    "includeCompanyProfile": true
}
```

- `domains` — up to 500 domains or website URLs. They are normalized (no `https://`, `www.` or path) and duplicates are checked once.
- `platforms` — `google` and `meta` by default; LinkedIn (beta) — add `linkedin` to platforms. A platform you leave out is `null` in the row.
- `includeSampleAds` / `maxSampleAdsPerPlatform` — sample ads per platform (0–10). The evidence ad is always included.
- `includeCompanyProfile` — adds the company profile; it never triggers an extra paid lookup.

### Output — one row per domain

Illustrative row (shortened):

```json
{
    "schemaVersion": "1",
    "domain": "tayloremmet.co.uk",
    "status": "ok",
    "isRunningAds": true,
    "platformsWithAds": ["google", "meta", "linkedin"],
    "totalAdsCount": 58,
    "google": {
        "status": "active",
        "adsCountEstimate": 40,
        "adAccountName": "Taylor&Emmet LLP",
        "isThirdPartyAccount": false,
        "lastSeen": "2026-10-16",
        "evidence": { "adId": "CR0123…", "libraryUrl": "https://adstransparency.google.com/advertiser/AR…/creative/CR…" },
        "scope": { "window": "7d", "dateFrom": "2026-10-09", "dateTo": "2026-10-16", "regions": "anywhere" }
    },
    "meta": { "status": "active", "pageName": "Taylor Emmet", "pageMatch": "high", "activeAdsCount": 17 },
    "linkedin": { "status": "active", "beta": true, "adsCount": 1, "firstSeen": null, "evidence": { "dateSource": "query_window" } },
    "company": { "name": "Taylor Emmet Solicitors", "industry": "Legal Services", "employeeRange": "201-500 employees", "hqCountry": "GB", "source": "linkedin" }
}
```

| Field | Meaning |
|---|---|
| `status` | `ok` = every selected platform resolved; `partial` = some platform `unknown`; `unknown` = none resolved; `invalid_input`; `skipped` |
| `crossDomainRedirect` | `true` when the website redirects to another domain; results still describe the entered domain only |
| `redirectUnrelated` | `true` when that other domain does not look related by name (information only) |
| `redirectTarget` | information only: the destination domain and its Google result; never changes status or billing |
| `isRunningAds` | `true` = ads found on a selected platform; `false` = not found on any selected platform in its window; `null` = not known |
| `google` / `meta` / `linkedin` | per-platform status, confidence, reason, counts, dates, evidence, scope, sample ads |
| `company` | light company profile (never lists people) |
| `billed` | `true` when this row was charged |

Dataset views: **Competitor ads overview**, **Lead qualification** and **Sample ads**.

### How to see what ads a company is running

1. Add the company domains to `domains` (or paste website URLs).
2. Pick the ad libraries in `platforms`.
3. Run the Actor and open the **Competitor ads overview** view.
4. Click the `libraryUrl` or evidence link to open the ads in the public ad library.

One run covers three ad libraries for up to 500 domains.

### Pricing

**$0.07 per checked domain; unknown = free.** That is the Starter plan price: $0.10 on Free, $0.06 on Scale, $0.05 on Business and higher. A domain is charged once, only when every selected platform returned `active` or `not_found` (row status `ok`), LinkedIn (beta) included when added. Rows with status `partial`, `unknown`, `invalid_input` or `skipped` are free, and duplicates are merged before checking. Cap the spend with the run's maximum cost: domains over it come back as `skipped`, not charged. Free-plan runs check up to 10 domains.

### Ready-made tasks

- [Check competitor ads on Google, Meta and LinkedIn](https://apify.com/doesaiknow/competitor-ads-checker/examples/check-competitor-ads-saas)
- [Google Ads Library search by domain](https://apify.com/doesaiknow/competitor-ads-checker/examples/google-ads-library-by-domain)
- [LinkedIn Ads Library: which B2B companies run ads](https://apify.com/doesaiknow/competitor-ads-checker/examples/linkedin-ads-library-b2b-companies)
- [Companies advertising on Facebook (Meta Ad Library)](https://apify.com/doesaiknow/competitor-ads-checker/examples/companies-advertising-on-facebook)
- [PPC leads: which businesses already run ads](https://apify.com/doesaiknow/competitor-ads-checker/examples/qualify-ppc-leads)
- [Facebook ads checker](https://apify.com/doesaiknow/competitor-ads-checker/examples/facebook-ads-checker)

### Integrations

Use the Apify API or MCP (mcp.apify.com), or schedule it weekly to track competitors.

### FAQ

#### Is it legal to check competitor ads?

It reads public ad libraries that anyone can browse without logging in. See Data & privacy below.

#### Why not the Meta Ad Library API?

The official API covers political and issue ads and ads delivered in the EU. Commercial ads elsewhere are visible in the public library, which is what this Actor checks.

#### Why is Meta sometimes `unknown`?

Meta is `unknown` (free) when a short or generic domain floods the keyword search with unrelated ads (`pagination_unresolved`), or the Facebook page cannot be identified with certainty (`meta_page_unresolved`). `not_found` with `confidence: "medium"` (charged) means no ad links to the domain, but the company's page was only matched by name or is missing from the library's page search. In blind tests Meta resolved 23/30 company domains on the latest version, 18–29/30 on earlier ones.

#### Does it show ad spend or targeting?

No. Ad libraries do not publish spend for commercial ads.

#### Can I check ads in a specific country?

Google is checked for any region and Meta for all countries. Country filters are not available in this version.

#### Can I search the Facebook ad library by domain?

Yes — that is part of the Meta check: active ads that link to your domain are found even when no Facebook page is linked from the website.

### Data & privacy

The Actor reads only public ad transparency libraries and the public homepage of each domain. It follows data minimisation: no ad text, no targeting, no employee lists and no people are collected on purpose. Ad account and page names that look like a private person's name are withheld (`null` with `nameWithheld: true`); this is a heuristic, not a classification. Company profiles come from company pages only.

### Related Actors

- Google Ads Transparency Intel — find the advertisers bidding on a keyword.
- Website Traffic Estimator — estimate traffic for the same domains.

# Actor input Schema

## `domains` (type: `array`):

Up to 500 company domains or website URLs per run, for example "hubspot.com" or "https://www.pipedrive.com/pricing". Each entry is normalized (lowercase, no https://, no www., no path) and the same domain listed twice is checked once. Entries that are not valid domains come back as rows with status "invalid_input" and are not charged.

## `platforms` (type: `array`):

Which ad libraries to check. Default: "google" (Google Ads Transparency Center, ads shown in the last 7 days, any region) and "meta" (Meta Ad Library for Facebook & Instagram, ads with status ACTIVE). LinkedIn (beta) — add "linkedin" to platforms to also check the LinkedIn Ad Library (ads that ran in the last 30 days); it is checked when the company website links its LinkedIn page, otherwise LinkedIn is "unknown". A domain is charged only when every selected platform gets a clear answer, so adding LinkedIn can turn a row into "partial" (not charged). A platform you leave out is null in the output. At least one is required.

## `includeSampleAds` (type: `boolean`):

Add up to N sample ads per platform with ad ID, ad library link, first/last seen, format and creative link when the library returns one. Ad text is not included. Every platform with ads always gets at least one ad as evidence, even when this is off.

## `maxSampleAdsPerPlatform` (type: `integer`):

How many sample ads to include per platform (0–10).

## `includeCompanyProfile` (type: `boolean`):

Add a light company profile: name, website, LinkedIn page, industry, employee range and HQ country. It comes from the company's LinkedIn page when LinkedIn is checked and the page links back to the domain, otherwise from the homepage. It never adds an extra paid lookup and never includes people.

## Actor input object example

```json
{
  "domains": [
    "reliantplumbing.com",
    "tayloremmet.co.uk",
    "simplygreenpest.com"
  ],
  "platforms": [
    "google",
    "meta"
  ],
  "includeSampleAds": true,
  "maxSampleAdsPerPlatform": 3,
  "includeCompanyProfile": true
}
```

# 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 = {
    "domains": [
        "reliantplumbing.com",
        "tayloremmet.co.uk",
        "simplygreenpest.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("doesaiknow/competitor-ads-checker").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 = { "domains": [
        "reliantplumbing.com",
        "tayloremmet.co.uk",
        "simplygreenpest.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("doesaiknow/competitor-ads-checker").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 '{
  "domains": [
    "reliantplumbing.com",
    "tayloremmet.co.uk",
    "simplygreenpest.com"
  ]
}' |
apify call doesaiknow/competitor-ads-checker --silent --output-dataset

```

## MCP server setup

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

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/fCBjoWrc2hNQP7H5d/builds/51nvNjEEycA1FqfW2/openapi.json
