Tony Wang7 min readHow to Scrape Bluesky in 2026 (API & Python)
Bluesky is the most open platform in this series — AT Protocol needs no API key. See what a structured API adds instead, with real JSON.
Bluesky is the most open platform in this series, and it's worth saying plainly: the AT Protocol it runs on was designed for zero-auth public reads, so there's no locked door to get through here. Its own developer docs confirm that the public AppView and the network firehose both require no authentication for public data, and Bluesky's Terms of Service don't contain a scraping or automated-access restriction at all. This guide covers the protocol's own open access, no-code tools, and a structured API — and is honest that the API's value here isn't "unlocking" anything.
Why scrape Bluesky?
Bluesky's public feed, follower graphs, and threads feed a handful of recurring jobs:
- Social listening and trend tracking — watch what a brand, topic, or hashtag-equivalent keyword is generating on Bluesky in near real time.
- Creator and account research — pull a handle's profile, post history, and follower/following counts for outreach or competitive research.
- Conversation and thread analysis — read a post's full reply tree to understand how a specific conversation unfolded.
- Cross-platform social comparison — pair Bluesky activity with X/Twitter or Threads data to see where a story or account is actually landing.
- Trending-topic monitoring — track what's surfacing platform-wide without maintaining a keyword list yourself.
Is it legal to scrape Bluesky?
Bluesky's Terms of Service (last updated 14 August 2025) do not contain a scraping, robots, data-mining, or automated-access restriction — a search of the full document turns up no clause matching any of those terms. That's a genuine departure from most platforms in this series, where a Conditions of Use or Automated Data Collection Terms page explicitly prohibits bots and crawlers. Bluesky's own developer docs go further: the API Hosts and Auth guide states plainly that "many Bluesky Lexicon endpoints are public, and do not require authentication," and that the network firehose "does not require auth" either. Parts of the stack — the reference client, and much of the protocol's core code — are also released under MIT and Apache open-source licenses per the Terms' Open Source Software section.
None of that is a blank check. Collect only what's public (private accounts and DMs are never in scope), treat handles, bios, and post text as personal data under GDPR/CCPA where applicable, and Bluesky's rate-limits guide notes the AppView's limits are "generous" but real — sustained abuse can still get you throttled. See is web scraping legal for the fuller picture.
Option 1: The AT Protocol directly (and the real work it takes)
Because Bluesky's public reads need no auth, you can hit the AppView directly with a plain HTTP request — no key, no session, no login:
curl "https://public.api.bsky.app/xrpc/app.bsky.feed.getAuthorFeed?actor=bsky.app&limit=5"
That's the protocol's own open access working exactly as designed — app.bsky.feed.getAuthorFeed is a real, documented Lexicon endpoint, and it returns actual post data with no credential of any kind. So unlike almost every other guide in this series, the friction here isn't anti-bot defense or a login wall — there genuinely isn't one for public data. The real work shows up once you go past a single lookup:
- Raw AT Protocol records, not normalized JSON. Each Lexicon endpoint returns records shaped by its own schema (
app.bsky.feed.post,app.bsky.actor.profile, and so on), with nestedat://URIs, CIDs, and DIDs you have to resolve and flatten yourself before the data is usable downstream. - DID resolution is its own subsystem. Every account and record is keyed by a decentralized identifier (DID), not a stable username — handles can change, so a real pipeline needs to resolve and cache DID-to-handle mappings, not just store the handle you searched for.
- The firehose is the real infrastructure commitment. If you want every public post network-wide instead of one account at a time, that means consuming
com.atproto.sync.subscribeRepos— a WebSocket stream of CBOR-encoded repository updates — or its lighter JSON sibling, Jetstream, and running that consumer continuously with cursor tracking, reconnect logic, and CBOR/MST decoding. - One protocol, one mental model, just for this platform. If your pipeline already normalizes Reddit, TikTok, Instagram, X, and Threads into one schema, AT Protocol's record/lexicon/DID model is a genuinely different shape to bolt on next to them — open, but not the same shape as anywhere else you're pulling from.
Option 2: No-code tools
Marketplace scraper actors and browser extensions exist for Bluesky exports, but given that the underlying API is already open and keyless, most of them are thin wrappers around the same public AppView endpoints described above. They're fine for a one-off profile pull or a spreadsheet export, but they don't solve the actual gap — normalized fields across platforms and a maintained firehose consumer — any more than calling the AppView directly does.
Option 3: A structured Bluesky API (via Crawlora)
If you want Bluesky alongside other social platforms in one normalized shape — one API key, one JSON schema, no firehose infrastructure to run — a Bluesky scraping API gives you that. Look up a profile:
curl "https://api.crawlora.net/api/v1/bluesky/profile?actor=bsky.app" \
-H "x-api-key: $CRAWLORA_API_KEY"
{
"code": 200,
"msg": "OK",
"data": {
"did": "did:plc:z72i7hdynmk6r22z27h6tvur",
"handle": "bsky.app",
"display_name": "Bluesky",
"description": "official Bluesky account",
"avatar_url": "https://cdn.bsky.app/img/avatar/plain/did:plc:z72i7hdynmk6r22z27h6tvur/...",
"banner_url": "https://cdn.bsky.app/img/banner/plain/did:plc:z72i7hdynmk6r22z27h6tvur/...",
"followers_count": 34416262,
"follows_count": 11,
"posts_count": 804,
"created_at": "2023-04-12T04:53:57.057Z",
"indexed_at": "2025-10-27T21:05:26.152Z"
}
}
Then pull an account's feed and the platform's trending topics in Python (real fields — check the docs):
import requests
h = {"x-api-key": "YOUR_API_KEY"}
base = "https://api.crawlora.net/api/v1/bluesky"
profile = requests.get(f"{base}/profile", headers=h, params={"actor": "bsky.app"}).json()["data"]
feed = requests.get(f"{base}/author-feed", headers=h, params={"actor": "bsky.app", "limit": 25}).json()["data"]
for post in feed["posts"]:
print(post["author"]["handle"], post["like_count"], post["text"])
trending = requests.get(f"{base}/trending-topics", headers=h).json()["data"]["topics"]
author-feed returns each post as normalized JSON — no at:// parsing, no CID handling:
{
"data": {
"posts": [
{
"uri": "at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post/3l6oveex3ii2l",
"url": "https://bsky.app/profile/bsky.app/post/3l6oveex3ii2l",
"author": { "did": "did:plc:z72i7hdynmk6r22z27h6tvur", "handle": "bsky.app", "display_name": "Bluesky" },
"text": "Welcome to Bluesky!",
"created_at": "2024-10-17T07:06:51.491Z",
"reply_count": 8545,
"repost_count": 9534,
"like_count": 63579,
"quote_count": 705
}
],
"cursor": "1234567890::bafyabc"
}
}
trending-topics needs no parameters beyond the API key and returns a flat, ready-to-display list:
{
"data": {
"topics": [
{ "topic": "Big Brother 28", "link": "/profile/trending.bsky.app/feed/821933789" },
{ "topic": "NFL Preseason", "link": "/profile/trending.bsky.app/feed/821883116" }
],
"suggested": [
{ "topic": "Popular with Friends", "link": "/profile/bsky.app/feed/with-friends" }
]
}
}
/bluesky/search-actors (param q) finds accounts by keyword, /bluesky/followers and /bluesky/follows return an actor's social graph with cursor-based pagination, and /bluesky/post-thread (param uri) returns a post and its full nested reply tree. Store one row per post or profile and re-run on a schedule.
What you can collect
Public Bluesky data: account search by keyword; full profiles (handle, DID, display name, description, avatar/banner, follower/follows/posts counts, created and indexed timestamps); an account's post feed (text, engagement counts, language, timestamps); followers and follows lists; a post's full nested reply thread; and platform-wide trending topics.
Limitations and common challenges
- The value here is convenience, not access. Public data is already open through the AT Protocol itself — a structured API saves you normalization and firehose infrastructure, not a locked door.
- Firehose/Jetstream is a real infrastructure commitment if you go that route. Consuming the network-wide stream directly means running a persistent WebSocket consumer with cursor tracking and CBOR/MST decoding, not a one-off script.
- DIDs, not fixed usernames. Handles can change; the durable identifier is the DID, and a real pipeline should resolve and store both.
- Cursor-based pagination. Feeds, followers, and follows all page via an opaque
cursorvalue rather than a page number. - Public data only. Private accounts, DMs, and anything behind a login wall are out of scope for every approach here, and should stay that way.
Where this gets used
- Social listening dashboards — track brand or topic mentions across public posts in near real time.
- Creator research — profile and post-history lookups for outreach or partnership vetting.
- Conversation analysis — pull a viral post's full reply tree to understand how a thread developed.
- Cross-platform monitoring — combine Bluesky activity with X/Twitter and Threads data in one social-listening pipeline.
Sources
Start collecting
Try it first, free: run any public URL through the Free Web Scraper, or check whether a site blocks bots with the Anti-Bot Checker — no signup.
Test the profile, author-feed, and search endpoints in the Playground, check the schema in the API docs, and review pricing. Bluesky tells you what's happening on the newest major open social network; pair it with X/Twitter for the incumbent's data and Threads for Meta's entrant, and you've got the full "which X-alternative actually won a given conversation" picture in one normalized schema. For the same conversation on longer-form, community-moderated ground, how to scrape Reddit covers the threads where a topic usually surfaces first. See also how to choose a web scraping API and is web scraping legal.
Part of our how-to-scrape guide series — every platform we cover, in one index.
Frequently asked questions
Does Bluesky require an API key to read public data?
No. Bluesky's AT Protocol AppView (public.api.bsky.app) serves profiles, posts, and feeds with zero authentication by design — no API key, no OAuth token, no login. A structured API like Crawlora's still requires its own key, but that's Crawlora's key for normalized access across platforms, not a Bluesky credential.
Is it legal to scrape Bluesky?
Bluesky's Terms of Service, last updated 14 August 2025, contain no scraping, robots, or automated-access restriction — unusual among the platforms this series covers. Collect only public data, respect rate limits, and treat handles, bios, and post text as personal data under GDPR/CCPA where applicable. This isn't legal advice.
Can I stream all of Bluesky's public posts in real time?
Yes, via the network firehose (com.atproto.sync.subscribeRepos) or its lighter JSON sibling Jetstream, both of which require no auth. Doing so means running a persistent WebSocket consumer with cursor tracking and CBOR/MST decoding — real infrastructure to build and maintain yourself.
What is a DID in Bluesky and the AT Protocol?
A DID (decentralized identifier, e.g. did:plc:z72i7hdynmk6r22z27h6tvur) is the durable, protocol-level identity behind an account. Handles like bsky.app can change; the DID is what stays constant, so pipelines that track accounts long-term should store the DID, not just the handle.
What's the difference between the firehose and a structured Bluesky API?
The firehose delivers raw AT Protocol records for every public write network-wide, which you must decode, filter, and normalize yourself. A structured API like Crawlora's Bluesky endpoints returns already-normalized JSON for a specific profile, feed, or thread — less infrastructure, narrower scope.
Can I get a Bluesky account's followers and following lists?
Yes — /bluesky/followers and /bluesky/follows both take an actor (handle or DID) and return the account's social graph with cursor-based pagination, alongside the subject account's own profile.
Can I read a Bluesky post's full reply thread?
Yes — /bluesky/post-thread takes a post's at:// URI and returns the post plus its complete nested reply tree.