# 👻 Snapchat Ads Scraper: Creative Analysis (`scrapium/snapchat-ads-scraper`) Actor

Snapchat Ads Scraper: Creative Analysis the Snapchat Ads Library by advertiser / brand name. Get ad creatives, headlines, impressions, media download links, and full ad metadata at scale.

- **URL**: https://apify.com/scrapium/snapchat-ads-scraper.md
- **Developed by:** [Scrapium](https://apify.com/scrapium) (community)
- **Categories:** Automation, Lead generation, Social media
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.99 / 1,000 results

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/platform/actors/running/actors-in-store#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

### Snapchat Ads Scraper — Extract Creatives, Advertisers, CTA Data

This Snapchat ads scraper searches the public Snapchat Ads Library by advertiser or brand name and returns every ad as one typed JSON row that carries the raw creative — headline, media link, format, impressions, status — alongside computed creative-analysis fields: headline word/character counts, detected call-to-action keywords, media presence, a normalized creative format, and whether the brand name appears in its own headline. Every response is typed, normalized JSON — no HTML, no selectors, no parsing. By the end of this page you will know exactly which 22 keys land in every row, which of them are scraped and which are computed, and the exact rule behind each computed one.

### What is Snapchat Ads Scraper: Creative Analysis?

**Snapchat Ads Scraper: Creative Analysis** is an Apify Actor that queries Snapchat's public Ads Library — the same transparency tool that powers `adsgallery.snap.com` — by one or more advertiser names, and enriches every ad it retrieves with a rule-based creative breakdown computed from the ad's own real fields. It does not use an AI model to review creatives; every derived field is a deterministic function over `headline`, `adType`, `snapMediaType` and `snapMediaDownloadLink`.

No Snapchat account, login or API key is required. The underlying endpoint (`adsapi.snapchat.com/v1/ads_library/ads/search`) is the open, unauthenticated one the public Ads Library website itself calls — the Actor sends the same kind of request a browser sends when you search the library manually, just at scale and with filters and analysis layered on top.

Key capabilities:

- **Scrape ad creatives** — headline, media type, media download link, ad format, start date, delivery status, and lifetime impressions
- **Scrape advertiser / brand identity** — account name, paying advertiser name, profile name, profile logo, and brand name for every ad
- **Compute creative-analysis metrics** — headline length, CTA keyword detection, media presence, a normalized `creativeFormat`, and brand-in-headline matching, for every ad, every run
- **Filter and de-duplicate** — drop ads below an impression floor, without media, or without a CTA headline, and collapse repeat creatives automatically
- Export as JSON, CSV, Excel, XML or HTML from the Apify dataset — no proxy management, no parsing on your side

### 🎨 What data does Snapchat Ads Scraper collect?

Every row blends three kinds of data on one ad: who is running it, what the creative actually says and shows, and what the Actor computed about it. All three arrive together — there is no separate advertiser or metrics endpoint to call.

| Data Type | Key Fields | JSON Field Names |
| --- | --- | --- |
| **Ad creative** | Headline text, media type, media download link, ad format, delivery status, lifetime impressions, start date | `headline`, `snapMediaType`, `snapMediaDownloadLink`, `adType`, `adStatus`, `totalImpressions`, `startDate` |
| **Advertiser / brand** | Account name, paying advertiser name, profile name, profile logo, resolved brand name | `accountName`, `payingAdName`, `profileName`, `profileLogoUrl`, `brandName` |
| **Computed creative-analysis metrics** | Headline word/character count, CTA detection, media presence, normalized creative format, brand-in-headline flag | `headlineWordCount`, `headlineCharCount`, `headlineHasCta`, `detectedCtaKeywords`, `hasMedia`, `creativeFormat`, `brandInHeadline` |

#### Need more Snapchat data?

API Empire runs several other Apify Actors alongside this one if you need to go beyond ad creatives. Pair a Snapchat public-profile or Snap Map scraper for audience-side data, or run this Actor first to shortlist advertisers by CTA and impression volume, then feed the winners into a downstream enrichment pipeline of your own.

### How does the creative analysis actually work?

Every computed field lives in one module, `analyze_creative()` in `src/creative.py`, and every one of them is a deterministic rule over the ad's own scraped fields — there is no AI model, no external scoring service, and no field that is guessed when data is missing. Read the exact rule for each field here, because this is where "analyzer" tools usually disagree with each other.

**Headline metrics.** `headlineWordCount` counts word-boundary tokens (`\w+`, Unicode-aware) in `headline`; `headlineCharCount` is the raw string length. Both are `0` when the ad has no headline at all — never `null`, because an absent headline is a real, countable state (zero words), not missing data.

**CTA detection.** `detectedCtaKeywords` scans the lowercased headline against a fixed eight-term list: `shop`, `buy`, `learn more`, `sign up`, `download`, `get`, `order`, `discover`. The two multi-word phrases (`learn more`, `sign up`) match as plain substrings. The six single words match only on a whole-word boundary using a tokenizer, specifically so `"getaway"` or `"forget"` do not falsely trigger `get`. `headlineHasCta` is simply `true` when that list is non-empty. There is no scoring, no confidence value and no synonym expansion beyond these eight terms — a CTA phrased as "check it out" is not detected, and a headline is either a documented match or it is not.

**Creative format.** `creativeFormat` is derived with a fixed precedence order, checked against the uppercased `adType` first: a `LENS`, `AR`, or `AR_` marker → `ar_lens`; `COLLECTION` → `collection`; `STORY` → `story_ad`; `COMMERCIAL` → `commercial`. Only when none of those match does the function fall back to `snapMediaType` (`VIDEO` → `video_snap`, `IMAGE` → `image_snap`), and only when even that is absent does it fall back to the raw `adType` lowercased. The value is `null` only when both `adType` and `snapMediaType` are empty.

**Media presence.** `hasMedia` is `bool(snapMediaDownloadLink)` — a truthy download link means media, nothing more nuanced than that. It does not verify the link resolves or that the media file still exists.

**Brand-in-headline.** `brandInHeadline` first checks whether the full `brandName` string appears anywhere in the lowercased headline. If not, it falls back to checking each individual token of `brandName` that is 3 or more characters long as a plain substring of the headline — not a whole-word match like the CTA check. That asymmetry is real: a short brand token can match inside an unrelated word (a 3-letter brand fragment showing up mid-word would still flag `true`), so treat this field as a directional signal, not a guarantee of an exact brand mention.

**Where `brandName` itself comes from matters here.** `src/extract.py` uses the ad's native `brand_name` field when Snapchat sets one (mainly AR lens creatives) and otherwise approximates it as the first whitespace-separated word of `accountName` — `"IKEA Global"` becomes `"IKEA"`, but a two-word brand like `"Under Armour"` inside an account name would only capture `"Under"`. Both `brandInHeadline` and the de-duplication key below inherit this approximation.

#### De-duplication and filter order

`dedupe_key()` builds an identity string from `headline` (lowercased, trimmed), `adType` (uppercased, trimmed) and `brandName` (lowercased, trimmed), joined with a control character so a partial match on one field cannot collide with another. The order the run actually applies things in, per ad examined (`src/main.py`, `_scrape_one_query`):

1. Check the de-duplication key against everything already **saved** in this run (across every search query, not just the current one).
2. If it is a repeat, drop it — `dropped_duplicate` — and move to the next ad without touching the filters below.
3. Otherwise apply `minImpressions`, then `requireMedia`, then `ctaOnly`, in that order, dropping and counting at whichever gate fails first.
4. Only an ad that survives every gate is recorded into the de-duplication set and pushed.

The consequence worth knowing: an ad that gets dropped by a filter is **never** recorded as "seen," so it can never block a later, different ad from being evaluated on its own merits — de-duplication only ever collapses creatives that would otherwise have been saved twice, not creatives that failed a filter once.

### Why not build this yourself?

Snapchat does not publish a documented, versioned developer API for querying the Ads Library programmatically. The endpoint this Actor calls — `POST https://adsapi.snapchat.com/v1/ads_library/ads/search` — is the same internal JSON endpoint the public `adsgallery.snap.com` website's own frontend calls when you type a search into the browser. It is open and requires no authentication, but it is not a documented product: there is no published schema, no versioning guarantee, and no rate-limit page to read.

That last part is the real cost of doing it yourself. `src/scraper.py` shows the endpoint rate-limits aggressively per source IP, signalled by `error_code: "E1009"`, HTTP 429, or a 5xx response. A DIY script needs its own exponential backoff, its own decision about when a single retry ladder is exhausted and a proxy swap is needed, and its own proxy tier to fall back to when direct requests get blocked. This Actor already implements all three — internal retry-with-backoff inside every request, and a three-tier proxy escalation (direct → Apify datacenter → Apify residential) that engages automatically and stays engaged once triggered — so a search that would otherwise stall on rate limiting keeps running.

### Why do developers and teams scrape Snapchat ads?

Snapchat ad creative data serves distinctly different workflows depending on who is pulling it.

#### For marketers and brand teams

Competitive creative research is the primary use case: pull every ad a competitor is running by searching their brand name, then use `headlineHasCta` and `detectedCtaKeywords` to see which CTA language they lean on, and `creativeFormat` to see whether they favor video, image or AR lens placements. Set `ctaOnly: true` and `minImpressions` above a floor to surface only the creatives worth studying, and `requireMedia: true` to drop text-only rows before you export a swipe file.

#### For researchers and analysts

The Ads Library exists as an advertising-transparency tool, and this Actor only reads what it already publishes for that purpose — no login, no private ad-account access, no data beyond what the library itself surfaces. A typical study fixes a set of advertiser names or runs an empty-string query to sample broadly within a `country` and `status` filter, and reports `startDate`, `adStatus` and `totalImpressions` alongside the computed fields as the observed creative landscape for that slice.

#### For AI engineers and agent builders

Every row is typed JSON with a stable 22-key schema, so it drops into a retrieval pipeline with no HTML-parsing step. A typical agent tool calls the Actor with a brand name, embeds `headline` into a vector store, and stores `creativeFormat`, `headlineHasCta` and `totalImpressions` as metadata filters — so an agent can answer "show me this brand's highest-impression video ads with a CTA" as a filtered query rather than a manual library search.

#### For developers building data products

Schedule the Actor from the Apify Console to track a fixed list of advertisers over time, and the dataset's stable key names mean an ingest job written once keeps working. Filter on `hasMedia` and `creativeFormat` to route rows into per-format tables, or diff `totalImpressions` per `id` across runs to detect which creatives are still live and growing.

### How to scrape Snapchat ads (step by step)

This Actor runs on the Apify platform only — from the Apify Console or through the Apify API, using your Apify API token. There is no separate Snapchat signup and no Snapchat credential of any kind.

1. **Open the Actor on its Apify Store listing** and click **Try for free**, or open it directly in the Apify Console if you already have it saved.
2. **Fill in `searchQueries`** — one or more advertiser or brand names (`Ikea`, `Nike`, `McDonald's`). This is the only required input; use a single empty string `""` to fetch all ads in the library instead of filtering by name.
3. **Set your filters** — `country` (EU only), `status` (active/paused/any), `startDate`/`endDate`, plus the creative-analysis filters `minImpressions`, `requireMedia` and `ctaOnly` to shrink the saved rows to the creatives you actually want.
4. **Start the run.**
5. **Download results** as JSON, CSV, Excel, XML or HTML from the dataset, or read them straight through the Apify API.

#### What to do when Snapchat changes its Ads Library structure

This Actor is maintained, and its dataset schema is the contract: the 22 field names and types you build against today are what you get after an update, so an ingest job or dashboard built on this Actor keeps working across changes to Snapchat's underlying endpoint.

### ⬇️ Input

Every parameter below is read directly from `.actor/actor.json`. Only `searchQueries` is marked required.

| Parameter | Required | Type | Description | Example Value |
| --- | --- | --- | --- | --- |
| `searchQueries` | Yes | `array` of strings (`stringList` editor) | One or more advertiser / brand names to search for. Bulk input is fully supported — every keyword is processed independently and tagged with the search query in the output. Use a single empty string `""` to fetch all ads, filtered only by country/date/status. **Default:** `["Ikea"]`. | `["Ikea", "Nike", "McDonald's"]` |
| `maxItems` | No | `integer` — min `1`, max `10000` | Maximum number of ads **retrieved** from the library **per search keyword**. Creative filters and de-duplication are applied afterward, so the number of rows actually saved is often lower than this. **Schema default: `10`.** See the caveat below the table. | `100` |
| `country` | No | `string`, `select` — enum of 27 EU country codes plus `""` | Restrict results to ads served in a single EU country. An unrecognized code is ignored with a log warning; the run continues with no country filter. **Default: `""`** (all countries). | `"DE"` |
| `status` | No | `string`, `select` — enum `""`, `"ACTIVE"`, `"PAUSED"` | Filter by current delivery status. An unrecognized value is ignored with a log warning. **Default: `""`** (any). | `"ACTIVE"` |
| `startDate` | No | `string`, `datepicker` | Only return ads with a `start_date` on or after this date (`YYYY-MM-DD`). Malformed values are ignored with a log warning. No default. | `"2026-01-01"` |
| `endDate` | No | `string`, `datepicker` | Only return ads with a `start_date` on or before this date (`YYYY-MM-DD`) — the underlying filter compares against the ad's *start* date on both ends, exactly as the schema describes. Malformed values are ignored with a log warning. No default. | `"2026-06-30"` |
| `minImpressions` | No | `integer` — min `0` | Only save ads whose lifetime `totalImpressions` is greater than or equal to this number. Ads with a missing impression count are treated as `0`. **Default: `0`** (no filter). | `5000` |
| `requireMedia` | No | `boolean` | When `true`, only save ads that carry a downloadable top-Snap media link (`snapMediaDownloadLink`). Ads with no media are dropped. **Default: `false`.** | `true` |
| `ctaOnly` | No | `boolean` | When `true`, only save ads whose headline contains a detected CTA keyword (`shop`, `buy`, `learn more`, `sign up`, `download`, `get`, `order`, `discover`). **Default: `false`.** | `true` |
| `proxyConfiguration` | No | `object`, `proxy` editor | By default the Actor runs **without a proxy**. If Snapchat rate-limits or rejects a request, it automatically escalates to a datacenter proxy, then a residential proxy (up to 3 retries), and stays on residential once engaged. **Default and prefill:** `{"useApifyProxy": false}`. | `{"useApifyProxy": true}` |

#### Behaviour worth knowing before you run

- **An empty `searchQueries` array is not rejected — it silently falls back to `["Ikea"]`.** If your input has no usable search terms after normalization (an empty list, a missing key, or values that reduce to nothing), the Actor logs a warning — *"No search queries provided — falling back to default keyword 'Ikea'"* — and runs anyway, billing you for whatever it finds. Always send at least one real string, or the deliberate `[""]` for "all advertisers."
- **`maxItems: 0` (or omitting the key on a raw API call) does not fall back to the schema's documented default of `10`.** The engine's internal fallback constant is `30` (`src/main.py`, `DEFAULT_MAX_ITEMS = 30`), which only matches the Console's prefilled value by coincidence being higher, not equal. If you call the Actor from code and skip `maxItems`, send it explicitly.
- **`maxItems` bounds retrieval, not what gets saved.** Creative filters (`minImpressions`, `requireMedia`, `ctaOnly`) and de-duplication run after retrieval, so a `maxItems: 100` run can save fewer than 100 rows per query.

#### Example JSON input

```json
{
  "searchQueries": ["Ikea", "Nike"],
  "maxItems": 100,
  "country": "DE",
  "status": "ACTIVE",
  "startDate": "2026-01-01",
  "endDate": "2026-06-30",
  "minImpressions": 1000,
  "requireMedia": true,
  "ctaOnly": false,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

### ⬆️ Output

Results are pushed live to the run's default dataset as each ad clears filtering, so you can start reading rows before the run finishes. Export as JSON, JSONL, CSV, Excel, XML, RSS or HTML from the Apify Console or API. Unusually for a scraper, the dataset's default view is not a subset here — the same 22 keys the row-building code writes (`{"searchQuery": query, **ad, **analysis}` in `src/main.py`) are exactly the 22 columns the default view (`🔥 Analyzed Ads`) surfaces. Nothing is hidden.

#### Scraped ad creative

```json
{
  "searchQuery": "Ikea",
  "id": "8f3c1a92-77e4-4b1d-9c2a-1e5f6d0a3b21",
  "adName": "IKEA_Summer_Collection_2026_Video",
  "adType": "SNAP_AD",
  "payingAdName": "IKEA",
  "accountName": "IKEA Global",
  "profileLogoUrl": "https://ads-library-cdn.snap.com/logos/ikea-global.png",
  "profileName": "IKEA",
  "brandName": "IKEA",
  "headline": "Shop the new summer collection today",
  "totalImpressions": 184320,
  "adStatus": "ACTIVE",
  "snapMediaDownloadLink": "https://ads-library-cdn.snap.com/media/8f3c1a92-topsnap.mp4",
  "snapMediaType": "VIDEO",
  "startDate": "2026-05-12",
  "creativeFormat": "video_snap",
  "headlineWordCount": 6,
  "headlineCharCount": 37,
  "headlineHasCta": true,
  "detectedCtaKeywords": ["shop"],
  "hasMedia": true,
  "brandInHeadline": false
}
```

Every key on this row comes from one of two places, and knowing which is which matters:

**Scraped as-is from the Snapchat Ads Library response** (`src/extract.py`, `map_ad_preview`): `id`, `adName`, `adType`, `payingAdName`, `accountName`, `profileLogoUrl`, `profileName`, `headline`, `totalImpressions`, `adStatus`, `snapMediaDownloadLink`, `snapMediaType`, `startDate`. `searchQuery` is added by the run, not by Snapchat.

`brandName` is a partial exception — it is the ad's own `brand_name` field when Snapchat sets one (mainly on AR lens creatives), and otherwise **approximated** as the first whitespace-separated word of `accountName` (`"IKEA Global"` → `"IKEA"`). This approximation feeds both `brandInHeadline` and the de-duplication key below, so a multi-word account name with no native `brand_name` can produce an imprecise brand token.

**Computed by this Actor** (`src/creative.py`, `analyze_creative`), from the scraped fields above and nothing else: `headlineWordCount`, `headlineCharCount`, `headlineHasCta`, `detectedCtaKeywords`, `hasMedia`, `creativeFormat`, `brandInHeadline`. The exact rule behind each one — including the CTA keyword list, the `creativeFormat` precedence order, and the brand-matching logic — is documented in full in **How does the creative analysis actually work?** above.

### 💸 What gets charged, and what does not

This Actor is pay-per-event with a single charged event: **`row_result`**. One `row_result` is charged for every row that reaches `Actor.push_data()` — which, because de-duplication and every creative filter run *before* the push call in `src/main.py`, means duplicates and rows dropped by `minImpressions`, `requireMedia` or `ctaOnly` are never pushed and never charged. There is no separate, uncharged accounting or diagnostic row type in this Actor — unlike some of API Empire's other Actors, every row that lands in your dataset here was billed; there is no `type != "Accounting"` filter to apply because that row type does not exist here.

One implementation detail worth knowing if you reconcile row counts against charges: `src/main.py` calls `Actor.push_data(record, charged_event_name="row_result")` and logs an error if the charge attempt itself reports failure, but it still increments the run's "pushed" counter and prints the row as saved regardless of that charge outcome. In the ordinary case this is a distinction without a difference — the row is pushed either way — but it does mean the live log's running total is a count of **push attempts**, not a confirmed-charged count, if you are auditing billing down to the row.

### How does Snapchat Ads Scraper compare to other Snapchat ad scrapers?

| Feature | Snapchat Ads Scraper: Creative Analysis | Generic Snapchat ads scraper |
| --- | --- | --- |
| Output format | Typed JSON, 22 fixed keys per row, every run | Varies; often raw HTML fields or an unstable subset |
| Creative-analysis fields | Computed on every row: CTA detection, headline metrics, format, brand-in-headline — deterministic, not AI | Usually absent; raw ad fields only |
| De-duplication | Automatic, keyed on headline + ad type + brand, applied only to rows that already passed every filter | Typically none — repeat creatives across pages are returned as separate rows |
| Filtering | `minImpressions`, `requireMedia`, `ctaOnly` applied server-side in the run, reducing billed rows | Typically post-processed client-side after paying for every row |
| Anti-block handling | Automatic escalation: direct → Apify datacenter → Apify residential (3 retries), sticky once engaged; internal exponential backoff per request | Varies; often requires you to configure and pay for a proxy yourself |

Checked on the Apify Store on 2026-07-25, no directly competing Snapchat Ads Library Actor surfaced in the same search category as this one. The closest adjacent tool is `jy-labs/meta-ad-creative-intelligence`, which advertises an AI-driven creative "teardown" for Meta/Facebook ads — hook, offer, CTA and visual-style labels generated by a Gemini model, priced per analyzed image (`$0.05`) or video (`$0.20`) as an add-on to raw scraping. This Actor takes the opposite approach for Snapchat: every creative-analysis field is a deterministic, published rule over the ad's own scraped fields, computed for every row at no separate per-analysis charge, rather than a model-generated review.

If you're building an AI agent or RAG pipeline, the output-format row is the decision-maker — parsing HTML inside an agent loop is a reliability failure mode, not a feature.

### How many results can you scrape with Snapchat Ads Scraper?

`maxItems` accepts up to `10000` **per search keyword**, and that ceiling is enforced by the input schema, not discovered at runtime — there is no separate actor-side hard cap beyond it. The real constraint is Snapchat's own rate limiting, which is why the Actor exists around a retry-and-proxy-escalation loop rather than a single request per query.

Pagination as implemented (`src/main.py`, `src/scraper.py`): each request asks for `min(50, remaining)` ads at a time — `50` is the Actor's page size — and follows the response's `paging.next_link` cursor until either `maxItems` is reached for that query or Snapchat reports no further pages. Ads are examined and, if they clear de-duplication and every active filter, pushed to the dataset live as each page completes. Between pages the Actor sleeps roughly 1.0–1.6 seconds (a fixed 1-second delay plus randomized jitter up to 0.6 seconds) to stay under Snapchat's rate limit rather than trigger the escalation ladder unnecessarily.

No benchmark timings are published here because none have been measured for this Actor.

### Integrate Snapchat Ads Scraper and automate your workflow

This Actor works with any language or tool that can send an HTTP request to the Apify API. It runs on the Apify platform only — via the Apify Console or the Apify API, using your Apify API token. There is no separate Snapchat key.

#### REST API integration

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_API_TOKEN>")

run = client.actor("<YOUR_USERNAME>/snapchat-ads-creative-analyzer").call(
    run_input={
        "searchQueries": ["Ikea", "Nike"],
        "maxItems": 100,
        "country": "DE",
        "ctaOnly": True,
    }
)

for ad in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(ad["brandName"], ad["headline"], ad["creativeFormat"], ad["totalImpressions"])
```

Works in Python, Node.js, Go, Ruby and cURL — the Apify API is plain REST, and the dataset can also be fetched directly as JSON or CSV from `/v2/datasets/<datasetId>/items`.

#### Automation platforms (n8n, Make, LangChain)

**n8n** — use the **Apify node** with the *Run Actor* operation, pass the same JSON input shown above, and chain a *Get Dataset Items* step to route ad rows into a sheet or database.

**Make** — the **Apify** app's *Run an Actor* module can be scheduled to poll a fixed advertiser list, with *Watch Actor Runs* iterating results into a Google Sheets or Airtable module.

**LangChain** — wrap the Actor call in a tool function that returns the dataset items directly; rows are already typed JSON, so `headline`, `creativeFormat` and `detectedCtaKeywords` can be used as structured-output fields in a creative-monitoring chain with no parsing step.

The Apify platform's own integrations — webhooks, scheduling, Zapier, Slack, Google Sheets, Airbyte — also work with this Actor's dataset without extra code.

### Is it legal to scrape Snapchat ads?

Yes — this Actor returns only publicly available data from Snapchat's own Ads Library, a repository Snap operates and publishes specifically as advertising-transparency data, not gated content. Ad creatives, headlines, advertiser account names and impression counts are business and marketing records, not personal data about identifiable individuals, so the GDPR/CCPA framework that governs profile or reviewer scraping does not attach to this output in the same way.

What does apply is Snapchat's terms of service and any local rules on systematic reuse of a compiled dataset. Country and date filters here reflect the fields Snap itself publishes through this library — they do not grant or restrict any additional right to the underlying data beyond what the library already makes public.

Consult legal counsel for commercial use cases involving bulk data collection or republication.

### ❓ Frequently asked questions

#### Does Snapchat Ads Scraper work without a Snapchat account?

Yes. No Snapchat account, login, cookie or API key is required or accepted anywhere in the input schema. The Actor calls the same open, unauthenticated endpoint the public Ads Library website uses. The only credential you need is your Apify API token, to run the Actor itself.

#### How often is the scraped data updated?

Every run fetches live from Snapchat's Ads Library — nothing is cached between runs. Schedule the Actor in the Apify Console for recurring snapshots of a fixed advertiser list.

#### What happens if a search query returns zero ads?

The Actor logs "Snapchat returned no ads" for that query and moves to the next one in your `searchQueries` list — it does not error out or halt the run. A search with no matching advertiser, an overly narrow `country`/`status`/date combination, or a genuinely inactive brand can all produce zero rows for a given query while other queries in the same run succeed normally.

#### Can I scrape ads outside the EU country list, or private ad-account data?

No, on both counts. `country` only accepts the 27 EU country codes in the schema's enum — this Actor scrapes the public Ads Library's search surface as published, and does not access any non-EU country filter, private ad-account dashboards, or campaign data behind an advertiser's own login. Everything returned is what the public library itself already serves.

#### Do I need to configure anything to run this at scale?

No signup beyond your Apify account, and no separate pricing tier to unlock features — every input parameter documented above is available on every run. The Actor's own cost behaviour (rows charged, filters that reduce billed rows) is described in the Input and Output sections; current per-event pricing is shown on the Actor's Apify Store listing.

#### Does Snapchat Ads Scraper work for AI agent workflows and LLM pipelines?

Yes. It is callable as a standard HTTP endpoint by any agent framework — LangChain, a custom tool-use loop, or an n8n/Make automation. Every response is typed, normalized JSON with a fixed 22-key schema: no HTML, no selectors, no parsing step before passing rows to an LLM or indexing `headline` into a vector store.

#### How does Snapchat Ads Scraper handle Snapchat's rate limiting?

With two layers, both implemented in the source. **Per-request backoff** — `src/scraper.py` retries a request up to three times internally with exponential backoff (capped at 30 seconds) before giving up on the current proxy level. **Proxy escalation** — when the endpoint returns a rate-limit signal (`error_code: "E1009"`, HTTP 429, or a 5xx), the Actor escalates from no proxy to an Apify datacenter proxy, then to an Apify residential proxy with up to 3 further retries, and stays on residential for the rest of the run once engaged.

#### How does Snapchat Ads Scraper compare to other ad-library scrapers on the Apify Store?

No Snapchat-specific competitor surfaced in the same search category, checked on the Apify Store 2026-07-25. The closest adjacent tool, `jy-labs/meta-ad-creative-intelligence`, targets Meta/Facebook ads and adds an optional, separately priced AI "teardown" (`$0.05` per image, `$0.20` per video, per its own listing) on top of raw scraping. This Actor's creative-analysis fields are computed by fixed, published rules for every row, with no separate per-analysis fee.

#### Does Snapchat Ads Scraper return data in a format LLMs can use directly?

Yes. Every row is typed, normalized JSON with stable field names across every run. Pass a row straight into an LLM context window, index it into a vector store, or hand it to an agent tool — no transformation step required.

#### Can I use Snapchat Ads Scraper without managing proxies?

Yes. The default `proxyConfiguration` is `{"useApifyProxy": false}` — the Actor talks to Snapchat directly and escalates to Apify's datacenter and then residential proxy pools automatically only when Snapchat pushes back. No proxy account or manual rotation is required.

#### What happens when Snapchat changes its Ads Library structure?

The Actor is maintained, and the dataset schema is the contract — the 22 field names and types stay stable on your end even when the underlying endpoint changes. No numeric turnaround time is promised here.

### 💬 Your feedback

Found a bug, or a creative-analysis rule that misclassifies a real ad? We want to know. Open an issue on the Actor's Issues tab in the Apify Console with the run ID and the ad's `id` — concrete examples are what keep the CTA keyword list and format rules accurate.

# Actor input Schema

## `searchQueries` (type: `array`):

🏷️ Enter one or more **advertiser / brand names** to search the Snapchat Ads Library for (e.g. `Ikea`, `Nike`, `McDonald's`). Bulk input is fully supported — every keyword is processed independently and its results are tagged with the search query in the output.

💡 Use an empty string `""` as a single entry to fetch **all ads** in the library (filtered only by country / date / status).

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

🎯 Maximum number of ads to **retrieve** from the library **per search keyword**. Creative filters (below) and de-duplication are applied afterward, so the number of rows saved may be lower than this. Higher numbers = more data and longer runtime.

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

🇪🇺 Restrict results to ads served in a single EU country. Leave empty to include **all countries**.

## `status` (type: `string`):

🟢 Filter by the current delivery status of the ad. Choose **Any** to include both active and paused ads.

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

📆 Only return ads with a `start_date` **on or after** this date (format: `YYYY-MM-DD`). Leave empty for no lower bound.

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

📆 Only return ads with a `start_date` **on or before** this date (format: `YYYY-MM-DD`). Leave empty for no upper bound.

## `minImpressions` (type: `integer`):

👁️ Only save ads whose lifetime `totalImpressions` is **greater than or equal to** this number. Ads with a missing impression count are treated as 0. Default `0` = no impression filter.

## `requireMedia` (type: `boolean`):

⬇️ When enabled, only save ads that carry a downloadable top-Snap media link (`snapMediaDownloadLink`). Ads with no media are dropped.

## `ctaOnly` (type: `boolean`):

🛒 When enabled, only save ads whose headline contains a detected call-to-action keyword (shop, buy, learn more, sign up, download, get, order, discover).

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

🛡️ By default the actor runs **without a proxy**. If Snapchat rate-limits or rejects the request, the actor automatically falls back to a **datacenter proxy**, then to a **residential proxy** (with up to **3 retries** on residential). Once residential is engaged, it stays engaged for the rest of the run.

## Actor input object example

```json
{
  "searchQueries": [
    "Ikea"
  ],
  "maxItems": 10,
  "country": "",
  "status": "",
  "minImpressions": 0,
  "requireMedia": false,
  "ctaOnly": false,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# 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 = {
    "searchQueries": [
        "Ikea"
    ],
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapium/snapchat-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 = {
    "searchQueries": ["Ikea"],
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("scrapium/snapchat-ads-scraper").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{
  "searchQueries": [
    "Ikea"
  ],
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call scrapium/snapchat-ads-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=scrapium/snapchat-ads-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/0zGnjN4HddExOP76G/builds/UNICzl0jqhErOyUjZ/openapi.json
