Bluesky Scraper โ Posts, Profiles & Followers, No Login
Pricing
from $0.55 / 1,000 posts
Bluesky Scraper โ Posts, Profiles & Followers, No Login
Scrape Bluesky without login for brand monitoring and audience research: search posts, account posts, profiles, followers and following lists, post likers and reposters, people search, reply threads and custom feeds, as clean JSON.
Pricing
from $0.55 / 1,000 posts
Rating
0.0
(0)
Developer
Cliqto Media
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
0
Monthly active users
14 days ago
Last modified
Categories
Features
Share
Bluesky Scraper reads public Bluesky data with no login: search posts, account posts, profiles, followers and following lists, who liked or reposted a post, people search, reply threads and custom feeds, returned as clean JSON rows.
Use it for social listening, audience research, lead lists or AI pipelines: the smallest run is one search query, such as apify, which returns up to 30 recent posts in a few seconds for about $0.03.

Table of contents
- What it does
- How it works
- Quick start
- Use cases
- Input
- Output
- Pricing
- Limits
- API, export and integrations
- Troubleshooting
- FAQ
- Related Actors
- Support
What it does
Bluesky Scraper reads the public Bluesky AppView API (AT Protocol) without an account, a password, cookies or proxies. It has ten modes:
| Mode | You give | You get |
|---|---|---|
searchPosts | Keywords, #hashtags, phrases, operators like from:bsky.app, lang:en, domain:github.com | Up to 100 matching posts per query, latest or top |
authorFeed | Handles, DIDs or profile links | Posts from each account, newest first, with optional replies and reposts |
profiles | Handles, DIDs or profile links | One profile row per account: bio, follower/following/post counts, avatar, pinned post |
thread | Post links or at:// URIs | The post, its parent posts and the replies below it, with threadRole and threadDepth |
feed | Custom feed links or list links | Posts from the feed or list |
followers | Handles, DIDs or profile links | The accounts that follow each account |
following | Handles, DIDs or profile links | The accounts each account follows |
postLikers | Post links or at:// URIs | The accounts that liked each post, with the like time |
postReposters | Post links or at:// URIs | The accounts that reposted each post |
searchProfiles | A name or keyword | Bluesky accounts that match it |
An account, post or feed that does not exist returns a not_found row instead of silently disappearing, so the number of answers always matches your input.
Good fit
- Track what people post about a brand, product, hashtag or domain on Bluesky.
- Export the followers of an account, or the people who liked or reposted a post, for audience research or lead lists.
- Collect the posts and profiles of a list of accounts, or pull a full reply thread for discussion analysis.
- Feed clean, typed Bluesky rows into an AI agent, a spreadsheet or a database.
How it works
- You choose a mode and give search queries, account handles, post links or feed links.
- The Actor reads the public Bluesky API page by page, at most four requests per second, until it reaches your
maxItemsor the end of the list. It does not log in and does not get around any Bluesky limit. - It saves one row per post, profile or account in the Dataset, a
not_found,invalid_inputorerrorrow for every value that produced no data, and a run summary (RUN_SUMMARY) with a status for each input value.
Quick start
-
Open the Input tab. Keep What to scrape on Search posts (box 1 in the picture).
-
Add a word to Search queries, e.g.
apify(box 2). -
Keep Max results per query, account, post or feed at the prefilled
30(box 3; the runtime default is100). -
Click Start at the bottom of the page (on your own copy of the form the button may read Save & start).
-
Open the Posts view in the Output tab (box 1) and check the post text (box 2), the date and the likes (box 3). The author handle is under All fields or in the JSON.
Expected result: up to 30 of the newest posts that match the query, one row per post.
Use cases
| Buyer job | Input path | Output entity | Key fields | Next step |
|---|---|---|---|---|
| Brand or keyword listening | mode: searchPosts, queries | post | text, createdAt, likeCount, author.handle, links | Schedule the Actor, export to Sheets or a database |
| Audience research | mode: followers, handles | account | handle, displayName, description, createdAt | Enrich with profiles mode, CRM import |
| Engagement and topic accounts | mode: postLikers, postReposters (postUrls) or searchProfiles (queries) | account | handle, displayName, likedAt, description | Contest draws, influencer and outreach lists |
| Account and influencer research | mode: authorFeed or profiles, handles | post, profile | text, isRepost, followersCount, postsCount | Compare accounts, CRM import |
| Discussion and feed analysis | mode: thread (postUrls) or feed (feedUrls) | post | threadRole, threadDepth, text, hashtags | Topic analysis, AI summarisation |
Input
Input fields
| Field | Plain name | Type | Required | Default / prefill | Allowed values | Effect | Example |
|---|---|---|---|---|---|---|---|
mode | What to scrape | string | yes | searchPosts | the ten modes above | Picks the list that is read | "followers" |
queries | Search queries | string[] | for searchPosts, searchProfiles | prefill ["apify"] | up to 100 | One search per line | ["#bluesky", "from:bsky.app lang:en"] |
handles | Handles | string[] | for authorFeed, profiles, followers, following | none | handle, @handle, DID, profile link; up to 1,000 for profiles, 100 otherwise | Accounts to read; a bare name becomes name.bsky.social | ["bsky.app", "did:plc:z72i7hdynmk6r22z27h6tvur"] |
postUrls | Post links | string[] | for thread, postLikers, postReposters | none | bsky.app post links or at:// URIs; up to 100 | Posts to read | ["https://bsky.app/profile/bsky.app/post/3l6oveex3ii2l"] |
feedUrls | Feed or list links | string[] | for feed | none | โฆ/feed/โฆ or โฆ/lists/โฆ links; up to 50 | Feeds or lists to read | ["https://bsky.app/profile/bsky.app/feed/whats-hot"] |
maxItems | Max results per query, account, post or feed | integer | no | 100, prefill 30 | 1โ5,000 | Cap of posts or accounts for each input value; ignored in profiles | 500 |
since | From date | string | no | none | YYYY-MM-DD or ISO date-time, UTC | Post modes: only posts created on or after it | "2026-10-01" |
until | To date | string | no | none | same; must be after since | Post modes: only posts created before it | "2026-10-08" |
searchSort | Search order | string | no | latest | latest, top | Order of post search results | "top" |
language | Post language | string | no | all | language code, e.g. en, pt-BR | Post search only | "en" |
includeReplies | Include replies | boolean | no | true | Drop replies in search and account posts when off | false | |
includeReposts | Include reposts | boolean | no | false | Account posts and feeds: also return reposts, marked isRepost | true | |
threadDepth | Reply depth | integer | no | 10 | 0โ100 | Reply levels below the post; 0 = post and parents only | 3 |
Lines pasted with several values are split, and duplicates are removed. A value that cannot be read (e.g. not a handle!) returns an invalid_input row, and the other values are still processed. A wrong field combination, such as followers with no handles or since after until, stops the run at once with a clear message and no rows.
Search operators
Post search accepts Bluesky's own operators inside the query text. Checked live on 2026-10-28:
| Operator | Example | Finds |
|---|---|---|
from: | from:bsky.app | Posts by one account |
mentions: | mentions:bsky.app | Posts that mention an account (plural mentions; mention: finds nothing) |
domain: | domain:github.com | Posts that link to a site |
lang: | lang:en | Posts in a language |
#tag | #opendata | Posts with a hashtag |
since: | since:2026-10-01 | Posts from a date |
"โฆ" | "open data" | An exact phrase |
-word | apify -job | Leave out posts with a word |
Operators can be combined: from:bsky.app lang:en #update.
Interacting settings
| When | Input A | Input B | Effective behavior |
|---|---|---|---|
| Search with a lone hashtag and dates | queries: ["#ai"] | since / until | Sent to Bluesky as a tag search, which is the only form it accepts with dates |
| Account posts with dates | handles | since | Reading stops once the feed reaches posts older than since |
| Account posts with strict filters | includeReplies: false, since / until | โ | If fewer than 1 in 100 feed items match after 1,000 items, the account is stopped as partial with a warning. Use a search with from:handle instead |
| Post search | maxItems | โ | Search never returns more than 100 posts per query, whatever maxItems says |
| Account lists | followers, following, postLikers, postReposters, searchProfiles | since, until, language, includeReplies, includeReposts, searchSort | Post filters are ignored; maxItems caps the accounts per input |
Mode names from other Bluesky scrapers
| Other Actor | This Actor |
|---|---|
mode: search-posts | mode: searchPosts |
mode: posts | mode: authorFeed |
mode: profiles | mode: profiles |
mode: threads / post-urls | mode: thread |
mode: followers / following | mode: followers / following |
mode: search-people / search-users | mode: searchProfiles |
likers, reposters | mode: postLikers, postReposters |
sort | searchSort |
maxResults | maxItems |
Input examples
Search posts about a topic
{"mode": "searchPosts","queries": ["apify", "#webscraping"],"maxItems": 50,"searchSort": "latest","language": "en"}
Expected output: up to 50 post rows per query with text, author, likeCount, links, hashtags.
Followers of an account
{"mode": "followers","handles": ["bsky.app"],"maxItems": 500}
Expected output: up to 500 account rows with relation: "follower", handle, displayName, description.
Who liked a post
{"mode": "postLikers","postUrls": ["https://bsky.app/profile/bsky.app/post/3l6oveex3ii2l"],"maxItems": 200}
Expected output: up to 200 account rows with relation: "liker" and likedAt.
Recent posts from accounts
{"mode": "authorFeed","handles": ["bsky.app", "@jay.bsky.team"],"since": "2026-10-01","includeReposts": true,"maxItems": 200}
Profiles
{"mode": "profiles","handles": ["bsky.app", "jay.bsky.team", "https://bsky.app/profile/pfrazee.com"]}
A reply thread
{"mode": "thread","postUrls": ["https://bsky.app/profile/bsky.app/post/3l6oveex3ii2l"],"threadDepth": 3,"maxItems": 200}
Output
Every row goes to the default Dataset and has recordType: post, profile, account or status. The Output tab has Posts, Profiles, Accounts and Not found & errors views. Views choose columns, not rows: status rows appear in every view with empty data columns. To get only one type, filter by recordType in your export or code. The run summary is in the key-value store as RUN_SUMMARY.
Output entities and surfaces
| Situation | Where | Result shape | What to do |
|---|---|---|---|
| Normal result | Dataset, Posts / Profiles / Accounts views | post, profile or account row | Open, export or read through the API |
| Nothing found | Dataset, Not found & errors view | status row with not_found | Check the handle, link or query |
| Search stopped at 100 posts, or time ran out | RUN_SUMMARY in the key-value store | partial input with a message | Narrow the query (dates, from:) or raise the run timeout |
| Bluesky error after retries | Dataset status row with error, plus RUN_SUMMARY | Error code and message | Run again later; contact support if it repeats |
Post fields
| Field | Meaning | Type | Can be empty? |
|---|---|---|---|
uri, cid | AT Protocol identifiers of the post | string | cid can be null |
url | bsky.app link to the post | string | rarely null |
author | did, handle, displayName, avatar, url | object | name and avatar can be null |
text | Post text | string | empty for image-only posts |
createdAt, indexedAt | Created (by the author's record) and indexed by Bluesky, ISO 8601 | string | can be null |
langs | Declared languages | string[] | often empty |
replyCount, repostCount, likeCount, quoteCount, bookmarkCount | Counts at fetch time | number | null when Bluesky does not report it (never a fake 0) |
isReply, replyTo, rootPost | Reply status, parent and root post | boolean, object | objects are null for top-level posts |
isRepost, repostedBy, repostedAt | Repost marker in account posts and feeds | boolean, object, string | null when not a repost |
isPinned | Pinned on the author's profile | boolean | |
isQuote, quotedPost | Quoted post: uri, url, authorHandle, text | boolean, object | null when not a quote |
embedType | images, video, external, record, recordWithMedia | string | null without an embed |
images | url, thumbnailUrl, alt, width, height | object[] | empty without images |
video | playlistUrl, thumbnailUrl, alt | object | null without a video |
externalLink | Link card: url, title, description | object | null without a card |
links | All links in the text and the link card | string[] | |
hashtags, mentions | Tags and mentioned accounts (did, handle) | array | |
labels | Moderation labels | string[] | |
threadRole, threadDepth | anchor, parent or reply, and level (thread mode) | string, number | null outside thread mode |
mode, input, fetchedAt, source | Which input produced the row and when | string |
Profile fields
did, handle, displayName, description, url, avatar, banner, followersCount, followsCount, postsCount, listsCount, feedsCount, starterPacksCount, isLabeler, verifiedStatus, pinnedPostUrl, labels, createdAt, indexedAt, plus mode, input, fetchedAt, source.
Account fields
Rows from followers, following, postLikers, postReposters and searchProfiles. Bluesky lists do not carry follower or post counts; run the handles through profiles mode when you need them.
| Field | Meaning | Type | Can be empty? |
|---|---|---|---|
relation | follower, following, liker, reposter or search_match | string | no |
position | Place in the list as Bluesky returned it, from 1 | number | no |
did, handle | Account identifiers | string | no |
displayName, description | Name and bio | string | null when not set |
url, avatar | Profile link and avatar image | string | avatar can be null |
verifiedStatus | Bluesky verification status | string | null when not verified |
labels | Moderation labels | string[] | |
createdAt, indexedAt | When the account was created and indexed | string | can be null |
likedAt | When the like was made (postLikers only) | string | null in other modes |
mode, input, fetchedAt, source | Which input produced the row and when | string |
Field names from other Bluesky scrapers
| Other Actor | This Actor |
|---|---|
authorHandle, authorName, authorDid, authorAvatar | author.handle, author.displayName, author.did, author.avatar |
lang | langs |
mediaUrls, hasMedia | images[].url, video.playlistUrl, embedType |
embedUrl, embedTitle | externalLink.url, externalLink.title (also in links) |
scrapedAt | fetchedAt |
Status rows
status | When | errorCode examples |
|---|---|---|
not_found | The account, post, feed or list does not exist | NOT_FOUND |
invalid_input | The value cannot be read as a handle, link or query | INVALID_INPUT |
error | Bluesky failed or refused the request after retries | RATE_LIMITED, SOURCE_FORBIDDEN, SOURCE_UNAVAILABLE |
A source failure is never shown as "no results": it is an error row and the run status says so.
Run summary (RUN_SUMMARY)
| Field | Meaning |
|---|---|
status | COMPLETE, PARTIAL, NO_RESULTS, CAPPED, ERROR, INVALID_INPUT or CANCELLED |
errorCode | Set when the whole run failed for one reason, e.g. INVALID_INPUT or RUN_TIMEOUT_TOO_SHORT; otherwise null |
message | One plain-English sentence about the result |
counts | posts, profiles, accounts, notFound, errors, invalidInputs, duplicatesSkipped, chargedPosts, chargedProfiles, chargedAccounts, chargedEmptyResults, unprocessedInputs |
inputs | One entry per input value (input, status, items, message): complete, partial, no_results, not_found, error, invalid_input or skipped, with the row count |
warnings | Plain-English notes, e.g. the 100-per-query search limit |
Output example
A fictional post, trimmed to the main fields:
{"recordType": "post","status": "complete","mode": "searchPosts","input": "opendata","uri": "at://did:plc:aaaaaaaaaaaaaaaaaaaaaaaa/app.bsky.feed.post/3mexample2abc","url": "https://bsky.app/profile/author.example/post/3mexample2abc","author": {"did": "did:plc:aaaaaaaaaaaaaaaaaaaaaaaa","handle": "author.example","displayName": "Sample Author","url": "https://bsky.app/profile/author.example"},"text": "Our open dataset is live! Thanks @team.example #opendata https://data.example/downloads","createdAt": "2026-10-12T09:15:00.000Z","likeCount": 2431,"repostCount": 318,"replyCount": 154,"isReply": false,"isRepost": false,"embedType": "images","images": [{ "url": "https://cdn.bsky.app/img/feed_fullsize/plain/did:plc:aaaaaaaaaaaaaaaaaaaaaaaa/bafkexample@jpeg", "alt": "Chart of monthly downloads" }],"links": ["https://data.example/downloads"],"hashtags": ["opendata"],"mentions": [{ "did": "did:plc:bbbbbbbbbbbbbbbbbbbbbbbb", "handle": "team.example" }],"quotedPost": null,"threadRole": null,"fetchedAt": "2026-10-13T01:52:14.278Z","source": "Bluesky AppView API (AT Protocol)"}
A fictional account row from followers mode:
{"recordType": "account","status": "complete","mode": "followers","input": "author.example","relation": "follower","position": 1,"did": "did:plc:cccccccccccccccccccccccc","handle": "reader.example","displayName": "Sample Reader","description": "Data journalist. Open data, maps, charts.","url": "https://bsky.app/profile/reader.example","verifiedStatus": null,"createdAt": "2024-11-20T08:00:00.000Z","likedAt": null,"fetchedAt": "2026-10-28T10:00:00.000Z","source": "Bluesky AppView API (AT Protocol)"}
null means Bluesky did not provide the value; it never stands for zero.
Pricing
Pricing unit
You pay per event. Platform usage (compute, storage) is included in the price.
| Event | Free | Bronze | Silver | Gold | Platinum | Diamond |
|---|---|---|---|---|---|---|
| Post row | $0.0009 | $0.00085 | $0.0007 | $0.00055 | $0.00055 | $0.00055 |
| Profile row | $0.0027 | $0.0026 | $0.0021 | $0.0016 | $0.0016 | $0.0016 |
| Account row | $0.0009 | $0.0009 | $0.0007 | $0.00055 | $0.00055 | $0.00055 |
| Empty result | $0.0005 | $0.0005 | $0.0005 | $0.0005 | $0.0005 | $0.0005 |
| Actor start | $0.0045 | $0.0045 | $0.0045 | $0.0045 | $0.0045 | $0.0045 |
- Post, profile or account row: one per data row written.
- Empty result: one per input value that produced no data: a search with no matches, an account with no matching posts or no followers, a profile, post or feed that does not exist, or a value that cannot be read (for example a typo in a link). If every value is unreadable, the run ends as
NO_RESULTSwith a message saying so, and each value is one empty result. It covers the requests made for that value. - Actor start: charged by Apify once per run, per GB of memory (the default 256 MB counts as one).
- Free:
errorrows (Bluesky failed), skipped inputs, duplicate rows and duplicate input values (the same account, post or feed written another way, e.g.@Name.bsky.socialand its profile link).
A run whose input is rejected before any request (e.g. profiles without handles) costs only the start fee.
Cost examples (Free plan prices)
| Scenario | Input | Charged events | Cost |
|---|---|---|---|
| Prefill search | 1 query, 30 posts | start + 30 posts | about $0.03 |
| 1,000 posts from accounts | 2 accounts ร 500 | start + 1,000 posts | about $0.90 |
| 1,000 followers | 1 account, maxItems: 1000 | start + 1,000 accounts | about $0.90 |
| 100 profiles | 100 handles | start + 100 profiles | about $0.27 |
| Search with no matches | 1 query | start + 1 empty result | $0.005 |
Spending limit
Set Maximum cost per run in the run options; the smallest allowed value is $0.0072, enough for the start and one row of any type. The Actor checks the limit before every row; when the next row would exceed it, the run stops with status CAPPED and keeps everything written so far.
Limits
- No private data: no DMs, no muted or blocked lists, nothing that needs login. DMs will never be supported.
- Post search: up to 100 posts per query, because Bluesky serves further search pages only to logged-in clients. The Actor does not try to get around this. Split the topic into narrower queries or date ranges to see more.
- People search: Bluesky returns only the best matches for a name or keyword, often fewer than
maxItems. - Follower, following, liker and reposter lists: up to 5,000 accounts per input value (
maxItems), read in pages of 100. An input that reachesmaxItemswhile Bluesky has more iscomplete, and its message inRUN_SUMMARYsays that the list goes on. - Account posts: up to 10,000 feed items scanned per account. A scan also stops early when fewer than 1 in 100 items match the filters after the first 1,000 items, so a sparse filter does not read the whole history. Feeds use the same sparse stop.
- Speed at 256 MB (tests on 2026-10-28): one account's 1,075 posts in about 7 s (build 0.1.9); 10,000 posts from two accounts (5,000 each) in about 1.5 minutes; 5,000 followers in about 17 s. Large jobs need a longer run timeout than the default 300 s; an input cut by the timeout is marked
partialwith its row count. since/untilcompare the post'screatedAt(for reposts,repostedAt). Account feeds are ordered by when Bluesky indexed the post, so posts imported later with an older date can be outside the scanned range.- Counts (likes, reposts, followers) are a snapshot at fetch time.
API, export and integrations
- API: start a run with
POST https://api.apify.com/v2/acts/cliqtomedia~bluesky-scraper/runsand the JSON input above; read rows from the run's default Dataset. - Export: the Dataset downloads as JSON, CSV, Excel, XML or RSS from the Output tab or the API.
- Scheduling: use Apify Schedules, e.g. a daily search with
sinceset to yesterday. - Webhooks / no-code: Make, Zapier, n8n, Google Sheets and webhooks connect through the Apify integrations tab; the next system reads
url,textorhandlefrom each row.
Use with AI agents (MCP)
- MCP server with only this Actor:
https://mcp.apify.com?tools=cliqtomedia/bluesky-scraper. - Smallest useful input:
{"mode": "searchPosts", "queries": ["apify"], "maxItems": 30}. - Key fields:
text(post text),author.handle(who posted),createdAt(when),likeCount(engagement),url(link to cite). Account rows addrelationandhandle. - A small search finishes in a few seconds and costs about $0.03. At most 100 posts per search query;
not_foundanderrorrows explain missing data.
Troubleshooting
Search stops at 100 posts
Symptom: you set maxItems above 100, a query returns 100 posts, its entry in inputs is partial and there is a warning about 100 posts per query. With maxItems of 100 or less the query is complete.
Reason: Bluesky serves only the first search page without login.
Action: add operators (lang:en, from:, domain:) or a narrower since / until.
An account returns no posts or no followers
Check the account's entry in inputs in RUN_SUMMARY. not_found means the handle does not exist. no_results means the filters matched nothing or the list is empty; partial with the sparse-scan warning means the filters matched fewer than 1 in 100 items. Loosen since / until, turn on replies or reposts, or use search with from:handle.
Followers stop before the full list
Symptom: the input's message in RUN_SUMMARY says "Stopped at maxItems". Action: raise maxItems (up to 5,000) and the run timeout.
The run ends as PARTIAL with skipped inputs
The run hit its timeout. Raise the timeout in the run options or split the input into smaller runs.
The run fails with RUN_TIMEOUT_TOO_SHORT
The run timeout leaves no time to fetch data and save the summary. Set the timeout to at least 60 seconds; the default 300 seconds is enough for small runs. Nothing is charged except the start.
Rows with error status
Bluesky failed or refused the request after retries (RATE_LIMITED, SOURCE_FORBIDDEN, SOURCE_UNAVAILABLE). These rows are free; run the failed values again later.
FAQ
Do I need a Bluesky account or app password?
No. The Actor reads only public data from the public Bluesky API and never asks for login.
Can I export the followers of a Bluesky account?
Yes. Use followers mode with the handle and set maxItems up to 5,000 per account. following mode returns the accounts it follows.
Why do account rows have no follower counts?
Bluesky's follower and like lists return only basic account data. Put the handles into profiles mode to get followersCount, postsCount and the rest.
Why is search limited to 100 posts per query?
Bluesky serves further search pages only to logged-in clients. The Actor respects that and tells you in the run summary when a query hit the limit.
Are the counts live?
They are a snapshot at fetchedAt. Run again to refresh them.
Is this data complete?
It is what the Bluesky API returns at fetch time. Deleted posts, blocked content and posts hidden by moderation are not included, and counts can be null when Bluesky does not report them.
Is scraping Bluesky allowed?
The Actor uses only public endpoints at a modest request rate and does not bypass any limit. You are responsible for how you store and reuse the content, including personal data and authors' rights. Sources: the Bluesky API directory, the search API reference and the Bluesky Terms of Service.
Related Actors
- Threads Scraper โ the same posts, profiles and search for Threads.
- Reddit Scraper โ posts, comments and search from Reddit discussions.
- Truth Social Scraper โ posts and profiles from Truth Social, no login.
- Telegram Channel Scraper โ posts and views from public Telegram channels.
- Hacker News Scraper โ stories and comments from Hacker News.
Support
Use the Issues tab of this Actor for bugs, questions and feature requests. Include the run ID, the mode and the error text. Never post tokens, cookies or private data.

