Bluesky Posts Scraper
Pricing
from $0.93 / 1,000 post rows
Bluesky Posts Scraper
Search Bluesky posts by keyword, read an account's posts, or read a post and its replies. Exact like, reply, repost, and quote counts, with dates, hashtags, links, and images. No login and no key. You pay per row.
Pricing
from $0.93 / 1,000 post rows
Rating
0.0
(0)
Developer
Mamba Labs
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
an hour ago
Last modified
Categories
Security
Limited permissionsShare
Give keywords, Bluesky accounts, or post addresses. The actor returns one row per post or reply, with exact like, reply, repost, and quote counts, dates, hashtags, links, and images. You pay per row returned. Mamba Labs pays the platform costs of the run.
Features
- Keyword search, sorted by latest or top, with date and language filters.
- An account's own posts, newest first, with or without its replies and reposts.
- A post and its replies, from a post address.
- Replies under every post a search or account read finds, as a charged option.
- One free status row per source, which says how many rows it returned, whether the list was complete, or why it stopped.
- A post found by two sources is returned and charged once.
How the data is read
The actor reads Bluesky's public AT Protocol API at api.bsky.app on the Apify proxy default pool, with no login, no key, and no other developer's actor. An HTTP 429 waits and rereads once, as Bluesky's robots.txt asks, and then returns a free row.
Input
| Field | Type | Default | What it does |
|---|---|---|---|
queries | array | Keywords to search, up to 20. Without your own app password each search returns one page, about 100 posts. Text from Clay splits on new lines, so a query can hold a comma. | |
authors | array | Bluesky handles, profile addresses, or DIDs, up to 20. Returns each account's own posts, newest first. | |
post_urls | array | Addresses of Bluesky posts to read, up to 100. | |
sort | string | latest | latest or top. Default latest. Used for queries. |
since_date | string | Only posts from this date, such as 2026-10-01. | |
until_date | string | Only posts before this date, such as 2026-11-01. | |
language | string | Language code such as en or de. Used for queries. Reads the language the post's author set. | |
max_posts_per_source | string | 100 | Per query or account, up to 2000. Default 100. A search with no app password stops at about 100. |
include_replies | string | no | yes or no. Default no. Reads the replies under each post found. Each reply is a charged row. |
max_replies_per_post | string | 20 | Up to 200. Default 20. |
include_account_replies | string | no | yes or no. Default no. Used for accounts. |
include_reposts | string | no | yes or no. Default no. Used for accounts. A repost row shows the original author. |
bluesky_handle | string | Optional, with the app password. Used only to page a search past its first page. | |
bluesky_app_password | string | Optional. Create an app password in Bluesky settings; never use your main password. Sent only to Bluesky. Without it a search returns one page, about 100 posts. Accounts and post addresses need no password. | |
max_charge_usd | string | Optional. The run stops before a charge that would pass this amount. Summary rows are always returned. |
Output
| Field | Type | What it holds |
|---|---|---|
row_type | string | One of: post, reply, source_status, run_summary. run_summary is the one free row written when the input is refused. |
row_key | string | Unique key for this row: the post's AT URI. |
source_type | string | query, author, post, or login: the kind of input this row came from. |
query | string | The search query that found this row. |
account | string | The account input that found this row. |
input_url | string | The post address input that found this row. |
query_in_text | boolean | Search post rows: whether the post text holds the query. Bluesky also matches on things a row does not show, such as a link target, so false is not always wrong. |
post_uri | string | The post's AT URI. |
url | string | The post on bsky.app. |
text | string | The post text. |
author_handle | string | The author's handle. |
author_name | string | The author's display name, where set. |
author_did | string | The author's stable ID. |
author_url | string | The author's profile on bsky.app. |
created_at | string | When the post was written, as its author's client states it, UTC. |
indexed_at | string | When Bluesky indexed the post, UTC. |
likes | integer | Exact like count. |
replies | integer | Exact reply count. |
reposts | integer | Exact repost count. |
quotes | integer | Exact quote count. |
languages | array | Language codes the post's author set. |
hashtags | array | Tags in the post. |
mentions | array | DIDs of accounts mentioned. |
links | array | Link targets in the post and its link card. |
image_urls | array | Full size images on the post. |
is_reply | boolean | Whether the post answers another post. |
parent_uri | string | The post this one answers. |
root_uri | string | The first post of this thread. |
posts_returned | integer | source_status rows: posts this source returned. |
replies_returned | integer | source_status rows: replies this source returned. |
list_complete | boolean | source_status rows: true when Bluesky offered no further page, false when the run stopped first. |
row_status | string | ok, or why this row carries no data: no_results, no_change, partial, blocked, or error. |
error_reason | string | Why the row is not ok. A refused input reads input_invalid: followed by what to fix. |
billable_events_json | string | What this row was billed as, for example {"post-row":1}. {} means free. |
scraped_at | string | When the row was written, UTC. |
Pricing
Pay per event. Summary rows and a refused input are free.
| Event | Free plan | Bronze | Silver | Gold |
|---|---|---|---|---|
post-row, one post or reply | $0.0011 | $0.001045 | $0.00099 | $0.000935 |
apify-actor-start, once per run (one per GB of memory; the default 256 MB is one) | $0.0065 | $0.0065 | $0.0065 | $0.0065 |
Per 1,000 rows of post-row: $1.10 on the free plan, $0.94 on Gold.
Free plan cap: 1.50 USD of evaluation a month, about 1364 rows. Any paid Apify plan removes the cap, and it resets on the 1st. The run stops before a charge that passes your maximum charge per run in Apify or your max_charge_usd input.
Mamba Labs pays the platform costs of your run, Apify proxy traffic included.
Usage examples
Clay. Number and on or off inputs are text fields, which is what Clay sends. A list input takes a JSON list, so put the column inside a one item list.
MCP. The Mamba Labs MCP server @mambalabsdev/mcp-bluesky-posts-scraper exposes this actor to Claude and other MCP clients.
Error handling
| Case | What you get |
|---|---|
| Missing or unreadable input | One free row, row_type run_summary, error_reason starting input_invalid: with what to fix. The run ends successfully and charges nothing but the run start |
| A source refuses, finds nothing, or does not exist | One free source_status row with row_status blocked, partial, or no_results, and the next source still runs |
| The run is close to its timeout | The actor stops starting new work 90 seconds before the timeout and delivers what it has |
Limitations
- A search with no account returns one page, about 100 posts per query. Bluesky answers any later search page with HTTP 403 "forbidden by administrative rules" from every exit tested. The status row reads
partialand says so. The actor does not slice dates to get past this. To page further, give your ownbluesky_handleandbluesky_app_password. - The app password route has not run on a real account. Fixtures and the refused password path are the test, and the first buyer's password is the live test. Use an app password, never your main password. It is sent only to Bluesky, stays out of rows and logs, and is used only for search.
- A login session lasts about 2 hours by Bluesky's own setting, and a longer search then ends as
partial. - Accounts page with no account of your own: 2,000 unique posts from one account in one test run, 303 seconds. 2,000 per source is the actor's maximum.
- Bluesky decides what a search matches, and it also matches things a row does not show, such as a link target.
query_in_textreads false on those rows. In one test, 13 of 99 read false, and these rows are charged. - Replies come from the first 6 levels of a thread. Blocked and deleted replies are skipped.
- If the first 5 sources return nothing, the run stops with a free row, so a list of dead entries does not run on. After any row is returned, every source runs.
- A repost row shows the original author and post, not who reposted it.
- An author who asks Bluesky not to show them to logged out viewers is skipped, free, on every route, including when you give an app password. On one search page, 10 of 100 posts were skipped this way, so a search can return fewer rows than the maximum. An account or post input like this reads
no_resultsand says why.
Mamba Labs actor suite
Built by Mamba Labs.