Snapchat Profile Scraper
Pricing
$19.99/month + usage
Snapchat Profile Scraper
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.
Pricing
$19.99/month + usage
Rating
0.0
(0)
Developer
Scraper Engine
Maintained by CommunityActor stats
1
Bookmarked
47
Total users
0
Monthly active users
7 days ago
Last modified
Categories
Share
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.
publicProfileInfoanduserInfoare discriminated by a$casekey. 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
videoMetadatamirror alongside realengagementStats, 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.
- Open Snapchat Profile Scraper on Apify and click Try for free
- Add one or more entries to Usernames or profile URLs (
urls) — this is the only required input, and it is prefilled with["djkhaled305"] - Leave Proxy settings (
proxyConfiguration) alone unless you need a specific exit country — the default is a direct connection with no proxy - Optionally lower Max attempts per profile (
maxRetries) or raise Request timeout (requestTimeoutSecs) - Click Start — rows are written to the dataset as each handle finishes, not at the end
- 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.subscriberCountalongsideplaysandcommentsacrossdata.spotlightto see whether a large subscriber number is matched by clips people actually watch. - 🔗 A lead researcher enriching a handle list pulls
websiteUrlandbioout 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,sharesandcommentsperspotlightIdto see which clips are still accruing views rather than which merely existed. - 🎬 A creative researcher building a reference library collects
mediaUrl,thumbnailSrcandspotlightTitleso 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,titleand eachspotlightTitleinto a vector store, keepingsubscriberCount,categoryStringIdandplaysas 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:
maxRetriesandrequestTimeoutSecsare 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 of0,nullor a missing field falls back to the default.- ⚠️ Two undocumented input aliases exist.
profileUrlsis accepted as a fallback forurls, andrequestTimeoutas a fallback forrequestTimeoutSecs. 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 formaxRetriesorproxyConfiguration. 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 withhttp://orhttps://or containssnapchat.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 likedjkhaled305/, 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 ininput, and the run moves on.
Example input
{"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. Noerror, noerrorReason, noinput. - A fetch-failure row has ten keys: the same envelope minus
data, pluserroranderrorReason.profileTypeisnullandprofileFoundisfalse. - An invalid-input row has four keys only:
ok,input,error,errorReason. There is nousername,url,httpStatus,attemptorproxyModeon 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:
items = list(client.dataset(run["defaultDatasetId"]).iterate_items())profiles = [row for row in items if row.get("ok")] # charged rowsmisses = [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:
{"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:
{"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:
{"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
from apify_client import ApifyClientclient = 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"):continueprofile = 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.