Bluesky Scraper — Posts, Profiles & Followers avatar

Bluesky Scraper — Posts, Profiles & Followers

Pricing

from $1.50 / 1,000 results

Go to Apify Store
Bluesky Scraper — Posts, Profiles & Followers

Bluesky Scraper — Posts, Profiles & Followers

Scrape Bluesky (AT Protocol) with no login. Search posts by keyword and filter by date, language, author, hashtag or linked domain. Pull profiles, follower and following lists, single posts and whole accounts — text, likes, reposts, replies, quotes, images. JSON or CSV.

Pricing

from $1.50 / 1,000 results

Rating

0.0

(0)

Developer

Muhamed Didovic

Muhamed Didovic

Maintained by Community

Actor stats

0

Bookmarked

17

Total users

16

Monthly active users

3 days ago

Last modified

Share

Bluesky Scraper

All-in-one Bluesky (AT Protocol) scraper — search posts by keyword, pull profiles, export follower and following lists, or grab a single post. Paste a URL or a handle, or just type a keyword, and get clean JSON or CSV.

InputRow(s) emitted
Keywordpost rows across the whole network — full text, likes, reposts, replies, quotes, images, author. Or profile rows, or both.
Handle or profile URLbsky.app/profile/<handle>One profile row: bio, follower / following / post counts, avatar, banner, join date
Handle + "also fetch posts"That account's recent post rows, with reposts labelled rather than passed off as its own
Handle + "also export followers / following"One follower or following row per account, each tagged with whose list it came from
Post URLbsky.app/profile/<handle>/post/<id>That single post with full text and engagement

Pure HTTP against the public AT Protocol appview. No login, no cookies, no app password, no browser, no anti-bot service.

How it works

How the Bluesky Scraper works

Logged out, Bluesky serves keyword search one page at a time and refuses to follow a cursor — its appview rejects any search request carrying one. Most Bluesky scrapers therefore stop at about 100 posts.

This Actor steps backwards through time instead. It takes the oldest post of each page and uses that timestamp as the next request's cut-off, so every request stays a first page and the walk keeps going. Measured on a live run: 349 unique posts across 2.5 days, zero duplicates, for a request the one-page limit would have capped at 100.

That needs time-ordered results, so it works with sort: "latest". A top search is relevance-ordered and genuinely cannot go past one page — the Actor says so in the log rather than pretending otherwise.

Profiles, followers, following, post threads and account search are ordinary cursor pagination and are not affected by any of this.

Input

FieldTypeRequiredNotes
searchQueriesstring[]noKeywords to search. Returns posts by default.
searchTypeenumnoposts (default), accounts, or both.
sortenumnolatest (default, no result cap) or top (capped at ~100 by Bluesky).
since / untilstringnoAbsolute date bounds, e.g. 2026-09-01. Bluesky filters on its own sortAt (roughly the earlier of created and indexed).
langstringnoTwo-letter language code, e.g. en.
fromAuthorstringnoOnly posts written by this handle or DID.
mentionsAuthorstringnoOnly posts mentioning it — brand monitoring.
domainstringnoOnly posts linking to this domain.
hashtagsstring[]noOnly posts carrying these hashtags.
startUrlsarraynoBluesky profile or post URLs.
handlesstring[]noHandles or DIDs.
includePostsbooleannoAlso fetch each profile's recent posts.
maxPostsPerProfileintegernoCap per profile. Default 100.
includeFollowersbooleannoAlso export who follows each profile.
includeFollowingbooleannoAlso export who each profile follows.
maxFollowsPerProfileintegernoCap per profile. Default 500.
maxItemsintegernoCap across the whole run. Default 1000. The Apify free plan returns at most 50 rows whatever you set.
proxyobjectnoApify Proxy, on by default. Post search is rate-limited per IP.

Example input

{
"searchQueries": ["climate"],
"searchType": "posts",
"sort": "latest",
"lang": "en",
"maxItems": 500
}

Lead generation — a competitor's follower list:

{
"handles": ["apify.com"],
"includeFollowers": true,
"maxFollowsPerProfile": 2000
}

Output schema

Every row carries a type field, so mixed runs split cleanly.

type: "post"

{
"type": "post",
"uri": "at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post/3l6oveex3ii2l",
"cid": "bafyrei…",
"text": "👋 Bluesky is an open social network…",
"createdAt": "2024-10-22T18:22:11.000Z",
"indexedAt": "2024-10-22T18:22:12.114Z",
"likeCount": 63673,
"repostCount": 9526,
"replyCount": 8579,
"quoteCount": 1204,
"bookmarkCount": 88,
"langs": ["en"],
"images": [],
"authorDid": "did:plc:z72i7hdynmk6r22z27h6tvur",
"authorHandle": "bsky.app", // on a repost, the ORIGINAL author
"authorDisplayName": "Bluesky",
"isRepost": false,
"repostedBy": null, // handle of the reposter when isRepost is true
"repostedAt": null,
"url": "https://bsky.app/profile/bsky.app/post/3l6oveex3ii2l"
}

type: "profile"

{
"type": "profile",
"did": "did:plc:z72i7hdynmk6r22z27h6tvur",
"handle": "bsky.app",
"displayName": "Bluesky",
"description": "official Bluesky account…",
"followersCount": 34586228,
"followsCount": 11,
"postsCount": 808,
"avatar": "https://cdn.bsky.app/img/avatar/plain/…",
"banner": "https://cdn.bsky.app/img/banner/plain/…",
"createdAt": "2023-04-12T04:53:57.057Z",
"indexedAt": "2026-09-01T10:14:02.881Z",
"url": "https://bsky.app/profile/bsky.app"
}

type: "follower" / type: "following"

