Bluesky Posts & Profiles Search
Pricing
Pay per event
Bluesky Posts & Profiles Search
Bluesky author-feed and profile scraper for public handles. Export posts and profiles as JSON. Public keyword search can be denied; source errors are recorded.
Pricing
Pay per event
Rating
0.0
(0)
Developer
Grit
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
10 days ago
Last modified
Categories
Share
Bluesky posts scraper returns public posts and profiles from requested feeds or handles. Bluesky API keyword search also returns public posts when its endpoint allows access.
Public keyword search returned HTTP 403 during the 2026-10-05 live check. This actor records that denial as an uncharged error and continues other sources. It does not log in or switch hosts to evade denied access. For a working public-data run, supply author feeds or profile handles.
What it returns
Each result has a type: post, profile or error. Posts are deduplicated by their AT URI, profiles by DID. The first source to return a duplicate supplies its provenance. Reposts retain the original author's identity and the reposter's identity separately.
| Field | Type | Example value |
|---|---|---|
type | string | "error" |
source | string | "search" |
input | string | "bluesky" |
error_code | string | "public_access_denied" |
http_status | integer | 403 |
errors | array | [] |
fetched_at | string | "2026-10-05T13:19:01.033361Z" |
did | string | "did:plc:z72i7hdynmk6r22z27h6tvur" |
handle | string | "bsky.app" |
url | string | "https://bsky.app/profile/did:plc:z72i7hdynmk6r22z27h6tvur" |
display_name | string | "Bluesky" |
description | string | "official Bluesky account (check username👆)\n\nBugs, feature requests, feedback: support@bsky.app" |
avatar | string | "https://cdn.bsky.app/img/avatar/plain/did:plc:z72i7hdynmk6r22z27h6tvur/bafkreihwihm6kpd6zuwhhlro75p5qks5qtrcu55jp3gddbfjsieiv7wuka" |
banner | string | "https://cdn.bsky.app/img/banner/plain/did:plc:z72i7hdynmk6r22z27h6tvur/bafkreichzyovokfzmymz36p5jibbjrhsur6n7hjnzxrpbt5jaydp2szvna" |
followers_count | integer | 35154701 |
follows_count | integer | 15 |
posts_count | integer | 865 |
created_at | string | "2023-04-12T04:53:57.057Z" |
indexed_at | string | "2025-10-27T21:05:26.152Z" |
pinned_post_uri | string | "at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post/3l6oveex3ii2l" |
labels | array | [] |
verification | object | {"verifications": [], "verifiedStatus": "none", "trustedVerifierStatus": "valid"} |
uri | string | "at://did:plc:ifn645rwvsuolxg7o3w7ouo4/app.bsky.feed.post/3mwqgsd4cgs2a" |
cid | string | "bafyreiesf6zo5wqojuoxdekbqap7pqev6t6xa2q5kzayntr6bgrt2ixi7m" |
text | string | "If you're in line to vote for @bsky38.com, please stay in line! Polls close in just under 5 hours." |
author_did | string | "did:plc:ifn645rwvsuolxg7o3w7ouo4" |
author_handle | string | "aster.id" |
author_display_name | string | "Aster ID" |
author_avatar | string | "https://cdn.bsky.app/img/avatar/plain/did:plc:ifn645rwvsuolxg7o3w7ouo4/bafkreicid7hoyx5d2if24eryyhb3lg4ioy2mdrmiriga3k2cay6k5io4ta" |
like_count | integer | 740 |
repost_count | integer | 148 |
reply_count | integer | 18 |
quote_count | integer | 34 |
bookmark_count | integer | 154 |
languages | array | ["en"] |
links | array | [] |
hashtags | array | [] |
mentions | array | [] |
images | array | [] |
video | null | null |
external | object | null |
quoted_uri | string | "at://did:web:bsky38.com/app.bsky.feed.post/3muphbj35cs2h" |
reply_parent_uri | null | null |
reply_root_uri | null | null |
is_repost | boolean | true |
reposted_by_handle | string | "bsky.app" |
reposted_by_did | string | "did:plc:z72i7hdynmk6r22z27h6tvur" |
reposted_at | string | "2026-09-30T18:15:05.366Z" |
Missing optional values are null or empty collections, depending on the field. Missing counts remain null. Counts and content are snapshots and can change between requests. Fields for other result types are absent.
Input example
{"handles": ["bsky.app"],"maxItems": 10}
Use the input schema for the remaining filters and defaults.
Output example
This is a selected-field excerpt of the first object in sample_output.json.
{"type": "error","source": "search","input": "bluesky","error_code": "public_access_denied","http_status": 403,"fetched_at": "2026-10-05T13:19:01.033361Z"}
Pricing
Proposed custom pay-per-event prices: $0.0005 per post and $0.0005 per profile ($0.50 per 1,000 successful results). No custom fee for errors, duplicates or empty results. Platform compute/storage usage is additional. Pricing is a proposal for the owner to configure before publication.
The highest-use comparable Bluesky post extractor in the Store API query on 2026-10-05 was lexis-solutions/bluesky-posts-scraper, with 322 total users: $0.0011/result on FREE, $0.00072 on its lowest tier and $0.004/start. The proposal is below its result price on every listed tier. See PRICING.md for the comparison and publishing configuration.
Limits and reliability
- Reads only the three official public AppView endpoints named below, without credentials.
- Pagination follows supplied cursors; an empty page, source cap or repeated cursor ends that source. Search pagination does not guarantee the entire result set.
- Each HTTP request has a 20-second timeout. Network failures, HTTP 429 and server errors receive at most three retries with exponential backoff and bounded Retry-After handling. HTTP 401/403 is not retried.
- A target failure or malformed individual post produces an uncharged error row while other targets continue. Storage and billing failures fail the run instead of being disguised as source errors.
maxItemsincludes error rows. The actor also stops when the SDK reports that the configured event charge limit is reached.- Deleted accounts/posts, public visibility restrictions, API changes, rate limits and search denial can reduce available results. Media URLs can expire; the actor exports references, not downloaded files.
- Cloud execution, actual paid billing and Docker execution have not been exercised locally.
FAQ
What limits the output? The maxItems input caps output rows; its schema maximum is 100,000. Source availability and errors may yield fewer rows.
Does it need a proxy, login, or API key? The actor uses public AppView without login. Public keyword search can be denied; denied searches produce error rows.
How is it priced? PRICING.md proposes $0.0005 per unique post or profile row. Error and duplicate rows do not trigger those custom events; this proposal is unpublished.
What happens on rate limits? The client retries throttling and server failures with backoff and Retry-After. A denied public search is reported as an error.
How fresh is the data? Rows reflect the public AppView response at run time. Use fetched_at to judge retrieval freshness.
Does it include private posts? No login is used; the actor retrieves what the public AppView supplies.
Use with AI agents / MCP
After the owner publishes the actor, add its actor identifier to an Apify MCP integration or invoke it through the Apify API. This project does not implement its own MCP server and has not been published. An agent can provide handles or profileHandles for public-account retrieval, cap the output with maxItems, then process JSON rows by type. Treat type: error as a source failure and preserve fetched_at for freshness.
The output schema exposes the default dataset items URL as {{links.apiDefaultDatasetUrl}}/items. Do not ask an agent to supply login credentials or bypass a public search denial.
Development
Use the existing project Python environment without installing packages into it:
C:/GritWork/earn/.venv/Scripts/python.exe -X utf8 -m pytest -qC:/GritWork/earn/.venv/Scripts/python.exe -X utf8 -m pytest -q --live -m live -sapify validate-schemaC:/GritWork/earn/.venv/Scripts/python.exe -X utf8 scripts/run_local.py examples/input.json --storage storage-demo
Run these commands from the actor directory. The local helper requires a fresh storage directory, sets APIFY_LOCAL_STORAGE_DIR, writes key_value_stores/default/INPUT.json and starts the same interpreter with python -m src. It clears inherited Apify cloud settings for local execution. No Apify CLI is used to run the actor. The standard storage layout is in storage/; RUN_SUMMARY records output counts, duration and event calls. Local charging is disabled by default, so a local event call is not evidence of paid cloud billing.
Real public response fixtures, fetch timestamps and source URLs are saved in tests/fixtures/. Controlled pagination and malformed variants are created in tests from those responses. The live smoke test is explicitly marked live and is opt-in. Docker installs the pinned runtime requirements from requirements.txt on apify/actor-python:3.12.
Official sources: public API routing, searchPosts lexicon, getAuthorFeed lexicon, getProfile lexicon, Apify output schema.