Bluesky Scraper - Posts, Profiles, Followers, Threads & Search avatar

Bluesky Scraper - Posts, Profiles, Followers, Threads & Search

Pricing

Pay per event

Go to Apify Store
Bluesky Scraper - Posts, Profiles, Followers, Threads & Search

Bluesky Scraper - Posts, Profiles, Followers, Threads & Search

Scrape Bluesky without login: search posts, profiles, author feeds, followers/following, full threads & user search. Clean JSON/CSV rows with text, metrics, hashtags, links & media. Cheapest per-result pricing.

Pricing

Pay per event

Rating

0.0

(0)

Developer

Creator Fusion

Creator Fusion

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Share

Bluesky Scraper — Posts, Profiles, Followers, Threads & Search for AI Agents & Data Pipelines

The all-in-one Bluesky scraper for AI agents and data pipelines: search posts, fetch profiles, export author feeds, followers/following graphs, full conversation threads, and user search — and get back typed JSON rows validated against a published output schema (documented below). Built on the official public AT Protocol AppView API: no login, no cookies, no proxies, data is near-realtime, and every run's output is exportable as JSON, CSV, or Excel.

Why AI agents use this Bluesky scraper

  • Deterministic typed output — every dataset row validates against the actor's dataset schema; field names and types never drift between runs or modes.
  • Cost-predictable per-event pricing — a fixed price per result plus a start fee ($1 per 1,000 results, cheapest in the Apify Store). maxItems hard-caps spend, so autonomous budgets are safe.
  • Zero auth, zero proxies — reads the official public AT Protocol AppView anonymously. No credentials to manage, no account risk, and no hidden proxy costs ever (many scrapers bill proxy traffic on top of results — this one cannot, by construction).
  • Clear error semantics — invalid input fails fast with exit code 1 and a human-readable status message; nothing fails silently (details in Error semantics below).
  • Rate-limit handling built in — 429/5xx responses retry automatically with exponential backoff; WAF 403s fall back to an alternate official host.

Modes

ModeWhat it doesRow shapeNeeds
search-posts (default)Full-text post search with sort, since, until, author, mentions, lang filterspostqueries
profilesFull profile details (bio, banner, follower/post counts)profiletargets
author-feedAll posts by given accounts, filterable (no replies / with media / …)posttargets
followersWho follows the given accountsprofiletargets
followingWho the given accounts followprofiletargets
threadFull conversation thread for given post URLs, flattened to rowspostpostUrls
search-usersSearch user accounts by keywordprofilequeries

Input schema

