Bluesky Scraper avatar

Bluesky Scraper

Pricing

from $0.40 / 1,000 useful results

Go to Apify Store
Bluesky Scraper

Bluesky Scraper

Extract public Bluesky profiles, posts, search results, feeds, followers, and following through official AT Protocol APIs.

Pricing

from $0.40 / 1,000 useful results

Rating

0.0

(0)

Developer

Aung Kyaw Khant

Aung Kyaw Khant

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

8 days ago

Last modified

Share

Extract public Bluesky profiles, posts, search results, feeds, followers, and following lists through the official AT Protocol APIs.

The Actor returns normalized JSON records in the default Apify dataset. It does not scrape the Bluesky website, run a browser, download media files, or access private content.

Billing

The configured public price is:

$0.0004 per saved useful result
$0.40 per 1,000 saved useful results

A billing event is attempted only when one of these useful records is saved:

profile
post
search_post
feed_post
follower
following

These records are never billed:

feed_metadata
run_summary
error

The Actor also does not bill for:

  • AT Protocol requests or pagination requests
  • retries
  • filtered records
  • duplicate records skipped during the run
  • records rejected as malformed
  • requested results that were not saved

Every dataset row includes:

{
"data": {
"isBillable": true,
"billingCategory": "billable_useful_result"
}
}

Control rows use isBillable=false and billingCategory="non_billable_control_record".

To prevent accidental overcharging, an Apify cloud run refuses to start unless all of the following are true:

  • the pricing model is Pay Per Event
  • the useful-result event price is exactly $0.0004
  • the synthetic default dataset-item event is absent or exactly $0

This prevents useful rows from being charged at an unintended rate, prevents double charging, and prevents summary or error rows from being charged.

Run scale and safety caps

runMode controls only result and page safety caps. It does not make a run free and it does not change the per-result price.

Run modeMaximum useful resultsMaximum pagesBilling
evaluation1003Per saved useful result
scale10,000100Per saved useful result

Requests above the selected cap are clamped and reported in run_summary.warnings.

limit is a maximum, not a guaranteed delivery count. Output can be lower when the upstream result set ends, filters remove records, maxPages is reached, a failure occurs, or the Apify run charge limit is reached.

Operation-aware input contract

The Actor never switches operations or infers intent from unrelated fields. A meaningful operation-specific field supplied with the wrong operation fails validation before Bluesky client creation, route dispatch, useful-result dataset writes, or billing. For example, {"operation":"posts","query":"Bitcoin"} fails clearly because query belongs to search; it does not fetch the default bsky.app posts and produces zero useful-result charge attempts.

Operation-specific fields are enforced as follows:

FieldAccepted operation(s)
handleprofile, posts, connections
query, language, sortsearch
feedUrl, feedAliasfeed
connectionTypeconnections
includeReplies, includeReposts, includeQuotesposts
cursorposts, search, feed, connections
since, untilposts, search, feed

The Apify form can automatically include harmless defaults such as handle="bsky.app", feedAlias="whats-hot", connectionType="followers", includeReplies=false, includeReposts=false, and includeQuotes=true. Those defaults are accepted when irrelevant. Changing an irrelevant field to a meaningful non-default value is rejected instead of ignored. When feedUrl is provided, leave feedAlias omitted or at its harmless default.

Supported operations

Profile

Fetch one public profile.

{
"operation": "profile",
"handle": "bsky.app"
}

A profile run always applies a useful-result limit of one.

User posts

Fetch posts from one author feed.

{
"operation": "posts",
"handle": "bsky.app",
"limit": 100,
"maxPages": 3,
"runMode": "evaluation",
"includeReplies": false,
"includeReposts": false,
"includeQuotes": true
}

Search posts

Search currently requires Bluesky authentication.

{
"operation": "search",
"query": "atproto",
"sort": "latest",
"limit": 100,
"language": "en",
"blueskyIdentifier": "your-handle.bsky.social",
"blueskyAppPassword": "your-dedicated-app-password"
}

Use a dedicated Bluesky app password, not your account password. Both credential fields must be supplied together.

Search ordering is explicit:

  • sort="latest" is the default and requests the newest matching posts first.
  • sort="top" requests Bluesky's relevance-ranked results. Bluesky controls that ranking, so top must not be described as simply the most-liked posts.

limit=10 means the Actor saves up to the first 10 qualifying posts in the selected upstream ordering. It does not mean the oldest 10 posts or every historical match.

Feed

Fetch the built-in What's Hot feed:

{
"operation": "feed",
"feedAlias": "whats-hot",
"limit": 100
}

Fetch a specific feed using an AT URI or Bluesky feed URL:

{
"operation": "feed",
"feedUrl": "https://bsky.app/profile/example.bsky.social/feed/news",
"limit": 100
}

A feed run can include one non-billable feed_metadata row in addition to billable feed_post rows and the non-billable run_summary.

Followers or following

{
"operation": "connections",
"handle": "bsky.app",
"connectionType": "followers",
"limit": 100
}

Set connectionType to following to fetch accounts followed by the target.

Filters

since and until apply to post-shaped records from posts, search, and feed.

Timestamps must include a date, time, and explicit timezone:

{
"since": "2026-07-01T00:00:00Z",
"until": "2026-07-07T23:59:59Z"
}

Accepted timestamps are normalized to UTC before use. Date-only and timezone-less values are rejected to avoid silent timezone assumptions. Upstream post timestamps are held to the same rule for filtering: malformed, timezone-less, or implausibly future timestamps are not used as sortAt; the Actor falls back to a valid indexedAt when available.

language is optional for search. Leave it blank when results should not be restricted by language.

