Bluesky Scraper — Posts, Profiles, Followers & Trends
Pricing
from $0.36 / 1,000 results
Bluesky Scraper — Posts, Profiles, Followers & Trends
Scrape Bluesky posts by keyword with no 100-post cap, plus account posts, profiles, followers, likes, reply threads, quotes, feeds, lists, starter packs and trending topics. No login. Export JSON, CSV, Excel.
Pricing
from $0.36 / 1,000 results
Rating
0.0
(0)
Developer
Nice Dev
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
0
Monthly active users
14 hours ago
Last modified
Categories
Share
🦋 What is Bluesky Scraper?
Bluesky Scraper extracts posts, profiles, followers, likes, reply threads, quotes, feeds, lists, starter packs and trending topics from Bluesky: text, likes, reposts, replies, quotes and bookmarks, images with their descriptions, videos, link cards, hashtags, mentions, and the author's followers — for brand monitoring, research, lead lists or social listening.
Type a keyword (climate, #ai, "open source"), an account (nytimes.com) or paste any Bluesky URL, click Start, and download the results in JSON, CSV or Excel. No Bluesky account, no app password, nothing to set up — and it is fast: 1,000 posts in under 3 minutes for $0.89, with no 100-post cap on searches.
📋 What data can you extract from Bluesky?
One item per result — a post, an account, a trending topic, a list or a starter pack — 125 fields:
| Category | What you get |
|---|---|
| 📝 Post | text, link, creation date, language — A new era of sleep tech tracks your brain waves… |
| 📊 Engagement | likes, reposts, replies, quotes, bookmarks and their total, and the engagement rate against the author's followers — 13 likes · 2 reposts · 1 quote · 7 bookmarks |
| 🖼️ Media and links | every image (full size, thumbnail, description), video, link card (title, description, picture), quoted post |
| #️⃣ Hashtags, mentions, links | hashtags, mentioned accounts, every link of the post |
| 🧵 Thread | reply or not, parent and root post (with their links and the handle of the account answered), depth in a reply thread, repost and pinned marks |
| 👤 Author | handle, name, avatar, verified check, pronouns, followers, follows, posts count, bio with the e-mail addresses and phone numbers written in it, website, banner |
| 🪪 Account | bio with the e-mail addresses and phone numbers written in it, pronouns, website, avatar, banner, followers, follows, posts, verified check, lists / feeds / starter packs created, pinned post, starter pack it joined by |
| 📈 Account activity (optional) | the account's latest post (date, text, link) and its posting statistics — posts per week, average and median likes, replies and reposts, its most liked post — 3.5 posts a week · median 12 likes |
| 🤖 AI (optional) | an AI sentiment label (positive, neutral or negative, with a score) and your own AI labels (the ones you wrote that apply to the post, with a score) — negative, 0.91 · pricing, 0.82 |
| 📁 Extras (optional) | a summary of each keyword (posts per day, average and median likes, top hashtags, top authors, top posts) and the contacts found on the websites of the authors — climate: 412 posts, median 3 likes · hello@acme-shop.fr |
| 📰 Feed | for the posts of a feed (or of the feeds an account created): the feed's name, description and creator |
| 📚 List / starter pack | name, description, purpose, number of members, last update and creator of a list or a starter pack — Community Showcase · 21 members |
| 🔥 Trending topic | title, category, post count, start time, rank, top accounts |
| 🔗 Where it comes from | the keyword, account or URL that found it, the post or account it relates to, when the account liked it |
Every field, with an example, is listed in the Output section below.
✅ Why use Bluesky Scraper?
- 🚀 Fast: 1,000 search results in under 3 minutes, 180 mixed results (accounts, followers, lists, feeds) in 3 seconds.
- 🔎 Searches with no 100-post cap: the search goes back in time page after page, as deep as Max results, with every Bluesky search filter (language, dates, author, mentions, website, hashtags, exact phrase).
- 🌐 The whole network, not only the search: the Network stream reads every post published on Bluesky in the last hours (up to 36) and keeps the ones that match your keywords.
- 🧩 All of Bluesky in one Actor: searches, accounts (profile, posts, followers, follows, liked posts, reposts, the feeds and starter packs they created), a post's reply thread, quotes, likers and reposters, custom feeds, lists, starter packs, trending topics with their posts — combined in one run.
- 🤖 What they think: tick AI sentiment label for a positive / neutral / negative label on each post, and write AI labels (
pricing,complaint: The author reports a problem) to see which of your topics each post is about. - 📊 A report per keyword: tick Keyword statistics and get, next to the dataset, one record that sums up each keyword you searched: how many posts and replies, posts per day, average and median likes, replies, reposts and quotes, median engagement rate, the top hashtags, authors and posts.
- 🔔 Monitoring built in: tick Only new results, schedule the Actor, and each run returns (and charges) only what it has never delivered before.
- 🔐 No login: no account, no app password to hand over.
- 🔌 API, scheduling, monitoring, integrations (Make, Zapier, n8n, Google Sheets…) and JSON/CSV/Excel export via the Apify platform.
🚀 How to scrape Bluesky
- Create a free Apify account.
- Open Bluesky Scraper and type a Search keyword (e.g.
climate), or add Accounts (e.g.nytimes.com). - Or paste Bluesky URLs into Bluesky URLs: a post, a profile, a custom feed, a list, a starter pack, a search or a hashtag page.
- Set Max results (100 by default, 0 = no limit) — and Max per source when you combine several sources — then click Start.
- Download the dataset in JSON, CSV, Excel or via API.
💰 How much does it cost to scrape Bluesky?
This Actor uses pay per event pricing: $0.89 per 1,000 results (posts, accounts, topics, lists or starter packs) — plus $0.0006 per run start (one per GB of memory: one at the default 512 MB, 6 cents per 100 runs). Higher Apify plans pay less per result: $0.72 (Bronze), $0.54 (Silver) and $0.36 (Gold) per 1,000. The Network stream adds $0.002 per hour of the network read (6 hours = $0.012; a started hour counts as a full one), whatever the number of posts it finds. The filters Bluesky does not apply itself (dates, language, the likes / reposts / replies / engagement minimums, mediaOnly, withLinksOnly, excludeKeywords, and minFollowers) are checked by the Actor on every post it reads, kept or not: each check costs $0.01143 per 1,000 posts checked ($0.01095 Bronze, $0.01048 Silver, $0.01 Gold) — filter-check, and detail-filter-check for minFollowers. Author profiles (includeAuthorProfiles, off by default: the author's followers, follows, posts count, bio, website and banner added to each post, and the counts and website added to each follower, follow, liker or list member) are charged apart, as the Author profile event: $0.19 per 1,000 rows completed ($0.18 Bronze, $0.17 Silver, $0.16 Gold); leave the option unticked to pay for the rows alone. Four more options are off by default and charged apart, each by its own event (see the Pricing tab for the amounts): a price per post given an AI sentiment label (post-sentiment; a post without text costs nothing), a price per post and per label judged with AI labels (ai-label; each label you write is charged once for each post it is judged on, whether it applies or not), a price per keyword summarised (Keyword statistics, keyword-stats; a keyword needs at least 5 posts in the run, at most 50 keywords are given), and a price per author whose website gives an e-mail or a phone number (Contacts from the author's website, website-contacts; a website that gives neither costs nothing). The two AI options need 2 GB of memory (Run options → Memory): with less, they are skipped, the log says so and nothing is charged. The engagement rate is free. Example: 20,000 posts ≈ $17.80; a daily watch of 300 new posts ≈ $0.27 a day. Platform usage (compute, proxy) is included in the price.
⚙️ Input
{"query": "climate","language": "en","maxItems": 500}
Several keywords and accounts, a cap per source, only recent posts, only the ones not delivered before:
{"searchQueries": ["climate", "#ai", "\"open source\""],"handles": ["nytimes.com"],"accountData": ["profile", "posts", "followers"],"maxItemsPerQuery": 200,"postedAfter": "7 days","onlyNew": true,"stateKey": "brand-watch"}
Or with Bluesky URLs:
{"startUrls": [{ "url": "https://bsky.app/profile/bsky.app/post/3l6oveex3ii2l" },{ "url": "https://bsky.app/starter-pack/emilyliu.me/3kvq2t2ctis2z" }],"postData": ["replies", "quotes", "likers"],"maxItems": 1000}
| Field | Notes |
|---|---|
query, searchQueries | Keyword, hashtag or phrase, as typed in the Bluesky search box (climate, #ai, "open source"); searchQueries adds more searches. |
sort | latest (no cap, goes back in time as deep as maxItems) or top (Bluesky's most engaged posts, 100 per search at most). |
searchEngine, streamHours | auto (search, network stream if Bluesky refuses the search), search, or stream: every post of the last streamHours hours (1-36) whose text has the keyword. |
handles | Accounts: a handle (nytimes.com, @user.bsky.social), a DID or a profile URL. |
accountData | What to collect for each account: profile, posts, followers, follows, likes, reposts, feeds (the posts of every feed the account created) starter-packs (the members of every starter pack it created, after the pack's own card) and lists (one card row for each list it made); each feed and pack has its own Max per source cap, up to 500 of each per account. |
authorFeedFilter, includeReposts | Which posts of an account: posts_with_replies, posts_no_replies, posts_with_media, posts_with_video, posts_and_author_threads; reposts kept or not. |
profileSearchQueries | Find accounts by name or bio keyword. |
includeLatestPost, includeAccountStats | Two extras on the profile row of each account of accountData (Profile) and of profileSearchQueries, off by default and included in the price of the row (one extra request per account): its latest own post (date, text, link), and its posting rate and engagement over its last 100 own posts (no reposts, no replies). |
startUrls | Bluesky URLs (or at:// URIs): post, profile, followers / follows page, custom feed, list, starter pack, search, hashtag — e.g. https://bsky.app/profile/nytimes.com. |
postData, threadDepth | For each post URL: its replies (reply thread, threadDepth levels), quotes, likers, reposters. |
repliesPerPost | Also read the replies under each post found by a search, an account, a feed or a list: up to this many per post (0 = off). Each reply is a result of its own and counts in maxItems; a post that has replies keeps room for them (up to this number), and the room a post's replies do not use goes back to the next posts. |
feedPresets | Public feeds: discover, hot-classic, news, science, bsky-team. |
includeTrends, maxPostsPerTrend | The current trending topics (up to 25), and up to N posts of each. |
maxItems, maxItemsPerQuery | Stop after this many results for the whole run / for each source (0 = unlimited). |
includeAuthorProfiles | Author followers, follows, posts count, bio (with its e-mail addresses), website, banner and profile indexing date on each post; counts and website on each account a page gives short (followers, likers, list members). Default off, charged per row it completes (Author profile event); off = the rows alone. minFollowers reads the counts by itself (not charged as Author profile). |
includeSentiment | An AI sentiment label (positive / neutral / negative) and its score on each post and reply, in two extra fields (off by default). Charged once per row labelled; nothing for a post without text. Requires 2 GB of memory (Run options → Memory); with less the option is skipped and nothing is charged. |
aiLabels | AI labels: your own labels, one per entry — a name (pricing) or a name and a sentence that is true when the label applies (competitor: The author compares tools); up to 20. Each post and reply gets the labels that apply, with a score from 0 to 1, in aiLabelMatches. Empty (the default) = off. Charged per row and per label judged, whether it applies or not; nothing for a post without text. Requires 2 GB of memory (Run options → Memory); with less the option is skipped and nothing is charged. |
includeKeywordStats | A summary of each searched keyword in the KEYWORD_STATS record of the run's key-value store, never in the dataset (off by default). Charged once per keyword given; a keyword needs at least 5 posts in the run. A run with no keyword search gives nothing and charges nothing. |
includeWebsiteContacts | Open the website of each author (the profile's website, the links of its bio, or its handle when that is a domain of its own) and add the e-mails, phone numbers and social links found there (off by default; needs includeAuthorProfiles, without it nothing happens). Charged once per author for whom at least one e-mail or phone number is found; a website that cannot be opened or gives none costs nothing. |
postedAfter, postedBefore | Creation date range of posts: 2026-09-01, or a period before now (7 days, 24 hours, 2 weeks). |
language | Language of the posts, as a code (en, fr, pt-br); posts without a declared language are dropped. |
author, mentions | Search filters: posts of an account, posts mentioning an account (a handle such as nytimes.com, or a DID). |
domain, exactUrl | Search filters: posts linking to a website (nytimes.com), or to one exact page. |
hashtags | Search filter: posts carrying all these hashtags (ai, climate). |
includeReplies, mediaOnly, withLinksOnly | Keep replies or not; only posts with images or video; only posts with a link. |
minLikes, minReposts, minReplies, minEngagement, minFollowers | Minimum likes, reposts, replies, total engagement, author followers. |
includeKeywords | Keep only the posts whose text, image descriptions or link card contains at least one of these words (case and accents ignored). |
excludeKeywords | Drop the posts whose text, image descriptions or link card contains one of these words (case and accents ignored). |
onlyNew, stateKey, resetState | Monitoring: only the results never delivered under this memory key; resetState forgets the memory. |
| Advanced | proxyConfiguration (Apify proxy by default, included in the price; the residential proxy is not available), maxConcurrency, maxRequestsPerMinute, maxRequestRetries, debugLog. |
📦 Output
A post (shortened: empty fields left out):
{"type": "post","id": "at://did:plc:eclio37ymobqex2ncko63h4r/app.bsky.feed.post/3mw5qqzz4cl2o","url": "https://bsky.app/profile/nytimes.com/post/3mw5qqzz4cl2o","source": "account-posts","sourceInput": "nytimes.com","scrapedAt": "2026-09-23T03:18:35.976Z","uri": "at://did:plc:eclio37ymobqex2ncko63h4r/app.bsky.feed.post/3mw5qqzz4cl2o","text": "A new era of sleep tech tracks your brain waves to gauge your sleep stages. […] Wirecutter tested three of these devices: nyti.ms/4ArDN3l","createdAt": "2026-09-23T03:00:03.221Z","indexedAt": "2026-09-23T03:00:06.375Z","langs": [],"likeCount": 13,"repostCount": 2,"replyCount": 0,"quoteCount": 1,"bookmarkCount": 7,"engagementTotal": 16,"hashtags": [],"mentions": [],"links": ["https://nyti.ms/4ArDN3l"],"images": [],"externalLink": {"url": "https://nyti.ms/4ArDN3l","title": "I Tried to Hack My Brain Waves to Improve My Sleep","description": "EEG is the next frontier of sleep tech. But does it work?","thumb": "https://cdn.bsky.app/img/feed_thumbnail/plain/did:plc:eclio37ymobqex2ncko63h4r/bafkreicnzvgcnslu3wavzowzodglmefukxisgzsh2tfrw4df3o477poph4"},"embedType": "external","hasMedia": false,"isReply": false,"isRepost": false,"labels": [],"facets": [],"authorDid": "did:plc:eclio37ymobqex2ncko63h4r","authorHandle": "nytimes.com","authorDisplayName": "The New York Times","authorVerified": true,"authorFollowersCount": 1348269,"authorFollowsCount": 123,"authorPostsCount": 43234,"authorEmails": [],"authorPhones": [],"authorWebsiteEmails": [],"authorWebsitePhones": [],"authorSocialLinks": [],"authorLabels": [],"authorUrl": "https://bsky.app/profile/nytimes.com","emails": [],"phones": [],"topAccounts": []}
You can download the dataset in various formats such as JSON, HTML, CSV or Excel.
Keyword statistics
With Keyword statistics, the run also writes one record, KEYWORD_STATS, to its key-value store (a link is in the run's log). It is not a row of the dataset. Shortened example:
{"generatedAt": "2026-10-10T12:00:00.000Z","onlyNew": false,"rowsRead": 412,"minRowsPerKeyword": 5,"keywordsFound": 1,"keywordsDelivered": 1,"keywords": [{"keyword": "climate","posts": 380,"replies": 32,"firstPostAt": "2026-10-09T09:42:16.000Z","lastPostAt": "2026-10-10T08:58:13.000Z","periodDays": 0.95,"postsPerDay": 412,"perPost": {"likes": { "mean": 6.34, "median": 1 },"replies": { "mean": 0.66, "median": 0 },"reposts": { "mean": 1.48, "median": 0 },"quotes": { "mean": 0.28, "median": 0 },},"engagementRateMedian": null,"mediaShare": 0.169,"authors": 260,"verifiedAuthorsShare": 0.0769,"rowsWithLanguage": 398,"languages": [{ "language": "en", "share": 0.71, "rows": 283 }],"topHashtags": [{ "hashtag": "climate", "rows": 41 }],"topAuthors": [{ "username": "example.bsky.social", "rows": 12, "likes": 285 }],"topPosts": [{"id": "at://did:plc:x/app.bsky.feed.post/3abc","url": "https://bsky.app/profile/example.bsky.social/post/3abc","likes": 806,},],"sentiment": null,},],"notDelivered": [],}
- A post counts under the keyword it was found with (a search keyword, a search or hashtag URL, or a keyword read from the Network stream), once: a post found by two keywords of the same run counts under the first one only. Posts of accounts, feeds, threads and trending topics, and profile rows, belong to no keyword.
- Averages and medians cover posts and replies together; a count Bluesky does not give is left out, never counted as 0.
postsPerDayis the number of rows divided by the days between the first and the last post, a period under one day counting as one day.languageslists the first language each post declares.sentimentis filled when AI sentiment label is on: the share of positive, neutral and negative posts among those labelled. - A keyword needs at least 5 posts (replies included) in the run; at most 50 keywords are given, the biggest first, and the keywords left out are named in
notDelivered. With Only new results, only the posts delivered in this run are counted (onlyNew: truein the record).
Contacts from the author's website
With Contacts from the author's website (and Author follower counts, where the profile comes from), the Actor opens the website of each author: the website of its profile, the links written in its bio, and its handle when that is a domain of its own (nytimes.com; never name.bsky.social). It reads the home page and up to two pages of the same site that the home page links as contact, about or legal notice (a link-in-bio page such as Linktree is one page). The row gets authorWebsiteRead (the site opened), authorWebsiteEmails and authorWebsitePhones (+ and digits for an international number), and authorSocialLinks (Instagram, Facebook, LinkedIn, X, YouTube, TikTok, WhatsApp and Telegram profiles, from the bio and from the website). Each website is read once per run: every post of the author carries the same values.
An author is charged once per run, and only when at least one e-mail address or phone number is found on its website; the social links are free. Links to Instagram, Facebook, YouTube, TikTok, X, LinkedIn, WhatsApp, Telegram and Threads are never opened, and the Actor reads only the website the author itself cites. A website that cannot be opened, is protected or gives nothing costs nothing, and the log counts them.
All 125 fields
| Fields | What you get |
|---|---|
type, id, url | Result: post, profile, trend or list (a list or a starter pack); the post's AT-URI, the account's DID, trend:<topic> or the AT-URI of the list; its bsky.app page |
source, sourceInput, scrapedAt | what found it (search, stream, account-posts, followers, likes, post, post-replies, quotes, trend-posts, account-lists…), the keyword / account / URL, ISO timestamp |
uri, cid, text, createdAt, indexedAt, langs | Post: AT-URI, content hash, full text, creation and indexing dates, declared languages |
likeCount, repostCount, replyCount, quoteCount, bookmarkCount, engagementTotal | Engagement at scrape time; the total = likes + reposts + replies + quotes |
engagementRate, engagementBase | Engagement rate (computed for you, free): the total divided by the author's follower count (followers; needs Author follower counts), 4 decimals — 0.0249. Bluesky shows no views. null when the followers are not known, and on accounts and topics |
sentiment, sentimentScore, aiLabelMatches | AI (options): positive, neutral or negative for the text of the post or reply, and the confidence in it from 0 to 1 (AI sentiment label); the labels you wrote that apply to the text, each as { "label", "score" }, [] when none applies (AI labels). null when the option is off or skipped, or when the row has no text |
hashtags, mentions, links, facets | hashtags without #, mentioned accounts (DID and handle), every link, rich-text facets (byte ranges of links, mentions and tags) |
images, video, externalLink, quotedPost, embedType, hasMedia | Media: images (full size, thumbnail, description, size), video (playlist, thumbnail), link card, quoted post, kind of embed |
isReply, replyParentUri, replyRootUri, replyToHandle, replyToUrl, replyRootUrl, threadDepth, replyDisabled | Thread: reply or not, parent and first post (their bsky.app pages, and the handle of the account answered where the page shows it: an account's posts, a feed, a list, a reply thread — empty for a post found by a search), depth in a reply thread (0 = the post, 1 = direct reply, -1 = its parent), replies restricted |
isRepost, repostedByHandle, repostedAt, isPinned, labels | repost in an account's timeline (by whom, when), pinned post, moderation labels |
feedName, feedDescription, feedCreatorHandle, feedCreatorDisplayName | Feed a post was read from (a feed URL, a preset, or a feed an account created): its name, description, the handle and name of its creator; empty for any other source |
authorDid, authorHandle, authorDisplayName, authorAvatar, authorCreatedAt, authorVerified, authorPronouns, authorLabels, authorUrl | Author of the post (the pronouns they wrote, as typed) |
authorFollowersCount, authorFollowsCount, authorPostsCount, authorDescription, authorEmails, authorPhones, authorWebsite, authorBanner, authorIndexedAt | the author's counts, bio, the e-mail addresses and phone numbers written in the bio, website, banner and profile indexing date (with includeAuthorProfiles) |
authorWebsiteRead, authorWebsiteEmails, authorWebsitePhones, authorSocialLinks | Author's website (Contacts from the author's website): the website opened, the e-mails and phone numbers found on it, the social profiles of the bio and of the website. null and [] when the option is off |
did, handle, displayName, description, emails, phones, pronouns, website, avatar, banner | Account: DID, handle, name, bio (or trending topic description), the e-mail addresses and the phone numbers written in the bio, pronouns, website (a profile row or a list that includes the author profiles), pictures |
followersCount, followsCount, postsCount, isVerified, verification, pinnedPostUri | the account's counts, verified check and its details, pinned post |
latestPostAt, latestPostText, latestPostUrl | Latest post (Latest post of each account, profile rows): when it was written, its text and its bsky.app page; null when the option is off or the account has no post of its own among its last entries |
postsSampled, postsPerWeek, avgLikes, avgReplies, avgReposts, medianEngagement, medianLikes, medianReplies, medianReposts, topPostLikes, topPostUrl | Statistics (Statistics of each account, profile rows): computed on the account's last own posts (100 at most; no repost, no reply) — how many were read, posts per week (their number over the time between the oldest and the newest, at least one day), means and medians of likes, replies and reposts (2 decimals), the median of likes + replies + reposts + quotes, the most liked post (likes and page). null when the option is off or the account has no post |
listsCount, feedsCount, starterPacksCount, isLabeler, chatAllowIncoming, associated | lists, feeds and starter packs it created, moderation service or not, who can message it, raw details |
joinedViaStarterPack, hidesFromLoggedOut | the starter pack it joined by; the account asked apps not to show it to logged-out visitors |
relatedTo, likedAt | the account / post / list / topic the result relates to (followers of…, quotes of…), when the account liked or reposted the post |
topic, category, status, postCount, startedAt, rank, feedUri, topAccounts | Trending topic: id, category, status, post count, start time, rank (1 = first), its feed, the accounts driving it |
purpose, listItemCount, updatedAt, creatorHandle, creatorDid | List or starter pack (type = list; the name, description and picture are in displayName, description and avatar, the creation date of a starter pack in createdAt): curatelist, modlist, referencelist or starterpack, how many accounts it holds, when it was last updated, and who made it |
💡 Tips
How to get more results
Leave sort on latest and raise maxItems: the search goes back in time page after page, with no cap of 100 posts. Combine keywords, accounts and URLs in one run, and set maxItemsPerQuery so that one big source does not use up the whole budget.
How to reduce costs
The price is per result, so the levers are maxItems, maxItemsPerQuery, the filters (a filtered-out post costs only its small filter check) and onlyNew for recurring runs (you never pay twice for the same result). Author follower counts is charged per row it completes: leave it off (the default) when you do not need follower counts. The AI options are charged per post (and per label): leave them off, or keep the number of labels low, when you only need the text. Keyword statistics is charged per keyword and Contacts from the author's website per author with a contact: leave them off when the dataset is enough. Search filters (author, mentions, domain, hashtags…) are applied by Bluesky itself: they cost nothing and make each page count; dates and language are sent to Bluesky too, then checked on each post.
Several searches in one run
Fill searchQueries, handles and startUrls: each keyword, each account × each kind of account data, each URL is a source of its own. A result found by several sources is saved — and charged — once. Set maxItemsPerQuery to give every source its own cap: without it the first sources can use up the whole maxItems budget.
Monitoring: only the new results
Tick Only new results (onlyNew) and schedule the Actor. The first run returns everything; each later run skips the results already delivered: they are not saved and not charged. The memory lives in a named key-value store of your account (bluesky-posts-scraper-seen, up to 150,000 results per key) and is only updated with results that really reached the dataset, so a failed run never hides anything. Give each schedule its own stateKey (two schedules sharing a key would hide each other's results), and tick resetState once to start over. A search sorted by latest, an account's timeline, followers and follows stop once they meet a run of results you already have — half your cap, 20 at most (the pinned post and reposts are not counted) — so the older results your cap left out are never delivered later.
Filter by creation date
postedAfter and postedBefore take a date (2026-09-01, the whole day is included, UTC) or a period before now (7 days, 2 weeks, 1 month; via the API also 24 hours or a full ISO date-time). The filter reads a post's createdAt; accounts and trending topics are not filtered by date. Filtered-out posts are not saved, not charged as results and do not count in maxItems; each post the date filter checks is charged a small filter check. Searches send the dates to Bluesky, and an account's timeline stops at the first page that is entirely too old.
Search or network stream?
The search finds posts by word, hashtag, image description and link, ranked by date or engagement, as far back as Bluesky keeps them. The Network stream (searchEngine: stream) reads the whole network for the last streamHours hours instead: every post whose TEXT contains the keyword, none left out, at $0.002 per hour read.
🔌 Integrations and API
Call the Actor via the Apify API, the JavaScript or Python clients, or connect it with integrations and webhooks (Make, Zapier, n8n, Google Sheets, Slack, Airtable…). The dataset can be fetched as JSON or CSV from any tool.
🤖 Use with AI agents (MCP)
AI agents (Claude, ChatGPT, Cursor…) can find and run this Actor through the Apify MCP server, billed to their Apify account like any run. It returns one item per Bluesky result: a post, an account or a trending topic (type says which). Actor id: nice_dev/bluesky-posts-scraper; MCP server with this Actor only: https://mcp.apify.com/?tools=fetch-actor-details,nice_dev/bluesky-posts-scraper.
Smallest input, for a cheap first call:
{"query": "climate","maxItems": 10}
Key output fields: type, url, text, createdAt, likeCount, repostCount, replyCount, authorHandle.
Cost: $0.89 per 1,000 results plus $0.0006 per run start ($0.36 per 1,000 on the Gold plan); the Network stream, the Actor's own filters and the options (author profiles, AI sentiment label, AI labels, keyword statistics, author websites; all off by default) cost extra, see the pricing section above. Cap each call with maxItems and, through the API, with the run option maxTotalChargeUsd.
❓ FAQ
Is it legal to scrape Bluesky?
The Actor only reads what Bluesky shows publicly to anyone, without an account. It logs in to nothing. With Contacts from the author's website it also opens the public website an author itself cites in its profile, and nothing else. Results contain personal data (handles, names, bios, posts, and the e-mails or phone numbers people wrote in their bio or published on their website), which is protected by GDPR: do not store it without a legitimate reason, and delete what an author has deleted. You are responsible for using the data in compliance with Bluesky's Terms of Service and applicable law. This Actor is not affiliated with Bluesky.
Does it need a Bluesky account, an app password or a proxy?
No account and no app password. The proxy is included in the price: leave the default setting (the residential proxy is not available). With searchEngine: auto, a keyword whose search Bluesky refuses is read from the network stream instead.
Is the data safe to open in Excel or to show on a web page?
Posts and bios are their authors' own words, copied as they are. A text can begin with -, +, = or @ (a mention, -20% today): Excel and Google Sheets may read such a cell of a CSV file as a formula. The Actor leaves the text as it is, so that the JSON and the API give the real value: when you open a CSV, import these columns as text. Every URL field holds an http(s) URL or null. On a web page, escape every field like any text written by a stranger.
Known limitations
topsearches return 100 posts at most per search (Bluesky does not page them);latesthas no such cap.- Trending topics: 25 at most, as Bluesky lists them.
- The network stream reaches 36 hours back at most, matches the post's text only (not image descriptions nor link cards), and reads the period from its start: with a cap, the earliest posts of the period come first. It applies
from:andlang:; a keyword withto:,mentions:,domain:,since:oruntil:is skipped (use thementions,domainand date fields), and so is a keyword made of operators only. - An account's likes are read on its own server when it is a Bluesky server; an account hosted elsewhere is listed in
FAILED_REQUESTSinstead. replyToHandleis given where the page shows the post answered (an account's posts, a feed, a list, a reply thread); a post found by a search has it empty, itsreplyToUrlandreplyRootUrlare still given.- Latest post of each account and Statistics of each account read the last entries of the account's own timeline (20, or 100 for the statistics); an account whose last 20 entries are all reposts has no latest post. Followers, follows and likers get neither.
- A pasted list or starter pack URL, and
lists/starter-packsofaccountData, give one card row for each list or pack (a result like any other, counted inmaxItems), before the posts and the members. onlyNewremembers result ids, not their content: a post whose likes changed is not returned again.
A run the platform stops without warning (out of memory, run timeout)
- Resurrect it: it goes on from where it stood at most a minute before the stop. The results already saved are skipped: none is delivered or charged twice, and
maxItemsstill counts them. - With
onlyNew, the memory is saved after each write (at most once every 2 seconds): resurrect the stopped run and the results it had saved meanwhile join the memory; leave it stopped for good, and the next run may return the results of its last 2 seconds once more.
Something doesn't work?
The last line of the log counts the results saved, filtered out, not found on Bluesky (an account, post or list that does not exist or was deleted), and the requests that failed after every retry. Those requests and the targets not found are listed, with the reason, in the FAILED_REQUESTS record of the run's key-value store. A run that saved nothing and had failed requests fails, and its last message gives the cause. A run that saved some results fails too when at least as many requests failed for good as were read (page 1 read, the next pages refused): a green run with a short dataset would hide the outage. One failed request among many is only a warning, and a search Bluesky refuses that the network stream reads instead (Search source Auto) is not counted as lost.
If Bluesky changes its answers, you are told instead of paying for blank rows: if the first 20 posts read all lack their creation date, text or author handle — or the first 20 accounts their handle —, the run saves nothing more, stops and fails, and its last message names the missing field.
🛟 Support
Open an issue in the Issues tab with a link to your run: the run log and the FAILED_REQUESTS record of the key-value store show exactly what failed and why.