npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

snapchat-scraper-api

v0.0.1

Published

Snapchat scraper API client: public profile data, follower counts and Spotlight videos via ScrapingBee.

Readme

snapchat-scraper-api

Public Snapchat profile data for Node. One credit a profile, and a client that already knows about the two page layouts.

npm install snapchat-scraper-api

Node 16 or newer. One dependency, axios.

const { SnapchatScraper } = require('snapchat-scraper-api');
const bee = new SnapchatScraper(process.env.SCRAPINGBEE_API_KEY);

Key with 1,000 free credits: scrapingbee.com.

Run against snapchat.com/add/teamsnapchat and snapchat.com/add/mrbeast on 2026-09-10.


"I want a profile record"

const p = await bee.profile('teamsnapchat');

p.display_name;    // 'Team Snapchat'
p.username;        // 'teamsnapchat'
p.subtitle;        // 'Add me on Snapchat!'
p.snapcode;        // the Snapcode SVG deeplink
p.profile_image;   // the web capture preview
p.canonical;       // 'https://www.snapchat.com/@teamsnapchat'

bee.lastCost reads 1 after that. Snapchat delivers these pages as plain HTML, so the proxy ladder settles on its cheapest rung and no browser is opened. Adding render_js here costs five times more and returns the same thing.

The same call works on a creator profile, which is the part worth explaining.

"Why does my own scraper work on some profiles and not others"

Because Snapchat ships two layouts that disagree about headings. One rule set, two handles:

| Selector | teamsnapchat | mrbeast | |---|---|---| | h1 span | empty | MrBeast | | h4 span | Team Snapchat | empty | | h5 span | teamsnapchat | a content tile, wrong node | | [data-testid="snapCodeImage"] | the Snapcode | empty |

They never both populate. This client asks for both headings and keeps whichever answered, so profile() returns a filled record either way. Written against h4 span alone, a scraper works on half of Snapchat and quietly writes empty strings for the rest, at HTTP 200.

Two more things it handles:

  • Username from the canonical URL, not a heading. The canonical is identical on both layouts and never changes with locale, always https://www.snapchat.com/@<handle>.
  • Snapcode rebuilt when the attribute is missing. That data-testid only exists on basic cards. Since the endpoint is keyed on username, the client constructs it, and the constructed string matched the live value exactly on both handles. No second request.
SnapchatScraper.snapcodeUrl('mrbeast');   // free, no network call

And the rule that saves the most time: never select on a Snapchat CSS class. They look like UserDetailsCard_title__K9Awz and Heading_h400Emphasis__SQXxl. The tail after the double underscore is a build hash, so it changes on deploy and your selector starts returning nothing without any error.

"I want the follower count"

It is not in the page text. The visible DOM holds Snapchat's i18n placeholder, literally {subscriberCount} suscriptores, so a text selector hands you the template.

const s = await bee.stats('mrbeast');

s.followers;         // 1463400
s.created;           // '2019-05-16T14:46:37.345Z'
s.modified;          // '2026-08-13T13:01:39.438Z'
s.locale;            // 'en-US'
s.family_friendly;   // true

1 credit. The number lives in the structured data, in the counter whose interaction type is FollowAction, and this client digs it out.

created and modified are the other two you cannot get elsewhere. modified separates an active creator from a parked handle without touching a single post.

"I want their public videos"

const clips = await bee.spotlight('mrbeast');
clips.length;   // 5
clips[0];       // { url, name, description, thumbnail }

1 credit. An empty array is a normal answer, not a bug. teamsnapchat returned zero from the same call, because it has no public video.

"I want to check whether a handle exists"

await bee.exists('teamsnapchat');   // true
await bee.exists('dailymail');      // false
await bee.profile('dailymail');     // {}

A handle with no public page answers two different ways, both observed on the same handle at different times: once as HTTP 200 with a 5,973 byte body, a bare Snapchat title and no profile object, and once as a genuine HTTP 404 forwarded from Snapchat, because 404 is one of the few statuses the API passes through untouched.

This client folds both into "no page" instead of throwing, so a batch of handles does not die on the first bad one. Test the returned value, not the status code.

"The page came back in the wrong language"

Snapchat localises on proxy exit IP. Two captures of one creator URL, identical parameters, nothing changed in between:

capture 1   MrBeast (@mrbeast) | Historias de Snapchat, Spotlight y Lentes
capture 2   og_title: MrBeast pe Snapchat

Spanish, then Romanian. stats() surfaces it as locale.

What actually moves:

| Field | Locale dependent | |---|---| | og_title, og_description, page title, labels | yes | | canonical, username, profile_image, snapcode | no | | followers, created, modified | no |

So pin the country only when you read the strings:

await bee.profile('mrbeast', 'us');   // 25 credits, English
await bee.stats('mrbeast');           // 1 credit, same number either way

Pinned and unpinned runs returned an identical follower value. Geotargeting needs the premium tier, so it is a twenty five times increase for a field you may not be using.

Cost

await bee.profile('teamsnapchat');
bee.lastCost;   // 1
await bee.usage();   // free

| Configuration | Credits | |---|---| | Auto mode, plain HTML rung | 1 | | Premium proxy with a country pinned | 25 | | Validation error | 0 |

Auto mode bills only the rung that worked and nothing when all of them fail. It cannot be combined with render_js, premium_proxy or stealth_proxy, and sending both gives HTTP 400 at no charge.

250,000 credits is 250,000 profile checks. Plan tiers.

Scope

Public profile pages. Private accounts, friend lists, Snap Map data, direct messages and story views need a signed in session, and scraping under login credentials is prohibited by ScrapingBee's terms of service.

Elsewhere

Patreon scraper API, TikTok API, TikTok search API, Twitch API, Substack scraper API, YouTube shorts API, YouTube video scraper API, YouTube metatags.

Features: data extraction, AI web scraping, screenshots, markdown scraper.

The selector durability ladder, rung by rung: github.com/ScrapingBee/snapchat-scraper-api.

License

MIT