# Bluesky Posts, Profiles, Feeds & Threads Scraper (`nimbryxo-studio/bluesky-posts-profiles-feeds-and-threads-scraper`) Actor

Search Bluesky posts by keyword, hashtag, author, mention, domain or URL; read profiles, threads, lists, starter packs, feeds and followers; export likes, reposts, follows, quotes and relationships.

- **URL**: https://apify.com/nimbryxo-studio/bluesky-posts-profiles-feeds-and-threads-scraper.md
- **Developed by:** [Nimbryxo-studio](https://apify.com/nimbryxo-studio) (community)
- **Categories:** Social media, Automation, Developer tools
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.49 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
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 Posts, Profiles, Feeds & Threads Scraper

Scrape **Bluesky** public data without a login, cookies or an API key. Search posts by keyword, hashtag, author, mention, domain or URL; read profiles, author feeds, full thread reply branches, lists, starter packs, custom feeds, likes, reposts, quotes, actor liked posts and public relationships; and discover trending topics and suggested communities. Every record has flat top-level fields with a few natural nested objects, absolute URLs, ISO dates and omit-empty output.

### What this actor does

- **24 modes** across posts, accounts, threads, lists, starter packs, feed generators, relationships and discovery.
- **Post search with the full public filter surface**: keyword, hashtag, author, mention, domain, URL, date range, language and top/latest ranking.
- **Search beyond the first page**: both `searchSort=latest` and `searchSort=top` walk backwards through time windows. Latest is newest-first; Top preserves engagement ranking within each window. Cursor paging is not used because Bluesky rejects anonymous cursors.
- **Full thread reply trees**: `threadDepth` walks deeper than the ~10-level per-request limit by fetching truncated reply branches separately. `maxRepliesPerPost` bounds fan-out and `threadTruncated` marks records whose children were cut short.
- **Actor likes without credentials**: `mode=actorLikes` reads the account's public like records from its personal data server and hydrates the liked posts, with `likedAt` and like-record fields.
- **More public surfaces**: `mode=actorFeeds` lists an account's feed generators, `mode=starterPackSearch` searches starter packs, and `mode=relationships` checks public follow/block relationships for supplied accounts.
- **Rich records**: engagement counts, languages, mentions, hashtags, links, domains, images, video, external embeds, quoted posts, reposts, reply roots, labels, self-labels, verification, live status, pinned posts, profile counts and flat author follower counts.
- **Privacy-aware**: accounts marked `!no-unauthenticated` are skipped by default, and the run status reports how many records were skipped.
- **Batch-friendly**: multiple profiles, posts, lists, starter packs, feeds and URLs per run, with automatic de-duplication.
- **No credentials required.** Every mode reads public data. If Bluesky temporarily blocks a search request from the actor's network, the actor retries it automatically and reports a clear status if it still fails.

### Output per record

Every record carries `recordType`, `sourceUrl` and `scrapedAt`. Empty values are omitted at every depth, so the dataset never contains nulls or empty placeholders.

#### Post records (`recordType=post`)

- `uri` - canonical AT URI
- `cid` - content identifier
- `id` - same as `uri`
- `url` - public `bsky.app` post URL
- `sourceUrl` - same as `url`
- `text` - post text
- `createdAt`, `indexedAt` - ISO 8601 timestamps
- `language` - first declared language
- `languages[]` - all declared languages
- `replyCount`, `repostCount`, `likeCount`, `quoteCount`, `bookmarkCount`
- `isReply` - true when the post replies to another post
- `inReplyToUri`, `inReplyToCid` - parent post
- `rootUri`, `rootCid` - thread root
- `isRepost` - true when surfaced as a repost
- `repostedBy` - handle that reposted it
- `repostUri`, `repostCid`, `repostedAt` - repost metadata
- `isQuote` - true when the post quotes another post
- `threadTruncated` - true on a thread record whose direct replies were cut by `maxRepliesPerPost`, `maxItems` or a public-API gap (`mode=postThread`)
- `likedAt`, `likedBy`, `likedByDid`, `likeUri`, `likeCid` - like-record metadata added by `mode=actorLikes`
- `quotedPost` - `{uri, cid, text, createdAt, indexedAt, authorHandle, authorDid, authorDisplayName, authorAvatar, url, likeCount, repostCount, replyCount, quoteCount, labels, labelValues, mediaAttachments}`
- `mentions[]` - mentioned DIDs
- `tags[]` - hashtags, lowercase
- `urls[]` - external links
- `domains[]` - hostnames from `urls[]`
- `mediaAttachments[]` - `{type, url, previewUrl, description, width, height, presentation, title}` for images, video and external embeds
- `hasMedia`, `mediaTypes[]`
- `labels[]` - `{val, src, uri, cid, cts, ver}`
- `labelValues[]` - label values only
- `selfLabels[]` - author-applied labels
- `author` - `{did, handle, displayName, avatar, createdAt, profileUrl, labels, labelValues, verification, status}`; with `hydrateAuthors=true` (the default) it also includes `description`, `banner`, `followersCount`, `followsCount`, `postsCount`, `indexedAt`, `pinnedPostUri`, `pinnedPostCid`. The hydrated profile's `handle` and `did` replace stale values, and `url`, `sourceUrl` and `authorHandle` are recomputed
- `authorFollowersCount`, `authorFollowsCount`, `authorPostsCount`, `authorVerified` - flat author stats added when `hydrateAuthors=true`
- `authorHandle` - convenience field
- `hitsTotal`, `searchHitsTotal` - search-result totals when Bluesky returns them (the value is capped/rounded by the source, so treat it as an estimate)

#### Account records (`recordType=account`, `like`, `listMember`, `repost` or `starterPackMember`)

- `did`, `id`, `handle`, `displayName`, `description`, `pronouns`
- `avatar`, `banner`
- `followersCount`, `followsCount`, `postsCount`
- `createdAt`, `indexedAt`
- `url`, `sourceUrl` - public profile URL
- `labels[]`, `labelValues[]`
- `verification` - `{verifiedStatus, trustedVerifierStatus, verifications[]}`
- `associated` - `{listsCount, feedGeneratorsCount, starterPacksCount, isLabeler, chatAllowIncoming, chatAllowGroupInvites, activitySubscription}`
- `status` - `{type, createdAt, durationMinutes, expiresAt, isActive, embed}`
- `pinnedPostUri`, `pinnedPostCid`
- `relation` - `profile`, `searchMatch`, `follower`, `following`, `liked`, `reposted`, `listMember`, `starterPackMember`, `suggestedFollow` or `relationship`
- `likedAt`, `likedPostUri` - for `mode=getLikes`
- `repostedPostUri` - for `mode=repostedBy`
- `listUri`, `listName` - for list members
- `starterPackUri` - for starter-pack members
- `relationshipActor`, `isFollowing`, `isFollowedBy`, `isMutualFollow` - for `mode=relationships`
- `following`, `followedBy`, `blocking`, `blockedBy` - public relationship record URIs when present

#### List records (`recordType=list`)

- `uri`, `cid`, `id`, `name`, `purpose`, `description`
- `listItemCount`
- `creator` - account summary
- `creatorHandle`, `creatorDid`
- `labels[]`, `labelValues[]`, `indexedAt`
- `url`, `sourceUrl` - public list URL

#### Starter-pack records (`recordType=starterPack`)

- `uri`, `cid`, `id`, `name`, `description`
- `creator` - account summary
- `creatorHandle`, `creatorDid`
- `listUri`, `listName`, `listItemCount`
- `joinedAllTimeCount`, `joinedWeekCount`
- `createdAt`, `updatedAt`
- `feeds[]` - `{uri, displayName, description, avatar, creatorHandle}`
- `listItemsSample[]` - sample account summaries
- `labels[]`, `labelValues[]`, `indexedAt`
- `url`, `sourceUrl` - public starter-pack URL

#### Feed-generator records (`recordType=feedGenerator`)

- `uri`, `cid`, `id`, `did`
- `displayName`, `description`, `avatar`
- `likeCount`, `acceptsInteractions`
- `creator` - account summary
- `creatorHandle`, `creatorDid`
- `labels[]`, `labelValues[]`, `indexedAt`
- `isOnline`, `isValid` - when fetched with `mode=feedGenerator`
- `url`, `sourceUrl` - public feed URL

#### Discovery records

- `recordType=trendingTopic`: `topic`, `displayName`, `description`, `link`, `url`, `sourceUrl`, `rank`
- `recordType=taggedSuggestion`: `tag`, `subjectType`, `subject`, `url`, `sourceUrl`

When a discovery item has no public target URL, `url` and `sourceUrl` fall back to the Bluesky home page so every record still has a source link.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `mode` | Enum | `search` | `search` / `profile` / `profiles` / `actorSearch` / `feed` / `postThread` / `urlFetch` / `getPosts` / `follows` / `followers` / `getLikes` / `repostedBy` / `quotes` / `actorLikes` / `getList` / `listFeed` / `lists` / `actorFeeds` / `getStarterPack` / `starterPackSearch` / `feedGenerator` / `feedGeneratorsDiscovery` / `relationships` / `discovery` |
| `searchQuery` | String | `bluesky` | Keyword, hashtag, phrase or Lucene-style query. Used by `search`, `actorSearch`, `starterPackSearch` and `feedGeneratorsDiscovery` |
| `searchSort` | Enum | `latest` | `latest` (newest first; supports deeper collection) or `top` (engagement-ranked, capped at about 100 posts) |
| `searchTags` | Array | `[]` | Hashtags without `#`; multiple tags are AND-matched (`mode=search`) |
| `searchDomain` | String | – | Only posts linking to this domain (`mode=search`) |
| `searchUrl` | String | – | Only posts linking to this exact URL (`mode=search`) |
| `mentionsUser` | String | – | Only posts mentioning this handle or DID (`mode=search`) |
| `fromUser` | String | – | Only posts authored by this handle or DID (`mode=search`) |
| `since` | String | – | Earliest post date (`YYYY-MM-DD` or ISO datetime); a bare date starts at 00:00 UTC (`mode=search`) |
| `until` | String | – | Latest post date (`YYYY-MM-DD` or ISO datetime); a bare date includes the whole day through 23:59:59.999999 UTC (`mode=search`) |
| `language` | Enum | – | ISO 639-1 language filter; empty means no filter |
| `authorFeedFilter` | Enum | `posts_with_replies` | `posts_with_replies` / `posts_no_replies` / `posts_with_media` / `posts_and_author_threads` (`mode=profile`) |
| `includePins` | Boolean | `false` | Include the pinned post at the top of the author feed (`mode=profile`) |
| `includeParents` | Boolean | `false` | Include ancestor posts above the requested post (`mode=postThread`) |
| `threadDepth` | Integer | `6` | Reply and ancestor levels to walk, 0-1000. Depths beyond about 10 reply levels are reached by walking branches across requests (`mode=postThread`) |
| `maxRepliesPerPost` | Integer | `100` | Maximum direct replies followed under each post while walking a thread, 1-1000 (`mode=postThread`) |
| `actor` | String | – | Handle or DID. Used by `profile`, `follows`, `followers`, `lists`, `actorLikes`, `actorFeeds`, `relationships` and `discoveryType=suggestedFollows` |
| `actors` | Array | `[]` | Handles, DIDs or profile URLs for `mode=profiles`; the accounts to check against `actor` for `mode=relationships` |
| `postUri` | String | – | AT URI of a post. Used by `postThread`, `getLikes`, `repostedBy` and `quotes` |
| `postUris` | Array | `[]` | AT URIs to hydrate (`mode=getPosts`; fallback for single-post modes) |
| `postUrls` | Array | `[]` | bsky.app post URLs for `getPosts`; post, profile, list, feed or starter-pack URLs for `urlFetch`; fallback for single-post modes |
| `listUri` | String | – | AT URI of a list (`mode=getList`, `mode=listFeed`) |
| `listUris` | Array | `[]` | AT URIs or list URLs for batch list processing (`mode=getList`) |
| `starterPackUri` | String | – | AT URI of a starter pack (`mode=getStarterPack`) |
| `starterPackUris` | Array | `[]` | AT URIs or starter-pack URLs for batch processing (`mode=getStarterPack`) |
| `feedPreset` | Enum | - | `whats-hot` / `bsky-team` / `hot-classic`; an explicit `feedUri` always wins. Legacy names (`discover`, `mutuals`, `popular-with-friends`, `for-you`, `news`, `trending-videos`, `quiet-posters`) are accepted and mapped to What's Hot. If neither is set, `mode=feed` uses What's Hot |
| `feedUri` | String | – | AT URI or bsky.app feed URL (`mode=feed`, `mode=feedGenerator`) |
| `feedUris` | Array | `[]` | AT URIs or feed URLs for batch lookup (`mode=feedGenerator`) |
| `discoveryType` | Enum | `trendingTopics` | `trendingTopics` / `suggestedFeeds` / `suggestedStarterPacks` / `taggedSuggestions` / `suggestedFollows` (`mode=discovery`) |
| `excludeReplies` | Boolean | `false` | Drop posts that are replies |
| `excludeReposts` | Boolean | `false` | Drop reposts |
| `mediaOnly` | Boolean | `false` | Only posts with at least one image, video or external embed |
| `excludeLabeled` | Boolean | `false` | Drop posts and accounts that carry moderation or self-labels, including labels on a post's author |
| `respectNoUnauthenticated` | Boolean | `true` | Skip records whose author carries `!no-unauthenticated`; the run status reports the skipped count |
| `verifiedOnly` | Boolean | `false` | Only verified accounts, or posts whose author is verified. Records with no verification object are excluded; post modes hydrate author profiles to check |
| `minLikes` | Integer | – | Drop posts below this like count, 0-10,000,000 |
| `minReposts` | Integer | – | Drop posts below this repost count, 0-10,000,000 |
| `minReplies` | Integer | – | Drop posts below this reply count, 0-10,000,000 |
| `minQuotes` | Integer | – | Drop posts below this quote count, 0-10,000,000 |
| `minFollowers` | Integer | – | Keep accounts with at least this many followers, 0-1,000,000,000 |
| `maxFollowers` | Integer | – | Keep accounts with at most this many followers, 0-1,000,000,000 |
| `includeListMetadata` | Boolean | `false` | Emit a list record before its members (`mode=getList`) |
| `includeStarterPackMembers` | Boolean | `true` | Emit the pack's member accounts (`mode=getStarterPack`) |
| `hydrateAuthors` | Boolean | `true` | Fetch full author profiles for post records in batches of 25 and add flat author follower, following, post and verification fields. Automatically enabled when `verifiedOnly=true`; the daily profile prefill leaves it off to stay cheap |
| `maxItems` | Integer | `40` | Hard cap on emitted records, 1-5000. Source exhaustion or Bluesky caps may return fewer; the run status reports a shortfall |

Legacy saved tasks that specify `mode=getActorLikes` are mapped to `actorLikes` with a status message. That mode reads the account's public like records from its personal data server and hydrates the liked posts; no login or app password is used.

#### Example: brand monitoring with filters

```json
{
  "mode": "search",
  "searchQuery": "bluesky",
  "searchSort": "latest",
  "searchTags": ["opensource"],
  "language": "en",
  "minLikes": 10,
  "maxItems": 300
}
```

#### Example: a user's profile and recent media posts

```json
{
  "mode": "profile",
  "actor": "bsky.app",
  "authorFeedFilter": "posts_with_media",
  "includePins": true,
  "maxItems": 50
}
```

#### Example: a thread with parents and replies

```json
{
  "mode": "postThread",
  "postUri": "at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post/3l6oveex3ii2l",
  "includeParents": true,
  "threadDepth": 30,
  "maxRepliesPerPost": 100,
  "maxItems": 200
}
```

#### Example: posts an account liked

```json
{
  "mode": "actorLikes",
  "actor": "bsky.app",
  "maxItems": 100
}
```

#### Example: audience export with follower counts

```json
{
  "mode": "followers",
  "actor": "jay.bsky.team",
  "minFollowers": 100,
  "maxItems": 500
}
```

#### Example: discover trending topics

```json
{
  "mode": "discovery",
  "discoveryType": "trendingTopics",
  "maxItems": 20
}
```

### Use cases

- **Social listening and brand monitoring** - track keywords, hashtags, mentions, domains and authors with date and engagement filters.
- **Influencer and KOL research** - combine account search with follower ranges, verification and profile counts.
- **Audience and lead export** - export followers, follows, likers, reposters, list members and starter-pack members with handles, bios and counts.
- **Conversation analysis** - capture a post, its ancestors and replies up to `threadDepth` for sentiment or narrative research.
- **Content and LLM datasets** - collect post text, languages, hashtags, links, media metadata and engagement counts.
- **Community mapping** - explore lists, list feeds, starter packs, custom feeds and trending topics.

### FAQ

**Do I need a Bluesky account or login?**
No. Every mode reads public data. No cookies or API keys are needed. If Bluesky temporarily blocks a search request from the actor's network, the actor retries it automatically through an Apify connection and reports a clear status if it still fails. No proxy setup is required.

**How far back can a keyword search go?**
`searchSort=latest` walks backwards through time windows and can return far more than the 100-post relevance cap. `searchSort=top` is relevance-ranked and limited to about 100 posts per query. Time-window search is approximate: pages can overlap, so results are de-duplicated by post URI, and a narrow or wide date range changes how predictable the volume is. When Bluesky returns a search hit total it is exposed as `hitsTotal`/`searchHitsTotal`, but the source caps and rounds that number.

**Why do some posts have no text?**
Bluesky allows media-only posts. The actor omits empty `text` and keeps the media metadata.

**Why are some fields missing from an account?**
Basic account views do not always include a bio, banner, follower counts or a pinned post. The actor hydrates accounts in batches so those fields are present whenever Bluesky publishes them. Labels, verification and live status are conditional by nature.

**Why are some accounts missing from results?**
Some accounts opt out of unauthenticated access and carry the `!no-unauthenticated` label. They are skipped by default and counted in the run status. Set `respectNoUnauthenticated=false` to include them when Bluesky returns them.

**Why does a follower export contain fewer rows than the profile's follower count?**
Profile counts are cached totals; deactivated or unindexed accounts can make them larger than the returned list. `!no-unauthenticated` accounts are filtered by this actor by default. The export is complete for what the public API exposes after that policy.

**How are reposts and quote posts handled?**
A repost is one record with `isRepost=true`, `repostedBy` and the original post's content. A quote post is one record with the quoter's text and a nested `quotedPost` object containing the quoted post's text, author, URL, counts and media.

**What is the difference between `mode=search` and `mode=actorSearch`?**
`search` finds posts. `actorSearch` finds accounts by handle, display name or bio.

**What is a feed, and which presets are available?**
A feed is a curated post stream produced by a feed generator. The built-in presets are What's Hot, Bluesky Team and Hot Classic. Paste any other generator's AT URI or bsky.app feed URL into `feedUri`. An explicit `feedUri` always takes precedence over `feedPreset`; legacy preset names are mapped to What's Hot.

**Does `feedPreset` override my custom `feedUri`?**
No. `feedUri`/`feedUris` win whenever they are supplied. `feedPreset` is only used when no feed URI is given, and `mode=feedGenerator` never adds an unrequested feed.

**How does `verifiedOnly` work?**
It keeps only accounts with a valid Bluesky verification and posts whose author is verified. Records with no verification object are excluded. Post modes hydrate author profiles so verification can be checked, which adds batched profile requests and may return fewer posts than an unfiltered search.

**How do actor likes work without a login?**
`mode=actorLikes` resolves the account's personal data server from its public DID document, lists the public `app.bsky.feed.like` records stored there, and hydrates each liked post. No credentials are used. Likes whose posts were deleted, or accounts whose DID document does not expose a public data server, return no rows for that part. `mode=getLikes` is the opposite direction: it lists the accounts that liked a given post.

**How deep can a thread go?**
`threadDepth` can be set up to 1000. The public thread endpoint returns about 10 reply levels per request, so the actor detects reply branches that still have children and fetches those branches separately. `maxRepliesPerPost` caps how many direct replies are followed under each post; when a branch is cut, the affected post carries `threadTruncated=true` and the run status explains why.

**What is `mode=relationships`?**
It checks public follow and block relationships between `actor` and each account in `actors` (up to batches of 30). Each account record gets `isFollowing`, `isFollowedBy`, `isMutualFollow` and the public relationship record URIs. It is not a replacement for a logged-in viewer's personalised "known followers" list, which needs an account context.

**Why did I get fewer records than `maxItems`?**
Filters can exclude many candidates, Bluesky can exhaust the source, and some search modes have source caps. The run status message reports the shortfall; it does not silently pretend the result is complete.

**Can I export followers of a very large account?**
Yes, up to `maxItems`. Bluesky returns followers in cursor-paged batches; the actor keeps walking short pages until the cap or the end of the list.

**How current is the data?**
Live. Every run reads Bluesky at request time, so schedule the actor hourly or daily for ongoing refreshes.

**Is this affiliated with Bluesky?**
No. This is an independent actor that reads public Bluesky data.

### Limitations

- **Top search uses time windows.** `searchSort=top` is engagement-ranked; the actor walks older time windows to collect more than one page, preserving ranking within each window. Results are deduplicated by post URI.
- **Time-window search is approximate.** The `latest` walk can return posts outside the exact window, can overlap between windows, and is de-duplicated by post URI. The `until` boundary uses the newer of a post's `createdAt` and `indexedAt`, and a date-only `until` includes the whole UTC day.
- **Search hit totals are estimates.** When Bluesky returns `hitsTotal`/`searchHitsTotal`, the source caps and rounds the number; it is not an exact result count.
- **Search can be temporarily blocked.** If Bluesky returns HTTP 403 even after the automatic Apify retry, the run stops with a clear status message. Try again later or use `mode=profile`, `mode=feed` or a direct post/list feed.
- **Search coverage is not exhaustive.** Bluesky's search index is recency-weighted and does not include every post.
- **Thread walking is bounded on purpose.** Each request returns about 10 reply levels, so deeper replies are fetched branch by branch. The walk stops at `threadDepth`, `maxRepliesPerPost`, `maxItems` or the per-run branch budget; affected records carry `threadTruncated=true`. Deleted, blocked, hidden or rate-limited branches still create gaps.
- **Actor likes depend on the public data server.** The like records are read from the account's personal data server rather than the public feed service. If that server is unreachable, does not appear in the DID document, or no longer stores the like record, those likes cannot be returned. Deleted posts are skipped even when their like record remains.
- **Personalised known followers need a login.** The public relationship check in `mode=relationships` can tell you whether supplied accounts follow or block a target, but a viewer-specific "followers you know" list requires an authenticated account context, which this actor does not use.
- **Starter-pack typeahead is not a separate mode.** `mode=starterPackSearch` uses the public starter-pack search endpoint, which covers keyword search and paging; a viewer-boosted typeahead ordering is not exposed without an account context.
- **Hidden accounts are skipped by default.** Accounts that carry `!no-unauthenticated` are filtered out unless `respectNoUnauthenticated=false`, and the status message reports how many records were skipped.
- **Follower counts can exceed exported rows.** Profile totals are cached by Bluesky and include accounts that are deactivated or no longer indexed. `!no-unauthenticated` accounts are returned by the API and then filtered by this actor by default, so `followersCount` can exceed the exported rows. Set `respectNoUnauthenticated=false` to include them.
- **Private data is out of scope.** Direct messages, private accounts, notifications and muted/blocked relationships are not available.
- **Some fields are conditional.** Labels, verification, live status, pinned posts, banners and media are present only when the source has them.
- **Custom feeds depend on third-party generators.** If a generator is offline, its feed returns no posts.
- **Follower lists can be very large.** Use `maxItems` to bound a run.
- **`maxItems` is capped at 5,000 per run.** Larger requests are clamped by the input schema.
- **Filtered account modes keep walking cursor pages until the cap or source exhaustion.** If Bluesky has no more matches, the run reports a shortfall in its status message.
- **`verifiedOnly` can return far fewer records.** Most post authors do not publish a verification object, so verified-only post searches are intentionally narrow.
- **`mode=feedGenerator` returns only the feeds you request.** It does not append a default What's Hot record.

# Actor input Schema

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

What to fetch.

## `searchQuery` (type: `string`):

Keyword, hashtag, phrase, or Lucene-style query. Used by search, actorSearch, starterPackSearch, and feedGeneratorsDiscovery.

## `searchSort` (type: `string`):

Search ranking. `latest` returns newest first; `top` returns engagement-ranked posts. Both use time-window pagination to collect beyond one page.

## `searchTags` (type: `array`):

Only posts with these hashtags (without `#`). Multiple tags are AND-matched. (mode=search)

## `searchDomain` (type: `string`):

Only posts linking to this domain, e.g. `github.com`. (mode=search)

## `searchUrl` (type: `string`):

Only posts linking to this exact URL. (mode=search)

## `mentionsUser` (type: `string`):

Only posts mentioning this handle or DID. (mode=search)

## `fromUser` (type: `string`):

Only posts authored by this handle or DID. (mode=search)

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

Earliest post date as `YYYY-MM-DD` or an ISO datetime. (mode=search)

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

Latest post date as `YYYY-MM-DD` or an ISO datetime. (mode=search)

## `language` (type: `string`):

ISO 639-1 language filter for post search and post records. Empty means no filter.

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

Which posts to include in the author feed. (mode=profile)

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

Include the pinned post at the top of the author feed. (mode=profile)

## `includeParents` (type: `boolean`):

Include ancestor posts above the requested post. (mode=postThread)

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

How many reply levels and ancestor levels to walk. Depths beyond about 10 reply levels are reached by walking reply branches across requests. 0 returns only the requested post. (mode=postThread)

## `maxRepliesPerPost` (type: `integer`):

Maximum direct replies followed under each post while walking a thread. Larger values reach more branches but cost more requests. (mode=postThread)

## `actor` (type: `string`):

Bluesky handle or DID. Used by profile, follows, followers, lists, actorLikes, actorFeeds, relationships, and discoveryType=suggestedFollows.

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

Handles, DIDs, or profile URLs for batch lookup. Used by profiles and by relationships as the accounts to check against `actor`.

## `postUri` (type: `string`):

AT URI of a post. Used by postThread, getLikes, repostedBy, and quotes.

## `postUris` (type: `array`):

AT URIs of posts to hydrate. Used by getPosts and as a fallback for single-post modes.

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

bsky.app post, profile, list, feed, or starter-pack URLs. Used by urlFetch, getPosts, and as a fallback for single-post modes.

## `listUri` (type: `string`):

AT URI of a Bluesky list. Used by getList and listFeed.

## `listUris` (type: `array`):

AT URIs or bsky.app list URLs for batch list processing. (mode=getList)

## `starterPackUri` (type: `string`):

AT URI of a starter pack. (mode=getStarterPack)

## `starterPackUris` (type: `array`):

AT URIs or bsky.app starter-pack URLs for batch processing. (mode=getStarterPack)

## `feedPreset` (type: `string`):

Built-in feed preset. Live presets are listed first, followed by legacy aliases mapped to What's Hot. An explicit `feedUri` always takes precedence; if neither is set, What's Hot is used for mode=feed.

## `feedUri` (type: `string`):

AT URI or bsky.app feed URL of a custom feed generator. (mode=feed / feedGenerator)

## `feedUris` (type: `array`):

AT URIs or bsky.app feed URLs for batch feed-generator lookup. (mode=feedGenerator)

## `discoveryType` (type: `string`):

Which discovery surface to fetch. (mode=discovery)

## `excludeReplies` (type: `boolean`):

Drop posts that are replies to another post.

## `excludeReposts` (type: `boolean`):

Drop posts surfaced as reposts.

## `mediaOnly` (type: `boolean`):

Only emit posts with at least one image, video, or external embed.

## `excludeLabeled` (type: `boolean`):

Drop posts and accounts that carry moderation or self-labels, including labels on a post's author.

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

Skip records whose author carries the `!no-unauthenticated` label. The run status reports how many records were skipped.

## `verifiedOnly` (type: `boolean`):

Only emit accounts with a valid Bluesky verification, or posts whose author is verified. Records with no verification object are excluded. Post modes hydrate author profiles so verification can be checked.

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

Drop posts with fewer likes.

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

Drop posts with fewer reposts.

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

Drop posts with fewer replies.

## `minQuotes` (type: `integer`):

Drop posts with fewer quote posts.

## `minFollowers` (type: `integer`):

Keep accounts with at least this many followers.

## `maxFollowers` (type: `integer`):

Keep accounts with at most this many followers.

## `includeListMetadata` (type: `boolean`):

Emit a list record before its members. (mode=getList)

## `includeStarterPackMembers` (type: `boolean`):

Emit the accounts in the starter pack's list after the pack record. (mode=getStarterPack)

## `hydrateAuthors` (type: `boolean`):

Fetch full author profiles for post records. Adds one batched profile request per 25 unique authors and flat author follower, following, post and verification fields. Enabled by default; the daily profile prefill leaves it off to stay cheap.

## `maxItems` (type: `integer`):

Hard cap on emitted records. For batch modes the cap applies across the whole run.

## Actor input object example

```json
{
  "mode": "profile",
  "searchQuery": "bluesky",
  "searchSort": "latest",
  "searchTags": [],
  "language": "",
  "authorFeedFilter": "posts_with_replies",
  "includePins": false,
  "includeParents": false,
  "threadDepth": 6,
  "maxRepliesPerPost": 100,
  "actor": "bsky.app",
  "actors": [],
  "postUris": [],
  "postUrls": [],
  "listUris": [],
  "starterPackUris": [],
  "feedPreset": "whats-hot",
  "feedUris": [],
  "discoveryType": "trendingTopics",
  "excludeReplies": false,
  "excludeReposts": false,
  "mediaOnly": false,
  "excludeLabeled": false,
  "respectNoUnauthenticated": true,
  "verifiedOnly": false,
  "includeListMetadata": false,
  "includeStarterPackMembers": true,
  "hydrateAuthors": false,
  "maxItems": 3
}
```

# Actor output Schema

## `records` (type: `string`):

Dataset containing all scraped posts, accounts, lists, starter packs, feed generators and discovery records.

# 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 = {
    "mode": "profile",
    "searchQuery": "bluesky",
    "searchSort": "latest",
    "searchTags": [],
    "language": "",
    "authorFeedFilter": "posts_with_replies",
    "includePins": false,
    "includeParents": false,
    "threadDepth": 6,
    "maxRepliesPerPost": 100,
    "actor": "bsky.app",
    "actors": [],
    "postUris": [],
    "postUrls": [],
    "listUris": [],
    "starterPackUris": [],
    "feedPreset": "whats-hot",
    "feedUris": [],
    "discoveryType": "trendingTopics",
    "excludeReplies": false,
    "excludeReposts": false,
    "mediaOnly": false,
    "excludeLabeled": false,
    "respectNoUnauthenticated": true,
    "verifiedOnly": false,
    "includeListMetadata": false,
    "includeStarterPackMembers": true,
    "hydrateAuthors": false,
    "maxItems": 3
};