sort applies only to search and accepts latest or top. The default is latest. The Apify form may include that default for other operations harmlessly, but sort="top" with a non-search operation is rejected before scraping or billing.

Output format

Every row uses a common envelope:

{
"recordType": "post",
"operation": "posts",
"schemaVersion": "1.0",
"scrapedAt": "2026-07-11T00:00:00Z",
"sourceUrl": "https://bsky.app/profile/example.bsky.social/post/abc",
"input": {
"operation": "posts",
"handle": "example.bsky.social",
"limit": 100,
"runMode": "evaluation"
},
"data": {
"uri": "at://did:plc:example/app.bsky.feed.post/abc",
"authorHandle": "example.bsky.social",
"text": "Example post",
"isBillable": true,
"billingCategory": "billable_useful_result"
}
}

Input metadata is allowlist-based. Credential fields and unknown custom fields are not copied into dataset rows.

Run summary

The Actor attempts to store one non-billable run_summary row containing:

  • requested and applied limits
  • useful records saved
  • pages fetched
  • duplicates and filtered records skipped
  • authentication mode
  • run scale
  • retry count
  • billing attempts, confirmed charges, denials, and unconfigured events
  • charge-limit status
  • continuation cursor and resume safety status
  • warnings and final status

billingEventsCharged is null when the platform does not provide a trustworthy confirmed charge count. A local run therefore does not pretend that zero is a verified platform charge.

Pagination and resume behavior

Paginated operations can return run_summary.data.nextCursor.

AT Protocol cursors are page-level rather than item-level. If a run stops after saving only part of a page, resuming from the preserved page-start cursor can repeat rows already saved in the previous run. Check:

resumeCursorStatus
resumeCursorNote
warnings

When no safe continuation cursor exists, the summary says so instead of presenting an unsafe cursor as exact.

Authentication and privacy

Anonymous mode is used for supported public reads when credentials are omitted. Search requires authenticated Bluesky access.

Is it safe to provide Bluesky credentials?

It is reasonably safe when you follow the precautions below, but no credential handoff to a third-party Actor is risk-free. Use a dedicated, revocable Bluesky app password created specifically for this Actor. Never provide your main Bluesky account password.

Both blueskyIdentifier and blueskyAppPassword are declared as Apify secret inputs. Apify encrypts secret input fields before saving them to the run's default key-value store, including when a run is started through the API. Apify decrypts them only inside the Actor run so the Actor can authenticate with Bluesky.

The Actor's credential data flow is:

  1. Apify receives the two secret input fields and stores encrypted values.
  2. The running Actor receives the decrypted values in memory.
  3. The Actor sends them to Bluesky through the AT Protocol client to create an authenticated session.
  4. The Actor does not write the credentials to its dataset, run summary, error records, or status messages.
  5. The plaintext values are discarded when the Actor process ends. The encrypted run input remains subject to the user's Apify storage retention settings.

The important trust boundary is step 2: the Actor code necessarily receives plaintext credentials during the run. The current implementation uses them only for Bluesky authentication and has automated and live leakage checks, but users should still limit the impact of any unexpected platform, dependency, or account compromise by using a dedicated app password.

Credential safety checklist

  • Create a dedicated app password in Bluesky under Settings → Privacy and security → App passwords. Do not reuse it for another application.
  • Provide both credential fields through the generated Apify input form or as secret fields in the JSON request body. Never place credentials in URLs, query strings, task names, dataset fields, source code, or ordinary log messages.
  • If an automation platform, proxy, shell command, or API client starts the run, make sure it also protects request bodies and command history. Apify can encrypt the stored Actor input, but it cannot prevent another system from logging the request before it reaches Apify.
  • Restrict access to the Apify account and API token that can start or inspect runs.
  • Revoke the dedicated Bluesky app password after one-time use, when it is no longer needed, or immediately if you suspect exposure. Revocation stops future logins; do not assume it cancels a session that is already running.
  • Delete the Apify run/default key-value store sooner if your retention requirements demand it. Apify documents the retention behavior for key-value stores; until deletion or expiry, these two fields remain encrypted at rest.

What the Actor guarantees

  • credential fields and unknown custom fields are excluded from dataset input metadata
  • credential values are redacted from Actor-generated status and error output
  • secret-bearing exception chains are suppressed before they reach the Actor boundary
  • credentials are not stored in Actor source, result datasets, summaries, or control records
  • search request parameters contain the query and filters, not credential fields; authentication is handled by the AT Protocol session

These guarantees cover this Actor's code and output. They do not cover a user's local machine, browser extensions, external automation tools, network proxies, compromised Apify or Bluesky accounts, or Bluesky's own authentication service.

Validation behavior

The Actor rejects malformed input instead of silently changing the request. Examples include:

  • unknown field names
  • explicit null or empty integer and boolean values
  • unsupported enum values
  • non-object input
  • incomplete credentials
  • invalid timestamps
  • malformed feed URLs or AT URIs

Useful records must contain stable identity fields before they can be saved and billed. Malformed upstream records fail closed rather than becoming blank billable rows.

Scope and limitations

The Actor extracts public data exposed by Bluesky's AT Protocol services. Search ordering is requested with sort="latest" or sort="top", but the exact ranking, result availability, feed contents, rate limits, and historical coverage are controlled by the upstream service and can change over time.

The Actor does not provide:

private or blocked content extraction
browser or HTML scraping
firehose streaming
media downloads
persistent monitoring or change tracking
multi-account bulk jobs
raw SDK response output
sentiment or content classification

Use the extracted data in accordance with applicable law, Bluesky's terms, Apify's terms, and the privacy rights of the people represented in the data.