Bluesky Scraper - Posts, Profiles, Threads, Followers avatar

Bluesky Scraper - Posts, Profiles, Threads, Followers

Pricing

from $2.00 / 1,000 results

Go to Apify Store
Bluesky Scraper - Posts, Profiles, Threads, Followers

Bluesky Scraper - Posts, Profiles, Threads, Followers

Scrape Bluesky (bsky.app) without login: keyword post search (latest/top, date, language, author, hashtag, domain filters), author feeds with engagement counts and media, profiles, full reply threads, followers, follows and people search. Pure HTTP via the public AT Protocol API.

Pricing

from $2.00 / 1,000 results

Rating

0.0

(0)

Developer

Thirdwatch

Thirdwatch

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

4 hours ago

Last modified

Categories

Share

What you get

Scrape Bluesky (bsky.app) without an account, an app password or a browser. This Actor talks to the public AT Protocol AppView over plain HTTP, so it is fast, cheap and does not risk any login of yours. One Actor covers seven jobs:

ModeYou giveYou get
searchPostskeywords (+ your app password)posts matching the query, newest-first or ranked by engagement, with date / language / author / hashtag / domain filters
authorFeedhandlesan account's posts with like, repost, reply, quote and bookmark counts, media, quoted posts and a repost flag
profilehandles or DIDsone profile record per account: bio, follower / following / post counts, avatar, banner, verification
threadpost URLsthe post plus every reply below it, flattened with a depth field
followershandleswho follows the account
followshandleswho the account follows
searchActorskeywordspeople search: profiles whose name, handle or bio matches

It accepts plain handles (bsky.app), @handles, DIDs (did:plc:...), at:// URIs and any bsky.app URL (profile, post, followers, follows, hashtag, search) and routes each one to the right mode automatically.

One exception to "no login": in 2026 Bluesky switched off post search for logged-out clients (bsky.app itself shows "Search is currently unavailable when logged out"). Six of the seven modes above are fully public. searchPosts alone needs a Bluesky app password of your own, passed in the optional identifier / appPassword fields; the Actor opens a session on your account's PDS and runs the search through it. Never use your main password - create an app password under Settings -> Privacy and security -> App passwords and revoke it whenever you like.

Pricing is $0.50 per 1,000 records on the free plan and drops on paid Apify plans. There is no platform fee on top; you pay only for what lands in the dataset.

Why scrape Bluesky?

Bluesky has grown into one of the largest open social networks, and unlike most platforms its data model (AT Protocol) is designed to be read publicly. That makes it the easiest place to get real, timestamped, engagement-scored social data for:

  • Brand and keyword monitoring - track mentions of your product, competitor or campaign hashtag as they happen with searchPosts sorted by latest.
  • Audience research - pull an account's followers or the accounts it follows, then profile them.
  • Influencer discovery - searchActors for a niche, then profile the shortlist to compare follower counts and posting volume.
  • Content analysis - export an author's full feed with engagement counts to see what performs.
  • Conversation mining - pull a viral post's whole reply thread with thread for sentiment or Q&A datasets.
  • Academic and journalism datasets - reproducible keyword pulls with since / until windows and language filters.

Input

All fields are optional; give at least one of queries, handles or postUrls.

FieldTypeDefaultNotes
modeselectautoauto derives the mode from your inputs: postUrls -> thread, queries -> searchPosts, handles -> authorFeed. Set profile, followers, follows or searchActors explicitly.
querieslist of stringsKeywords for searchPosts / searchActors. Inline operators work: from:handle, mentions:handle, lang:en, since:2026-01-01, #tag, domain:example.com.
handleslist of stringsAccounts for authorFeed / profile / followers / follows. Handle, @handle, DID or profile URL.
postUrlslist of stringsPosts for thread. https://bsky.app/profile/{handle}/post/{id} or at:// URI. Profile / followers / follows / hashtag / search URLs are accepted here too and routed by shape.
maxItemsinteger100Cap per keyword / handle / post URL (1 - 10,000). profile mode always returns one record per handle.
sortlatest / toplatestsearchPosts ordering.
since, untildatesearchPosts window. YYYY-MM-DD or full ISO 8601.
languagestringsearchPosts. BCP-47 code such as en, pt, ja.
authorstringsearchPosts. Only posts by this handle / DID.
mentionsstringsearchPosts. Only posts mentioning this handle / DID.
hashtagstringsearchPosts. Only posts carrying this tag.
domainstringsearchPosts. Only posts linking to this domain.
identifierstringsearchPosts only. Your Bluesky handle or email.
appPasswordsecret stringsearchPosts only. An app password for that account (encrypted at rest by Apify).
includeRepliesbooleanfalseauthorFeed. Include the account's replies to other people.
includeRepostsbooleantrueauthorFeed. Include reposts (flagged is_repost: true).
threadDepthinteger6thread. Reply levels to collect (1 = direct replies only, max 100).
minLikes, minReposts, minRepliesinteger0Drop posts / replies below these counts. Applied locally after fetching.

