# Bluesky Scraper API - posts, followers, likers, starter packs (`beguiling_equator/bluesky-public-data`) Actor

Search Bluesky posts, export timelines, threads, quote posts, profiles, followers, likers, reposters and starter-pack members via the official public AT Protocol API. No login. Engagement filters, only-new-posts monitoring. $1 / 1,000 posts.

- **URL**: https://apify.com/beguiling_equator/bluesky-public-data.md
- **Developed by:** [WEIGUO HAN](https://apify.com/beguiling_equator) (community)
- **Categories:** Social media, Lead generation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.80 / 1,000 posts

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Bluesky Scraper API - posts, profiles, followers, likers, starter packs & threads (no login)

Extract **public Bluesky data** through the **official AT Protocol API** - no account, no API key, no browser. One Actor covers every read use case:

| Mode | What you get | Input |
|---|---|---|
| `search` | Posts matching a keyword, phrase, hashtag, domain, mention or author, newest first, with full time-window paging | `queries` (+ filters) |
| `authorFeed` | Every post / reply / repost of an account (filterable: no replies, media only, video only) | `actors` |
| `thread` | A post with **all parents and the full reply tree** | `postUrls` |
| `profile` | Detailed profiles: bio, follower / following / post counts, labels, pinned post | `actors` |
| `followers` / `follows` | The follower or following list of an account (bounded) | `actors` |
| `customFeed` | Posts from any custom feed generator (Discover, What's Hot, niche community feeds) | `feedUrls` |
| `likers` / `reposters` | **Who liked or reposted a post** (profiles, with the like time) - find the most engaged audience of any post | `postUrls` |
| `quotes` | Every **quote post** of a post (what people said when they shared it) | `postUrls` |
| `listMembers` | Members of a **starter pack** or curated / moderation list - ready-made niche audiences | `listUrls` |

**Engagement filters** (`minLikes`, `minReposts`, `minReplies`) work in every post mode: keep only the posts that took off, pay only for those.

Every post comes back **normalized**: `uri`, `cid`, `url`, author (DID / handle / display name), `text`, `langs`, `createdAt`, reply / quote / repost relations, like / repost / reply / quote counts, images with alt text, video, external link card, the quoted post, hashtags, mentions, links and moderation labels - plus the raw record on request.

Turn on **"Only new items since last run"** and schedule the Actor: it keeps a checkpoint per query and only outputs (and charges) posts it has not seen before - a ready-made **Bluesky monitoring / social listening feed** for brand mentions, keywords, competitors or creators.

**Pricing: $1 per 1,000 posts, $0.50 per 1,000 profiles** (pay per event, no extra start fee, details below).

### Why this Actor

- **Official, public API only.** All data comes from Bluesky's public AppView (`public.api.bsky.app` / `api.bsky.app`) - the same endpoints the Bluesky web app uses for logged-out visitors. No scraping of HTML, no bypassing of anything.
- **Search that keeps paging.** Since mid-2026 the public API rejects `cursor` pagination for logged-out search (HTTP 403 on page 2). This Actor pages by time instead (`sort=latest` + `until=<oldest post of the previous page>`, deduplicated on `uri`), so you still get thousands of results per keyword without logging in.
- **Eleven modes, one schema.** Search results, timelines, threads, quote posts and custom feeds all produce the same post object; profiles, followers, likers, reposters and list / starter-pack members share one profile object. Build once, reuse everywhere.
- **Simple per-record pricing.** Profile, follower, following, liker, reposter and list-member records cost **$0.50 per 1,000**; posts cost **$1 per 1,000**; there is no extra start fee beyond Apify's platform default (prices checked 2026-10-16). Items skipped by deduplication, engagement filters or incremental monitoring are free.
- **Monitoring built in.** Named key-value-store checkpoints per query, "only new since last run", no duplicates across runs, no double charging.
- **Respects user choices.** Accounts that opted out of logged-out visibility (`!no-unauthenticated`) are skipped by default; labels are included in the output.
- **Bring your own App Password (optional).** Log in with your own Bluesky account through the official session endpoint to unlock cursor pagination (including `sort=top`), see opted-out accounts and use your own rate limits. Stored encrypted, never logged.
- **Polite and fast.** Small JSON requests, max 8 requests/second, exponential backoff on 429 / 5xx; a 1,000-post run finishes in well under a minute.

### Tutorial: how to scrape Bluesky posts

1. Open the Actor and pick a **Mode**.
2. Fill in the matching input:
   - `search` -> **Search queries**: `apify`, `"web scraping"`, `#opensource`, `from:bsky.app` style filters go into the dedicated fields (author, mentions, domain, URL, language, hashtags, since / until).
   - `authorFeed`, `profile`, `followers`, `follows` -> **Accounts**: `bsky.app`, `@name.bsky.social`, `did:plc:...` or `https://bsky.app/profile/...`.
   - `thread` -> **Post URLs**: `https://bsky.app/profile/bsky.app/post/3mx5e63uvns2d` or an `at://` URI.
   - `customFeed` -> **Feed URLs**: `https://bsky.app/profile/bsky.app/feed/whats-hot` or an `at://.../app.bsky.feed.generator/...` URI.
3. Set **Max items per query** (default 100, up to 20,000).
4. For a monitor, switch on **Only new items since last run** and create a schedule (hourly / daily). Connect a webhook, Zapier, Make, n8n or Google Sheets integration to get notified.
5. Click **Start**. Results land in the dataset (table / JSON / CSV / Excel / API); a per-query summary with request counts and warnings is stored under the `SUMMARY` key of the run's key-value store.

#### Input example: brand monitoring

```json
{
  "mode": "search",
  "queries": ["apify", "crawlee"],
  "mentions": "apify.com",
  "lang": "en",
  "sort": "latest",
  "maxItemsPerQuery": 500,
  "sinceLastRun": true,
  "stateStoreName": "my-bluesky-monitor"
}
```

#### Input example: viral posts for a keyword (engagement filter)

```json
{ "mode": "search", "queries": ["#buildinpublic"], "lang": "en", "minLikes": 50, "maxItemsPerQuery": 200 }
```

#### Input example: everyone who liked a post / members of a starter pack

```json
{ "mode": "likers", "postUrls": ["https://bsky.app/profile/bsky.app/post/3mx5e63uvns2d"], "maxItemsPerQuery": 5000 }
```

```json
{ "mode": "listMembers", "listUrls": ["https://bsky.app/starter-pack/bsky.app/3mv3qhvnmkd2l"] }
```

Liker records carry `edgeType: "liker"`, `subjectUri` (the post) and `likedAt`; list members carry `edgeType: "listMember"`, `listName` and `starterPackName`.

#### Input example: export an account and its audience

```json
{ "mode": "authorFeed", "actors": ["https://bsky.app/profile/bsky.app"], "authorFeedFilter": "posts_no_replies", "includeReposts": false, "maxItemsPerQuery": 2000 }
```

```json
{ "mode": "followers", "actors": ["bsky.app"], "maxItemsPerQuery": 5000 }
```

#### Output example: one post

```json
{
  "uri": "at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post/3mx5e63uvns2d",
  "cid": "bafyreicnhzopqetyqko7bcc2gul2dkgtyxgnl3creeklgugdagxp7anaxe",
  "rkey": "3mx5e63uvns2d",
  "url": "https://bsky.app/profile/bsky.app/post/3mx5e63uvns2d",
  "authorDid": "did:plc:z72i7hdynmk6r22z27h6tvur",
  "authorHandle": "bsky.app",
  "authorDisplayName": "Bluesky",
  "authorAvatar": "https://cdn.bsky.app/img/avatar/plain/did:plc:z72i7hdynmk6r22z27h6tvur/...",
  "authorLabels": [],
  "text": "We just shipped ...",
  "langs": ["en"],
  "createdAt": "2026-10-04T18:02:11.513Z",
  "indexedAt": "2026-10-04T18:02:12.020Z",
  "isReply": false,
  "replyParentUri": null,
  "replyRootUri": null,
  "isQuote": false,
  "quotedPostUri": null,
  "isRepost": false,
  "repostedByDid": null,
  "repostedByHandle": null,
  "repostIndexedAt": null,
  "isPinned": false,
  "likeCount": 1843,
  "repostCount": 212,
  "replyCount": 32,
  "quoteCount": 41,
  "bookmarkCount": 57,
  "embedType": "images",
  "images": [{ "url": "https://cdn.bsky.app/img/feed_fullsize/plain/...", "thumb": "https://cdn.bsky.app/img/feed_thumbnail/plain/...", "alt": "Screenshot of the new feature", "width": 1600, "height": 900 }],
  "video": null,
  "externalLink": null,
  "quotedPost": null,
  "hashtags": ["bluesky"],
  "mentions": [],
  "links": ["https://bsky.social/about/blog/..."],
  "labels": [],
  "threadgate": false,
  "source": "authorFeed",
  "feedActor": "bsky.app",
  "scrapedAt": "2026-10-07T09:12:30Z"
}
```

A search result additionally carries `"source": "search", "query": "apify"`; a thread post carries `threadPosition` (`parent` / `focus` / `reply`), `threadDepth` and `threadRootUri`; a custom-feed post carries `feedUri`. With `sinceLastRun` every item has `"isNew": true`. Set `includeRaw: true` to get the untouched `app.bsky.feed.defs#postView` under `raw`.

#### Output example: one profile / graph edge

```json
{
  "did": "did:plc:vu5ic5ygdeyunpefgmlamsmw",
  "handle": "apify.com",
  "displayName": "Apify",
  "description": "The full-stack web scraping and automation platform.",
  "avatar": "https://cdn.bsky.app/img/avatar/plain/...",
  "banner": "https://cdn.bsky.app/img/banner/plain/...",
  "url": "https://bsky.app/profile/apify.com",
  "followersCount": 2410,
  "followsCount": 118,
  "postsCount": 356,
  "createdAt": "2024-11-12T10:21:44.000Z",
  "indexedAt": "2026-10-06T22:14:01.000Z",
  "labels": [],
  "pinnedPostUri": null,
  "listsCount": 0,
  "feedgensCount": 0,
  "starterPacksCount": 1,
  "bioLinks": ["https://example.com"],
  "isLabeler": false,
  "verificationStatus": null,
  "source": "followers",
  "edgeType": "follower",
  "subjectDid": "did:plc:z72i7hdynmk6r22z27h6tvur",
  "subjectHandle": "bsky.app",
  "scrapedAt": "2026-10-07T09:12:30Z"
}
```

`edgeType` is `profile` (profile mode), `follower` (X follows the subject) or `following` (the subject follows X). Graph entries are basic profile views: counts are included when the API returns them.

### Pricing (pay per event)

| Event | What it means | Price |
|---|---|---|
| `post` | One normalized post written to the dataset (search, authorFeed, thread, customFeed, quotes) | **$0.001** ($1 / 1,000 posts) |
| `profile` | One profile written to the dataset (profile lookup, or one follower / following / liker / reposter / list-member entry) | **$0.0005** ($0.50 / 1,000 profiles) |
| Actor start | Apify's standard synthetic start event at the platform default | platform default |

Posts skipped by deduplication, by the engagement filters, by the `!no-unauthenticated` filter, by `includeReposts: false` or by incremental monitoring are never charged. Failed queries cost nothing. Proposed volume discounts for higher Apify plans: `post` $0.0008 (Silver) / $0.0006 (Gold); `profile` $0.0004 / $0.0003.

**Cost examples**

- Hourly brand monitor on 3 keywords with `sinceLastRun`, ~150 new posts a day -> about **$0.15 per day**.
- One-off export of an account's last 5,000 posts -> **$5.00**.
- Follower list of a 20,000-follower account -> **$10.00**.
- Everyone who liked 10 viral posts (~3,000 likers each) -> 30,000 profiles -> **$15.00**.
- Research dataset: 50 keywords x 2,000 posts -> **$100** for 100,000 posts.

### Use cases

- **Social listening & brand monitoring** - track mentions of your brand, product or domain; push new posts to Slack, HubSpot or a Google Sheet via Apify integrations.
- **Creator / competitor analytics** - export timelines with engagement counts, compute best posting times, top formats, growth of follower counts over time (schedule the `profile` mode).
- **Audience research** - followers / following graphs for influencer mapping and community detection.
- **Academic & journalism research** - keyword corpora with language filters and exact time windows (`since` / `until`), full conversation threads for discourse analysis.
- **AI agents, RAG and MCP** - "what are people on Bluesky saying about X today?" becomes one Actor call with clean JSON.
- **Migration from X/Twitter data pipelines** - same concepts (posts, reposts, quotes, threads, followers) in a stable schema.

### How it works and which endpoints are used

| Mode | Endpoint (public AppView) | Paging |
|---|---|---|
| `search` | `app.bsky.feed.searchPosts` on `api.bsky.app` | logged-out: `sort=latest` + `until` time window (the API forbids `cursor` for logged-out calls); logged-in: `cursor` |
| `authorFeed` | `app.bsky.feed.getAuthorFeed` | `cursor`, 100 per page |
| `thread` | `app.bsky.feed.getPostThread` (`depth` up to 1000, `parentHeight` up to 1000) | single request |
| `profile` | `app.bsky.actor.getProfiles` | 25 accounts per request |
| `followers` / `follows` | `app.bsky.graph.getFollowers` / `getFollows` | `cursor`, 100 per page |
| `customFeed` | `app.bsky.feed.getFeed` | `cursor`, 100 per page |
| handle -> DID | `com.atproto.identity.resolveHandle` | cached per run |

The endpoints are defined by the public AT Protocol lexicons (`app.bsky.*`) and served by Bluesky's public AppView for logged-out reads. Bluesky documents no fixed read quota for the AppView ("generous rate limits"); the Actor defaults to 3 requests/second, never exceeds 8, honours `Retry-After` / `RateLimit-Reset` and stops a query on persistent 429 or 403 instead of hammering the service.

### Incremental monitoring (only new posts since last run)

With `sinceLastRun: true` the Actor stores, per query / account / thread / feed, the newest timestamp and the most recent 5,000 item ids in the named key-value store `stateStoreName` (default `bluesky-public-data-state`). On the next run:

- items already seen are skipped (not charged);
- chronological modes (`search` with `sort=latest`, `authorFeed`, `customFeed`) stop paging as soon as they reach the previous watermark, so a monitor costs only what is new;
- `thread` and graph modes output only new replies / new followers;
- the checkpoint is only advanced after a successful run - if a run stops because your charge limit was reached, the next run catches up instead of silently skipping.

Different filters (language, author, sort ...) produce different checkpoints; use different `stateStoreName` values for independent monitors. The first run seeds the checkpoint and returns the items currently visible (up to `maxItemsPerQuery`).

### Optional: use your own Bluesky App Password

Without login everything in this Actor works, with two limitations imposed by Bluesky for logged-out traffic: search cannot use `cursor` pagination (the Actor pages by time instead, which only works with `sort=latest`), and accounts that opted out of logged-out visibility are hidden.

If you need `sort=top` beyond the first 100 results, want to see opted-out accounts you are allowed to see, or prefer your own account's rate limits, create an **App Password** in Bluesky (Settings -> Privacy and security -> App Passwords) and fill in `identifier` + `appPassword`. The Actor calls the official `com.atproto.server.createSession` endpoint on your PDS (`pdsHost`, default `https://bsky.social`), routes all requests through it with a bearer token and refreshes the session automatically. The password is a secret input: encrypted by Apify, hidden in the UI, never logged, never written to the dataset. Never use your main account password.

### Limitations and good-to-know

- **Logged-out search**: only `sort=latest` can page beyond the first 100 results; `sort=top` returns the first page only. `hitsTotal` from the API is approximate. Very busy keywords with many posts sharing the same second can lose a handful of posts at page boundaries.
- **Search freshness**: Bluesky's search index lags the firehose by seconds to minutes; `getAuthorFeed` is the most complete source for a single account.
- **Threads** are returned as the AppView renders them: replies hidden by the author (threadgates), blocked or deleted posts appear as warnings in `SUMMARY`, not as items. Extremely large threads may be truncated by the AppView regardless of `threadDepth`.
- **Follower lists** of huge accounts (millions) are expensive and slow to walk; `maxItemsPerQuery` is capped at 20,000 per run.
- **Deleted content**: the Actor reads live data; it does not keep posts that were deleted after your run. Please honour deletions in your own storage (Bluesky developer guidelines).
- Notifications, private lists and direct messages are not covered (they are never public). Likers, reposters, quote posts, lists and starter packs are covered since 1.1.
- Media files are returned as CDN URLs, not downloaded.

### FAQ

**Do I need a Bluesky account or API key?** No. Every mode works logged-out through Bluesky's public API. An App Password is optional.

**Is this allowed?** Bluesky states that posts on Bluesky are public and readable through its open API, and operates `public.api.bsky.app` precisely for logged-out reads. The Actor only reads public data, respects the `!no-unauthenticated` opt-out by default, surfaces moderation labels, and never performs writes, likes, follows or messages. You are responsible for complying with Bluesky's Terms of Service, the Developer Guidelines and applicable privacy laws for the data you store.

**How do I get only new posts?** Set `sinceLastRun: true` and run on a schedule. The first run seeds the checkpoint.

**How do I search for mentions of my account?** Mode `search`, put your handle (e.g. `apify.com`) in **Only posts mentioning** and `*` or your brand name in **Search queries**.

**Can I search posts in a date range?** Yes: `since` / `until` accept ISO datetimes (`2026-10-01T00:00:00Z`).

**Why are there fewer posts than `hitsTotal`?** `hitsTotal` is an estimate from the search index; filtered accounts, deleted posts and `maxItemsPerQuery` reduce the count. Check `SUMMARY` in the run's key-value store for `fetched` vs `pushed` per query.

**How do I find the most engaged accounts in a niche?** Use `search` with `minLikes` to find the posts that took off, then run `likers` / `reposters` on their URLs - or export a niche **starter pack** with `listMembers`.

**Do engagement filters apply to monitoring?** Yes. With `sinceLastRun` each post is evaluated once, when it is first seen; a post that gains likes later is not re-checked.

**Can I pass a Bluesky URL instead of a handle?** Yes - profile, post and feed URLs from bsky.app are all accepted and converted to AT URIs.

**Can it run locally?** Yes: `apify run` with the Apify CLI, or `python -m src` inside a venv. Unit tests: `pytest` (recorded fixtures, no network).

### Changelog

- **1.1.0** (2026-10-16) - new modes `likers`, `reposters`, `quotes` and `listMembers` (curated lists and **starter packs**); engagement filters `minLikes` / `minReposts` / `minReplies` for every post mode (filtered posts are free). Profiles gain `bioLinks` (website / link-in-bio URLs found in the bio; e-mail addresses are deliberately not extracted).
- **1.0.0** - initial release: search (time-window paging), authorFeed, thread, profile, followers, follows, customFeed; incremental monitoring; optional App Password; pay-per-event charging.

# Actor input Schema

## `mode` (type: `string`):

search: keyword search for posts (uses `queries`). authorFeed: posts by one or more accounts (uses `actors`). thread: a post and its full reply tree (uses `postUrls`). profile: detailed profiles (uses `actors`). followers / follows: the follower or following list of accounts (uses `actors`). customFeed: posts from a custom feed generator (uses `feedUrls`). likers / reposters: who liked or reposted the given posts (postUrls). quotes: every post quoting the given posts (postUrls). listMembers: members of a curated list or starter pack (listUrls).

## `queries` (type: `array`):

One search per entry (mode = search). Plain words, phrases in quotes, hashtags (#apify) or domains. Lucene-style syntax is accepted by Bluesky but not guaranteed.

## `actors` (type: `array`):

For authorFeed, profile, followers and follows modes. Accepts bsky.app profile URLs (https://bsky.app/profile/bsky.app), handles (bsky.app, name.bsky.social, @name) or DIDs (did:plc:...).

## `postUrls` (type: `array`):

For thread mode. Accepts https://bsky.app/profile/{handle}/post/{id} links or at://did:plc:.../app.bsky.feed.post/... URIs. Used by thread, likers, reposters and quotes modes.

## `feedUrls` (type: `array`):

For customFeed mode. Accepts https://bsky.app/profile/{handle}/feed/{name} links or at://did:plc:.../app.bsky.feed.generator/... URIs.

## `listUrls` (type: `array`):

For mode listMembers: curated / moderation list URLs (https://bsky.app/profile/<handle>/lists/<id>), starter-pack URLs (https://bsky.app/starter-pack/<handle>/<id>) or their at:// URIs. Short go.bsky.app links are not supported.

## `maxItemsPerQuery` (type: `integer`):

Stop after this many posts (or profiles / graph entries) per query, account, thread or feed. Hard cap 20,000. In thread mode this caps the number of posts collected from one thread.

## `includeRaw` (type: `boolean`):

Attach the untouched post / profile view from the API under `raw` (larger dataset items).

## `sort` (type: `string`):

latest (chronological, supports paging past the first 100 results without login) or top (relevance; without login only the first page of up to 100 posts is reachable).

## `since` (type: `string`):

Only posts created at or after this time, e.g. 2026-10-01T00:00:00Z. Ignored when `sinceLastRun` finds a stored checkpoint for the query.

## `until` (type: `string`):

Only posts created before this time, e.g. 2026-10-07T00:00:00Z.

## `lang` (type: `string`):

BCP-47 language code such as en, de, ja, pt-BR.

## `author` (type: `string`):

Restrict the search to one account (handle or DID).

## `mentions` (type: `string`):

Restrict the search to posts that mention this account (handle or DID). Great for brand monitoring.

## `domain` (type: `string`):

Restrict to posts containing a link to this domain, e.g. apify.com.

## `url` (type: `string`):

Restrict to posts containing this exact link.

## `tags` (type: `array`):

Restrict to posts carrying all of these hashtags (without #).

## `minLikes` (type: `integer`):

Only output posts with at least this many likes (0 = no filter). Posts below the threshold are not written and not charged. Applies to every post mode.

## `minReposts` (type: `integer`):

Only output posts with at least this many reposts (0 = no filter).

## `minReplies` (type: `integer`):

Only output posts with at least this many replies (0 = no filter).

## `authorFeedFilter` (type: `string`):

Which posts of an account to return in authorFeed mode.

## `includePins` (type: `boolean`):

authorFeed mode: return the pinned post at the top of the feed.

## `includeReposts` (type: `boolean`):

authorFeed / customFeed modes: keep reposts (marked isRepost = true with repostedByHandle). Turn off to export only original posts and replies.

## `threadDepth` (type: `integer`):

thread mode: how many levels of replies to include (0-1000; the AppView may cap very deep threads).

## `threadParentHeight` (type: `integer`):

thread mode: how many parent posts above the given post to include (0-1000).

## `graphSort` (type: `string`):

followers mode only: latest (most recent followers first) or top.

## `sinceLastRun` (type: `boolean`):

Keep a checkpoint per query / account / thread / feed in the named key-value store and output only posts (or graph entries) not seen in previous runs. Schedule the Actor to build a monitor; the first run seeds the checkpoint.

## `stateStoreName` (type: `string`):

Name of the key-value store holding the checkpoints. Use different names for independent monitors.

## `respectNoUnauthenticated` (type: `boolean`):

Bluesky users can ask that their profile and posts are hidden from logged-out viewers (the !no-unauthenticated label). When on (recommended), such accounts and their posts are skipped unless you run with your own credentials.

## `requestsPerSecond` (type: `integer`):

Global request rate against the Bluesky API (1-8). The AppView documents no fixed quota for reads; the Actor also honours 429 / Retry-After with exponential backoff.

## `maxRetries` (type: `integer`):

Retries for 429 / 5xx / network errors (0-5). After that the query is skipped with a warning.

## `identifier` (type: `string`):

Optional. Together with an App Password the Actor logs in to YOUR account through the official com.atproto.server.createSession endpoint. Logged-in requests can page through search results with cursors (also sort=top), see accounts that opted out of logged-out visibility, and get your own account's rate limits. Never required.

## `appPassword` (type: `string`):

An App Password for the account above (format xxxx-xxxx-xxxx-xxxx). Marked secret: encrypted at rest, hidden in the UI, never written to logs or the dataset.

## `pdsHost` (type: `string`):

Only used with identifier + appPassword. Default https://bsky.social; set your own PDS URL if your account lives elsewhere.

## Actor input object example

```json
{
  "mode": "authorFeed",
  "queries": [
    "apify"
  ],
  "actors": [
    "bsky.app"
  ],
  "feedUrls": [
    "https://bsky.app/profile/bsky.app/feed/whats-hot"
  ],
  "maxItemsPerQuery": 25,
  "includeRaw": false,
  "sort": "latest",
  "authorFeedFilter": "posts_with_replies",
  "includePins": false,
  "includeReposts": true,
  "threadDepth": 50,
  "threadParentHeight": 80,
  "graphSort": "latest",
  "sinceLastRun": false,
  "stateStoreName": "bluesky-public-data-state",
  "respectNoUnauthenticated": true,
  "requestsPerSecond": 3,
  "maxRetries": 3,
  "pdsHost": "https://bsky.social"
}
```

# Actor output Schema

## `items` (type: `string`):

No description

## `summary` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "queries": [
        "apify"
    ],
    "actors": [
        "bsky.app"
    ],
    "feedUrls": [
        "https://bsky.app/profile/bsky.app/feed/whats-hot"
    ],
    "maxItemsPerQuery": 25
};

// Run the Actor and wait for it to finish
const run = await client.actor("beguiling_equator/bluesky-public-data").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "queries": ["apify"],
    "actors": ["bsky.app"],
    "feedUrls": ["https://bsky.app/profile/bsky.app/feed/whats-hot"],
    "maxItemsPerQuery": 25,
}

# Run the Actor and wait for it to finish
run = client.actor("beguiling_equator/bluesky-public-data").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "queries": [
    "apify"
  ],
  "actors": [
    "bsky.app"
  ],
  "feedUrls": [
    "https://bsky.app/profile/bsky.app/feed/whats-hot"
  ],
  "maxItemsPerQuery": 25
}' |
apify call beguiling_equator/bluesky-public-data --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,beguiling_equator/bluesky-public-data"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/oRh98S3uNU6KhfBye/builds/LwSPgolNg5P9LlXRq/openapi.json