FieldTypeRequiredDefaultDescription
modestring (enum)yessearch-postsOne of search-posts, profiles, author-feed, followers, following, thread, search-users.
queriesarray of stringsfor search-posts, search-usersKeywords or phrases to search for. Supports Bluesky search syntax ("exact phrase", from:handle).
targetsarray of stringsfor profiles, author-feed, followers, followingAccounts — handles (bsky.app or @bsky.app), DIDs (did:plc:...), or profile URLs (https://bsky.app/profile/bsky.app).
postUrlsarray of stringsfor threadPost URLs (https://bsky.app/profile/<handle>/post/<id>) or AT-URIs (at://<did>/app.bsky.feed.post/<id>); returns root + all replies up to 10 levels deep.
maxItemsintegerno100Maximum result rows in total across all queries/targets. Controls cost: you are charged per result.
sortstring (enum)nolatestsearch-posts only. latest (newest first) or top (most relevant).
sincestringnosearch-posts only. Only posts on/after this date, ISO format (2024-11-01 or 2024-11-01T00:00:00Z).
untilstringnosearch-posts only. Only posts before this date, ISO format.
authorstringnosearch-posts only. Only posts by this account (handle, DID, or profile URL).
mentionsstringnosearch-posts only. Only posts mentioning this account (handle, DID, or profile URL).
langstringnosearch-posts only. 2-letter language code (en, ja, pt).
postFilterstring (enum)noposts_no_repliesauthor-feed only. posts_no_replies, posts_with_replies, posts_with_media, or posts_and_author_threads.

Output schema

Rows come in two shapes — post rows (search-posts, author-feed, thread) and profile rows (profiles, followers, following, search-users) — plus a few mode-specific fields. Fields absent from a row's shape are simply omitted.

FieldTypeNullableModesDescription
querystringnosearch-posts, search-usersThe search query that produced this row.
targetstringnoauthor-feed, followers, followingNormalized target account (handle or DID) this row was scraped for.
threadOfstringnothreadThe input post URL whose thread this row belongs to.
parentUristringyesthreadAT-URI of the parent post; null for the thread root when it has no parent.
depthintegernothread0 = the requested post, 1 = direct reply, etc.
isRepostbooleannoauthor-feedFeed entry is a repost by the target account.
isPinbooleannoauthor-feedFeed entry is the account's pinned post.
uristringnopost rowsCanonical AT-URI (at://did/app.bsky.feed.post/rkey).
cidstringnopost rowsContent identifier of the post record.
urlstringnoallbsky.app post URL (post rows) or profile URL (profile rows).
authorobjectnopost rows{ handle, displayName, did, avatar } — each string or null.
textstringnopost rowsFull post text (empty string for text-less posts).
createdAtstringyesallISO 8601 creation time of the post (post rows) or account (profile rows).
likeCountintegernopost rowsLike count (0 when absent).
repostCountintegernopost rowsRepost count.
replyCountintegernopost rowsReply count.
quoteCountintegernopost rowsQuote-post count.
langsarray of stringsnopost rowsLanguage codes declared on the post record.
hashtagsarray of stringsnopost rowsHashtags parsed from rich-text facets (no # prefix).
linksarray of stringsnopost rowsExternal link URLs parsed from rich-text facets.
embedTypestringyespost rowsAT Protocol embed view type (e.g. app.bsky.embed.images#view); null when no embed.
imageUrlsarray of stringsnopost rowsFull-size image URLs from image embeds (empty when none).
videoPlaylistUrlstringyespost rowsHLS playlist URL for video embeds; null otherwise.
externalUrlstringyespost rowsURL of an external-link card embed; only present when the post has one.
didstringnoprofile rowsDecentralized identifier of the account.
handlestringnoprofile rowsBluesky handle.
displayNamestringyesprofile rowsDisplay name.
descriptionstringyesprofile rowsProfile bio.
avatarstringyesprofile rowsAvatar image URL.
bannerstringyesprofile rowsBanner image URL (full profile views; null in list views).
followersCountintegeryesprofile rowsFollower count (null in list views that omit counts).
followsCountintegeryesprofile rowsFollowing count (null in list views that omit counts).
postsCountintegeryesprofile rowsPost count (null in list views that omit counts).
indexedAtstringyesprofile rowsWhen the AppView last indexed the account.
labelsarray of stringsnoprofile rowsModeration label values (e.g. !no-unauthenticated).

Example item — search-posts

{
"query": "apify",
"uri": "at://did:plc:3qx3wgjvwbhbr2uvbv5qnot4/app.bsky.feed.post/3mt2dck2a3u2p",
"cid": "bafyreif5cxxqatw3om7sfto6xhtoovzfq6ewpw6dpcruhavjwv3cvsdgcq",
"url": "https://bsky.app/profile/pingoru.bsky.social/post/3mt2dck2a3u2p",
"author": {
"handle": "pingoru.bsky.social",
"displayName": "Pingoru.io",
"did": "did:plc:3qx3wgjvwbhbr2uvbv5qnot4",
"avatar": "https://cdn.bsky.app/img/avatar/plain/did:plc:.../bafkrei..."
},
"text": "Update: Apify marked this resolved at 14:05 UTC.",
"createdAt": "2026-08-14T14:05:43Z",
"likeCount": 12,
"repostCount": 3,
"replyCount": 1,
"quoteCount": 0,
"langs": ["en"],
"hashtags": ["apify"],
"links": ["https://status.apify.com"],
"embedType": "app.bsky.embed.images#view",
"imageUrls": ["https://cdn.bsky.app/img/feed_fullsize/plain/did:plc:.../bafkrei..."],
"videoPlaylistUrl": null
}

Example item — profiles

{
"did": "did:plc:z72i7hdynmk6r22z27h6tvur",
"handle": "bsky.app",
"url": "https://bsky.app/profile/bsky.app",
"displayName": "Bluesky",
"description": "The official Bluesky account.",
"avatar": "https://cdn.bsky.app/img/avatar/plain/did:plc:.../bafkrei...",
"banner": "https://cdn.bsky.app/img/banner/plain/did:plc:.../bafkrei...",
"followersCount": 3452871,
"followsCount": 8,
"postsCount": 312,
"createdAt": "2023-04-12T04:53:57Z",
"indexedAt": "2026-08-10T02:11:08Z",
"labels": []
}

Example item — author-feed

Same shape as search-posts but with target, isRepost, isPin instead of query:

{
"target": "bsky.app",
"isRepost": false,
"isPin": true,
"uri": "at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post/3l6oveex3ii2l",
"cid": "bafyreihx...",
"url": "https://bsky.app/profile/bsky.app/post/3l6oveex3ii2l",
"author": { "handle": "bsky.app", "displayName": "Bluesky", "did": "did:plc:z72i7hdynmk6r22z27h6tvur", "avatar": "https://cdn.bsky.app/img/avatar/plain/..." },
"text": "Welcome to Bluesky!",
"createdAt": "2024-10-24T17:02:23Z",
"likeCount": 88231, "repostCount": 12045, "replyCount": 4102, "quoteCount": 903,
"langs": ["en"], "hashtags": [], "links": [],
"embedType": null, "imageUrls": [], "videoPlaylistUrl": null
}

Example item — followers / following

Profile shape plus target:

{
"target": "bsky.app",
"did": "did:plc:44ybard66vv44zksje25o7dz",
"handle": "jay.bsky.team",
"url": "https://bsky.app/profile/jay.bsky.team",
"displayName": "Jay",
"description": "CEO of Bluesky.",
"avatar": "https://cdn.bsky.app/img/avatar/plain/...",
"banner": null,
"followersCount": null,
"followsCount": null,
"postsCount": null,
"createdAt": "2023-04-12T04:53:57Z",
"indexedAt": "2026-08-01T00:00:00Z",
"labels": []
}

(List views omit counts — expect null for followersCount/followsCount/postsCount/banner here; run profiles mode on the handles if you need counts.)

Example item — thread

Post shape plus threadOf, parentUri, depth:

{
"threadOf": "https://bsky.app/profile/bsky.app/post/3l6oveex3ii2l",
"parentUri": "at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post/3l6oveex3ii2l",
"depth": 1,
"uri": "at://did:plc:abc.../app.bsky.feed.post/3l6ovxyz...",
"cid": "bafyreig...",
"url": "https://bsky.app/profile/someone.bsky.social/post/3l6ovxyz...",
"author": { "handle": "someone.bsky.social", "displayName": "Someone", "did": "did:plc:abc...", "avatar": null },
"text": "Congrats on the launch!",
"createdAt": "2024-10-24T17:10:00Z",
"likeCount": 5, "repostCount": 0, "replyCount": 0, "quoteCount": 0,
"langs": ["en"], "hashtags": [], "links": [],
"embedType": null, "imageUrls": [], "videoPlaylistUrl": null
}

Example item — search-users

Profile shape plus query:

{
"query": "apify",
"did": "did:plc:xyz...",
"handle": "apify.com",
"url": "https://bsky.app/profile/apify.com",
"displayName": "Apify",
"description": "Full-stack web scraping platform.",
"avatar": "https://cdn.bsky.app/img/avatar/plain/...",
"banner": null,
"followersCount": null,
"followsCount": null,
"postsCount": null,
"createdAt": "2023-07-01T09:00:00Z",
"indexedAt": "2026-08-12T10:00:00Z",
"labels": []
}

Error semantics

  • Bad input fails fast. A mode missing its required field (queries, targets, or postUrls), or a malformed post URL in thread mode, aborts the run immediately with exit code 1 and an explanatory status message (e.g. Mode "profiles" requires at least one entry in "targets"). No partial charge beyond the start event.
  • Invalid targets fail loudly. A nonexistent handle or invalid parameter returns a permanent 4xx from the API; the run fails with the API's error detail in the message. Rows scraped before the failure are already in the dataset — nothing is silently dropped.
  • Transient errors are retried. 429 and 5xx responses get 3 attempts with exponential backoff (1s, 2s); 403 WAF blocks automatically fall back to the alternate official host. Only after all retries fail does the run fail.
  • Zero results is a success. A query matching nothing completes normally with an empty dataset (exit 0) — treat "run succeeded, 0 items" as a valid empty answer, not an error to retry.

Use from AI agents (MCP)

{
"mcpServers": {
"apify": {
"url": "https://mcp.apify.com/?tools=apricot_blackberry/bluesky-all-in-one",
"headers": { "Authorization": "Bearer <YOUR_APIFY_TOKEN>" }
}
}
}

Works in Claude, Cursor, ChatGPT deep research connectors, and any MCP client; the input schema above is the tool's parameter schema.

Use from code

curl (synchronous run, returns dataset items directly):

curl -X POST "https://api.apify.com/v2/acts/apricot_blackberry~bluesky-all-in-one/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"mode":"search-posts","queries":["apify"],"maxItems":100}'

JavaScript (apify-client):

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('apricot_blackberry/bluesky-all-in-one').call({
mode: 'search-posts',
queries: ['apify'],
maxItems: 100,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();

Python (apify-client):

from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("apricot_blackberry/bluesky-all-in-one").call(run_input={
"mode": "search-posts",
"queries": ["apify"],
"maxItems": 100,
})
items = client.dataset(run["defaultDatasetId"]).list_items().items

Use from automation platforms

  • n8n / Make / Zapier — use the native Apify integration and pick this actor by name (bluesky-all-in-one); the run's dataset feeds directly into the next step.
  • LangChain / LlamaIndex — call it through the Apify tool wrappers (ApifyActorsTool / ApifyActor loader) with the input JSON shown above.
  • Webhooks — Apify webhooks can fire on run completion (e.g. ACTOR.RUN.SUCCEEDED) to hand results to your own pipeline automatically, no polling required.

Pricing

Pay per event — you only pay for what you scrape:

EventPriceWhen
Actor startper runOnce per run
Resultper rowEvery dataset row, all modes

That works out to $1 per 1,000 results — no subscription, no proxy fees, no platform-usage surprises, and maxItems caps your spend on every run.

FAQ

Do I need a Bluesky account or app password? No. The actor only reads public data through Bluesky's official public AppView API. Nothing to configure, no account at risk.

Can I export Bluesky followers to CSV? Yes — run the followers mode with your target handle, then download the dataset as CSV from the run's Storage tab (or via the dataset API).

How fresh is the data? Near-realtime. The AppView indexes the AT Protocol firehose continuously; a search-posts run with sort: latest returns posts from seconds ago.

Does it scrape private or deleted content? No. Only publicly visible content served by the official AppView. Blocked, deleted, or private data is not accessible — this keeps the actor compliant and stable.

What happens if I hit a rate limit? The actor retries automatically with exponential backoff. For very large jobs, split targets across multiple runs.

Can I search posts by a specific user within a date range? Yes — use search-posts with author, since, and until set. Or use author-feed to get a user's complete timeline.

Changelog

  • 1.1 — Published dataset output schema (typed, validated rows) + agent-first docs: MCP config, curl/JS/Python snippets, error semantics.
  • 1.0 — Initial release: 7 modes (search-posts, profiles, author-feed, followers, following, thread, search-users), facet parsing (hashtags/links), media extraction, pay-per-result pricing.