Example: keyword search, English, last week, top posts

{
"queries": ["open source AI", "#rustlang"],
"sort": "top",
"since": "2026-09-01",
"language": "en",
"maxItems": 200,
"identifier": "you.bsky.social",
"appPassword": "xxxx-xxxx-xxxx-xxxx"
}

Example: an account's posts plus its followers

{
"handles": ["bsky.app"],
"postUrls": ["https://bsky.app/profile/bsky.app/followers"],
"maxItems": 500,
"includeReposts": false
}

Example: full reply thread of a post

{
"postUrls": ["https://bsky.app/profile/cee.wtf/post/3muplmkx56s2k"],
"threadDepth": 10,
"maxItems": 2000
}

Example: profiles for a list of handles

{
"mode": "profile",
"handles": ["bsky.app", "jay.bsky.team", "did:plc:z72i7hdynmk6r22z27h6tvur"]
}

Output

Every record has a kind discriminator (post, reply, profile, follower, follow), a query field naming the input that produced it, and scraped_at. Records are deduplicated by post URI or account DID across the whole run.

Post and reply records

{
"kind": "post",
"uri": "at://did:plc:xb2urvqt5f4zzccjs46hysbf/app.bsky.feed.post/3muplmkx56s2k",
"cid": "bafyreidu2rxt55wzm3wmadbp6fzfrik6idvmh34qk23gwsssx6fqdgxbqm",
"id": "3muplmkx56s2k",
"url": "https://bsky.app/profile/cee.wtf/post/3muplmkx56s2k",
"text": "throng.cee.wtf i've invented a new way to browse bluesky",
"created_at": "2026-09-04T18:25:36.591Z",
"indexed_at": "2026-09-04T18:26:09.958Z",
"langs": ["en"],
"author_did": "did:plc:xb2urvqt5f4zzccjs46hysbf",
"author_handle": "cee.wtf",
"author_display_name": "cee",
"author_avatar": "https://cdn.bsky.app/img/avatar/plain/...",
"author_verified": false,
"author_url": "https://bsky.app/profile/cee.wtf",
"like_count": 7838,
"repost_count": 1856,
"reply_count": 339,
"quote_count": 622,
"bookmark_count": 1492,
"is_reply": false,
"reply_parent_uri": null,
"reply_root_uri": null,
"is_repost": false,
"reposted_by_did": null,
"reposted_by_handle": null,
"is_pinned": false,
"links": ["https://throng.cee.wtf"],
"mentions": [],
"tags": [],
"labels": [],
"embed_type": "video",
"images": [],
"video": {
"playlist": "https://video.bsky.app/watch/.../playlist.m3u8",
"thumbnail": "https://video.bsky.app/watch/.../thumbnail.jpg",
"alt": "",
"width": 1874,
"height": 1298
},
"external": null,
"quoted_post": null,
"query": "https://bsky.app/profile/cee.wtf/post/3muplmkx56s2k",
"scraped_at": "2026-09-07T17:38:52+00:00"
}

Field notes:

  • embed_type is one of images, video, external (link card), record (quote post), recordWithMedia (quote + media), gallery, or null.
  • images is a list of {thumb, fullsize, alt, width, height}. external is {uri, title, description, thumb}.
  • quoted_post carries the quoted post's uri, url, author, text, created_at and its own engagement counts, or {"uri": ..., "unavailable": "viewNotFound" | "viewBlocked" | "viewDetached"} when it cannot be shown.
  • links, mentions (DIDs) and tags are parsed from the post's rich-text facets, so links are the real targets rather than truncated display text.
  • is_repost / reposted_by_handle are set on authorFeed entries the account reposted; the record itself is the original post by its original author.
  • Reply records from thread add source_post_uri, source_post_url, source_post_author_handle and depth (1 = direct reply to the post). The root post is included as kind: "post" with depth: 0.
  • labels are moderation labels applied to the post (for example !no-unauthenticated on the author).