{
"type": "follower",
"of": "apify.com", // whose list this row came from
"did": "did:plc:…",
"handle": "pfrazee.com",
"displayName": "Paul Frazee",
"description": "…",
"avatar": "https://cdn.bsky.app/img/avatar/plain/…",
"createdAt": "2023-04-22T09:11:40.002Z",
"indexedAt": "2026-08-30T17:03:55.410Z",
"url": "https://bsky.app/profile/pfrazee.com"
}

Pricing

Pay per event: $0.005 when a run starts, plus $0.0015 for every row delivered. A run that Bluesky rate-limits to zero rows still costs the $0.005 start fee.

EventWhenRate
Actor startOnce per run, per GB of memory$0.005
ResultEach post, profile, follower or following row$0.0015

What makes this richer than the competition

CapabilityTypical Bluesky ActorThis Actor
Posts per keyword search~100 (the logged-out one-page limit)Hundreds — 349 measured on one live run, by walking backwards through time
Reposts in an account's feedEmitted as the account's own postsLabelled isRepost + repostedBy, original author preserved
Follower / following exportOften absent or a separate paid ActorIncluded, tagged with whose list it is
Short or throttled runSilent — looks like a complete resultStatus message plus a RUN_REPORT record naming requested vs delivered, for every input path
Search filtersKeyword onlyDate range, language, author, mentions, domain, hashtags
Login requiredSome ask for your Bluesky app passwordNever

Use cases

  • Brand monitoringmentionsAuthor with your own handle returns every post mentioning you, with engagement counts, so you can spot a thread before it grows.
  • Social listeningsearchQueries plus lang and a date window gives a corpus for sentiment or topic analysis without touching a browser.
  • Lead generationincludeFollowers on a competitor's handle exports their audience with handles, names and profile URLs.
  • Creator research — a handle with includePosts returns an account's recent output and how each post performed, reposts labelled separately from original writing.
  • Feeding an LLM or agent — every row is flat JSON with a type field; see the agent section below.

FAQ

Why did my keyword search return nothing?

Bluesky rate-limits keyword post search per IP and answers with a 403. Roughly a third of heavy search runs hit it. The run reports this in its status message rather than failing silently. Wait a few minutes and re-run — the cooldown lengthens each time it is tripped, so retrying immediately makes it worse. Profile, follower and following inputs are not rate-limited and always work.

Why did I get about 100 posts when I asked for more?

You are sorting by top. Bluesky serves logged-out relevance search a single page and refuses to page further. Switch sort to latest, which is time-ordered and lets the Actor walk backwards through time for far more.

Why does a post in my results have someone else's handle?

That row is a repost. authorHandle is always whoever wrote the post; isRepost and repostedBy tell you which account amplified it into the feed you asked for.

Do I need a Bluesky account or app password?

No. Everything runs logged out against the public AT Protocol appview.

Notes & limitations

  • sort: "top" is capped at one page (~100). Relevance order cannot be walked through time. Use latest for volume.
  • Keyword post search is rate-limited per IP by Bluesky, which answers with a 403 and a cooldown that lengthens each time it is tripped. Roughly a third of heavy search runs hit it. The Actor rotates proxy IPs, backs off, keeps whatever it already collected, and tells you plainly when a run was cut short. Profiles, followers, following, post threads and account search are not rate-limited.
  • A run that returns nothing is reported as FAILED on purpose, with the reason named. It is not silently passed off as success.
  • Large accounts have millions of followers, so maxFollowsPerProfile bounds how much of one list a single run pulls.
  • Engagement counts are a snapshot at scrape time.

🤖 For AI Agents & LLM Apps

Purpose. Reads public Bluesky (AT Protocol) data: posts by keyword, profiles, follower/following lists, single posts. No authentication anywhere.

Minimal tested input.

{ "searchQueries": ["apify"], "searchType": "posts", "sort": "latest", "maxItems": 50 }

Output fields (flat). type, of, uri, cid, text, langs, images, likeCount, repostCount, replyCount, quoteCount, bookmarkCount, authorDid, authorHandle, authorDisplayName, isRepost, repostedBy, repostedAt, did, handle, displayName, description, followersCount, followsCount, postsCount, avatar, banner, createdAt, indexedAt, url.

Billing. $0.005 per run start (per GB), $0.0015 per row. Bound cost with maxItems.

Behaviours an agent should plan for.

  • Branch on type — one dataset mixes posts, profiles and network rows.
  • sort: "top" returns at most ~100 rows regardless of maxItems. Use "latest" when you need more.
  • Post search can be rate-limited; a short run still succeeds and reports the shortfall in the run status message and in the RUN_REPORT key-value record ({ emitted, maxItems, requestedMaxItems, sources: [{ source, kind, requested, delivered, stop, detail }] }). Check it rather than assuming maxItems rows arrived.
  • authorHandle is always the author. For reposts read repostedBy to learn who amplified it.
  • A zero-row run exits FAILED with the cause in the status message.

⚠️ Disclaimer

This Actor collects only public Bluesky data through the public AT Protocol appview. It does not log in, does not bypass any login wall, and does not circumvent access controls. You are responsible for using the data in line with Bluesky's Terms of Service, applicable data-protection law (GDPR/CCPA where relevant), and any restrictions on personal data in your jurisdiction.

Support

SEO Keywords

bluesky scraper, bluesky api, at protocol scraper, bluesky posts scraper, bluesky profile scraper, bluesky followers export, bluesky search api, scrape bluesky without login, bsky scraper, bluesky data extraction, bluesky keyword search, bluesky brand monitoring, bluesky lead generation, atproto scraper, bluesky social listening, export bluesky followers to csv, bluesky hashtag scraper, bluesky mentions tracker