By Nikhil Kumar. Last updated October 2026.
Here’s the thing most “Bluesky API” posts bury: you probably don’t need to pay anyone.
Bluesky runs on the AT Protocol. Its public data (profiles, posts, keyword search) comes over an open API that needs no login and no key. One GET request gets you a profile or a page of search results. So before anything else, know that the free option is real, it’s official, and for a lot of use cases it’s all you need.
Does Bluesky have an API, and is it free?
Yes, and for public data it is free. Bluesky is built on the AT Protocol, whose public AppView serves profiles, author feeds, and keyword search over XRPC with no authentication. A single anonymous GET returns JSON. That makes Bluesky the rare major platform where the official API is open by default, per Bluesky’s developer docs, rather than metered behind a paid tier like X or gated like Instagram.
So the honest framing for this whole post is: the free path works.
You call three public methods. app.bsky.actor.getProfile for a profile, app.bsky.feed.getAuthorFeed for a user’s posts, and app.bsky.feed.searchPosts for keyword search. No key, no OAuth dance, no app password, for reads.
That is genuinely different from every other platform I’ve written about here. Bluesky decided openness was the point.
How do I get Bluesky data with an API?
You call one of three reads: a profile by handle, a user’s posts by handle, or a keyword search by query. Each returns clean JSON. On the native AT Protocol, you hit the XRPC endpoint directly. Through a unified API like ours, the same three reads are GET /v1/bluesky/profile, /v1/bluesky/user-posts, and /v1/bluesky/search, each costing 1 credit per profile or post, with a key from the quickstart that ships with 25 free credits.
Here is the native, free way to search, no account required:
import requests
resp = requests.get( "https://api.bsky.app/xrpc/app.bsky.feed.searchPosts", params={"q": "wildfire smoke", "limit": 100}, timeout=60,)for post in resp.json()["posts"]: print(post["likeCount"], post["author"]["handle"], post["record"]["text"][:80])That costs nothing and returns real posts.
Here is the same search through a unified endpoint, which matters only if you want the result in the same shape as your Instagram, TikTok, or X pulls:
resp = requests.get( "https://api.scrapersocial.com/v1/bluesky/search", params={"query": "wildfire smoke", "limit": 100}, headers={"Authorization": "Bearer sk_live_..."}, timeout=60,)for post in resp.json()["data"]: print(post["likes"], post["author"]["handle"], post["caption"][:80])Same posts. The difference is the field names and whether you also want 30 other platforms in that envelope. More on when that trade is worth it below.
Do I need an app password or authentication?
No, not for reading public data. Profiles, author feeds, and keyword search all return anonymously. You only reach for credentials when you want to post, read your own private data, or page past the public search ceiling. Bluesky added OAuth for the AT Protocol on September 25, 2024, and told developers to prefer it over the legacy App Password system for new projects, though app passwords still work for personal scripts.
So for a read-only data pull, skip auth entirely.
The one place this bites you is pagination, which I’ll cover next. Anonymous reads are capped, and the cap is where the “do I need to log in” question actually comes from.
If you do authenticate, the flow is a createSession call with your handle and an app password, which returns an access token and a refresh token. Keep the app password scoped and server-side. It is a credential, even if it is a restricted one.
How do I search Bluesky by keyword?
Send your term to searchPosts and read the posts array back. The query string is where Bluesky gets powerful, because it accepts operators inside q: from:handle to scope to one account, since: and until: for date bounds, and lang:en to filter by language. So “wildfire smoke lang:en since:2026-09-01” is a valid single query.
This is the read most people actually want. Brand monitoring, topic tracking, catching mentions.
The operators are the trick for precision. Instead of pulling a noisy keyword and filtering client-side, push the filter into the query and let Bluesky do it.
params = {"q": "our-brand lang:en since:2026-10-01", "limit": 100}For a cross-platform version of keyword monitoring, the same pattern works on X through our Twitter search API, and the best social search APIs roundup maps which platforms support real keyword search at all.
What is the result cap on Bluesky search, and how do I page past it?
Up to 100 per query, and paging past that anonymously is blocked. The public endpoint caps limit at 100 with a default of 25, per the AT Protocol reference. The catch bites when you try to go further: adding a cursor to an unauthenticated request returns 403 Forbidden, a limitation reported on the official bsky-docs repo. So 100 posts per query is the reliable no-login ceiling.
This is the single most important limit to design around.
To get more than 100 without logging in, you slice the query. Run the same keyword across narrow since:/until: date windows, or split it by lang: or from:, and union the results. Each slice is its own 100-cap query.
That workaround is also exactly the kind of plumbing a managed API handles for you. A flat-credit endpoint handles the authentication and pages with a cursor, so you ask for more and get more without stitching date windows yourself. Whether that convenience is worth 1 credit per post depends on how much history you need.
The honest version: for live monitoring, 100 recent posts per query on a schedule is plenty, and it’s free. For deep backfill, the native cap is a real wall, and that’s where paying buys you something concrete.
What fields does a Bluesky post return?
The post text, the engagement counts, the author, and the timestamps. Every post comes back with its text, the like, reply, repost, and quote counts, the author’s handle and display name, and both the created and indexed timestamps. Reposts carry who reposted them, and each post keeps its AT URI and a web URL you can open.
{ "data": [ { "id": "3l5xabcd2ef", "url": "https://bsky.app/profile/alice.bsky.social/post/3l5xabcd2ef", "caption": "smoke rolled in overnight, air quality is brutal today", "likes": 412, "comments": 23, "shares": 57, "quotes": 9, "bookmarks": 14, "views": null, "author": { "handle": "alice.bsky.social", "name": "Alice" }, "languages": ["en"], "posted_at": "2026-10-10T13:40:00Z" } ], "request_id": "req_01K2Z9Q4TE"}One honest gap worth flagging: views is always null, because Bluesky does not expose a public view count the way TikTok or X do. If your analysis depends on impressions, Bluesky can’t give them to you, from any tool, because the number isn’t in the data.
The uri field is the one to hold onto. It is the post’s canonical AT Protocol identifier, and it is what you dedupe on and what you pass back if you later want the full thread or the author’s other posts. Treat it as the primary key, not the web URL.
The profile read is separate. Pass a handle and the profile endpoint hands back the bio, the follower and following totals, how many posts the account has published, whether it carries verification, and the date it was created.
What are the rate limits and other limits on Bluesky?
Expect roughly 3,000 requests per five minutes per IP on the public API, and a smaller, declining audience than the big platforms. The per-IP read budget is widely reported at about 3,000 requests per 5 minutes, though Bluesky’s exact current figure is worth confirming against their docs. Hit it and you get a 429; back off and retry.
The bigger “limit” is strategic, not technical.
Bluesky passed roughly 46 million registered accounts by August 2026, up from about 10 million in September 2024, according to SocialPilot’s statistics roundup. That growth is real. But registered is not active.
Mobile monthly active users fell to 10.4 million in June 2026, down 27.2% year over year on Similarweb’s estimate. So treat Bluesky as a focused, high-signal source, not a firehose the size of X. For a brand or topic, that’s often a feature: less noise.
If you’re running scheduled pulls, our rate limits docs cover concurrency and retry behavior on the managed side, which sits on top of Bluesky’s own limits rather than replacing them.
What can you do with Bluesky data?
Brand monitoring, topic research, and academic datasets, mostly. Repeat a keyword search over days and you build a running record of how an audience talks about a topic or a brand in public, and because the API is open and free, Bluesky has become a favorite for research that other platforms price out of reach.
Brand and topic monitoring is the common one. Search your name or a keyword, read the reply and repost counts to gauge reach, and you have a live pulse without a paid listening suite.
Research is where Bluesky genuinely stands out. Because the data is open and free, it has become a go-to source for computational social science, bot detection, and discourse studies that could never afford X’s $42,000-a-year enterprise archive. Whole datasets get built on the public firehose.
Trend and community tracking rounds it out. Follow a set of handles with the posts endpoint, watch which topics climb, and you spot a theme moving through a niche before it reaches the bigger networks.
The thing to keep honest: with monthly actives around 10 million, Bluesky is a strong signal on a smaller stage. It tells you what an engaged, often tech-and-news-heavy audience thinks. It does not stand in for the whole internet.
How do I monitor a Bluesky keyword or account over time?
Run the search on a schedule, keep the post ids you have seen, and drop the repeats. There is no single call that streams forever, so monitoring means polling searchPosts (or an author feed) on a cadence and deduping on each post’s id so every run only surfaces what is new.
seen = set()
def poll(term): r = requests.get("https://api.bsky.app/xrpc/app.bsky.feed.searchPosts", params={"q": f"{term} lang:en", "limit": 100}, timeout=60) fresh = [p for p in r.json()["posts"] if p["uri"] not in seen] seen.update(p["uri"] for p in fresh) return freshStore the ids between runs, not just in memory, so a restart does not re-process the backlog.
Keep the cadence sane. A brand term checked a few times an hour is plenty, since Bluesky’s volume is modest and the per-IP budget is real. Over-polling a quiet keyword just burns requests for nothing.
For deeper history on a term, run the same keyword across since:/until: windows and union the ids. That is the honest way around the 100-per-query wall without logging in.
When is a unified API worth it over calling Bluesky directly?
When you need Bluesky in the same shape as other platforms, want the pagination handled, or value skipping the XRPC learning curve. If Bluesky is your only source and your volume is low, call the free AT Protocol API directly. A managed API earns its credit when one of three things is true.
First, you already pull Instagram, TikTok, or X and want Bluesky posts in the identical { data, request_id } envelope, same field names, no per-platform parser.
Second, you need to page past the 100-result anonymous cap without building a login pool and date-window stitching yourself.
Third, you’d rather not learn AT Protocol’s XRPC conventions, the public.api.bsky.app versus api.bsky.app host quirk that makes search 403 in some regions, or the cursor behavior, just to get a list of posts.
| Native AT Protocol API | Unified API | |
|---|---|---|
| Cost for public reads | Free | 1 credit per profile or post |
| Auth for reads | None | One bearer key |
| Schema | Bluesky-specific XRPC | Same envelope as 30+ platforms |
| Paging past 100 | Authenticate + stitch windows | Cursor handled for you |
| Host/region quirks | You handle them | Handled |
| Best for | Bluesky-only, low volume | Multi-platform or high volume |
If none of those three apply, use the free API. I’d rather tell you that than sell you a credit you don’t need. For multi-platform social listening, the social listening API guide shows the unified pattern across networks, and the honest X comparison lives in the Twitter search API post.
Is it legal to scrape Bluesky?
Reading public Bluesky posts is generally lawful in the US, but the posts are still personal data. US courts have held that collecting publicly accessible data is not a hacking offense, and Bluesky’s public API is designed to serve exactly this data without a login. That does not erase privacy duties.
A post, a handle, and a bio describe a real person.
Store or process it for someone the EU’s GDPR or California’s CCPA covers and those rules bind you, public post or not. The fact that it sat on an open network is not a defense either regime recognizes.
My working rule: pull only public fields, keep request volume reasonable, don’t assemble profiles on named individuals without a defensible reason, and get legal advice before a commercial product. The AT Protocol being open makes the fetching easy; it does not move your data-protection duties onto Bluesky or any vendor.
Frequently asked questions
Does Bluesky have an API?
Yes. Bluesky runs on the AT Protocol, which exposes public read endpoints for profiles, author feeds, and keyword search over XRPC. Reading public data needs no login or API key, so you can pull a profile or search posts with a single HTTP GET. Writing posts or paging past the public limits is where authentication comes in.
Is the Bluesky API free?
For reading public data, yes. The AT Protocol’s public AppView serves profiles, posts, and search anonymously at no cost, which makes Bluesky unusual among big social platforms. You pay only if you want a managed layer that unifies Bluesky with other networks, handles pagination and rate limits, and skips the XRPC learning curve.
Do I need an app password to read Bluesky data?
No, not for public reads. Profiles, author feeds, and keyword search return without any credentials. You need an app password or OAuth only to post, to read your own private data, or to page past the unauthenticated search ceiling, since adding a cursor to an anonymous search request returns a 403.
How many posts can I get from Bluesky search?
Up to 100 per query on the public endpoint, with a default of 25 if you do not set a limit. Paging past 100 anonymously is blocked, so to go deeper you narrow the query with operators like since: and until:, split by language or author, or authenticate. Plan around 100 per query as the reliable unauthenticated ceiling.
Is it legal to scrape Bluesky?
Reading public Bluesky posts is generally lawful in the US, where collecting public data is not a hacking offense, but public posts are still personal data. If you store or process them for people in the EU or California, GDPR and CCPA apply. Pull only public fields, keep volume reasonable, and get legal advice before a commercial build.
Start with the free call
Open a terminal and hit app.bsky.feed.searchPosts with a keyword you care about. No key, no signup, real posts back. If you then want Bluesky in the same shape as your other platforms, or you need to page past 100 results, get a key on the quickstart and the 25 free credits cover 25 posts. Either way, start with the free one and only pay when it genuinely stops being enough for what you are building.