Bluesky Profile Scraper avatar

Bluesky Profile Scraper

Pricing

from $1.05 / 1,000 results

Go to Apify Store
Bluesky Profile Scraper

Bluesky Profile Scraper

Collect Bluesky profiles, posts, followers, follows and lists, or search for accounts by keyword. Get handles, DIDs, bios, profile counts, post text and engagement metrics in structured rows. Uses the public Bluesky API without login or a browser, with cursor-based pagination.

Pricing

from $1.05 / 1,000 results

Rating

0.0

(0)

Developer

Axiom Works

Axiom Works

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

5 days ago

Last modified

Categories

Share

Returns: one row per Bluesky profile, post, follower, follow or list with id, recordType, did, handle, displayName, createdAt, sourceUrl; profile counts and post text depend on the mode. Input: {"mode": "profiles", "handles": ["bsky.app", "jay.bsky.team"], "maxItems": 10} Best for: lead lists of Bluesky accounts, follower and audience analysis, social listening on an account's posts.

{
"id": "did:plc:z72i7hdynmk6r22z27h6tvur",
"recordType": "profile",
"handle": "bsky.app",
"displayName": "Bluesky",
"followersCount": 35250704,
"followsCount": 19,
"postsCount": 870,
"sourceUrl": "https://public.api.bsky.app/xrpc/app.bsky.actor.getProfiles?actors=bsky.app&actors=jay.bsky.team&actors=atproto.com"
}

What does Bluesky Profile Scraper do?

Bluesky Profile Scraper is a Bluesky scraper and Bluesky API wrapper in one Actor. It reads the open, keyless public Bluesky API and returns clean rows you can export as JSON, CSV or Excel. You give it handles, DIDs or profile links, choose a mode, and it collects profile details, an account's posts, its followers, the accounts it follows, its lists, or the results of a keyword search for accounts.

There is no login and no browser involved, so runs are fast and cheap. The Actor sends about two to three requests per second, follows the cursor pagination the API provides, and retries politely when the API asks it to slow down. Every row carries the sourceUrl of the API call it came from and a stable id, so you can de-duplicate across runs.

Typical jobs are building lead lists of accounts in a niche, measuring how big an account is and who follows it, tracking what a brand or creator posts and how many likes and reposts it gets, and mapping which accounts an organisation follows.

What data can you get from Bluesky?

Each row has a recordType that tells you which fields to expect. Fields that do not apply to a type are empty.

FieldDescriptionExample
recordTypeprofile, post, follower, follow or listprofile
idStable unique ID of the rowdid:plc:z72i7hdynmk6r22z27h6tvur
didDecentralized ID of the account (the author for posts, the creator for lists)did:plc:z72i7hdynmk6r22z27h6tvur
handleAccount handlebsky.app
displayNamePublic display nameBluesky
descriptionProfile bio, or list descriptionofficial Bluesky account
avatarAvatar image URLhttps://cdn.bsky.app/img/avatar/...
bannerBanner image URL (profiles mode)https://cdn.bsky.app/img/banner/...
pinnedPostUriAT URI of the pinned post (profiles mode)at://did:plc:.../app.bsky.feed.post/3l6oveex3ii2l
labelsModeration label values on the account (profiles mode)[]
listsCountLists the account created (profiles mode)18
feedGeneratorsCountCustom feeds the account created (profiles mode)7
starterPacksCountStarter packs the account created (profiles mode)15
isLabelerWhether the account is a moderation labeler (profiles mode)false
profileUrlPublic bsky.app profile page (profiles mode)https://bsky.app/profile/bsky.app
createdAtAccount creation time, or post creation time2023-04-12T04:53:57.057Z
verifiedVerification status, null when absent from the responsenone
followersCountNumber of followers (profiles mode)35133356
followsCountNumber of accounts followed (profiles mode)15
postsCountNumber of posts (profiles mode)865
subjectDidAccount whose followers or follows were listeddid:plc:z72i7hdynmk6r22z27h6tvur
subjectHandleHandle of that accountbsky.app
searchQueryQuery that matched the profile (searchProfiles mode)journalist
uriat:// URI of the post or listat://did:plc:.../app.bsky.feed.post/3mwolmfws5k2r
cidContent ID of the postbafyrei...
textPost textHappy opening day of hockey season
likeCountLikes on the post952
repostCountReposts of the post135
replyCountReplies to the post70
quoteCountQuote posts16
langsLanguage codes declared on the post["en"]
indexedAtPost or list indexing time; list createdAt retains its existing indexing-time value2025-08-06T03:43:31.609Z
trustedVerifierStatusAccount trusted verifier status, null when unavailablevalid
imagesPost image URLs and original alternative text, including media attached to quotes[{"fullsize":"https://cdn.bsky.app/...","thumb":"https://cdn.bsky.app/...","alt":"A blue sky"}]
videoPlaylistPost video playback playlist URLhttps://video.bsky.app/.../playlist.m3u8
externalUrlPost link card destination URLhttps://bsky.app
quotedPostUriAvailable quoted post AT URIat://did:plc:.../app.bsky.feed.post/...
hasMediaTrue when the post has an embed such as an image, video, link card or quotetrue
isRepostTrue when the feed entry is a repost by the target accountfalse
repostedAtWhen the account reposted it (reposts only)2026-09-30T13:30:00.000Z
postUrlbsky.app link to the posthttps://bsky.app/profile/.../post/3mwolmfws5k2r
nameList name (lists mode)Hockey writers
purposeList purpose, curation or moderationapp.bsky.graph.defs#curatelist
listItemCountNumber of accounts on the list120
accountTypeOptional AI classification: person, brand, media, bot or otherbrand
sourceUrlAPI endpoint the row came fromhttps://public.api.bsky.app/xrpc/...

