# Snapchat Profile Scraper (`scraper-engine/snapchat-profile-scraper`) Actor

Snapchat Profile Scraper collects public profile data, including usernames, bios, subscriber counts, stories, and spotlight content. Useful for influencer research, audience insights, or competitive analysis. Export structured data in JSON, CSV, or Excel for easy integration.

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

## Pricing

from $0.01 / 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/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

### Snapchat Profile Scraper — Bio, Subscribers and Spotlight Clips

Snapchat Profile Scraper turns a list of Snapchat usernames or profile URLs into structured JSON: the public profile block Snapchat serves — display name, bio, subscriber count, Snapcode, profile picture, website, category and related accounts — plus a per-clip Spotlight array with plays, shares, comments, media URL, thumbnail and transcript link. No Snapchat account, no login, no cookie. Paste handles and press Start.

⚠️ **Two things to know before your first run.** This Actor returns the profile record and **Spotlight clip metadata** only. Today's Story snaps and saved Highlight albums are deliberately out of scope and are never written to the dataset, and `transcriptUrl` is a link to Snapchat's transcript file — the transcript **text** is not fetched or returned. Second, the URL form `https://www.snapchat.com/djkhaled305` is **rejected**: the parser only recognises a URL when it contains `/@`, `/add/` or `/p/`. Use a bare handle, `@handle`, `/@handle` or `/add/handle`.

### What is Snapchat Profile Scraper?

Snapchat Profile Scraper is an Apify Actor that makes one keyless GET request to `https://www.snapchat.com/@<username>` per handle and parses the server-rendered `__NEXT_DATA__` JSON payload the page ships with. It writes one dataset row per input entry, live, as each profile finishes.

No Snapchat account, login, password or session cookie is required, and none is accepted — the page it reads is keyless and cookieless. The only credential involved is your Apify token.

It is built for influencer marketers qualifying Snapchat creators, social researchers benchmarking Spotlight performance, brand teams auditing their own and rivals' public profiles, and developers piping profile records into a warehouse, dashboard or AI agent.

### What Snapchat profile data is publicly available to scrape?

Snapchat serves a signed-out visitor the entire Public Profile block for creator and brand accounts — handle, display name, bio, subscriber count, Snapcode, avatar, website, category, address and related accounts — together with a metadata list of the profile's Spotlight clips. A personal account without a Public Profile is served a much thinner record, and everything that depends on being someone's friend is served to nobody.

| Data Category | Public profile page (no login) | Requires the Snapchat app or a friendship |
| ----- | ----- | ----- |
| Username, display name, bio | ✅ Public on a Public Profile | — |
| Subscriber count, category and subcategory labels | ✅ Public on a Public Profile | — |
| Snapcode image, profile picture, hero image, website, address | ✅ Public on a Public Profile | — |
| Related accounts shown on the profile | ✅ Public on a Public Profile | — |
| Spotlight clip metadata — plays, shares, comments, media and thumbnail URLs | ✅ Public | — |
| Personal account with no Public Profile | Username, display name, Snapcode and Bitmoji only | Bio, subscribers, content |
| Friend list, who added whom, Snap Map location | ❌ | App plus an accepted friendship |
| Direct snaps, chats, private Stories | ❌ | Never public |

Snapchat Profile Scraper only returns publicly visible data — what any visitor sees. Nothing behind a login wall.

