Bluesky Scraper — search posts, profiles, followers as JSON avatar

Bluesky Scraper — search posts, profiles, followers as JSON

Pricing

from $2.00 / 1,000 item results

Go to Apify Store
Bluesky Scraper — search posts, profiles, followers as JSON

Bluesky Scraper — search posts, profiles, followers as JSON

Search Bluesky posts by keyword or fetch profiles, followers, follows and author feeds via the open AT Protocol API. Engagement counts, embeds, language and direct links in clean JSON — no login, no API key, no proxies. Built for social analytics, research, brand monitoring and AI agents.

Pricing

from $2.00 / 1,000 item results

Rating

0.0

(0)

Developer

Daniel Posztos

Daniel Posztos

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

0

Monthly active users

3 days ago

Last modified

Categories

Share

SEO title: Bluesky Scraper — search posts, profiles, followers as JSON SEO description: Search Bluesky posts by keyword or fetch profiles, followers, follows and author feeds via the open AT Protocol API. Engagement counts, embeds, language and direct links in clean JSON — no login, no API key, no proxies. Built for social analytics, research, brand monitoring and AI agents.

One actor, five data types, zero credentials. Bluesky runs on the open AT Protocol: its AppView API is public and documented, so this actor talks to the same endpoints the official app uses — no scraping fragility, no anti-bot arms race, no proxy costs.

What it does

Pick a type and get clean, deduplicated JSON items:

typeWhat you getNeeds
posts (default)Keyword search across all Bluesky posts, newest firstsearch_query
profilesFull profile per account: bio, follower/post countshandles
followersWho follows the given account(s)handles
followsWho the given account(s) followhandles
author_feedRecent posts/reposts from the given account(s)' own timelineshandles

Input

FieldTypeDefaultDescription
typestring"posts"One of posts, profiles, followers, follows, author_feed.
search_querystringKeyword(s) for post search. Required for (and only used by) type: "posts".
handlesarray of strings[]Bluesky handles ("bsky.app") and/or DIDs ("did:plc:..."), mixable. Required for every type except posts.
max_itemsinteger (1–5000)100Total result cap. For followers/follows/author_feed with multiple handles the budget is split evenly across handles.
date_fromstringISO date ("2026-06-01") or datetime ("2026-06-01T00:00:00Z"). Only used by type: "posts": restricts results to posts on/after this moment.

Type-specific required fields are validated up front with an error message that says exactly what to add (e.g. type: "posts" without search_query"Post search needs a keyword: set e.g. 'search_query': "bitcoin"").

Worked example — input

{
"type": "posts",
"search_query": "bitcoin",
"max_items": 25
}

Worked example — output (1 of 25 items, real data)

{
"post_uri": "at://did:plc:22mtqdiuelequw4xh4hirr4w/app.bsky.feed.post/3mpwduk2qgk2u",
"cid": "bafyreidyobz5lit5f4k46yeyil3i26b3jvkdv35xuhhmztxuufdjwaqry4",
"author": {
"handle": "bufbvr.bsky.social",
"did": "did:plc:22mtqdiuelequw4xh4hirr4w",
"display_name": "Buffcoat and Beaver"
},
"text": "Just walked into the luncheonery store... Oh, macro-flavored Bitcoin.",
"created_at": "2026-07-05T19:49:34.521858+00:00",
"likes": 0,
"reposts": 0,
"replies": 0,
"quotes": 0,
"embeds": [],
"lang": "en",
"url": "https://bsky.app/profile/bufbvr.bsky.social/post/3mpwduk2qgk2u",
"scraped_at": "2026-07-05T19:51:39.872045+00:00"
}

A profile lookup ({"type": "profiles", "handles": ["bsky.app"]}) returns:

{
"did": "did:plc:z72i7hdynmk6r22z27h6tvur",
"handle": "bsky.app",
"display_name": "Bluesky",
"description": "official Bluesky account (check username👆)...",
"followers_count": 33981092,
"follows_count": 10,
"posts_count": 800,
"created_at": "2023-04-12T04:53:57.057Z",
"url": "https://bsky.app/profile/bsky.app",
"scraped_at": "2026-07-05T19:45:01.163306+00:00"
}

Output schema

type: "posts" and type: "author_feed" — post item

