Bluesky Scraper: Firehose Mentions & Hashtags
Pricing
from $0.58 / 1,000 post returneds
Bluesky Scraper: Firehose Mentions & Hashtags
Track Bluesky mentions of your keywords, hashtags or account, and every post linking to your site or page, read from Bluesky's own firehose over the last 1 to 36 hours. Quiet posts are found as surely as popular ones. One row per post with text, author, images, likes, reposts and replies.
Pricing
from $0.58 / 1,000 post returneds
Rating
0.0
(0)
Developer
Pradio Actors
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
19 days ago
Last modified
Categories
Share
What does Bluesky Scraper do?
Bluesky Scraper reads Bluesky's public firehose and returns the posts that mention your keywords, hashtags, account or links. It reads every public post of the last 1 to 36 hours, so a quiet post is found as surely as a popular one. You get one row per post: text, author, images, link card and live likes, reposts and replies, 31 fields in all. $0.88 per 1,000 posts, about $0.10 more to scan a whole day, and a free row when a topic finds nothing.
Your first run: a daily brand watch
{"keywords": ["acme analytics"],"domains": ["acme.com"],"lookbackHours": 24}
Who uses Bluesky Scraper
| Who | What they run it for |
|---|---|
| Brand and communications teams | Every post that names the brand or links to its site, once a day |
| Publishers and marketers | Which posts share their articles, and the engagement each got |
| Developer relations and open-source maintainers | Mentions of a project, its hashtag or its docs domain |
| Researchers | Every post on a topic over a fixed window, up to 5,000 a run |
Features
- The whole firehose, read end to end. Every public post in your window is read, so a quiet post is found as surely as a popular one.
- Five kinds of topic, one scan. Keywords, hashtags, mentions of an account, links to a domain and links to one page share one read; each row names its match.
- One page, by its address. See who shares one article or launch page; a tracking tag on the shared link still counts as a match.
- A window you place. Read the hours you choose, or end the window early for a fixed past slice.
- Resume a stopped run. Its last row carries the position reached; paste it back and the next run reads the rest without billing the hours already read.
- Fresh posts on every repeat. Paste the post addresses an earlier run returned and it skips them, so a daily schedule hands you new posts.
- Live engagement on every row. Likes, reposts, replies, quotes and bookmarks are read as the row is written.
- A language for untagged posts, and floors on engagement. Untagged text carries the language it reads as. Set a minimum of likes, reposts or replies and posts under it stay out.
What you can count on
- A post row is charged only after it is written to your dataset; a row you cannot see is never billed.
- A spending limit stops the run cleanly, with a last row saying how many rows came back and how many did not.
- Every run saves a summary of what it read, returned and charged, so a short run is easy to tell from a broken one.
- If the Bluesky firehose refuses a read or answers with an error, the run stops and says so in the log rather than returning empty rows.
- No value is invented: a field the firehose does not show on a post comes back null, and the field table below says when.
Why this one
| This Actor | Most-used alternative | |
|---|---|---|
| Posts returned on the run | 49 | 5 |
| Price per 1,000 posts | $0.88 | $1.10 |
| Fee to start a run | $0.00005 | $0.004 |
| Fee for each hour of Bluesky read | $0.004 | none |
| Memory a run takes | 1,024 MB | 4,096 MB |
Head to head on 2026-09-24: this Actor read a one-hour window for four topics at once; the alternative ran the single query "apify" with its limit set to 5 rows. This Actor reads every post in the window rather than a slice, and bills from $0.00058 a post on paid plans. The hourly read fee makes a quiet watch dearer than paying per post alone; the more posts a day your topics bring, the less this Actor costs per post. On a test list of 100 open-source project domains, 15 came back with posts in a day and every domain got an answer.
What data does Bluesky Scraper return?
One row per post, newest first. Two times ride on a row: when the author's app says it posted, and when Bluesky first saw it (empty on the few posts the API did not time-stamp). A content hash pins the exact version read. This is a real row from a run, shortened:
{"status": "ok","url": "https://bsky.app/profile/golangoss.bsky.social/post/3mwwtzyj4pu2x","text": "lakeFS by @lakeFS (⭐️ 5551)lakeFS - Data version control for your data lake | Git for data#go","created_at": "2026-10-03T02:35:17.000Z","indexed_at": "2026-10-03T02:35:18.662Z","author_name": "GolangOSS","author_handle": "golangoss.bsky.social","langs": ["en-UK"],"post_type": "post","matched_by": ["domain:github.com"],"like_count": 0,"has_media": false}
The fields a post row carries:
| Fields | What they are |
|---|---|
url, uri | The post's page on bsky.app (with the author's handle) and its at:// address, the identifier the API uses. |
text | The post's text as written; empty when the post is only a link card. |
created_at, indexed_at | When the author's clock says it posted, and when Bluesky's API first saw it. The second is empty on the posts the API did not time-stamp. |
author_name, author_handle, author_did | Display name (null when none is set), handle, and the permanent account identifier a handle change cannot move. |
langs, detected_language, post_type | The language tags the author set (null when none), the language untagged text reads as, and post, reply or quote. |
matched_by | Which of your topics the post matched, such as domain:example.com. |
links, images, external_link, hashtags, mentions, has_media, video_url, video_thumbnail | Links, images, link card, hashtags and @mentions; media flags and the video's addresses when present, null otherwise. Mention authors are checked like any author. |
quote_of, reply_to, reply_root | At:// addresses of the post quoted, the post answered and the thread's first post; null when the other author opted out. |
reply_count, repost_count, like_count, quote_count, bookmark_count | Replies, reposts, likes, quotes and bookmarks when the row was read. |
content_hash | The hash of the post version read, for per-row integrity. |
status, row_type | ok on a post, the miss reason on a free row; ROW, ITEM_STATUS, NO_POSTS_FOUND or STOPPED_EARLY. |
query, reason, resume_from, rowsFetched, rowsReturned, rowsRemaining | On answer and run-level rows: the miss's topic, what happened, the stopped run's scan position (paste back as Resume from), and the fetched/returned/remaining counts. |
How much does it cost?
$0.88 per 1,000 posts. Three charges make a run:
| What | Price |
|---|---|
post-returned: each post returned | $0.00088, after the row is written |
hour-scanned: each hour of Bluesky read | $0.004, after the read, for the hours actually read |
apify-actor-start: platform start | $0.00005, once per run at the default 1,024 MB |
A 24-hour look back is 24 scanned hours whatever your topics match, and a full day of Bluesky is about 3.4 million posts read.
One run returns at most 5,000 posts, so 10,000 posts means more than one run. Two daily runs over a 24-hour window:
| A daily run over 24 hours | Scan | Posts | Total |
|---|---|---|---|
| A niche brand, 60 posts | $0.096 | $0.0528 | about $0.15 |
| A busy topic, 1,000 posts | $0.096 | $0.88 | about $0.98 |
Run daily for a month and the scan costs $2.88 plus the posts. Paid Apify plans pay less per post: we charge $0.00074 on Bronze, $0.0007 on Silver and $0.00058 on Gold and above.
The rows that answer for your topics and the run itself come back free. Posts under your minimums, posts on your exclusion list and posts past the cap are billed at $0. To keep a run cheap, pick the shortest look back your schedule needs. An hourly run at 1 or a daily one at 24 reads each hour once; 25 reads one hour twice for a late schedule.
How do I use Bluesky Scraper?
- Open the Actor and go to the Input tab.
- Type your topics: keywords, hashtags, an account, a domain or a page. One is enough.
- Set how many hours to look back.
- Press Start. The posts appear in the Output tab and in the run's dataset.
Worked examples
Use it to collect a hashtag's best posts: every English post in the last day with at least 20 likes.
{"hashtags": ["buildinpublic"],"languages": ["en"],"lookbackHours": 24,"minLikes": 20}
Through the API:
curl -X POST "https://api.apify.com/v2/acts/Pradio~bluesky-mentions/runs?token=<YOUR_TOKEN>" \-H "Content-Type: application/json" \-d '{"keywords": ["apify"], "domains": ["apify.com"], "lookbackHours": 24}'
Input
| Field | Type | Default | What it does |
|---|---|---|---|
keywords | array | prefilled | Words or phrases, matched as whole words, case-insensitive. |
hashtags | array | prefilled | Hashtags, with or without the #. |
mentions | array | empty | Handles whose @mentions to track; posts by anyone that tag the account. |
domains | array | prefilled | Domains whose links to track; subdomains match too. |
urls | array | empty | Pages whose links to track, one address each; tracking tags still match. |
lookbackHours | integer | 24 | How far back the window starts, 1 to 36 hours. |
endHoursAgo | integer | 0 | How far back the window ends; must be smaller than the look back. |
resumeFrom | string | empty | A stopped run's carried position; reads the rest of the window. |
languages | array | empty | Keep posts tagged with one of these language codes. |
matchDetectedLanguage | boolean | false | Also keep an untagged post whose text reads as a listed language. |
includeReplies | boolean | true | Return replies beside top-level posts and quotes. |
excludeAuthors | array | empty | Handles or account identifiers whose posts stay out. |
excludePosts | array | empty | at:// addresses an earlier run returned; those posts are skipped. |
maxItems | integer | 1,000 | The most posts one run returns, newest first; at most 5,000. |
maxItemsPerTopic | integer | empty | The most posts one topic returns, so a busy topic shares the cap. |
minLikes | integer | empty | Keep posts with at least this many likes at read time. |
minReposts | integer | empty | Keep posts with at least this many reposts at read time. |
minReplies | integer | empty | Keep posts with at least this many replies at read time. |
A post matching any topic in any field returns once, with every topic it matched. A domain pasted as a full address reads as its domain; for one page, use Links to a page. A topic the run cannot read, such as a mistyped entry, is answered on a $0 row that names it.
Picking up where a run stopped
A stopped row carries the scan position a spending-limit stop reached. Paste it into Resume from and the next run reads the rest of the window, the hours already read unbilled; a position older than the stream keeps is refused. On a schedule, pair it with Exclude posts already returned. The exclusions apply on the resumed read, and those posts are skipped from matching, caps and billing.
Mentions of an account
A mention topic finds posts by anyone that tag the account with @; an account's own posts stay out. Before tracking, the run reads that one account's public profile. An account that asked Bluesky to hide it from logged-out viewers, or sits on the exclusion list, gets a $0 row saying why. The same holds for a keyword written as that account's handle.
Minimum likes, reposts and replies
Each floor you set must be met; a post under it counts toward neither your results nor the cap. The run keeps reading matched posts until the cap fills or the matches run out. The scanned hours still bill, and when every post a topic matched falls under your floors its answer row says so.
Output
The rows that answer for your topics and the run come first, so a spending limit that ends a run part-way cannot swallow them. Then the post rows, newest first.
| row_type | status | What it carries |
|---|---|---|
| ROW | ok | A post matching your topics. |
| ITEM_STATUS | no_posts | A topic matched nothing in the window, or its posts were withheld. |
| ITEM_STATUS | bad_input | A topic the run cannot read, or an account that opted out or is excluded. |
| ITEM_STATUS | not_found | A mentioned handle that resolves to nothing. |
| ITEM_STATUS | fetch_failed | The public API could not be asked. |
| ITEM_STATUS | blocked | Bluesky refused or rate-limited a read; the run ends as failed. |
| ITEM_STATUS | stopped | Your spending limit ended the run; resume_from holds the position reached. |
| ITEM_STATUS | not_reached | Posts matched but the cap was spent first; nothing billed for them. |
| NO_POSTS_FOUND | The run produced no rows at all. | |
| STOPPED_EARLY | Your spending limit ended the run, with rows returned and remaining. |
So a zero result means the window was read and nothing matched, told apart from a run that broke.
Personal data
- Transparency. The rows are public Bluesky posts, read without logging in from Bluesky's public stream and API. They identify people: the author's handle, name, account identifier and text, plus the handles a post mentions. These are personal data.
- Purpose. The rows are for monitoring topics, brands, keywords and links, not for profiling people. Do not use them to track a person or to build a picture of a private individual. Never use topics to single out people by health, sexual orientation, religion, politics or any other special-category trait. The Actor adds no such classification.
- You are the controller of what you keep. You need your own lawful basis and must tell the authors their posts are processed (or knowingly rely on the disproportionate-effort exemption). You honour an author's deletion request and remove a post once its author deletes it. Keep rows no longer than your purpose needs.
- What is left out, and how to object. An author who asked Bluesky to hide from logged-out viewers, and every account on the exclusion list, is left out. A profile yields only the handle, display name and opt-out label. A post deleted during the window is not returned, media comes as addresses (never downloaded), and the linked page is never opened. An author who objects can ask to be left out through this Actor's Issues tab. The request joins the exclusion list at the next release and every run applies it. The Exclude authors input does the same for your own list.
- Nothing kept. The Actor keeps nothing between runs. Your rows live in your Apify account.
Limits
- About 36 hours back, then it ends. Bluesky's stream keeps about a day and a half of posts. Older ones are out of reach, so a daily schedule builds your history from the first run.
- Whole words, not meaning. A keyword matches the words you type; synonyms and misspellings stay out.
- Languages as tagged. The filter reads the tags authors set; an untagged post stays out unless you turn on detected-language matching.
- Sparse columns stay null. In one run's 221 posts, 20 carried images, 40 were replies and 3 a video, and the link-card field came back filled on 69.7% of them. A field the post lacks comes back null rather than zero.
- Engagement at read time. Counts are read once, as the row is written, and keep changing after the run.
- The scan is the cost. Every post in the window is read whatever your topics match; a shorter look back costs less.
FAQ
Can I use integrations with Bluesky Scraper? Yes. Send the rows to Google Sheets, Slack, Zapier, Make or a webhook from the Integrations tab, for example after every scheduled run.
Can I use Bluesky Scraper through the API or an MCP server?
Yes to both. The API call above starts runs, and claude mcp add --transport http apify "https://mcp.apify.com?tools=Pradio/bluesky-mentions" registers it as a tool for an assistant that speaks MCP.
Is it legal to collect Bluesky posts? The Actor reads public posts from Bluesky's own public stream and public API, logged out, at the pace Bluesky asks for. The posts are personal data, so read the Personal data section and check your own use. You are responsible for how you use the rows.
Do I need a Bluesky account or key? None needed. The Actor reads Bluesky's public stream and public API the way any logged-out visitor can.
Can it read private or opted-out accounts? It reads what a logged-out visitor can see: public posts. An author who asked Bluesky to hide their posts stays out, and so does anyone on the exclusion list. If Bluesky blocks a read, the run stops and ends as failed.
Can I schedule it to run daily? Yes. A daily schedule with a day-long look back reads each hour once, and an hourly one with 1 does the same. Each run returns its own window, and Exclude posts already returned skips what the last run sent you.
Can I export the posts? Yes. Open the run's dataset and export it as JSON, CSV, Excel, XML or HTML, or read it through the API.
Why did I get fewer posts than my maximum? Maximum items is a ceiling: a run returns every matching post up to it. Posts under your minimums, deleted or withheld posts and posts you listed for exclusion stay out, and a quiet day still answers.
Why is my mention topic empty for an account that exists? A mention topic returns what others post about the account, and a keyword catches a handle spelled without the @. If the account asked Bluesky to hide it from logged-out viewers, or sits on the exclusion list, its row says why.
Will Bluesky block me, or do I need proxies? No. The Actor reads the public Bluesky API without a login, one request at a time, so proxies are not needed. If a read is refused or rate-limited the run stops with a free blocked row instead of asking again.
How do I get support or report a problem? Open an issue on the Actor's Issues tab and we answer within two days. Feature requests go there too.
Release notes
- 2026-09-30: a run stopped by your spending limit can be resumed from where it stopped, and a scheduled run can skip the posts an earlier run already returned.
- 2026-09-29: a topic whose matching posts the item cap cut is named on its own row, and every row carries a content hash pinning the exact version read.
- 0.1, 2026-09-24: first release.
See also
- Contact Details Scraper — the e-mail addresses and phone numbers the sites your posts link to publish.
- Broken Link Checker — watching links to your site pairs with checking the links on it.
Feedback
A problem or a missing field goes to the Actor's Issues tab in Apify Console, where we answer every report within two days. If the Actor saved you time, a review on the Store helps other buyers find it.
Not affiliated
Bluesky Scraper is an independent tool, not affiliated with, endorsed by or sponsored by Bluesky Social PBC. You choose the topics; it reads Bluesky's public posts on your behalf.