// Run the Actor and wait for it to finish
const run = await client.actor("nimbryxo-studio/bluesky-posts-profiles-feeds-and-threads-scraper").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 = {
    "mode": "profile",
    "searchQuery": "bluesky",
    "searchSort": "latest",
    "searchTags": [],
    "language": "",
    "authorFeedFilter": "posts_with_replies",
    "includePins": False,
    "includeParents": False,
    "threadDepth": 6,
    "maxRepliesPerPost": 100,
    "actor": "bsky.app",
    "actors": [],
    "postUris": [],
    "postUrls": [],
    "listUris": [],
    "starterPackUris": [],
    "feedPreset": "whats-hot",
    "feedUris": [],
    "discoveryType": "trendingTopics",
    "excludeReplies": False,
    "excludeReposts": False,
    "mediaOnly": False,
    "excludeLabeled": False,
    "respectNoUnauthenticated": True,
    "verifiedOnly": False,
    "includeListMetadata": False,
    "includeStarterPackMembers": True,
    "hydrateAuthors": False,
    "maxItems": 3,
}

# Run the Actor and wait for it to finish
run = client.actor("nimbryxo-studio/bluesky-posts-profiles-feeds-and-threads-scraper").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 '{
  "mode": "profile",
  "searchQuery": "bluesky",
  "searchSort": "latest",
  "searchTags": [],
  "language": "",
  "authorFeedFilter": "posts_with_replies",
  "includePins": false,
  "includeParents": false,
  "threadDepth": 6,
  "maxRepliesPerPost": 100,
  "actor": "bsky.app",
  "actors": [],
  "postUris": [],
  "postUrls": [],
  "listUris": [],
  "starterPackUris": [],
  "feedPreset": "whats-hot",
  "feedUris": [],
  "discoveryType": "trendingTopics",
  "excludeReplies": false,
  "excludeReposts": false,
  "mediaOnly": false,
  "excludeLabeled": false,
  "respectNoUnauthenticated": true,
  "verifiedOnly": false,
  "includeListMetadata": false,
  "includeStarterPackMembers": true,
  "hydrateAuthors": false,
  "maxItems": 3
}' |
apify call nimbryxo-studio/bluesky-posts-profiles-feeds-and-threads-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nimbryxo-studio/bluesky-posts-profiles-feeds-and-threads-scraper"
        }
    }
}
```

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/MFlAz6T5BhkrS9qhc/builds/QVca54L51kgdV5Lsb/openapi.json