FieldTypeNotes
post_uristringAT Protocol URI (at://did/app.bsky.feed.post/rkey) — the post's stable ID
cidstringContent hash of the post record
author.handlestringAuthor's handle (may read handle.invalid while an account's domain verification is broken)
author.didstringAuthor's stable decentralized ID
author.display_namestring or null
textstringPost text
created_atstring (ISO 8601)Author-declared creation time
likes / reposts / replies / quotesintegerEngagement counts at scrape time
embedsarraySimplified: {type, url, alt} with type one of image, link, quote, video, other. alt is image alt-text / link title.
langstring or nullPost's self-declared primary language ("en", …)
urlstringhttps://bsky.app/profile/{handle}/post/{rkey} — opens in a browser
is_repostbooleanauthor_feed only: the requested account reposted someone else's post (whose author is then the original author)
feed_ofstringauthor_feed only: the requested handle/DID this item came from
scraped_atstring (ISO 8601)When this actor fetched the data

type: "profiles" — profile item

FieldTypeNotes
didstringStable decentralized ID
handlestringe.g. "bsky.app"
display_namestring or null
descriptionstring or nullProfile bio
followers_count / follows_count / posts_countinteger
created_atstring (ISO 8601)Account creation time
urlstringhttps://bsky.app/profile/{handle}
scraped_atstring (ISO 8601)

type: "followers" and type: "follows" — connection item

FieldTypeNotes
subject_handlestringThe requested account this connection belongs to
subject_didstringThe requested account's DID
did, handle, display_name, description, created_at, urlThe connected profile (the follower, or the followed account) — same meaning as the profile item
followers_count / follows_count / posts_countnullAlways null here: Bluesky's follower/follow list API returns compact profiles without counts. Run a second type: "profiles" pass on the handles you care about to get counts.
scraped_atstring (ISO 8601)

Pricing (pay-per-event)

EventPriceWhen it's charged
item-result$0.002Once per item returned, regardless of type

Plus Apify's own apify-actor-start synthetic event (first 5 seconds of compute free, platform-managed).

Examples: 1000 posts = 1000 × $0.002 = $2.00. 25-post smoke run = $0.05. 10 profile lookups = $0.02. 5000 followers = $10.00.

If a run's cost would exceed the Max total charge USD you set for it, the actor stops producing further results at exactly that point — it never crashes and never produces unbilled/"free" results past the limit. Delivered count always equals charged count.

Error messages

  • type: "posts" without search_query: "Post search needs a keyword: set e.g. 'search_query': "bitcoin"..." — add the field (the message also tells you which type to use if you meant account lookups).
  • profiles/followers/follows/author_feed without handles: "This type needs at least one Bluesky handle or DID to look up..."
  • Unknown type: the message lists all five valid values and what each does.
  • max_items out of range / wrong types / bad date_from: the message states the expected format and a corrected example.
  • A handle that doesn't exist: that handle is skipped with a log warning naming it (other handles in the list still return data); if every input handle fails, the run ends with the explicit 0-results warning below.
  • 0 results with otherwise valid input: not an error — the run succeeds, but the log carries an explicit WARNING: 0 results produced despite valid input (...) line and the run's status message says so. Never a silent "nothing happened."

Known limitations (documented, not hidden)

  • Public data only. The actor reads Bluesky's open, unauthenticated AppView. No private accounts, no logged-in-only data, no DMs — by design.
  • Post search depth: Bluesky's API rejects unauthenticated cursor pagination on post search, so this actor paginates search results by time-windowing (until keyset on the post timestamp) instead. Results stay newest-first and are deduplicated; pathological corner cases (hundreds of posts sharing one millisecond) could theoretically end pagination early. Followers/follows/author feeds use normal cursor pagination (not affected).
  • Rate limits: the AppView rate-limits per IP; bursts get HTTP 429, which the actor retries with exponential backoff automatically. Very large runs simply take a bit longer.
  • Follower/follow counts are null in list items (see the connection-item table) — that's what the upstream list API provides.
  • author_feed includes reposts (marked is_repost: true); pinned-post duplication is avoided by URI-level dedup.

Data sources (public, no API key, no scraping)

Bluesky AppView (AT Protocol XRPC over HTTPS): app.bsky.feed.searchPosts, app.bsky.actor.getProfile, app.bsky.feed.getAuthorFeed, app.bsky.graph.getFollowers, app.bsky.graph.getFollows — the same documented, unauthenticated endpoints the official Bluesky app calls.