Profile, follower and follow records

{
"kind": "profile",
"did": "did:plc:z72i7hdynmk6r22z27h6tvur",
"handle": "bsky.app",
"display_name": "Bluesky",
"description": "official Bluesky account ...",
"avatar": "https://cdn.bsky.app/img/avatar/plain/...",
"banner": "https://cdn.bsky.app/img/banner/plain/...",
"url": "https://bsky.app/profile/bsky.app",
"followers_count": 34817452,
"follows_count": 14,
"posts_count": 827,
"created_at": "2023-04-12T04:53:57.057Z",
"indexed_at": "2025-10-27T21:05:26.152Z",
"verified": true,
"is_labeler": false,
"lists_count": 17,
"feedgens_count": 7,
"starter_packs_count": 14,
"pinned_post_uri": "at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post/3l6oveex3ii2l",
"labels": [],
"query": "bsky.app",
"scraped_at": "2026-09-07T17:38:50+00:00"
}

follower and follow records share this shape and add subject_did, subject_handle and subject_display_name (the account whose graph you asked for). Note that the Bluesky graph endpoints return the compact profile view, so followers_count, follows_count, posts_count, banner and the *_count association fields are null on those records; run profile mode on the DIDs if you need the counts.

How it works

The Actor calls the unauthenticated public AppView (public.api.bsky.app) endpoints defined by the AT Protocol lexicons: app.bsky.feed.searchPosts, app.bsky.feed.getAuthorFeed, app.bsky.actor.getProfiles, app.bsky.feed.getPostThread, app.bsky.graph.getFollowers, app.bsky.graph.getFollows, app.bsky.actor.searchActors, and com.atproto.identity.resolveHandle to turn a handle in a post URL into the DID an at:// URI needs. Pages of up to 100 items are walked with the server cursor until maxItems is reached or the feed ends. Between pages the Actor pauses briefly to stay far below Bluesky's published limit of 3,000 requests per five minutes per IP, and it backs off on 429.

Because everything is a JSON API call, one run of 1,000 posts typically finishes in well under a minute on 256 MB of memory.

Zero results and failures

The Actor distinguishes between "Bluesky answered and there was nothing" and "Bluesky could not be reached":

  • A keyword with no matches, an account with no posts, or a deleted post produce an empty result for that input and the run still succeeds. The status message says so.
  • A searchPosts input without identifier / appPassword is reported as unreachable with an explanation; if it was the only thing you asked for, the run fails with that message rather than returning an empty dataset.
  • If an input is not a valid handle, DID or bsky.app URL it is skipped with a warning.
  • If the AppView rejects or never answers every input (network outage, edge refusal), the run is marked failed so you are not billed for, and do not silently accept, an empty dataset.

Limitations

  • Only public data is returned. Accounts that opted out of unauthenticated viewing (!no-unauthenticated) show the label and may return no posts.
  • searchPosts requires your own app password (see above) and is served by Bluesky's search index, which is not exhaustive for very old posts; use since / until windows for historical pulls. Bluesky rate-limits authenticated sessions per account as well as per IP.
  • Followers / follows are returned in the AppView's own order (most recent first). Very large graphs (millions) are cut by maxItems.
  • Like lists, feeds generators and starter-pack contents are not scraped by this Actor.
  • No sentiment or "content category" scoring is added; the output is the raw, complete data so you can run your own models on it.

Integrations

Use the Apify API or one of the Apify client libraries (Python, JavaScript) to run the Actor and read its dataset, or connect it to Zapier, Make, Google Sheets, Slack, Airbyte or a webhook. Schedules let you re-run a keyword pull hourly or daily to build a rolling archive.

Other social scrapers by Thirdwatch: Twitter/X, Reddit, TikTok, Instagram, Pinterest, YouTube.

This Actor only reads data that Bluesky serves publicly to anyone without an account, through the API the network was designed around. You are responsible for using the output in line with Bluesky's terms, applicable privacy law and the rights of the people whose posts you collect.

Use cases

  • Marketers monitoring social presence and engagement
  • Researchers building social-media datasets
  • Teams tracking public figures and trends

Last verified: 2026-09

More scrapers at thirdwatch.dev.