Follower and follow rows come from list endpoints that do not include follower, follow or post counts. Use profiles mode when you need those numbers. Search rows are also list items and do not include the counts either.

How to use Bluesky Profile Scraper

  1. Open the Actor in Apify Console and pick a Mode.
  2. Add one or more Handles. You can paste a handle such as bsky.app, a DID, or a full bsky.app/profile/... link. In searchProfiles mode, fill Search query instead.
  3. Set Maximum results. This is the total across all handles, and it is the most important setting for followers and posts of large accounts.
  4. Click Start. When the run finishes, open the Output tab and export the dataset as JSON, CSV, Excel, XML or RSS.

Some tips. The profiles mode takes up to 25 handles per API call, so checking hundreds of accounts is quick. In posts mode, the default filter returns posts and reposts but not replies. The API only exposes a limited depth of an account's feed, so a very prolific account may return fewer posts than it has published. Follower lists of very large accounts run into the tens of millions of rows, so always set a sensible limit.

Input

For API and MCP calls, prefills are examples rather than automatic targets. Supply handles for account modes, or searchQuery with mode: "searchProfiles". The default mode is profiles; maxItems is one shared budget across every target, not a separate allowance for each handle. Three profile targets can therefore return three rows even when maxItems is 10. Search ignores handles, and postFilter affects only posts.

An empty handle list returns zero rows. Unknown accounts are skipped so another supplied account can still return data. If no rows are found, the final status identifies what to change: supply existing handles or profile URLs, broaden searchQuery, try another account or mode for an empty feed, or retry later and configure Apify Proxy for persistent blocks. A missing searchQuery in searchProfiles mode produces a validation message.

FieldTypeRequiredDescription
handlesarray of stringsYes (except searchProfiles)Handles, DIDs, AT profile URIs or bsky.app profile URLs, e.g. @bsky.app, one per line.
modestringNoprofiles, posts, followers, follows, lists or searchProfiles. Default profiles.
searchQuerystringOnly for searchProfilesKeyword to find accounts by name, handle or bio.
postFilterstringNoposts_no_replies, posts_with_replies or posts_with_media. Default posts_no_replies.
maxItemsintegerNoTotal rows to return, 1-10000, default 50.
enrichAccountTypebooleanNoAdd an AI account type classification. Default false.
proxyConfigurationobjectNoProxy settings. Default: no proxy, because the public API is open.
{
"mode": "followers",
"handles": ["bsky.app"],
"maxItems": 120,
"enrichAccountType": false,
"proxyConfiguration": { "useApifyProxy": false }
}

List records also include the creator’s displayName when available. Post embed fields are nullable: a text-only post has no image, video, external link, or quoted-post value. Image alternative text is copied verbatim and can be an empty string. An unavailable or blocked quoted post has no quotedPostUri.

Output

Every row is a flat JSON object. Here are real rows from a run: a profile and a post.

