Metadata-Version: 2.4
Name: patreon-scraper-api
Version: 0.0.1
Summary: Patreon scraper API client: public creator pages, entry tiers, about text and structured profile data via ScrapingBee.
Author: wordstotech
License: MIT
Project-URL: Homepage, https://github.com/ScrapingBee/patreon-api
Project-URL: Repository, https://github.com/ScrapingBee/patreon-api
Project-URL: Documentation, https://www.scrapingbee.com/documentation/
Keywords: patreon scraper,patreon api,scraper for patreon,creator economy,web scraping,scrapingbee
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Text Processing :: Markup :: HTML
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests>=2.25.0
Dynamic: license-file

# 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.

```bash
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

```python
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](https://www.scrapingbee.com/). Landing page for this target: [Patreon scraper API](https://www.scrapingbee.com/scrapers/patreon-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.

```python
bee.creator("kurzgesagt")
```

```python
{
 '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.

```python
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.

```python
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.

```python
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.

```python
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](https://www.scrapingbee.com/pricing).

## Related

Landing pages for adjacent creator platforms: [Substack scraper API](https://www.scrapingbee.com/scrapers/substack-scraper-api/), [Twitch API](https://www.scrapingbee.com/scrapers/twitch-api/), [TikTok API](https://www.scrapingbee.com/scrapers/tiktok-api/), [TikTok follower API](https://www.scrapingbee.com/scrapers/tiktok-follower/), [YouTube video scraper API](https://www.scrapingbee.com/scrapers/youtube-video-scraper-api/), [YouTube transcript scraper API](https://www.scrapingbee.com/scrapers/youtube-transcript-scraper-api/), [YouTube comment scraper API](https://www.scrapingbee.com/scrapers/youtube-comment-scraper-api/), [Snapchat scraper API](https://www.scrapingbee.com/scrapers/snapchat-scraper-api/).

Features: [AI web scraping](https://www.scrapingbee.com/features/ai-web-scraping-api/), [data extraction](https://www.scrapingbee.com/features/data-extraction/), [markdown scraper](https://www.scrapingbee.com/features/markdown-scraper/), [screenshots](https://www.scrapingbee.com/features/screenshot/), [JavaScript scenario](https://www.scrapingbee.com/features/javascript-scenario/), [n8n integration](https://www.scrapingbee.com/features/n8n/).

Reference: [extraction rules documentation](https://www.scrapingbee.com/documentation/data-extraction/). Guide version with the full route walkthrough: [github.com/ScrapingBee/patreon-api](https://github.com/ScrapingBee/patreon-api).

## License

MIT