One honest note on scope. The `__NEXT_DATA__` payload also carries `props.pageProps.story` (today's Story snaps) and `props.pageProps.curatedHighlights` (saved Highlight albums). Those are publicly visible on the page, but this Actor deliberately does not emit them — that boundary is written into the source as a scope comment, not left to chance. Competing Snapchat profile Actors do document curated highlights and latest Story snaps on their listings (checked on the Apify Store on 25 July 2026); this one does not return them.

### What data can I extract with Snapchat Profile Scraper?

Every dataset row carries a small envelope describing the fetch, and — on success — a `data` object holding the profile record and the Spotlight array.

| Field Name | Description |
| ----- | ----- |
| `ok` | Boolean. `true` only when a profile payload was parsed. **This is the charge marker** — see the Output section |
| `username` | The handle Snapchat returned for the profile, falling back to the cleaned handle you supplied |
| `url` | The profile URL the Actor actually requested, always `https://www.snapchat.com/@<handle>` |
| `httpStatus` | HTTP status of the last attempt. `null` if every attempt failed before a response arrived |
| `profileFound` | Boolean. `true` on success, `false` on a 404 or an exhausted retry budget |
| `profileType` | `"publicProfile"` for a Public Profile, `"personalProfile"` for an account without one. `null` on failure rows |
| `data` | The profile record and Spotlight array — see the nested keys below. **Present only on successful rows** |
| `attempt` | How many attempts this handle consumed, `1` when it worked first time |
| `proxyMode` | Which egress mode served the row: `direct`, `apify-selected`, `apify-datacenter` or `apify-residential` |
| `error` | Human-readable failure message. **Present only on failure rows** |
| `errorReason` | Machine-readable failure code. **Present only on failure rows.** One of `invalid_input`, `profile_not_found`, `blocked`, `http_error`, `parse_error`, `network_error` |
| `input` | The raw string you supplied, echoed back. **Present only on invalid-input rows** |

`data` carries four keys:

| Field Name | Description |
| ----- | ----- |
| `data.search` | Snapchat's own canonical profile URL, read from `pageLinks.snapchatCanonicalUrl`. The key name is legacy — it is a URL, not a search term |
| `data.userProfile` | Snapchat's profile block, passed through verbatim. Wrapped in `publicProfileInfo` or `userInfo` depending on `profileType` |
| `data.spotlight` | Array of normalised Spotlight clip objects, ten keys each — the fields the Actor actually builds |
| `data.spotlightHighlights` | Snapchat's raw `spotlightHighlights` array, passed through untouched. `[]` when the profile has none |

#### Profile identity fields

`data.userProfile` is a **verbatim passthrough**. The Actor picks the branch and does not rename, reshape or type-cast anything inside it, so its exact key membership is set by Snapchat, not by this Actor.

On a `publicProfile`, that block is `data.userProfile.publicProfileInfo`, and the keys Snapchat currently publishes there include `username`, `title` (display name), `bio`, `subscriberCount`, `snapcodeImageUrl`, `profilePictureUrl`, `websiteUrl`, `address`, `badge`, `categoryStringId`, `subcategoryStringId`, `publisherType`, `mutableName`, `squareHeroImageUrl`, `primaryColor`, `hasStory`, `hasCuratedHighlights`, `hasSpotlightHighlights` and `relatedAccountsInfo`.

**On the subscriber count specifically:** there is no top-level `subscriberCount` key and no `followersCount` key on the row. The count is whatever Snapchat publishes at `data.userProfile.publicProfileInfo.subscriberCount`, and it arrives exactly as Snapchat ships it — Snapchat serves its counters as **strings**, and this Actor does not parse, normalise or integer-cast anything inside the profile block. Cast it yourself before you sort on it. A profile Snapchat publishes no count for simply will not have the key.

On a `personalProfile`, the block is `data.userProfile.userInfo` and it is much thinner — Snapchat serves username, display name, Snapcode image URL and Bitmoji for accounts that never set up a Public Profile. There is no bio, no subscriber count and no Spotlight there. The Actor still reports this as a success with `ok: true`, because a personal account is a real answer about a real handle, not a failure.

#### Spotlight clip fields

Each entry in `data.spotlight` carries exactly ten keys, built by the Actor rather than passed through:

| Field Name | Description |
| ----- | ----- |
| `spotlightId` | Clip ID, extracted from inside Snapchat's AppsFlyer deeplink with a `spotlight/<id>` pattern. `null` when the entry carries no usable deeplink |
| `link` | Rebuilt watch URL, `https://www.snapchat.com/@<username>/spotlight/<spotlightId>`. `null` when either half is missing |
| `thumbnailSrc` | Thumbnail image URL. Empty strings are converted to `null` |
| `spotlightTitle` | First real title found, in order: `llmTitle`, embedded text caption, `videoMetadata.name`, then description. Snapchat's boilerplate titles are filtered out and return `null` |
| `plays` | View count as a non-negative integer, taken from `engagementStats` first and `videoMetadata` only as a fallback |
| `shares` | Share count as a non-negative integer, same precedence |
| `comments` | Comment count as a non-negative integer, from `engagementStats` only |
| `timestampInSec` | Unix epoch seconds as a **string**, unwrapped from Snapchat's `{"value": …}` envelope. `null` when no matching highlight entry carries one |
| `mediaUrl` | Direct video file URL. Empty strings are converted to `null` |
| `transcriptUrl` | URL of Snapchat's audio-transcription object for the clip, when one exists. The transcript **text** is not fetched |

Three behaviours worth knowing. First, `engagementStats` is treated as authoritative and `videoMetadata` as a stale mirror, because that mirror reports `0` shares and `-1` views on live clips — a `-1` is read as "not disclosed" and becomes `null`, never a negative number. Second, Snapchat mixes **placeholder entries** into its Spotlight list: no deeplink, no media, no engagement stats, and counters padded with `"0"`. When an entry has neither an ID nor engagement stats, `plays`, `shares` and `comments` are forced to `null` rather than reported as genuine zeros — so a `0` in this output means a real, published zero. Third, `timestampInSec` and `transcriptUrl` come from the separate `spotlightHighlights` array, matched by clip ID; the Actor walks every snap in the matched highlight and takes the first real value for each field independently, so one missing transcript does not cost you the timestamp.

#### 🤖 Add-on: Need additional Snapchat data?

**Snapchat Ads Scraper** covers the paid side of the same brands — Snapchat's public ads library, with creative, headlines, impressions and top-snap media download links for any advertiser you name. If you are benchmarking the same creators across platforms, **TikTok Profile Scraper** and **Instagram Profile Scraper** return the comparable profile-level identity and metric records, so a creator list can be qualified on all three surfaces from one pipeline.

### Why not build this yourself?

Snapchat has no public developer API that lets you look up a third party's profile. Snap's published developer surface is Snap Kit and its advertising APIs — scoped to your own app's users and your own ad accounts. There is no documented endpoint that takes an arbitrary username and returns that person's public profile record, which is why every tool in this category, including this one, reads the public profile page.

Reading that page yourself is a one-afternoon prototype and a long-running maintenance job. The specific things that break a home-grown version:

- **The payload is a Next.js hydration blob**, not an API response. It is loosely structured, occasionally carries trailing commas that break a strict JSON parser, and nests scalars inside `{"value": …}` protobuf envelopes at arbitrary depth. Half the work is unwrapping, not fetching.
- **Two mutually exclusive profile shapes.** `publicProfileInfo` and `userInfo` are discriminated by a `$case` key. A parser that only knows the first one silently reports success while throwing away every field of a personal account.
- **The deeplink is not the clip URL.** Spotlight IDs sit inside an AppsFlyer click URL with a second URL glued to the tail. Splitting on `?` — the obvious approach — yields an ID that fails every cross-lookup.
- **Truthful counters.** Snapchat ships a stale `videoMetadata` mirror alongside real `engagementStats`, and pads its Spotlight list with placeholder entries whose counters are all `"0"`. Reporting those as engagement data is fabricating numbers.
- **Egress.** A 404 is a permanent answer and a 429 is a temporary one, and treating them the same either burns your budget or loses your data. The proxy bill only starts when you actually get pushed back, which is why this Actor runs direct by default and escalates only on evidence.

You also inherit the boring parts: retry budgets, per-item live saving so a crash halfway through does not lose the first half, and a dataset schema that stays stable when Snapchat moves a key.

### How to use Snapchat Profile Scraper

Snapchat Profile Scraper runs on Apify. Start it from the Apify Console or call it through the Apify API — the only credential involved is your Apify token.

1. Open Snapchat Profile Scraper on Apify and click **Try for free**
2. Add one or more entries to **Usernames or profile URLs** (`urls`) — this is the only required input, and it is prefilled with `["djkhaled305"]`
3. Leave **Proxy settings** (`proxyConfiguration`) alone unless you need a specific exit country — the default is a direct connection with no proxy
4. Optionally lower **Max attempts per profile** (`maxRetries`) or raise **Request timeout** (`requestTimeoutSecs`)
5. Click **Start** — rows are written to the dataset as each handle finishes, not at the end
6. Export as JSON, CSV, Excel, XML or HTML, or read the dataset through the Apify API

**What a run missing `urls` does.** The schema marks it required with `minItems: 1`, so the Console will not let you submit it empty. A caller that assembles the input JSON itself and omits the field gets a run that starts cleanly, logs `Received 0 input item(s)`, writes zero rows, saves its summary record and finishes **successfully** — it does not error and it charges nothing.

#### How to scale to bulk profile extraction

`urls` is a list, so bulk is the normal mode — add as many handles as you want in one run, in mixed forms. Bare handles, `@handles`, `/@handle` URLs and `/add/handle` URLs can all sit in the same list.

Handles are processed strictly one at a time and each row is pushed the moment it is built, so a run that hits its timeout leaves a partial but valid dataset rather than nothing. Neither the schema nor the code caps how many entries one run may hold; the practical ceiling is the memory and timeout you assign the run on Apify.

On duplicates: the input schema sets `uniqueItems: true`, so Apify rejects two byte-identical entries at validation time. That check is on the exact string, and the code adds no deduplication of its own — so `djkhaled305`, `@djkhaled305` and `https://www.snapchat.com/@djkhaled305` are three distinct entries that resolve to the same handle, get fetched three times and are charged three times. Normalise your list before you submit it.

### What can you do with Snapchat profile data?

- 🎯 An **influencer marketer** qualifying Snapchat creators reads `data.userProfile.publicProfileInfo.subscriberCount` alongside `plays` and `comments` across `data.spotlight` to see whether a large subscriber number is matched by clips people actually watch.
- 🔗 A **lead researcher** enriching a handle list pulls `websiteUrl` and `bio` out of the profile block to find the shop, linktree or agency site behind each creator, then matches those domains against a CRM.
- 📈 A **social analyst** tracking a competitive set re-runs the same handle list weekly and diffs `plays`, `shares` and `comments` per `spotlightId` to see which clips are still accruing views rather than which merely existed.
- 🎬 A **creative researcher** building a reference library collects `mediaUrl`, `thumbnailSrc` and `spotlightTitle` so they have the actual clip next to its title instead of a screenshot of a tile.
- 🤖 An **AI engineer** building a creator-research agent indexes `bio`, `title` and each `spotlightTitle` into a vector store, keeping `subscriberCount`, `categoryStringId` and `plays` as metadata filters so the agent can answer "which music creators on Snapchat post clips that clear a million plays".

Every one of these is callable from an agent framework over the Apify API, since the Actor is a standard HTTP-triggered run.

### How does Snapchat Profile Scraper handle rate limits and blocking?

The Actor makes a plain HTTP GET per profile through `curl_cffi` with a Chrome TLS and HTTP/2 fingerprint and an `en-US` `Accept-Language` header. There is **no headless browser, no CAPTCHA solving and no login flow** anywhere in the code, and none is claimed. Snapchat's profile page is keyless and cookieless, which is why an HTTP client is enough.

**Egress starts direct.** The default is a direct connection with no proxy at all. If you supply `proxyConfiguration`, that becomes the starting mode instead. Blocking is detected on HTTP 401, 403, 407 or 429, and on network errors whose message mentions a timeout, proxy, block, TLS, tunnel or connection problem. When that happens the Actor climbs a two-rung ladder — the Apify datacenter group `BUYPROXIES94952`, then `RESIDENTIAL` — and logs each switch. Once a rung is engaged it stays engaged for the rest of the run; the Actor never drops back down and never climbs past the end of the ladder. Note that escalation overrides a proxy configuration you supplied yourself, so a run that starts on your chosen group can finish on a fallback group.

**Retries are per profile.** Each handle gets up to `maxRetries` attempts, default 3. Between attempts the Actor sleeps `1.5 × attempt` seconds, capped at 6 seconds — so 1.5s, then 3s, then 4.5s, and 6s from the fourth attempt on. **HTTP 404 is never retried**: a dead handle still serves a `__NEXT_DATA__` script, so the Actor treats the 404 as a definitive answer, returns immediately and moves on rather than burning three requests on it.

When every attempt is exhausted, the failure is written to the dataset as a typed row with `ok: false` and an `errorReason`, and the run continues to the next handle. A failing profile never fails the run.

### ⬇️ Input

Four parameters, one of them required. Everything below is read from the Actor's input schema as shipped.

| Parameter | Required | Type | Description | Example Value |
| ----- | ----- | ----- | ----- | ----- |
| `urls` | Yes | array | Snapchat usernames or profile URLs, one entry per line (`stringList` editor). Accepts `djkhaled305`, `@djkhaled305`, `https://www.snapchat.com/@djkhaled305` and `https://www.snapchat.com/add/djkhaled305`. `minItems: 1`, `uniqueItems: true`, no maximum. Prefilled with `["djkhaled305"]`. | `["djkhaled305", "@selenagomez", "https://www.snapchat.com/add/loganpaul"]` |
| `proxyConfiguration` | No | object | Apify Proxy settings (`proxy` editor). Prefilled with `{"useApifyProxy": false}` — Snapchat profile pages are public and are not blocked from Apify's network, so the default is a direct connection. Set it only when you need a specific exit country, because Snapchat's edge caches Spotlight view counts per region. | `{"useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"]}` |
| `maxRetries` | No | integer | Attempts per profile before giving up. Default `3`, minimum `1`, maximum `10`. HTTP 404 never retries regardless of this value. Lower it to finish faster when your list contains dead handles. | `5` |
| `requestTimeoutSecs` | No | integer | Per-request timeout in seconds. Default `30`, minimum `5`, maximum `180`. A large creator profile can return a 2 MB page, so keep this at 30 or above. | `45` |

Four honest notes on how these behave:

- **`maxRetries` and `requestTimeoutSecs` are genuinely read and applied**, not accepted and ignored. Both are re-clamped in code to exactly the schema bounds — 1 to 10, and 5 to 180 — so an out-of-range value sent through the API is pulled back into range rather than rejected. A value of `0`, `null` or a missing field falls back to the default.
- ⚠️ **Two undocumented input aliases exist.** `profileUrls` is accepted as a fallback for `urls`, and `requestTimeout` as a fallback for `requestTimeoutSecs`. Neither appears in the input schema, so neither is visible in the Console — they exist for callers migrating from an older input shape. There is no alias for `maxRetries` or `proxyConfiguration`. Prefer the documented names.
- **What counts as a valid handle.** After stripping, a candidate must match `^[A-Za-z0-9._-]{1,64}$` — letters, digits, dot, dash and underscore, 1 to 64 characters. A string is treated as a URL only if it starts with `http://` or `https://` or contains `snapchat.com`, **and** contains one of the markers `/@`, `/add/` or `/p/`. The `/p/` form is accepted but is not documented in the input schema. A URL with no marker — `https://www.snapchat.com/djkhaled305` — is rejected, and so is a trailing-slash bare handle like `djkhaled305/`, because the slash fails the pattern.
- **A rejected entry costs nothing.** It never becomes an HTTP request. It is written straight to the dataset as an uncharged four-key row with `errorReason: "invalid_input"` and your original string echoed in `input`, and the run moves on.

#### Example input

```json
{
  "urls": [
    "djkhaled305",
    "@selenagomez",
    "https://www.snapchat.com/add/loganpaul",
    "https://www.snapchat.com/@taylorswift"
  ],
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "maxRetries": 3,
  "requestTimeoutSecs": 30
}
```

### ⬆️ Output

One dataset row per input entry, written live as each handle finishes. Twelve top-level keys exist across the three row shapes, and the Actor's dataset schema declares all twelve as its default table view — `ok`, `username`, `profileType`, `profileFound`, `httpStatus`, `url`, `data`, `attempt`, `proxyMode`, `errorReason`, `error` and `input`. The view and the row builder match exactly at the top level; the view renders `data` as a single column, so the nested profile and Spotlight keys documented above appear in the JSON rather than as table columns. Export as JSON, CSV, Excel, XML or HTML, or read the dataset through the Apify API.

Not every row carries every key — **absent fields are omitted, not nulled**:

- A **success row** has nine keys: `ok`, `username`, `url`, `httpStatus`, `profileFound`, `profileType`, `data`, `attempt`, `proxyMode`. No `error`, no `errorReason`, no `input`.
- A **fetch-failure row** has ten keys: the same envelope minus `data`, plus `error` and `errorReason`. `profileType` is `null` and `profileFound` is `false`.
- An **invalid-input row** has four keys only: `ok`, `input`, `error`, `errorReason`. There is no `username`, `url`, `httpStatus`, `attempt` or `proxyMode` on it, because nothing was ever requested.

**On charging.** Exactly one pay-per-event event exists on this Actor: `row_result`. It is charged only when the row actually carries profile data. The marker is **`ok`** — a row with `ok: true` is charged, and every row with `ok: false` is pushed **uncharged**. That covers invalid input, a dead handle, a blocked fetch, a parse failure and an exhausted retry budget alike, so you can see every miss in the dataset without paying for any of them.

The filter is a single boolean:

```python
items = list(client.dataset(run["defaultDatasetId"]).iterate_items())
profiles = [row for row in items if row.get("ok")]        # charged rows
misses   = [row for row in items if not row.get("ok")]    # free diagnostics
```

The run also writes a `SUMMARY.json` record to the key-value store with `total`, `processed`, `successes`, `chargedRows`, `fallbackToResidential` and `fallbackProxyGroup` — reconcile `chargedRows` against your bill after a run.

#### Example output

One successful `publicProfile` row. The `userProfile` block is shown exactly as Snapchat serves it, including its `$case` discriminator and its string-typed `subscriberCount`:

```json
{
  "ok": true,
  "username": "djkhaled305",
  "url": "https://www.snapchat.com/@djkhaled305",
  "httpStatus": 200,
  "profileFound": true,
  "profileType": "publicProfile",
  "data": {
    "search": "https://www.snapchat.com/add/djkhaled305",
    "spotlight": [
      {
        "spotlightId": "W7_EDlXWTBiXAEEniNoMPwAAY",
        "link": "https://www.snapchat.com/@djkhaled305/spotlight/W7_EDlXWTBiXAEEniNoMPwAAY",
        "thumbnailSrc": "https://cf-st.sc-cdn.net/d/GB4OAfva0yenm5Om8cmK7.256.IRZXSOY?uc=46",
        "spotlightTitle": "another one in the studio",
        "plays": 1842300,
        "shares": 9412,
        "comments": 1387,
        "timestampInSec": "1768909134",
        "mediaUrl": "https://cf-st.sc-cdn.net/d/GB4OAfva0yenm5Om8cmK7.1034.IRZXSOY?uc=46",
        "transcriptUrl": "https://cf-st.sc-cdn.net/d/transcripts/GB4OAfva0yenm5Om8cmK7.vtt?uc=46"
      },
      {
        "spotlightId": "3GpyvIb9qbFYRBg7QhU4L",
        "link": "https://www.snapchat.com/@djkhaled305/spotlight/3GpyvIb9qbFYRBg7QhU4L",
        "thumbnailSrc": "https://cf-st.sc-cdn.net/c/3GpyvIb9qbFYRBg7QhU4L.256.IRZXSOY?uc=46",
        "spotlightTitle": null,
        "plays": 304118,
        "shares": 2205,
        "comments": 341,
        "timestampInSec": "1766104882",
        "mediaUrl": "https://cf-st.sc-cdn.net/c/3GpyvIb9qbFYRBg7QhU4L.1034.IRZXSOY?uc=46",
        "transcriptUrl": null
      }
    ],
    "userProfile": {
      "publicProfileInfo": {
        "username": "djkhaled305",
        "title": "DJ Khaled",
        "snapcodeImageUrl": "https://app.snapchat.com/web/deeplink/snapcode?username=djkhaled305&type=SVG&bitmoji=enable",
        "badge": 1,
        "categoryStringId": "public-profile-category-v3-people",
        "subcategoryStringId": "public-profile-subcategory-v3-musician-band",
        "subscriberCount": "5127400",
        "bio": "WE THE BEST MUSIC. New album out now.",
        "websiteUrl": "https://wethebestmusic.com/",
        "profilePictureUrl": "https://cf-st.sc-cdn.net/aps/bolt/aHR0cHM6Ly9jZi1zdC5zYy1jZG4ubmV0L2QvMGdiR0YxNEl0Q1JDYVNZOXlkbVBp._RS0,90_FMjpeg",
        "address": "Miami, FL",
        "hasCuratedHighlights": true,
        "hasSpotlightHighlights": true,
        "mutableName": "",
        "publisherType": "",
        "squareHeroImageUrl": "",
        "primaryColor": "",
        "hasStory": true,
        "relatedAccountsInfo": []
      },
      "$case": "publicProfileInfo"
    },
    "spotlightHighlights": []
  },
  "attempt": 1,
  "proxyMode": "direct"
}
```

A dead handle from the same run — note the missing `data` key and the `ok: false` that keeps it uncharged:

```json
{
  "ok": false,
  "username": "thishandledoesnotexist",
  "url": "https://www.snapchat.com/@thishandledoesnotexist",
  "httpStatus": 404,
  "profileFound": false,
  "profileType": null,
  "error": "Snapchat returned HTTP 404 for this handle",
  "errorReason": "profile_not_found",
  "attempt": 1,
  "proxyMode": "direct"
}
```

And an entry that never became a request at all:

```json
{
  "ok": false,
  "input": "https://www.snapchat.com/djkhaled305",
  "error": "Invalid Snapchat username / URL",
  "errorReason": "invalid_input"
}
```

### How does it work?

Snapchat renders its public profile page server-side and ships the whole record inside a `<script id="__NEXT_DATA__">` tag. Snapchat Profile Scraper normalises whatever you passed into a handle, requests `https://www.snapchat.com/@<handle>` once, and reads that script out of the HTML. It then recursively decodes the JSON-in-strings Snapchat nests inside the payload, picks the profile branch from the `$case` discriminator, and builds the Spotlight array from the clip metadata and highlight lists.

Requests go out over a direct connection with a Chrome TLS fingerprint. Only if Snapchat pushes back — a 401, 403, 407 or 429, or a connection-level failure — does the Actor escalate to an Apify datacenter proxy and then to residential, logging each switch. No account, cookie or session is used anywhere, so only publicly visible data is ever returned.

Because the data comes from a structured JSON payload rather than from CSS selectors, a Snapchat front-end redesign generally does not change your field names, and a key Snapchat moves degrades one field rather than breaking the row. The twelve top-level keys and the ten Spotlight keys are built by the Actor and stay put.

### Integrations

Snapchat Profile Scraper is an Apify Actor, so it works with anything that can call the Apify API or read a dataset.

#### Calling Snapchat Profile Scraper from Python

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")

run = client.actor("<YOUR_USERNAME>/snapchat-profile-scraper").call(run_input={
    "urls": ["djkhaled305", "@selenagomez", "https://www.snapchat.com/add/loganpaul"],
    "maxRetries": 3,
    "requestTimeoutSecs": 30,
})

for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    if not row.get("ok"):
        continue
    profile = row["data"]["userProfile"].get("publicProfileInfo", {})
    print(row["username"], profile.get("subscriberCount"), len(row["data"]["spotlight"]))
```

Works in Go, Ruby, Node.js, cURL — any language that can make an HTTP request.

#### No-code tools (n8n, Make, LangChain)

In n8n, use the Apify node — or an HTTP Request node pointed at the Actor's run endpoint with your token — and put a Filter node on `ok` immediately after it, so only charged profile rows reach your Google Sheets or Postgres step. In Make, the Apify module supports run-and-wait, which lets a weekly creator-list refresh feed an Airtable step with no glue code. In LangChain, wrap the run endpoint as a tool and hand the rows to the model directly, since they are already typed JSON. Apify schedules and webhooks cover recurrence and completion triggers, so a monthly subscriber snapshot needs nothing of your own.

### Is it legal to scrape Snapchat profiles?

Scraping publicly visible profile data is broadly treated as permissible where no login is used and no private account is accessed, and Snapchat Profile Scraper does neither — it reads only what a signed-out visitor is served on the public profile page.

**This output contains personal data about identifiable individuals.** On the row envelope, `username` and `url` identify a person. Inside `data.userProfile.publicProfileInfo`, the personal-data fields are `username`, `title`, `bio`, `subscriberCount`, `profilePictureUrl`, `snapcodeImageUrl`, `squareHeroImageUrl`, `websiteUrl`, `address`, `categoryStringId`, `subcategoryStringId` and `relatedAccountsInfo` — the last of which names third parties who never appeared in your input. On a personal account, `data.userProfile.userInfo` carries username, display name, Snapcode and Bitmoji. In `data.spotlight`, `mediaUrl`, `thumbnailSrc`, `link`, `spotlightTitle` and `transcriptUrl` point at video, images and speech of a real person, and `data.spotlightHighlights` carries the raw snap media URLs behind them.

Under GDPR, UK GDPR and the CCPA you need a lawful basis before you store or reuse any of it, and data minimisation applies at ingestion rather than after the fact. If your purpose is audience sizing, keep `username` and the count fields and drop the heaviest payload first — `profilePictureUrl`, `snapcodeImageUrl`, `relatedAccountsInfo`, the Spotlight media URLs and `data.spotlightHighlights`. Set a retention period that matches that purpose rather than warehousing rows indefinitely, and keep the `username`-to-source-row mapping you would need to honour an access or erasure request.

One factual point specific to this platform: Snapchat's user base skews young, and processing children's personal data carries heightened obligations — GDPR Article 8 on a child's consent, the UK's Age Appropriate Design Code, and COPPA in the United States. The public profile page carries no age field, so the Actor cannot and does not tell you which handles belong to minors.

Consult legal counsel if your use case involves bulk storage of personal data.

### ❓ Frequently asked questions

#### What Snapchat profile fields does Snapchat Profile Scraper return?

The five most used are `subscriberCount`, `bio`, `title` (display name), `profilePictureUrl` and the `data.spotlight` array with its `plays`, `shares` and `comments`. Every row carries a twelve-key envelope across three row shapes, and a successful row nests the full profile block plus a ten-key object per Spotlight clip — see the data fields tables above.

#### Does Snapchat Profile Scraper require a Snapchat account or login?

No. The profile page it reads is keyless and cookieless — there is no login flow, no password field and no session cookie anywhere in the input schema or the code, and none can be supplied. The only credential you need is your Apify token. The trade-off is that anything Snapchat serves only inside the app to an accepted friend — friend lists, private Stories, Snap Map location, chats — is out of reach by design.

#### Does it return Spotlight clips and Stories, or only profile metadata?

Both profile metadata and Spotlight clip metadata, but **not** Stories. `data.spotlight` gives you one object per Spotlight clip with ID, watch link, title, thumbnail, direct media URL, transcript URL, timestamp and the plays, shares and comments counts. `data.spotlightHighlights` gives you Snapchat's raw highlight array on top. What is deliberately excluded is `props.pageProps.story` — today's Story snaps — and `props.pageProps.curatedHighlights`, the saved Highlight albums. That is a scope decision written into the source, not a parsing failure. The transcript **text** is also out of scope: you get `transcriptUrl`, not the words.

#### How many Snapchat profiles can I extract in one run?

There is no cap in the input schema and none in the code — `urls` accepts as many entries as you want to give it. Profiles are fetched one at a time and each row is written the moment it is ready, so the real ceiling is the memory and timeout you assign the run on Apify. For very long lists it is usually better to split them across scheduled runs than to push a single run to its limit.

#### What happens if a profile is private, does not exist, or is region-restricted?

Each case behaves differently, and none of them crashes the run or costs you anything you did not get data for.

A **handle that does not exist** returns HTTP 404. That is treated as definitive and is **never retried** — the Actor logs it, writes a row with `ok: false`, `httpStatus: 404`, `profileFound: false` and `errorReason: "profile_not_found"`, and moves on uncharged. The same `errorReason` is used when a page returns 200 but carries no recognisable profile payload.

A **private account** — meaning a personal Snapchat account that never set up a Public Profile — is not an error here. Snapchat still serves a thin record, so you get a **charged success row** with `profileType: "personalProfile"` and `data.userProfile.userInfo` holding username, display name, Snapcode and Bitmoji. There is no bio, no subscriber count and no Spotlight on it. Branch on `profileType` before you read `publicProfileInfo`.

A **region-restricted or blocked** response is the retryable case. HTTP 401, 403, 407 and 429 mark the fetch as blocked, trigger the proxy escalation ladder, and are retried up to `maxRetries` times with a `1.5 × attempt` second pause capped at 6 seconds. If every attempt fails you get an uncharged row with `errorReason` set to `blocked`, `http_error` or `network_error` and the last `httpStatus` seen. Because Snapchat's edge caches Spotlight view counts per region, setting `proxyConfiguration` to a specific country is also how you pin which region's numbers you get.

#### Can I scrape multiple Snapchat profiles at once?

Yes. `urls` is a list and bulk is the normal mode — bare handles, `@handles`, `/@handle` URLs and `/add/handle` URLs can be mixed freely in one run. Two caveats. The schema's `uniqueItems: true` only catches byte-identical duplicates, so three different spellings of the same handle are fetched and charged three times. And an unparseable entry does not stop the run — it becomes a free `invalid_input` row and the next entry proceeds.

#### Does Snapchat Profile Scraper work with Claude, ChatGPT and other AI agent tools?

Yes. It is callable as a standard HTTP-triggered Apify run, so LangChain, CrewAI, n8n or a hand-written tool definition can start it and read back typed JSON with no parsing step. One thing to build into the tool wrapper: this Actor **does** write diagnostic rows, so filter on `row.get("ok")` before handing results to a model, or an agent will happily reason about a 404.

#### How does Snapchat Profile Scraper compare to other Snapchat profile scrapers?

All observations below were checked on the Apify Store on 25 July 2026 and describe what each listing documents — not measured performance.

`argusapi/snapchat-profile-scraper` is the most used of the three by run count. Its README states there is only one input, the Snapchat username, so profile-URL input is not documented on its listing. Its published sample nests everything under a `json` key with its own `error` object carrying `isError` and `errorMsg`, an `accountType` of `public` or `private`, and separate `spotlightHightlightSnaps`, `curatedHighlightSnaps` and `latestStorySnaps` arrays — so it documents curated highlights and Story snaps, which this Actor does not return. Its sample does not document per-clip plays, shares or comment counts.

`karamelo/snapchat-profile-scraper` documents the closest field set to this one: the same `publicProfileInfo` block, and a `spotlight` array with `spotlightId`, `link`, `thumbnailSrc`, `spotlightTitle`, `plays`, `shares`, `comments`, `timestampInSec` and `mediaUrl` — plus an `isSponsored` flag this Actor does not return, and a transcript key named `transcript` rather than `transcriptUrl`. Its README states pricing of $2 per 1,000 profiles and 2,500 profiles on the free plan; that is their published figure, not a claim about this Actor.

`easyapi/snapchat-profile-scraper` documents a `usernames` array plus `proxyConfiguration` as its only inputs, and its sample output is a **flat** profile row with no Spotlight clip array at all, including `creationTime` and `lastUpdateTime` — two fields this Actor does not surface at the top level.

This Actor's honest position: four accepted input forms including two URL shapes, an explicit `personalProfile` branch so an account without a Public Profile returns its real fields instead of an empty success, Spotlight IDs extracted from inside Snapchat's AppsFlyer deeplink rather than by splitting on `?`, `engagementStats` preferred over Snapchat's stale `videoMetadata` mirror with placeholder tiles nulled rather than reported as zeros, and **every failure written as an uncharged row** you can see and filter. What it does not do: no Stories, no saved Highlight albums, no transcript text, no `isSponsored` flag, no e-mail or phone extraction, no creator discovery by topic, and no normalisation of anything inside Snapchat's profile block.

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

Yes. Typed, normalized JSON with consistent field names across runs — no HTML parsing, no selectors. The envelope and the Spotlight objects are built by the Actor and are stable. Two things to map deliberately: `data.userProfile` is Snapchat's own block passed through verbatim, so its key membership is Snapchat's to change, and `subscriberCount` and `timestampInSec` arrive as strings rather than numbers.

#### What happens when Snapchat changes its layout or anti-bot system?

The scraper is maintained, and because it reads Snapchat's own `__NEXT_DATA__` JSON rather than rendered markup, a front-end redesign generally does not affect it — your top-level and Spotlight field names stay put. Values are read by explicit path with documented fallbacks, so a key Snapchat moves degrades a single field rather than failing the row. Anti-bot pressure shows up as a 429 or a connection failure, which the Actor answers with backoff and the direct → datacenter → residential ladder, with no configuration change on your side.

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

Yes, and the default is to use none at all. Leave `proxyConfiguration` alone and the run goes out direct; the Actor reaches for an Apify datacenter or residential proxy only if Snapchat actually pushes back, and it draws and rotates those IPs for you. You never create a proxy account. Set the field yourself only when you need a specific exit country — and be aware that a block during the run can still escalate you off your chosen group.

#### Which Snapchat profile fields work best for AI training data and RAG indexing?

For RAG, `bio` and each `spotlightTitle` carry the most information per record and chunk cleanly, with `title`, `username`, `categoryStringId` and `subcategoryStringId` as ready-made metadata filters and `websiteUrl` as an entity-resolution key. For training data, the most structurally consistent fields across records are `plays`, `shares`, `comments`, `profileType`, `badge`, `hasStory`, `hasCuratedHighlights` and `hasSpotlightHighlights`. Two things to handle before you feed any of it to a model: `subscriberCount` and `timestampInSec` are strings and need casting, and `plays`, `shares` and `comments` are `null` rather than `0` on placeholder Spotlight tiles, so impute or presence-check instead of treating a missing value as zero engagement.

### 🔗 Related scrapers

| Scraper Name | What it extracts |
| ----- | ----- |
| Snapchat Ads Scraper | Snapchat's public ads library — creative, headlines, impressions and top-snap media links for any advertiser |
| Instagram Profile Scraper | The same profile-level identity, bio, follower and post data for Instagram accounts |
| TikTok Profile Scraper | Profile-level identity and metric fields for TikTok creators |
| Instagram Related Profiles Scraper | Suggested-account graphs expanded from a seed Instagram profile |
| Reddit User Profile Posts and Comments Scraper | Public posts, comments and account age for a Reddit user |
| YouTube Channel Finder | Channel discovery and channel-level metadata by keyword or URL |

Which one suits which job on Snapchat: use **Snapchat Profile Scraper** when the creator or brand account itself is the record you want, with its Spotlight clip performance attached. Use **Snapchat Ads Scraper** when you want what those same brands are paying to put in front of people — the two together give you the organic and paid halves of one advertiser.

### 💬 Your feedback

Found a bug, or need a field that is in Snapchat's `__NEXT_DATA__` payload but not in the output? Open an issue on the Actor's Issues tab. Reports that include the exact input JSON and the handle you ran are the fastest to reproduce and fix — and if a handle produced an `errorReason` you did not expect, paste that row too.

# Actor input Schema

## `urls` (type: `array`):

One or more Snapchat usernames (e.g. djkhaled305) or profile URLs. Accepted forms: djkhaled305, @djkhaled305, https://www.snapchat.com/@djkhaled305 and https://www.snapchat.com/add/djkhaled305.

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

Optional. Snapchat profile pages are public and are not blocked from Apify's network, so the default is a direct connection with no proxy. Enable a proxy only if you need a specific exit country: Snapchat's edge caches Spotlight view counts per region.

## `maxRetries` (type: `integer`):

How many times to try a profile before giving up. HTTP 404 (the handle does not exist) never retries. Lower this to finish faster when your input list contains dead handles.

## `requestTimeoutSecs` (type: `integer`):

Per-request timeout. A large creator profile can return a 2 MB page, so keep this at 30 seconds or more.

## Actor input object example

```json
{
  "urls": [
    "djkhaled305"
  ],
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "maxRetries": 3,
  "requestTimeoutSecs": 30
}
```

# Actor output Schema

## `results` (type: `string`):

All scraped items in the Actor's default dataset.

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

A single JSON record (key SUMMARY.json) with total/processed/successes/chargedRows counts and proxy fallback status for the run.

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

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

# Run the Actor and wait for it to finish
run = client.actor("scraper-engine/snapchat-profile-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 '{
  "urls": [
    "djkhaled305"
  ],
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call scraper-engine/snapchat-profile-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scraper-engine/snapchat-profile-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/fzj6ILAayJyNSaxTg/builds/RN98laEHJbROCG0ZF/openapi.json
