Bluesky Posts Scraper ๐Ÿฆ‹ Any Account, No Login avatar

Bluesky Posts Scraper ๐Ÿฆ‹ Any Account, No Login

Pricing

from $1.43 / 1,000 post returneds

Go to Apify Store
Bluesky Posts Scraper ๐Ÿฆ‹ Any Account, No Login

Bluesky Posts Scraper ๐Ÿฆ‹ Any Account, No Login

Scrape posts from any Bluesky account. Pass handles or profile URLs; get post text, like, repost and reply counts, author handle, timestamp and a direct post URL as JSON or CSV. No login and no app password. Since date filter makes scheduled monitoring cheap.

Pricing

from $1.43 / 1,000 post returneds

Rating

0.0

(0)

Developer

Yaniv van der Stigchel

Yaniv van der Stigchel

Maintained by Community

Actor stats

1

Bookmarked

2

Total users

1

Monthly active users

4 hours ago

Last modified

Share

Bluesky Posts Scraper โ€” no login, no app password

Export posts from any Bluesky account. Text, likes, reposts, replies, quotes and timestamps. The AT Protocol network is public, so no authentication is required.

Scrape Bluesky posts in bulk

  • Export Bluesky posts to JSON or CSV
  • Download a Bluesky account's post history
  • Track Bluesky engagement metrics over time
  • Monitor Bluesky accounts for new posts
  • Build a dataset from Bluesky

Pass several accounts at once. Accepts handles, DIDs, or bsky.app profile URLs.

Incremental mode

Set since to the date of your last run and scraping stops as soon as it reaches older posts. Scheduled monitoring costs a fraction of a full history pull.

What you get per post

Post text, author handle, DID and display name, like/repost/reply/quote counts, creation and index timestamps, detected languages, the AT-URI, and a direct bsky.app link.

Honest failure reporting

Accounts that do not exist return a row with reason: "account-not-found" and cost you nothing. You are only charged for posts actually delivered.

Input

FieldRequiredDescription
actorsyesHandles, DIDs, or profile URLs
maxPostsPerActornoNewest first. Default 200. Your cost ceiling.
sincenoISO date. Only posts after it.

Example input

{
"actors": ["bsky.app", "@jay.bsky.team"],
"maxPostsPerActor": 500,
"since": "2026-08-01"
}

Output

Every row has the same fields whether it succeeded or failed, so you can select columns without branching. Failed rows are never charged.

FieldTypeDescription
successbooleanTrue when this row carries data. Failed rows are never charged.
sourceAccountstringThe account input this row came from.
postUrlstringPublic bsky.app link to the post.
postUristringThe post's at:// record URI.
authorHandlestringHandle of the account that posted.
authorDisplayNamestringProfile display name, if set.
authorDidstringDecentralised identifier, stable across handle changes.
textstringThe post body.
createdAtstringISO 8601 timestamp set by the author's client.
indexedAtstringISO 8601 timestamp when the network indexed the post.
languagesarrayBCP-47 language codes declared on the post.
likeCountintegerLikes at the time of the run.
repostCountintegerReposts at the time of the run.
replyCountintegerReplies at the time of the run.
quoteCountintegerQuote posts at the time of the run.
errorCodestringMachine-readable failure reason. Null on success.
errorMessagestringHuman-readable explanation of the failure. Null on success.

Example โ€” success

{
"success": true,
"sourceAccount": "bsky.app",
"postUrl": "https://bsky.app/profile/bsky.app/post/3k2aqvbhyq2b",
"postUri": "at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post/3k2aqvbhyq2b",
"authorHandle": "bsky.app",
"authorDisplayName": "Bluesky",
"authorDid": "did:plc:z72i7hdynmk6r22z27h6tvur",
"text": "We just shipped a new way to browse feeds. Let us know what you think.",
"createdAt": "2026-08-14T09:12:04.221Z",
"indexedAt": "2026-08-14T09:12:05.884Z",
"languages": [
"en"
],
"likeCount": 6042,
"repostCount": 1180,
"replyCount": 214,
"quoteCount": 73,
"errorCode": null,
"errorMessage": null
}

Example โ€” failure

A failure carries the same fields, so nothing downstream has to branch.

{
"success": false,
"sourceAccount": "not-a-real-handle.bsky.social",
"postUrl": null,
"postUri": null,
"authorHandle": null,
"authorDisplayName": null,
"authorDid": null,
"text": null,
"createdAt": null,
"indexedAt": null,
"languages": null,
"likeCount": null,
"repostCount": null,
"replyCount": null,
"quoteCount": null,
"errorCode": "account-not-found",
"errorMessage": "No Bluesky account exists for this handle or DID, or the request was refused."
}

Error codes

  • account-not-found
  • unparseable-actor
  • no-posts-found
  • error

Use it for

  • Bluesky account posts โ€” a profile's full public timeline
  • Bluesky profile monitoring โ€” scheduled runs with a since filter
  • AT Protocol (atproto) post data โ€” public records from any handle
  • Social listening and brand tracking
  • Research corpora and training datasets

Use it from an AI agent (MCP)

This Actor is callable as a tool through the Apify MCP server, so Claude, ChatGPT, Cursor and VS Code can run it directly.

Add the server to your MCP client:

{
"mcpServers": {
"apify": {
"url": "https://mcp.apify.com",
"headers": {
"Authorization": "Bearer <YOUR_APIFY_TOKEN>"
}
}
}
}

Then ask for what you want in plain language โ€” for example โ€œget the recent posts from this Bluesky accountโ€ โ€” and the agent calls cleanfeed/bluesky-posts-extractor with the right input. Every output field is described in the dataset schema, so the agent knows what it is getting back before it runs anything.

Call it from code

Python

from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("cleanfeed/bluesky-posts-extractor").call(run_input={
"actors": ["bsky.app"],
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
if item["success"]:
print(item)

JavaScript

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('cleanfeed/bluesky-posts-extractor').call({
actors: ["bsky.app"],
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.filter((i) => i.success));

cURL

curl -X POST "https://api.apify.com/v2/acts/cleanfeed~bluesky-posts-extractor/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>" \
-H 'Content-Type: application/json' \
-d '{"actors": ["bsky.app"]}'
If you needUse
Posts from public Threads profilesThreads Posts Scraper
Messages from public Telegram channelsTelegram Channel Scraper

Limitations

  • Public accounts only. Bluesky's public API is used, so no login and no app password are needed โ€” and nothing non-public is reachable.
  • The feed is the author's timeline, which includes their reposts. Filter on authorHandle if you want originals only.
  • Engagement counts are a snapshot at run time and will drift.

FAQ

Do I need a Bluesky account or app password?

No. Bluesky's public API is used, so there is no login and nothing to get rate-limited against your account.

What handle formats work?

A handle such as alice.bsky.social, a full did:plc:..., or a bsky.app/profile/... URL.

Can I fetch only new posts?

Yes โ€” set since to an ISO date and each run returns only posts after it, which keeps scheduled monitoring cheap.

Does this include reposts?

The author feed includes their reposts. Filter on authorHandle if you only want their own posts.

Can I read private accounts?

No. Only public posts are reachable, by design.

Notes

Only publicly available posts are collected, via the official public AT Protocol AppView. No login, no app password, no private data.