[
{
"id": "did:plc:oky5czdrnfjpqslsw2a5iclo",
"recordType": "profile",
"did": "did:plc:oky5czdrnfjpqslsw2a5iclo",
"handle": "jay.bsky.team",
"displayName": "Jay πŸ¦‹",
"description": "Founder & Chief Innovation Officer @ Bluesky\n\nWorking on @attie.ai\n\n🌱 πŸͺ΄ 🌳",
"createdAt": "2022-11-17T06:31:40.296Z",
"verified": "valid",
"sourceUrl": "https://public.api.bsky.app/xrpc/app.bsky.actor.getProfiles?actors=bsky.app&actors=jay.bsky.team&actors=atproto.com"
},
{
"id": "at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post/3mwolmfws5k2r",
"recordType": "post",
"handle": "bsky.app",
"text": "Happy opening day of hockey season, NHL fans!",
"createdAt": "2026-09-29T19:43:14.855Z",
"likeCount": 952,
"repostCount": 135,
"replyCount": 70,
"isRepost": false
}
]

Results can be downloaded in JSON, CSV, Excel, XML or RSS from the dataset, or read through the API.

How much does it cost to scrape Bluesky?

Bluesky Profile Scraper is charged per result returned. You pay only for the rows saved, and maxItems caps how many that can be. When you turn on the optional AI account classification, an additional charge applies only for rows where the classification succeeded. See the Pricing tab for the current prices.

Use with AI agents (MCP)

Connect Bluesky Profile Scraper to Claude, Cursor or any MCP client through the Apify MCP server:

{
"mcpServers": {
"apify": {
"url": "https://mcp.apify.com/?tools=axiomworks/bluesky-profile-scraper"
}
}
}

Example prompts:

  1. "Get the follower count and bio of the Bluesky accounts bsky.app and atproto.com."
  2. "Fetch the latest 50 posts from jay.bsky.team on Bluesky with their like and repost counts."
  3. "Find Bluesky accounts matching 'data journalist' and list their handles and bios."

Integrations and API

Run Bluesky Profile Scraper from your own code with the Apify API. Set APIFY_TOKEN to your Apify API token (send it in the Authorization header, never in the URL).

Python

from apify_client import ApifyClient
client = ApifyClient("<YOUR_API_TOKEN>")
run = client.actor("axiomworks/bluesky-profile-scraper").call(run_input={"mode": "profiles", "handles": ["bsky.app"], "maxItems": 10})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
print(item)

JavaScript

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: '<YOUR_API_TOKEN>' });
const run = await client.actor('axiomworks/bluesky-profile-scraper').call({ mode: 'posts', handles: ['bsky.app'], maxItems: 60 });
const { items } = await client.dataset(run.defaultDatasetId).listItems();

cURL

curl -X POST "https://api.apify.com/v2/acts/axiomworks~bluesky-profile-scraper/run-sync-get-dataset-items" \
-H "Authorization: Bearer $APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"mode": "profiles", "handles": ["bsky.app"], "maxItems": 10}'

The Integrations tab connects the dataset to Make, Zapier, n8n, Google Sheets and webhooks, so you can schedule runs and send results on.

FAQ

Do I need a Bluesky account or API key? No. The Actor reads the open public API and needs no login.

Why did a run return 0 results? The handle may not exist, may be deactivated, or the account may have no data of the requested type. Check the spelling and try the profiles mode first. The log lists handles that were not found.

Why are some follower rows missing counts? The follower, follow and search endpoints return a short profile view without counts. Run profiles mode on the handles you care about to get followersCount, followsCount and postsCount.

Are reposts included in posts mode? Yes, with the default filter. They have isRepost set to true, and the handle and did fields are those of the original author, not the account you scraped.

Can I search post text by keyword? No. Keyword search of posts is not available on the public API. Account search by keyword is available through searchProfiles mode.

How does the AI account classification work? When enabled, the bio and display name are sent to a third-party AI processor (Jev by TypeSafe), which picks one of person, brand, media, bot or other. If the processor fails, the row is still returned without accountType and no extra charge applies.

How fresh is the data? Every run reads live from the API, so counts and posts are current at run time.

Can I get the data on a schedule? Yes. Use Apify schedules and compare runs by id.

The Actor collects only publicly available data from the open Bluesky API, with no login and no private content. Profile data can be personal data, so you are responsible for using it lawfully, for example under GDPR, and for respecting the people behind the accounts. This is not legal advice. This Actor is an independent tool and is not affiliated with or endorsed by Bluesky.

Feedback

Found a bug or need a field that isn't here? Open an issue on the Issues tab and we'll look at it.