patreon-scraper-api
A Python client for scraping public Patreon creator pages through ScrapingBee. Four extraction routes, each with a documented cost and a documented failure mode.
Verified against https://www.patreon.com/kurzgesagt on 2026-09-10. Every field name, credit figure and return value below came from a real call. Two of the four routes have caveats that only a live run exposes, and both are written down rather than smoothed over.
pip install patreon-scraper-api
Requires Python 3.8 or newer and requests.
What is in scope
Public creator pages, read anonymously.
Reachable: creator display name, campaign tagline, the full about text, avatar and banner URLs, canonical URL, the complete tier list with prices and per tier post counts, and the entry price teaser.
Not reachable, by design: patron only posts, attachment downloads, member lists, individual pledge amounts and direct messages. Those require a signed in session, and scraping under login credentials is prohibited by ScrapingBee's terms of service. No parameter in this package changes that.
Authentication
from patreon_scraper_api import PatreonScraper
bee = PatreonScraper("YOUR_API_KEY")
Sent as Authorization: Bearer YOUR_API_KEY on every request. The api_key query parameter still answers but the current documentation marks it deprecated.
Key and 1,000 free credits: ScrapingBee. Landing page for this target: Patreon scraper API.
Method reference
creator(slug)
5 credits. Meta tags, via extract_rules. The route that does not break, because these tags exist for social preview cards rather than for the application.
bee.creator("kurzgesagt")
{
'creator': 'Kurzgesagt – In a Nutshell',
'title': 'Kurzgesagt – In a Nutshell — Creating Science Animation Videos | Patreon',
'og_title': 'Kurzgesagt – In a Nutshell — Creating Science Animation Videos',
'og_desc': 'Get more from Kurzgesagt – In a Nutshell on Patreon. Creating Science '
'Animation Videos. Support Kurzgesagt – In a Nutshell and get exclusive '
'access to their work.',
'campaign_tagline': 'Creating Science Animation Videos',
}
campaign_tagline is derived, not scraped. Patreon joins the creator name and the tagline with an em dash, while a creator name may itself contain an en dash, so splitting on the em dash is unambiguous. The client does that split for you.
mode=auto walks the proxy ladder and bills only the rung that worked, which was the JavaScript rung at 5 credits. spb-initial-status-code reported 308, which is Patreon's canonical redirect rather than a block.
profile(slug)
5 credits. The application/ld+json ProfilePage block, parsed after the fetch.
bee.profile("kurzgesagt")
# {'name': 'Kurzgesagt – In a Nutshell',
# 'alternate_name': 'Kurzgesagt',
# 'url': 'https://www.patreon.com/Kurzgesagt',
# 'about': '<4,476 characters>',
# 'image': 'https://c10.patreonusercontent.com/...',
# 'thumbnail': 'https://c10.patreonusercontent.com/...'}
Use this over creator() when you need the complete about copy, for search indexing or embeddings. The meta description truncates it. The live page carried 4,476 characters here against roughly 160 in og_desc.
Why this parses HTML instead of using extract_rules: because extract_rules cannot reach script tag contents. A rule of {"jsonld": {"selector": "script[type=\"application/ld+json\"]", "output": "@text"}} returns None. That was tested directly, not assumed. The client fetches the page and parses the block itself.
The live page carried six such blocks, and their order is not stable, so the client matches on @type. One of them is an Organization block describing Patreon rather than the creator, which is exactly the row an index based parser would pick up by mistake.
tiers(slug)
5 credits. The complete tier list, from Patreon's own bootstrap payload.
result = bee.tiers("kurzgesagt")
result["matched"] # True
result["count"] # 4
Live output, sorted cheapest first:
title |
amount_cents |
is_free_tier |
post_count |
declined_patron_count |
|---|---|---|---|---|
| Free | 0 | True | 8 | 2 |
| Trainee Producer | 314 | False | 1 | 31 |
| Producer | 1500 | False | 62 | 14 |
| Senior Producer | 4200 | False | 58 | 0 |
Every key on a tier object: title, amount_cents, currency, description, url, image_url, is_free_tier, post_count, published, requires_shipping, declined_patron_count, patron_amount_cents, patron_currency, discord_role_ids, remaining, user_limit.
declined_patron_count is not a patron count. It counts declined payments. It reads 2, 31, 14 and 0 across those tiers, which is nothing like a membership figure for a creator of that size. Patreon does not publish a per tier patron count on the public page, so this package passes the field through under its real name rather than relabelling it as something more useful.
amount_cents with currency is the tier price and is the pair to trust. patron_amount_cents and patron_currency also appear, reading 800 and DKK on the free tier, which does not correspond to the tier price, so they are returned raw without interpretation.
remaining and user_limit were both None on every tier here. They carry real values on creators who cap a tier.
This route reads a private application format Patreon has no obligation to keep stable. When the pattern stops matching, the client returns {"tiers": [], "matched": False, "fallback": <creator() result>} rather than an empty list that reads like a creator with no tiers.
price_ladder(slug) and entry_tier(slug)
Convenience wrappers over tiers(), same 5 credits.
bee.price_ladder("kurzgesagt")
# [{'title': 'Free', 'price': 0.0, 'currency': 'USD', 'free': True, 'posts': 8},
# {'title': 'Trainee Producer', 'price': 3.14, 'currency': 'USD', 'free': False, 'posts': 1},
# {'title': 'Producer', 'price': 15.0, 'currency': 'USD', 'free': False, 'posts': 62},
# {'title': 'Senior Producer', 'price': 42.0, 'currency': 'USD', 'free': False, 'posts': 58}]
bee.entry_tier("kurzgesagt")["title"] # 'Trainee Producer'
entry_tier() skips the free tier, because amount_cents of 0 is not an entry price.
entry_price(slug)
30 credits. Premium proxy plus JavaScript at 25, plus 5 for the AI query. Uses ai_extract_rules, so there is no parser to maintain.
bee.entry_price("kurzgesagt")
# {'creator_name': 'Kurzgesagt – In a Nutshell',
# 'about': '<full about text>',
# 'membership_tiers': ['Access exclusive benefits starting at $3.14/month']}
creator_name and about come back complete and correct. membership_tiers comes back as a single teaser string, not a tier array, because the tier cards are mounted by a component that has not rendered at capture time. The model described what was actually on the page, which is the honest result rather than a broken one.
That said, $3.14 matches the amount_cents value of 314 that tiers() reads out of the payload, so the two routes independently agree on the entry price. Use entry_price() when you want that one number and no parser. Use tiers() when you want the whole ladder.
usage()
Free. Account credits, concurrency and renewal date.
Choosing a route
| You need | Method | Credits |
|---|---|---|
| Name and tagline for a directory | creator |
5 |
| Full about copy for search or embeddings | profile |
5 |
| Every tier with its price and post count | tiers |
5 |
| A price ladder ready to display | price_ladder |
5 |
| Entry price only, no parser to own | entry_price |
30 |
creator() and profile() read the same fetched page, so if you want both, fetch raw once and run both parsers locally for a single 5 credit charge.
Credit cost
Measured from spb-cost headers. Available on bee.last_cost after every call.
| Configuration | Credits |
|---|---|
mode=auto on a creator page, settled at the JavaScript rung |
5 |
render_js plus premium_proxy |
25 |
The same plus ai_extract_rules |
30 |
| Validation error | 0 |
Auto mode is the right default here: it charges only for the configuration that worked, and nothing at all if every rung fails. It is incompatible with render_js, premium_proxy and stealth_proxy, and sending both returns HTTP 400 while billing nothing, which fails quietly if you do not read status codes.
At 5 credits per creator, 250,000 credits covers 50,000 creator checks. Plan tiers: ScrapingBee pricing.
Related
Landing pages for adjacent creator platforms: Substack scraper API, Twitch API, TikTok API, TikTok follower API, YouTube video scraper API, YouTube transcript scraper API, YouTube comment scraper API, Snapchat scraper API.
Features: AI web scraping, data extraction, markdown scraper, screenshots, JavaScript scenario, n8n integration.
Reference: extraction rules documentation. Guide version with the full route walkthrough: github.com/ScrapingBee/patreon-api.
License
MIT
Release files for patreon-scraper-api 0.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| patreon_scraper_api-0.0.1.tar.gz | 13.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| patreon_scraper_api-0.0.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 23.7 kB
Release files / patreon_scraper_api-0.0.1.tar.gz
| Download URL | patreon_scraper_api-0.0.1.tar.gz |
|---|---|
| Size | 13.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2f47809c5f9ad4b2a742b3e8f2de717083e263b0525bc1eef04180fcf5c4f6d6
|
|
BLAKE2b-256 checksum How to use checksums |
9aa3732d25d12781e2f39154ce7aa9e512e529f5a89cfbd475364a6ee6ca968c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.3
|
Release files / patreon_scraper_api-0.0.1-py3-none-any.whl
| Download URL | patreon_scraper_api-0.0.1-py3-none-any.whl |
|---|---|
| Size | 10.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ae4e394cf7f9934adc72ed8e03155b6446d6c308b43c4885329dbb69fac08cd4
|
|
BLAKE2b-256 checksum How to use checksums |
ffec35294dcb34716e81924aa7063aa8640ea6de3969f4ecf5f365c0672b667d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.3
|