snapchat-scraper-api 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 wordstotech
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,2 @@
1
+ include README.md
2
+ include LICENSE
@@ -0,0 +1,228 @@
1
+ Metadata-Version: 2.4
2
+ Name: snapchat-scraper-api
3
+ Version: 0.0.1
4
+ Summary: Snapchat scraper API client: public profile data, follower counts and Spotlight videos via ScrapingBee.
5
+ Author: wordstotech
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/ScrapingBee/snapchat-scraper-api
8
+ Project-URL: Repository, https://github.com/ScrapingBee/snapchat-scraper-api
9
+ Project-URL: Documentation, https://www.scrapingbee.com/documentation/
10
+ Keywords: snapchat scraper,snapchat api,snapchat profiles scraper,snapchat data scraper,web scraping,scrapingbee
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.8
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Internet :: WWW/HTTP
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Classifier: Topic :: Text Processing :: Markup :: HTML
25
+ Requires-Python: >=3.8
26
+ Description-Content-Type: text/markdown
27
+ License-File: LICENSE
28
+ Requires-Dist: requests>=2.25.0
29
+ Dynamic: license-file
30
+
31
+ # snapchat-scraper-api
32
+
33
+ A Python client for public Snapchat profile data through ScrapingBee. Built around the fact that most selectors on a Snapchat profile are guaranteed to break, and the ones that are not live somewhere unobvious.
34
+
35
+ **Verified against `snapchat.com/add/teamsnapchat` and `snapchat.com/add/mrbeast` on 2026-09-10.** Three findings below only surface on a live run: there are two incompatible page layouts, the follower count is not in the visible text at all, and the page language depends on which country the request left from.
36
+
37
+ ```bash
38
+ pip install snapchat-scraper-api
39
+ ```
40
+
41
+ Requires Python 3.8 or newer and `requests`.
42
+
43
+ ## The cheapest target in the series
44
+
45
+ One credit per profile. Snapchat serves these pages as delivered HTML, so `mode=auto` settles on the plain rung and never opens a browser. Do not add `render_js` out of habit, it buys nothing here and costs five times more.
46
+
47
+ ## What is in scope
48
+
49
+ Public profile pages, read anonymously: display name, username, subtitle, Snapcode, preview image, canonical URL, follower count, profile creation and last modified dates, Snapchat's family friendly flag, and public Spotlight or highlight videos.
50
+
51
+ Out of scope: private accounts, friend lists, Snap Map location data, direct messages and story view data. Those need a signed in session, and scraping under login credentials is prohibited by ScrapingBee's terms of service.
52
+
53
+ ## Authentication
54
+
55
+ ```python
56
+ from snapchat_scraper_api import SnapchatScraper
57
+
58
+ bee = SnapchatScraper("YOUR_API_KEY")
59
+ ```
60
+
61
+ Sent as `Authorization: Bearer YOUR_API_KEY`. Key and 1,000 free credits: [ScrapingBee](https://www.scrapingbee.com/). Landing page: [Snapchat scraper API](https://www.scrapingbee.com/scrapers/snapchat-scraper-api/).
62
+
63
+ ---
64
+
65
+ ## Method reference
66
+
67
+ ### `profile(username, country=None)`
68
+
69
+ **1 credit** unpinned, **25 credits** with a country pinned.
70
+
71
+ ```python
72
+ bee.profile("teamsnapchat")
73
+ ```
74
+
75
+ ```python
76
+ {'display_name': 'Team Snapchat',
77
+ 'username': 'teamsnapchat',
78
+ 'subtitle': 'Add me on Snapchat!',
79
+ 'snapcode': 'https://app.snapchat.com/web/deeplink/snapcode?username=teamsnapchat&type=SVG&bitmoji=enable',
80
+ 'profile_image': 'https://www.snapchat.com/web-capture/www.snapchat.com/@teamsnapchat/preview/square.jpeg?xp_id=1',
81
+ 'og_title': 'Team Snapchat on Snapchat',
82
+ 'og_description': 'Team Snapchat is on Snapchat!',
83
+ 'canonical': 'https://www.snapchat.com/@teamsnapchat'}
84
+ ```
85
+
86
+ The same call works on a creator profile, which matters more than it sounds.
87
+
88
+ **Snapchat serves two incompatible layouts.** Both handles below were fetched with one rule set:
89
+
90
+ | Selector | `teamsnapchat` (basic card) | `mrbeast` (creator profile) |
91
+ |---|---|---|
92
+ | `h1 span` | empty | `MrBeast` |
93
+ | `h4 span` | `Team Snapchat` | empty |
94
+ | `h5 span` | `teamsnapchat` | wrong node, the page has ten `h5` elements |
95
+ | `[data-testid="snapCodeImage"]` | the Snapcode URL | empty |
96
+
97
+ They are mutually exclusive. This client requests both headings and returns whichever is populated, so one call covers both page types. A scraper written against only `h4 span` works on half of Snapchat and returns empty strings on the other half, with a 200 status and no error.
98
+
99
+ Three more decisions the client makes for you:
100
+
101
+ - **The username comes from the canonical URL**, not from a heading. The canonical is byte identical across layouts and does not change with locale, and it always reads `https://www.snapchat.com/@<username>`.
102
+ - **The Snapcode is rebuilt when the selector is empty.** The endpoint is parameterised by username, and the constructed string matches the live selector value exactly, verified on both handles. No second request.
103
+ - **An empty dict means the handle has no public page.** More on that below.
104
+
105
+ **Never select on CSS module classes.** Snapchat ships class names like `UserDetailsCard_title__K9Awz`, `Heading_h400Emphasis__SQXxl` and `DesktopUserProfile_desktopContainer__UwOc_`. The suffix after the double underscore is a build hash. It rotates on deploy, and a scraper keyed on it silently returns empty strings afterwards.
106
+
107
+ ### `stats(username, country=None)`
108
+
109
+ **1 credit.** Follower count and account dates, from the `application/ld+json` block.
110
+
111
+ ```python
112
+ bee.stats("mrbeast")
113
+ # {'name': 'MrBeast',
114
+ # 'username': 'mrbeast',
115
+ # 'url': 'https://www.snapchat.com/@mrbeast',
116
+ # 'image': 'https://cf-st.sc-cdn.net/aps/bolt/...',
117
+ # 'followers': 1463400,
118
+ # 'created': '2019-05-16T14:46:37.345Z',
119
+ # 'modified': '2026-08-13T13:01:39.438Z',
120
+ # 'locale': 'en-US',
121
+ # 'family_friendly': True}
122
+ ```
123
+
124
+ **The follower count is not in the visible text.** The DOM carries the i18n template placeholder, literally `{subscriberCount} suscriptores`, so a text selector returns the template rather than a number. The real value sits in `interactionStatistic`, in the counter whose `interactionType` is `FollowAction`. This client digs it out.
125
+
126
+ `created` and `modified` are the other two fields available nowhere else. `modified` is how you separate an active creator from a dormant handle without fetching a single post.
127
+
128
+ Returns `None` when there is no public profile page.
129
+
130
+ Note this parses the fetched HTML rather than using `extract_rules`, because **`extract_rules` cannot read script tag contents**. A rule selecting `script[type="application/ld+json"]` returns `None`, tested directly.
131
+
132
+ ### `spotlight(username, country=None)`
133
+
134
+ **1 credit.** Public Spotlight and highlight videos, from the `ItemList` block.
135
+
136
+ ```python
137
+ bee.spotlight("mrbeast")
138
+ # [{'url': 'https://www.snapchat.com/@mrbeast/highlight/91542d31-...',
139
+ # 'name': 'A Snapchat video by MrBeast',
140
+ # 'description': 'A Snapchat video by MrBeast',
141
+ # 'thumbnail': 'https://cf-st.sc-cdn.net/d/...'}, ...]
142
+ ```
143
+
144
+ Five entries on the creator page tested. **An empty list is normal, not a failure.** The same block came back as an empty array on `teamsnapchat`.
145
+
146
+ ### `exists(username)`
147
+
148
+ **1 credit.** Whether the handle has a public profile page.
149
+
150
+ ```python
151
+ bee.exists("teamsnapchat") # True
152
+ bee.exists("dailymail") # False
153
+ ```
154
+
155
+ A missing handle answers in two different ways, and both were observed on the same handle at different times. One capture returned HTTP 200 with a 5,973 byte body, a bare `Snapchat` title and no `ProfilePage` block. A later capture returned a real **HTTP 404**, forwarded straight through because 404 is one of the few statuses ScrapingBee does not rewrite.
156
+
157
+ This client treats both as "no page" rather than raising, so a batch run does not die on one bad handle. Decide on the presence of the `ProfilePage` block, not on the status code.
158
+
159
+ ### `snapcode_url(username, svg=True, bitmoji=True)`
160
+
161
+ A static method. **0 credits, no request.**
162
+
163
+ ```python
164
+ SnapchatScraper.snapcode_url("teamsnapchat")
165
+ # 'https://app.snapchat.com/web/deeplink/snapcode?username=teamsnapchat&type=SVG&bitmoji=enable'
166
+ ```
167
+
168
+ ### `usage()`
169
+
170
+ Free. Account credits, concurrency and renewal date.
171
+
172
+ ---
173
+
174
+ ## The locale trap
175
+
176
+ Snapchat localises by proxy exit IP, and without a country parameter whichever region the request left from decides the language of every title, subtitle and label you parse.
177
+
178
+ Two captures of the same creator URL, same parameters, no change in between:
179
+
180
+ ```
181
+ capture 1 title: MrBeast (@mrbeast) | Historias de Snapchat, Spotlight y Lentes
182
+ capture 2 og_title: MrBeast pe Snapchat og_description: MrBeast este pe Snapchat!
183
+ ```
184
+
185
+ Spanish, then Romanian. The structured data tells you it happened, through `inLanguage`, which `stats()` returns as `locale`.
186
+
187
+ What moves and what does not:
188
+
189
+ | Field | Locale dependent |
190
+ |---|---|
191
+ | `og_title`, `og_description`, page title, visible labels | yes |
192
+ | `canonical`, `username`, `profile_image`, `snapcode` | no |
193
+ | `followers`, `created`, `modified` | no |
194
+
195
+ So pin the country only when you actually read the strings:
196
+
197
+ ```python
198
+ bee.profile("mrbeast", country="us") # 25 credits, English guaranteed
199
+ bee.stats("mrbeast") # 1 credit, the number is the same either way
200
+ ```
201
+
202
+ Pinning gave an identical `followers` value on both runs. Geotargeting requires the premium tier, which is a twenty five times cost increase, so do not turn it on for numeric work.
203
+
204
+ ## Credit cost
205
+
206
+ Measured from `spb-cost` headers. Available on `bee.last_cost`.
207
+
208
+ | Configuration | Credits |
209
+ |---|---|
210
+ | `mode=auto`, settled on plain HTML | 1 |
211
+ | `premium_proxy` with `country_code` | 25 |
212
+ | Validation error | 0 |
213
+
214
+ `mode=auto` bills only the rung that worked and nothing if every rung fails. It is incompatible with `render_js`, `premium_proxy` and `stealth_proxy`, and sending both returns HTTP 400 while billing nothing.
215
+
216
+ At 1 credit per profile, 250,000 credits covers 250,000 profile checks. Plan tiers: [ScrapingBee pricing](https://www.scrapingbee.com/pricing).
217
+
218
+ ## Related
219
+
220
+ Adjacent social and creator landing pages: [Patreon scraper API](https://www.scrapingbee.com/scrapers/patreon-api/), [TikTok API](https://www.scrapingbee.com/scrapers/tiktok-api/), [TikTok search API](https://www.scrapingbee.com/scrapers/tiktok-search-api/), [TikTok follower API](https://www.scrapingbee.com/scrapers/tiktok-follower/), [Twitch API](https://www.scrapingbee.com/scrapers/twitch-api/), [Substack scraper API](https://www.scrapingbee.com/scrapers/substack-scraper-api/), [YouTube shorts API](https://www.scrapingbee.com/scrapers/youtube-shorts-api/), [YouTube video scraper API](https://www.scrapingbee.com/scrapers/youtube-video-scraper-api/), [YouTube title scraper API](https://www.scrapingbee.com/scrapers/youtube-title-scraper-api/).
221
+
222
+ Features: [data extraction](https://www.scrapingbee.com/features/data-extraction/), [AI web scraping](https://www.scrapingbee.com/features/ai-web-scraping-api/), [screenshots](https://www.scrapingbee.com/features/screenshot/), [markdown scraper](https://www.scrapingbee.com/features/markdown-scraper/), [JavaScript scenario](https://www.scrapingbee.com/features/javascript-scenario/), [n8n integration](https://www.scrapingbee.com/features/n8n/).
223
+
224
+ Reference: [extraction rules documentation](https://www.scrapingbee.com/documentation/data-extraction/). The selector durability walkthrough is at [github.com/ScrapingBee/snapchat-scraper-api](https://github.com/ScrapingBee/snapchat-scraper-api).
225
+
226
+ ## License
227
+
228
+ MIT
@@ -0,0 +1,198 @@
1
+ # snapchat-scraper-api
2
+
3
+ A Python client for public Snapchat profile data through ScrapingBee. Built around the fact that most selectors on a Snapchat profile are guaranteed to break, and the ones that are not live somewhere unobvious.
4
+
5
+ **Verified against `snapchat.com/add/teamsnapchat` and `snapchat.com/add/mrbeast` on 2026-09-10.** Three findings below only surface on a live run: there are two incompatible page layouts, the follower count is not in the visible text at all, and the page language depends on which country the request left from.
6
+
7
+ ```bash
8
+ pip install snapchat-scraper-api
9
+ ```
10
+
11
+ Requires Python 3.8 or newer and `requests`.
12
+
13
+ ## The cheapest target in the series
14
+
15
+ One credit per profile. Snapchat serves these pages as delivered HTML, so `mode=auto` settles on the plain rung and never opens a browser. Do not add `render_js` out of habit, it buys nothing here and costs five times more.
16
+
17
+ ## What is in scope
18
+
19
+ Public profile pages, read anonymously: display name, username, subtitle, Snapcode, preview image, canonical URL, follower count, profile creation and last modified dates, Snapchat's family friendly flag, and public Spotlight or highlight videos.
20
+
21
+ Out of scope: private accounts, friend lists, Snap Map location data, direct messages and story view data. Those need a signed in session, and scraping under login credentials is prohibited by ScrapingBee's terms of service.
22
+
23
+ ## Authentication
24
+
25
+ ```python
26
+ from snapchat_scraper_api import SnapchatScraper
27
+
28
+ bee = SnapchatScraper("YOUR_API_KEY")
29
+ ```
30
+
31
+ Sent as `Authorization: Bearer YOUR_API_KEY`. Key and 1,000 free credits: [ScrapingBee](https://www.scrapingbee.com/). Landing page: [Snapchat scraper API](https://www.scrapingbee.com/scrapers/snapchat-scraper-api/).
32
+
33
+ ---
34
+
35
+ ## Method reference
36
+
37
+ ### `profile(username, country=None)`
38
+
39
+ **1 credit** unpinned, **25 credits** with a country pinned.
40
+
41
+ ```python
42
+ bee.profile("teamsnapchat")
43
+ ```
44
+
45
+ ```python
46
+ {'display_name': 'Team Snapchat',
47
+ 'username': 'teamsnapchat',
48
+ 'subtitle': 'Add me on Snapchat!',
49
+ 'snapcode': 'https://app.snapchat.com/web/deeplink/snapcode?username=teamsnapchat&type=SVG&bitmoji=enable',
50
+ 'profile_image': 'https://www.snapchat.com/web-capture/www.snapchat.com/@teamsnapchat/preview/square.jpeg?xp_id=1',
51
+ 'og_title': 'Team Snapchat on Snapchat',
52
+ 'og_description': 'Team Snapchat is on Snapchat!',
53
+ 'canonical': 'https://www.snapchat.com/@teamsnapchat'}
54
+ ```
55
+
56
+ The same call works on a creator profile, which matters more than it sounds.
57
+
58
+ **Snapchat serves two incompatible layouts.** Both handles below were fetched with one rule set:
59
+
60
+ | Selector | `teamsnapchat` (basic card) | `mrbeast` (creator profile) |
61
+ |---|---|---|
62
+ | `h1 span` | empty | `MrBeast` |
63
+ | `h4 span` | `Team Snapchat` | empty |
64
+ | `h5 span` | `teamsnapchat` | wrong node, the page has ten `h5` elements |
65
+ | `[data-testid="snapCodeImage"]` | the Snapcode URL | empty |
66
+
67
+ They are mutually exclusive. This client requests both headings and returns whichever is populated, so one call covers both page types. A scraper written against only `h4 span` works on half of Snapchat and returns empty strings on the other half, with a 200 status and no error.
68
+
69
+ Three more decisions the client makes for you:
70
+
71
+ - **The username comes from the canonical URL**, not from a heading. The canonical is byte identical across layouts and does not change with locale, and it always reads `https://www.snapchat.com/@<username>`.
72
+ - **The Snapcode is rebuilt when the selector is empty.** The endpoint is parameterised by username, and the constructed string matches the live selector value exactly, verified on both handles. No second request.
73
+ - **An empty dict means the handle has no public page.** More on that below.
74
+
75
+ **Never select on CSS module classes.** Snapchat ships class names like `UserDetailsCard_title__K9Awz`, `Heading_h400Emphasis__SQXxl` and `DesktopUserProfile_desktopContainer__UwOc_`. The suffix after the double underscore is a build hash. It rotates on deploy, and a scraper keyed on it silently returns empty strings afterwards.
76
+
77
+ ### `stats(username, country=None)`
78
+
79
+ **1 credit.** Follower count and account dates, from the `application/ld+json` block.
80
+
81
+ ```python
82
+ bee.stats("mrbeast")
83
+ # {'name': 'MrBeast',
84
+ # 'username': 'mrbeast',
85
+ # 'url': 'https://www.snapchat.com/@mrbeast',
86
+ # 'image': 'https://cf-st.sc-cdn.net/aps/bolt/...',
87
+ # 'followers': 1463400,
88
+ # 'created': '2019-05-16T14:46:37.345Z',
89
+ # 'modified': '2026-08-13T13:01:39.438Z',
90
+ # 'locale': 'en-US',
91
+ # 'family_friendly': True}
92
+ ```
93
+
94
+ **The follower count is not in the visible text.** The DOM carries the i18n template placeholder, literally `{subscriberCount} suscriptores`, so a text selector returns the template rather than a number. The real value sits in `interactionStatistic`, in the counter whose `interactionType` is `FollowAction`. This client digs it out.
95
+
96
+ `created` and `modified` are the other two fields available nowhere else. `modified` is how you separate an active creator from a dormant handle without fetching a single post.
97
+
98
+ Returns `None` when there is no public profile page.
99
+
100
+ Note this parses the fetched HTML rather than using `extract_rules`, because **`extract_rules` cannot read script tag contents**. A rule selecting `script[type="application/ld+json"]` returns `None`, tested directly.
101
+
102
+ ### `spotlight(username, country=None)`
103
+
104
+ **1 credit.** Public Spotlight and highlight videos, from the `ItemList` block.
105
+
106
+ ```python
107
+ bee.spotlight("mrbeast")
108
+ # [{'url': 'https://www.snapchat.com/@mrbeast/highlight/91542d31-...',
109
+ # 'name': 'A Snapchat video by MrBeast',
110
+ # 'description': 'A Snapchat video by MrBeast',
111
+ # 'thumbnail': 'https://cf-st.sc-cdn.net/d/...'}, ...]
112
+ ```
113
+
114
+ Five entries on the creator page tested. **An empty list is normal, not a failure.** The same block came back as an empty array on `teamsnapchat`.
115
+
116
+ ### `exists(username)`
117
+
118
+ **1 credit.** Whether the handle has a public profile page.
119
+
120
+ ```python
121
+ bee.exists("teamsnapchat") # True
122
+ bee.exists("dailymail") # False
123
+ ```
124
+
125
+ A missing handle answers in two different ways, and both were observed on the same handle at different times. One capture returned HTTP 200 with a 5,973 byte body, a bare `Snapchat` title and no `ProfilePage` block. A later capture returned a real **HTTP 404**, forwarded straight through because 404 is one of the few statuses ScrapingBee does not rewrite.
126
+
127
+ This client treats both as "no page" rather than raising, so a batch run does not die on one bad handle. Decide on the presence of the `ProfilePage` block, not on the status code.
128
+
129
+ ### `snapcode_url(username, svg=True, bitmoji=True)`
130
+
131
+ A static method. **0 credits, no request.**
132
+
133
+ ```python
134
+ SnapchatScraper.snapcode_url("teamsnapchat")
135
+ # 'https://app.snapchat.com/web/deeplink/snapcode?username=teamsnapchat&type=SVG&bitmoji=enable'
136
+ ```
137
+
138
+ ### `usage()`
139
+
140
+ Free. Account credits, concurrency and renewal date.
141
+
142
+ ---
143
+
144
+ ## The locale trap
145
+
146
+ Snapchat localises by proxy exit IP, and without a country parameter whichever region the request left from decides the language of every title, subtitle and label you parse.
147
+
148
+ Two captures of the same creator URL, same parameters, no change in between:
149
+
150
+ ```
151
+ capture 1 title: MrBeast (@mrbeast) | Historias de Snapchat, Spotlight y Lentes
152
+ capture 2 og_title: MrBeast pe Snapchat og_description: MrBeast este pe Snapchat!
153
+ ```
154
+
155
+ Spanish, then Romanian. The structured data tells you it happened, through `inLanguage`, which `stats()` returns as `locale`.
156
+
157
+ What moves and what does not:
158
+
159
+ | Field | Locale dependent |
160
+ |---|---|
161
+ | `og_title`, `og_description`, page title, visible labels | yes |
162
+ | `canonical`, `username`, `profile_image`, `snapcode` | no |
163
+ | `followers`, `created`, `modified` | no |
164
+
165
+ So pin the country only when you actually read the strings:
166
+
167
+ ```python
168
+ bee.profile("mrbeast", country="us") # 25 credits, English guaranteed
169
+ bee.stats("mrbeast") # 1 credit, the number is the same either way
170
+ ```
171
+
172
+ Pinning gave an identical `followers` value on both runs. Geotargeting requires the premium tier, which is a twenty five times cost increase, so do not turn it on for numeric work.
173
+
174
+ ## Credit cost
175
+
176
+ Measured from `spb-cost` headers. Available on `bee.last_cost`.
177
+
178
+ | Configuration | Credits |
179
+ |---|---|
180
+ | `mode=auto`, settled on plain HTML | 1 |
181
+ | `premium_proxy` with `country_code` | 25 |
182
+ | Validation error | 0 |
183
+
184
+ `mode=auto` bills only the rung that worked and nothing if every rung fails. It is incompatible with `render_js`, `premium_proxy` and `stealth_proxy`, and sending both returns HTTP 400 while billing nothing.
185
+
186
+ At 1 credit per profile, 250,000 credits covers 250,000 profile checks. Plan tiers: [ScrapingBee pricing](https://www.scrapingbee.com/pricing).
187
+
188
+ ## Related
189
+
190
+ Adjacent social and creator landing pages: [Patreon scraper API](https://www.scrapingbee.com/scrapers/patreon-api/), [TikTok API](https://www.scrapingbee.com/scrapers/tiktok-api/), [TikTok search API](https://www.scrapingbee.com/scrapers/tiktok-search-api/), [TikTok follower API](https://www.scrapingbee.com/scrapers/tiktok-follower/), [Twitch API](https://www.scrapingbee.com/scrapers/twitch-api/), [Substack scraper API](https://www.scrapingbee.com/scrapers/substack-scraper-api/), [YouTube shorts API](https://www.scrapingbee.com/scrapers/youtube-shorts-api/), [YouTube video scraper API](https://www.scrapingbee.com/scrapers/youtube-video-scraper-api/), [YouTube title scraper API](https://www.scrapingbee.com/scrapers/youtube-title-scraper-api/).
191
+
192
+ Features: [data extraction](https://www.scrapingbee.com/features/data-extraction/), [AI web scraping](https://www.scrapingbee.com/features/ai-web-scraping-api/), [screenshots](https://www.scrapingbee.com/features/screenshot/), [markdown scraper](https://www.scrapingbee.com/features/markdown-scraper/), [JavaScript scenario](https://www.scrapingbee.com/features/javascript-scenario/), [n8n integration](https://www.scrapingbee.com/features/n8n/).
193
+
194
+ Reference: [extraction rules documentation](https://www.scrapingbee.com/documentation/data-extraction/). The selector durability walkthrough is at [github.com/ScrapingBee/snapchat-scraper-api](https://github.com/ScrapingBee/snapchat-scraper-api).
195
+
196
+ ## License
197
+
198
+ MIT
@@ -0,0 +1,38 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "snapchat-scraper-api"
7
+ version = "0.0.1"
8
+ description = "Snapchat scraper API client: public profile data, follower counts and Spotlight videos via ScrapingBee."
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "wordstotech" }]
13
+ keywords = ["snapchat scraper", "snapchat api", "snapchat profiles scraper", "snapchat data scraper", "web scraping", "scrapingbee"]
14
+ dependencies = ["requests>=2.25.0"]
15
+ classifiers = [
16
+ "Development Status :: 4 - Beta",
17
+ "Intended Audience :: Developers",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Operating System :: OS Independent",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.8",
22
+ "Programming Language :: Python :: 3.9",
23
+ "Programming Language :: Python :: 3.10",
24
+ "Programming Language :: Python :: 3.11",
25
+ "Programming Language :: Python :: 3.12",
26
+ "Programming Language :: Python :: 3.13",
27
+ "Topic :: Internet :: WWW/HTTP",
28
+ "Topic :: Software Development :: Libraries :: Python Modules",
29
+ "Topic :: Text Processing :: Markup :: HTML",
30
+ ]
31
+
32
+ [project.urls]
33
+ Homepage = "https://github.com/ScrapingBee/snapchat-scraper-api"
34
+ Repository = "https://github.com/ScrapingBee/snapchat-scraper-api"
35
+ Documentation = "https://www.scrapingbee.com/documentation/"
36
+
37
+ [tool.setuptools.packages.find]
38
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,6 @@
1
+ """Snapchat scraper API client for public profile pages, built on ScrapingBee."""
2
+
3
+ from .client import SnapchatScraper, ScrapingBeeError
4
+
5
+ __all__ = ["SnapchatScraper", "ScrapingBeeError"]
6
+ __version__ = "0.0.1"
@@ -0,0 +1,265 @@
1
+ """ScrapingBee based Snapchat scraper for public profile pages.
2
+
3
+ Verified against snapchat.com/add/teamsnapchat and /add/mrbeast on
4
+ 2026-09-10.
5
+
6
+ Scope: public profile pages only. Private accounts, friend lists, Snap Map
7
+ location data, direct messages and story views are out of reach, and
8
+ scraping under login credentials is prohibited by ScrapingBee's terms of
9
+ service.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import json
15
+ import re
16
+ from typing import Any, Dict, List, Optional
17
+
18
+ import requests
19
+
20
+ __all__ = ["SnapchatScraper", "ScrapingBeeError"]
21
+
22
+ BASE_URL = "https://app.scrapingbee.com/api/v1/"
23
+
24
+ # Durable selectors only. Snapchat ships CSS module class names such as
25
+ # UserDetailsCard_title__K9Awz, where the suffix is a build hash that
26
+ # rotates on deploy. Never select on those: they return empty strings with
27
+ # a 200 status after the next release.
28
+ #
29
+ # There are TWO public profile layouts, and they disagree on headings:
30
+ # basic user card (for example teamsnapchat): name in h4 span, no h1
31
+ # creator profile (for example mrbeast): name in h1 span, no h4
32
+ # They are mutually exclusive, so both are requested and coalesced.
33
+ #
34
+ # Username comes from the canonical URL rather than a heading, because the
35
+ # canonical is identical on both layouts and is locale independent. og:title
36
+ # and og:description are NOT: live captures came back in Spanish and in
37
+ # Romanian depending on the proxy exit.
38
+ _PROFILE_RULES = {
39
+ "name_h1": {"selector": "h1 span", "output": "text"},
40
+ "name_h4": {"selector": "h4 span", "output": "text"},
41
+ "subtitle": {"selector": "h5 + div", "output": "text"},
42
+ "snapcode": {"selector": '[data-testid="snapCodeImage"]', "output": "@src"},
43
+ "profile_image": {"selector": 'meta[property="og:image"]', "output": "@content"},
44
+ "og_title": {"selector": 'meta[property="og:title"]', "output": "@content"},
45
+ "og_description": {"selector": 'meta[property="og:description"]', "output": "@content"},
46
+ "canonical": {"selector": 'link[rel="canonical"]', "output": "@href"},
47
+ }
48
+
49
+ _LD_BLOCK = re.compile(
50
+ r"<script[^>]*application/ld\+json[^>]*>(.*?)</script>", re.S
51
+ )
52
+
53
+
54
+ class ScrapingBeeError(RuntimeError):
55
+ def __init__(self, message: str, status: Optional[int] = None,
56
+ payload: Any = None) -> None:
57
+ super().__init__(message)
58
+ self.status = status
59
+ self.payload = payload
60
+
61
+
62
+ class SnapchatScraper:
63
+ """Public Snapchat profile data.
64
+
65
+ >>> bee = SnapchatScraper("YOUR_API_KEY")
66
+ >>> bee.profile("teamsnapchat")["username"]
67
+ 'teamsnapchat'
68
+
69
+ Credit cost, measured live: 1 credit for an unpinned request, because
70
+ Snapchat serves these pages as delivered HTML and auto mode settles on
71
+ the plain rung. 25 credits with a country pinned, which requires the
72
+ premium tier. Do not reach for ``render_js`` here, it buys nothing.
73
+ """
74
+
75
+ def __init__(self, api_key: str, base_url: str = BASE_URL,
76
+ timeout: int = 120,
77
+ session: Optional[requests.Session] = None) -> None:
78
+ if not api_key:
79
+ raise ValueError("api_key is required")
80
+ self.api_key = api_key
81
+ self.base_url = base_url
82
+ self.timeout = timeout
83
+ self._session = session or requests.Session()
84
+ self.last_cost: Optional[int] = None
85
+
86
+ # ------------------------------------------------------------------ core
87
+
88
+ def _fetch(self, username: str, *, country: Optional[str] = None,
89
+ **params: Any) -> requests.Response:
90
+ url = (
91
+ username if username.startswith("http")
92
+ else f"https://www.snapchat.com/add/{username}"
93
+ )
94
+ clean = {k: v for k, v in params.items() if v is not None}
95
+ if country:
96
+ # Geotargeting requires the premium tier.
97
+ clean.update(premium_proxy="true", country_code=country)
98
+ else:
99
+ clean.setdefault("mode", "auto")
100
+
101
+ response = self._session.get(
102
+ self.base_url,
103
+ headers={"Authorization": f"Bearer {self.api_key}"},
104
+ params={"url": url, **clean},
105
+ timeout=self.timeout,
106
+ )
107
+ cost = response.headers.get("spb-cost")
108
+ self.last_cost = int(cost) if cost and cost.isdigit() else None
109
+
110
+ if response.status_code == 400:
111
+ raise ScrapingBeeError(
112
+ "API rejected the request", 400, response.text[:500]
113
+ )
114
+ # ScrapingBee passes 404 through from the target rather than
115
+ # rewriting it. A handle with no public profile page can answer
116
+ # either a 404 or a 200 with a small body and no ProfilePage block,
117
+ # so both have to read as "no page" instead of as a failure.
118
+ if response.status_code == 404:
119
+ return response
120
+ response.raise_for_status()
121
+ return response
122
+
123
+ def _blocks(self, username: str, country: Optional[str] = None) -> List[Any]:
124
+ response = self._fetch(username, country=country)
125
+ if response.status_code == 404:
126
+ return []
127
+ html = response.text
128
+ out = []
129
+ for raw in _LD_BLOCK.findall(html):
130
+ try:
131
+ out.append(json.loads(raw))
132
+ except json.JSONDecodeError:
133
+ continue
134
+ return out
135
+
136
+ # --------------------------------------------------------------- methods
137
+
138
+ def profile(self, username: str, country: Optional[str] = None) -> Dict[str, Any]:
139
+ """Display name, username, Snapcode, preview image and canonical URL.
140
+
141
+ Handles both public layouts. The display name sits in an ``h1`` on
142
+ creator profiles and in an ``h4`` on basic user cards, so both are
143
+ requested and whichever is populated wins. The username is taken
144
+ from the canonical URL, which is identical across layouts and does
145
+ not change with locale.
146
+
147
+ The Snapcode ``data-testid`` is present only on basic cards, so it
148
+ is reconstructed from the username when the selector comes back
149
+ empty. The endpoint is parameterised by username, verified against
150
+ the live value.
151
+
152
+ Pass ``country`` whenever you rely on ``og_title`` or
153
+ ``og_description``. Snapchat localises by proxy exit IP: live
154
+ captures of the same page returned Spanish and Romanian titles on
155
+ different requests. Everything else here is locale independent.
156
+
157
+ Returns an empty dict when the handle has no public profile page.
158
+ """
159
+ response = self._fetch(
160
+ username, country=country,
161
+ extract_rules=json.dumps(_PROFILE_RULES),
162
+ )
163
+ if response.status_code == 404:
164
+ return {}
165
+ data = response.json()
166
+
167
+ data["display_name"] = (
168
+ data.pop("name_h1", "") or data.pop("name_h4", "") or None
169
+ )
170
+ data.pop("name_h1", None)
171
+ data.pop("name_h4", None)
172
+
173
+ canonical = data.get("canonical") or ""
174
+ handle = canonical.rsplit("/@", 1)[-1] if "/@" in canonical else None
175
+ data["username"] = handle
176
+
177
+ if not data.get("snapcode") and handle:
178
+ data["snapcode"] = self.snapcode_url(handle)
179
+
180
+ if not data.get("display_name"):
181
+ return {}
182
+ return data
183
+
184
+ def stats(self, username: str,
185
+ country: Optional[str] = None) -> Optional[Dict[str, Any]]:
186
+ """Follower count and account dates from structured data.
187
+
188
+ The follower number is not in the visible text. The DOM carries the
189
+ i18n template placeholder ``{subscriberCount}`` instead, so a text
190
+ selector returns the template rather than a number. The real value
191
+ is in ``interactionStatistic``, keyed by a FollowAction counter.
192
+
193
+ Returns None when the handle has no public profile page. That case
194
+ looks like a 200 status with a small body and no ProfilePage block,
195
+ so test for None rather than for a status code.
196
+ """
197
+ for obj in self._blocks(username, country):
198
+ if isinstance(obj, dict) and obj.get("@type") == "ProfilePage":
199
+ person = obj.get("mainEntity") or {}
200
+ followers = None
201
+ for counter in person.get("interactionStatistic") or []:
202
+ kind = (counter.get("interactionType") or {}).get("@type")
203
+ if kind == "FollowAction":
204
+ followers = counter.get("userInteractionCount")
205
+ return {
206
+ "name": person.get("name"),
207
+ "username": person.get("alternateName"),
208
+ "url": person.get("url"),
209
+ "image": person.get("image"),
210
+ "followers": followers,
211
+ "created": obj.get("dateCreated"),
212
+ "modified": obj.get("dateModified"),
213
+ "locale": obj.get("inLanguage"),
214
+ "family_friendly": obj.get("isFamilyFriendly"),
215
+ }
216
+ return None
217
+
218
+ def spotlight(self, username: str,
219
+ country: Optional[str] = None) -> List[Dict[str, Any]]:
220
+ """Public Spotlight and highlight videos.
221
+
222
+ An empty list is normal rather than an error. A creator page
223
+ returned five VideoObject entries, and a non creator account
224
+ returned an empty ItemList on the same call.
225
+ """
226
+ for obj in self._blocks(username, country):
227
+ if isinstance(obj, dict) and obj.get("@type") == "ItemList":
228
+ return [
229
+ {
230
+ "url": item.get("url"),
231
+ "name": item.get("name"),
232
+ "description": item.get("description"),
233
+ "thumbnail": item.get("thumbnailUrl"),
234
+ }
235
+ for item in obj.get("itemListElement") or []
236
+ ]
237
+ return []
238
+
239
+ def exists(self, username: str) -> bool:
240
+ """Whether the handle has a public profile page at all."""
241
+ return self.stats(username) is not None
242
+
243
+ def usage(self) -> Dict[str, Any]:
244
+ """Account credit usage. Free."""
245
+ response = self._session.get(
246
+ "https://app.scrapingbee.com/api/v1/usage",
247
+ headers={"Authorization": f"Bearer {self.api_key}"},
248
+ timeout=self.timeout,
249
+ )
250
+ response.raise_for_status()
251
+ return response.json()
252
+
253
+ @staticmethod
254
+ def snapcode_url(username: str, *, svg: bool = True,
255
+ bitmoji: bool = True) -> str:
256
+ """Build a Snapcode URL without a request.
257
+
258
+ The Snapcode endpoint is parameterised by username, so once you
259
+ know the pattern you can construct it for any handle.
260
+ """
261
+ return (
262
+ "https://app.snapchat.com/web/deeplink/snapcode"
263
+ f"?username={username}&type={'SVG' if svg else 'PNG'}"
264
+ f"&bitmoji={'enable' if bitmoji else 'disable'}"
265
+ )
@@ -0,0 +1,228 @@
1
+ Metadata-Version: 2.4
2
+ Name: snapchat-scraper-api
3
+ Version: 0.0.1
4
+ Summary: Snapchat scraper API client: public profile data, follower counts and Spotlight videos via ScrapingBee.
5
+ Author: wordstotech
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/ScrapingBee/snapchat-scraper-api
8
+ Project-URL: Repository, https://github.com/ScrapingBee/snapchat-scraper-api
9
+ Project-URL: Documentation, https://www.scrapingbee.com/documentation/
10
+ Keywords: snapchat scraper,snapchat api,snapchat profiles scraper,snapchat data scraper,web scraping,scrapingbee
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.8
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Internet :: WWW/HTTP
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Classifier: Topic :: Text Processing :: Markup :: HTML
25
+ Requires-Python: >=3.8
26
+ Description-Content-Type: text/markdown
27
+ License-File: LICENSE
28
+ Requires-Dist: requests>=2.25.0
29
+ Dynamic: license-file
30
+
31
+ # snapchat-scraper-api
32
+
33
+ A Python client for public Snapchat profile data through ScrapingBee. Built around the fact that most selectors on a Snapchat profile are guaranteed to break, and the ones that are not live somewhere unobvious.
34
+
35
+ **Verified against `snapchat.com/add/teamsnapchat` and `snapchat.com/add/mrbeast` on 2026-09-10.** Three findings below only surface on a live run: there are two incompatible page layouts, the follower count is not in the visible text at all, and the page language depends on which country the request left from.
36
+
37
+ ```bash
38
+ pip install snapchat-scraper-api
39
+ ```
40
+
41
+ Requires Python 3.8 or newer and `requests`.
42
+
43
+ ## The cheapest target in the series
44
+
45
+ One credit per profile. Snapchat serves these pages as delivered HTML, so `mode=auto` settles on the plain rung and never opens a browser. Do not add `render_js` out of habit, it buys nothing here and costs five times more.
46
+
47
+ ## What is in scope
48
+
49
+ Public profile pages, read anonymously: display name, username, subtitle, Snapcode, preview image, canonical URL, follower count, profile creation and last modified dates, Snapchat's family friendly flag, and public Spotlight or highlight videos.
50
+
51
+ Out of scope: private accounts, friend lists, Snap Map location data, direct messages and story view data. Those need a signed in session, and scraping under login credentials is prohibited by ScrapingBee's terms of service.
52
+
53
+ ## Authentication
54
+
55
+ ```python
56
+ from snapchat_scraper_api import SnapchatScraper
57
+
58
+ bee = SnapchatScraper("YOUR_API_KEY")
59
+ ```
60
+
61
+ Sent as `Authorization: Bearer YOUR_API_KEY`. Key and 1,000 free credits: [ScrapingBee](https://www.scrapingbee.com/). Landing page: [Snapchat scraper API](https://www.scrapingbee.com/scrapers/snapchat-scraper-api/).
62
+
63
+ ---
64
+
65
+ ## Method reference
66
+
67
+ ### `profile(username, country=None)`
68
+
69
+ **1 credit** unpinned, **25 credits** with a country pinned.
70
+
71
+ ```python
72
+ bee.profile("teamsnapchat")
73
+ ```
74
+
75
+ ```python
76
+ {'display_name': 'Team Snapchat',
77
+ 'username': 'teamsnapchat',
78
+ 'subtitle': 'Add me on Snapchat!',
79
+ 'snapcode': 'https://app.snapchat.com/web/deeplink/snapcode?username=teamsnapchat&type=SVG&bitmoji=enable',
80
+ 'profile_image': 'https://www.snapchat.com/web-capture/www.snapchat.com/@teamsnapchat/preview/square.jpeg?xp_id=1',
81
+ 'og_title': 'Team Snapchat on Snapchat',
82
+ 'og_description': 'Team Snapchat is on Snapchat!',
83
+ 'canonical': 'https://www.snapchat.com/@teamsnapchat'}
84
+ ```
85
+
86
+ The same call works on a creator profile, which matters more than it sounds.
87
+
88
+ **Snapchat serves two incompatible layouts.** Both handles below were fetched with one rule set:
89
+
90
+ | Selector | `teamsnapchat` (basic card) | `mrbeast` (creator profile) |
91
+ |---|---|---|
92
+ | `h1 span` | empty | `MrBeast` |
93
+ | `h4 span` | `Team Snapchat` | empty |
94
+ | `h5 span` | `teamsnapchat` | wrong node, the page has ten `h5` elements |
95
+ | `[data-testid="snapCodeImage"]` | the Snapcode URL | empty |
96
+
97
+ They are mutually exclusive. This client requests both headings and returns whichever is populated, so one call covers both page types. A scraper written against only `h4 span` works on half of Snapchat and returns empty strings on the other half, with a 200 status and no error.
98
+
99
+ Three more decisions the client makes for you:
100
+
101
+ - **The username comes from the canonical URL**, not from a heading. The canonical is byte identical across layouts and does not change with locale, and it always reads `https://www.snapchat.com/@<username>`.
102
+ - **The Snapcode is rebuilt when the selector is empty.** The endpoint is parameterised by username, and the constructed string matches the live selector value exactly, verified on both handles. No second request.
103
+ - **An empty dict means the handle has no public page.** More on that below.
104
+
105
+ **Never select on CSS module classes.** Snapchat ships class names like `UserDetailsCard_title__K9Awz`, `Heading_h400Emphasis__SQXxl` and `DesktopUserProfile_desktopContainer__UwOc_`. The suffix after the double underscore is a build hash. It rotates on deploy, and a scraper keyed on it silently returns empty strings afterwards.
106
+
107
+ ### `stats(username, country=None)`
108
+
109
+ **1 credit.** Follower count and account dates, from the `application/ld+json` block.
110
+
111
+ ```python
112
+ bee.stats("mrbeast")
113
+ # {'name': 'MrBeast',
114
+ # 'username': 'mrbeast',
115
+ # 'url': 'https://www.snapchat.com/@mrbeast',
116
+ # 'image': 'https://cf-st.sc-cdn.net/aps/bolt/...',
117
+ # 'followers': 1463400,
118
+ # 'created': '2019-05-16T14:46:37.345Z',
119
+ # 'modified': '2026-08-13T13:01:39.438Z',
120
+ # 'locale': 'en-US',
121
+ # 'family_friendly': True}
122
+ ```
123
+
124
+ **The follower count is not in the visible text.** The DOM carries the i18n template placeholder, literally `{subscriberCount} suscriptores`, so a text selector returns the template rather than a number. The real value sits in `interactionStatistic`, in the counter whose `interactionType` is `FollowAction`. This client digs it out.
125
+
126
+ `created` and `modified` are the other two fields available nowhere else. `modified` is how you separate an active creator from a dormant handle without fetching a single post.
127
+
128
+ Returns `None` when there is no public profile page.
129
+
130
+ Note this parses the fetched HTML rather than using `extract_rules`, because **`extract_rules` cannot read script tag contents**. A rule selecting `script[type="application/ld+json"]` returns `None`, tested directly.
131
+
132
+ ### `spotlight(username, country=None)`
133
+
134
+ **1 credit.** Public Spotlight and highlight videos, from the `ItemList` block.
135
+
136
+ ```python
137
+ bee.spotlight("mrbeast")
138
+ # [{'url': 'https://www.snapchat.com/@mrbeast/highlight/91542d31-...',
139
+ # 'name': 'A Snapchat video by MrBeast',
140
+ # 'description': 'A Snapchat video by MrBeast',
141
+ # 'thumbnail': 'https://cf-st.sc-cdn.net/d/...'}, ...]
142
+ ```
143
+
144
+ Five entries on the creator page tested. **An empty list is normal, not a failure.** The same block came back as an empty array on `teamsnapchat`.
145
+
146
+ ### `exists(username)`
147
+
148
+ **1 credit.** Whether the handle has a public profile page.
149
+
150
+ ```python
151
+ bee.exists("teamsnapchat") # True
152
+ bee.exists("dailymail") # False
153
+ ```
154
+
155
+ A missing handle answers in two different ways, and both were observed on the same handle at different times. One capture returned HTTP 200 with a 5,973 byte body, a bare `Snapchat` title and no `ProfilePage` block. A later capture returned a real **HTTP 404**, forwarded straight through because 404 is one of the few statuses ScrapingBee does not rewrite.
156
+
157
+ This client treats both as "no page" rather than raising, so a batch run does not die on one bad handle. Decide on the presence of the `ProfilePage` block, not on the status code.
158
+
159
+ ### `snapcode_url(username, svg=True, bitmoji=True)`
160
+
161
+ A static method. **0 credits, no request.**
162
+
163
+ ```python
164
+ SnapchatScraper.snapcode_url("teamsnapchat")
165
+ # 'https://app.snapchat.com/web/deeplink/snapcode?username=teamsnapchat&type=SVG&bitmoji=enable'
166
+ ```
167
+
168
+ ### `usage()`
169
+
170
+ Free. Account credits, concurrency and renewal date.
171
+
172
+ ---
173
+
174
+ ## The locale trap
175
+
176
+ Snapchat localises by proxy exit IP, and without a country parameter whichever region the request left from decides the language of every title, subtitle and label you parse.
177
+
178
+ Two captures of the same creator URL, same parameters, no change in between:
179
+
180
+ ```
181
+ capture 1 title: MrBeast (@mrbeast) | Historias de Snapchat, Spotlight y Lentes
182
+ capture 2 og_title: MrBeast pe Snapchat og_description: MrBeast este pe Snapchat!
183
+ ```
184
+
185
+ Spanish, then Romanian. The structured data tells you it happened, through `inLanguage`, which `stats()` returns as `locale`.
186
+
187
+ What moves and what does not:
188
+
189
+ | Field | Locale dependent |
190
+ |---|---|
191
+ | `og_title`, `og_description`, page title, visible labels | yes |
192
+ | `canonical`, `username`, `profile_image`, `snapcode` | no |
193
+ | `followers`, `created`, `modified` | no |
194
+
195
+ So pin the country only when you actually read the strings:
196
+
197
+ ```python
198
+ bee.profile("mrbeast", country="us") # 25 credits, English guaranteed
199
+ bee.stats("mrbeast") # 1 credit, the number is the same either way
200
+ ```
201
+
202
+ Pinning gave an identical `followers` value on both runs. Geotargeting requires the premium tier, which is a twenty five times cost increase, so do not turn it on for numeric work.
203
+
204
+ ## Credit cost
205
+
206
+ Measured from `spb-cost` headers. Available on `bee.last_cost`.
207
+
208
+ | Configuration | Credits |
209
+ |---|---|
210
+ | `mode=auto`, settled on plain HTML | 1 |
211
+ | `premium_proxy` with `country_code` | 25 |
212
+ | Validation error | 0 |
213
+
214
+ `mode=auto` bills only the rung that worked and nothing if every rung fails. It is incompatible with `render_js`, `premium_proxy` and `stealth_proxy`, and sending both returns HTTP 400 while billing nothing.
215
+
216
+ At 1 credit per profile, 250,000 credits covers 250,000 profile checks. Plan tiers: [ScrapingBee pricing](https://www.scrapingbee.com/pricing).
217
+
218
+ ## Related
219
+
220
+ Adjacent social and creator landing pages: [Patreon scraper API](https://www.scrapingbee.com/scrapers/patreon-api/), [TikTok API](https://www.scrapingbee.com/scrapers/tiktok-api/), [TikTok search API](https://www.scrapingbee.com/scrapers/tiktok-search-api/), [TikTok follower API](https://www.scrapingbee.com/scrapers/tiktok-follower/), [Twitch API](https://www.scrapingbee.com/scrapers/twitch-api/), [Substack scraper API](https://www.scrapingbee.com/scrapers/substack-scraper-api/), [YouTube shorts API](https://www.scrapingbee.com/scrapers/youtube-shorts-api/), [YouTube video scraper API](https://www.scrapingbee.com/scrapers/youtube-video-scraper-api/), [YouTube title scraper API](https://www.scrapingbee.com/scrapers/youtube-title-scraper-api/).
221
+
222
+ Features: [data extraction](https://www.scrapingbee.com/features/data-extraction/), [AI web scraping](https://www.scrapingbee.com/features/ai-web-scraping-api/), [screenshots](https://www.scrapingbee.com/features/screenshot/), [markdown scraper](https://www.scrapingbee.com/features/markdown-scraper/), [JavaScript scenario](https://www.scrapingbee.com/features/javascript-scenario/), [n8n integration](https://www.scrapingbee.com/features/n8n/).
223
+
224
+ Reference: [extraction rules documentation](https://www.scrapingbee.com/documentation/data-extraction/). The selector durability walkthrough is at [github.com/ScrapingBee/snapchat-scraper-api](https://github.com/ScrapingBee/snapchat-scraper-api).
225
+
226
+ ## License
227
+
228
+ MIT
@@ -0,0 +1,11 @@
1
+ LICENSE
2
+ MANIFEST.in
3
+ README.md
4
+ pyproject.toml
5
+ src/snapchat_scraper_api/__init__.py
6
+ src/snapchat_scraper_api/client.py
7
+ src/snapchat_scraper_api.egg-info/PKG-INFO
8
+ src/snapchat_scraper_api.egg-info/SOURCES.txt
9
+ src/snapchat_scraper_api.egg-info/dependency_links.txt
10
+ src/snapchat_scraper_api.egg-info/requires.txt
11
+ src/snapchat_scraper_api.egg-info/top_level.txt