# Bluesky Scraper – Posts, Search, Profiles & Followers (`openharvest/bluesky-scraper`) Actor

Bluesky scraper without login: search posts, author feeds, profiles, followers and follows, custom feeds and lists, full reply threads and trending topics. Flat output with likes, reposts, replies, quotes, hashtags, mentions, links, images and video.

- **URL**: https://apify.com/openharvest/bluesky-scraper.md
- **Developed by:** [Enes Bulanık](https://apify.com/openharvest) (community)
- **Categories:** Social media, Automation, AI
- **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.

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 – Posts, Search, Profiles & Followers

Bluesky scraper without login: search posts, author feeds, profiles, followers and follows, custom feeds and lists, full reply threads and trending topics. Flat output with likes, reposts, replies, quotes, hashtags, mentions, links, images and video.

**Price: $0.80 per 1,000 posts, $1 per 1,000 profiles.** No start fee, and inputs that fail are never charged.

### What does Bluesky Scraper do?

It reads Bluesky's **public AT Protocol API** (the same open API the Bluesky app uses) – no account, no API key, no browser:

| Mode | What you get |
|---|---|
| `searchPosts` | Posts matching a keyword, phrase, #hashtag, `from:handle` or `domain:` – sort by latest or top, filter by date and language |
| `authorFeed` | All posts of one or more accounts (optionally replies, media-only or reposts) |
| `profiles` | Profile details: followers, following, posts count, bio, avatar, verified badge, join date. Or search profiles by keyword |
| `followers` / `follows` | The account (with counts) plus the list of its followers / followed accounts |
| `customFeed` | Posts from any custom feed (e.g. "Discover", "What's Hot") or list feed |
| `postThread` | A post and **all its replies**, flattened with `threadDepth` |
| `trending` | Bluesky's current **trending topics** (rank, category, status, post count) – optionally with top posts per topic |

### Why use this Bluesky scraper?

- **All modes in one Actor** – no need to combine three different scrapers. Trending topics, custom feeds and threads are included.
- **No login needed** – everything uses public data. An optional app password is only used if Bluesky ever blocks anonymous search.
- **Search past the first page** – "latest" search results are paged back through time, beyond the first page Bluesky serves to anonymous users.
- **Flat output** – one row per post, with engagement counts as integers and embeds unpacked into simple columns.
- **Plain API calls** – about 100 posts per request, so 1,000 posts take a few seconds and cost $0.80.

### Use cases

- **Social listening & brand monitoring** – track mentions of your brand, product or hashtag.
- **Trend research & journalism** – collect what's trending and the posts behind it.
- **Influencer & audience analysis** – profile stats, follower lists, engagement per post.
- **Academic research & NLP datasets** – build corpora of public posts by keyword, language and date.
- **Community & feed analytics** – measure what a custom feed or list surfaces.
- **AI agents** – give an LLM a Bluesky search tool that returns current posts.

### How to use it

1. Choose **What to scrape** (mode).
2. Fill in the matching field: **Search query**, **Handles**, **Feed URLs** or **Post URLs** (trending needs nothing).
3. Set **Max items per input** and optional filters (sort, since/until, language).
4. Click **Start** and download JSON, CSV or Excel, or use the API.

### Input

| Field | Type | Used by | Description |
|---|---|---|---|
| `mode` | string | all | `searchPosts` (default), `authorFeed`, `profiles`, `followers`, `follows`, `customFeed`, `postThread`, `trending` |
| `query` | string | searchPosts, profiles | Search text. Supports `"phrases"`, `#tags`, `from:`, `mentions:`, `domain:`. Default `artificial intelligence`. |
| `handles` | array | authorFeed, profiles, followers, follows | `jay.bsky.team`, `@jay.bsky.team`, `https://bsky.app/profile/...` or a DID |
| `feedUrls` | array | customFeed | `https://bsky.app/profile/<handle>/feed/<name>`, `.../lists/<id>` or `at://` URIs |
| `postUrls` | array | postThread | `https://bsky.app/profile/<handle>/post/<id>` |
| `maxItems` | integer | all | Max results per query / handle / feed / thread. Default `50`. |
| `sort` | string | searchPosts | `latest` (default) or `top` |
| `since` / `until` | string | searchPosts, authorFeed, customFeed | `2025-01-31` or relative `7 days` |
| `lang` | string | searchPosts | Language code, e.g. `en` |
| `authorFeedFilter` | string | authorFeed | `posts_and_author_threads` (default), `posts_no_replies`, `posts_with_replies`, `posts_with_media`, `posts_with_video` |
| `includeReposts` | boolean | authorFeed | Include reposts. Default `false`. |
| `postsPerTrend` | integer | trending | Also fetch N posts per trending topic. Default `0`. |
| `blueskyHandle` / `blueskyAppPassword` | string | searchPosts | Optional login (app password, stored as a secret). Normally not needed. |

Example – search:

```json
{
    "mode": "searchPosts",
    "query": "#photography",
    "sort": "latest",
    "since": "7 days",
    "lang": "en",
    "maxItems": 500
}
```

Example – followers of two accounts:

```json
{
    "mode": "followers",
    "handles": ["bsky.app", "https://bsky.app/profile/jay.bsky.team"],
    "maxItems": 1000
}
```

### Output

Every item has `itemType`: `post`, `profile`, `trend`, `error` or `status`. Real samples (shortened):

**Post**

```json
{
    "itemType": "post",
    "uri": "at://did:plc:kwxpje5qog6wwndagwbssatp/app.bsky.feed.post/3mviutosqmk2t",
    "url": "https://bsky.app/profile/avclub.com/post/3mviutosqmk2t",
    "authorHandle": "avclub.com",
    "authorDisplayName": "The A.V. Club",
    "authorDid": "did:plc:kwxpje5qog6wwndagwbssatp",
    "text": "Less than 4 hours until the #Emmys kick off! You can keep up with all the commentary, winners, joy—and, let's face it, disappointment—via our Emmys Watch feed right here on @bsky.app. ...",
    "createdAt": "2026-09-14T19:47:12.655Z",
    "likeCount": 538,
    "repostCount": 39,
    "replyCount": 32,
    "quoteCount": 4,
    "bookmarkCount": 26,
    "langs": ["en"],
    "hashtags": ["Emmys"],
    "mentions": ["bsky.app"],
    "links": ["https://bsky.app/profile/avclub.com/feed/emmys-watch-r"],
    "embedType": "images",
    "images": [{ "url": "https://cdn.bsky.app/img/feed_fullsize/plain/did:plc:kwxpje5qog6wwndagwbssatp/bafkreicwxzp6...", "thumbnailUrl": "https://cdn.bsky.app/img/feed_thumbnail/plain/...", "alt": null }],
    "videoUrl": null,
    "externalUrl": null,
    "quotedPostUrl": null,
    "isReply": false,
    "isRepost": true,
    "repostedByHandle": "bsky.app",
    "threadDepth": null,
    "labels": [],
    "source": "authorFeed",
    "input": "bsky.app",
    "scrapedAt": "2026-10-07T19:17:11.763Z"
}
```

**Profile**

```json
{
    "itemType": "profile",
    "did": "did:plc:z72i7hdynmk6r22z27h6tvur",
    "handle": "bsky.app",
    "displayName": "Bluesky",
    "description": "official Bluesky account (check username👆)\n\nBugs, feature requests, feedback: support@bsky.app",
    "url": "https://bsky.app/profile/bsky.app",
    "followersCount": 35180843,
    "followsCount": 15,
    "postsCount": 866,
    "createdAt": "2023-04-12T04:53:57.057Z",
    "verified": false,
    "relation": "profile",
    "relatedTo": null
}
```

**Trending topic**

```json
{
    "itemType": "trend",
    "rank": 1,
    "topic": "Inktober 2026 day 7: panic",
    "description": "Artists share ink drawings for the seventh prompt, wrapping up the challenge's first week.",
    "category": "culture",
    "status": "trending",
    "postCount": 28356,
    "url": "https://bsky.app/profile/did:plc:qrz3lhbyuxbeilrc6nekdqme/feed/24cc588fc536",
    "feedUri": "at://did:plc:qrz3lhbyuxbeilrc6nekdqme/app.bsky.feed.generator/24cc588fc536"
}
```

Other post fields: `videoUrl` (HLS `.m3u8`), `videoThumbnail`, `externalTitle`/`externalDescription` (link card), `quotedPostUri`/`quotedPostText`/`quotedAuthorHandle`, `replyParentUri`, `replyRootUri`, `isPinned`, `threadRootUri`, `trendTopic`.

Followers / follows rows (`relation: "follower"` / `"follow"`, `relatedTo: <account>`) contain handle, name, bio, avatar and join date. Bluesky does not return per-follower counts in lists – run the `profiles` mode on them if you need counts.

Errors are free: `NOT_FOUND`, `INVALID_INPUT`, `UNAVAILABLE`, `FEED_UNAVAILABLE` (personalized or offline feed), `AUTH_REQUIRED`, `SCRAPE_FAILED`, `NO_RESULTS`.

The dataset has views: **Posts**, **Media & links**, **Profiles**, **Trending topics** and **Errors & status**.

### Pricing

Pay-per-event – you only pay for results.

| Event | Price | Charged for |
|---|---|---|
| `post` | **$0.0008** ($0.80 / 1,000) | Each post, and each trending-topic row |
| `profile` | **$0.001** ($1 / 1,000) | Each profile, follower or follow row |
| Errors / status rows | **Free** | Unknown handles, invalid URLs, offline feeds, empty results |

| Job | Cost |
|---|---|
| 1,000 search results | **$0.80** |
| Profiles of 100 accounts | **$0.10** |
| 10,000 followers of an account | **$10.00** |
| 25 trending topics + 10 posts each | 275 × $0.0008 = **$0.22** |

Set **Max cost per run** in the run options – the Actor stops exactly at your budget.

### Use with AI agents (MCP)

Use this Actor as a tool in Claude, ChatGPT, Cursor or any MCP client via the [Apify MCP server](https://mcp.apify.com): `https://mcp.apify.com?tools=openharvest/bluesky-scraper`. Agents can also find it with `search-actors` ("Bluesky posts", "Bluesky trending").

Example `call-actor` input:

```json
{
    "mode": "searchPosts",
    "query": "from:nytimes.com climate",
    "sort": "top",
    "maxItems": 20
}
```

Or ask for what's trending right now:

```json
{ "mode": "trending", "maxItems": 10, "postsPerTrend": 3 }
```

### FAQ

**Do I need a Bluesky account?**
No. All modes use public, unauthenticated API endpoints. The optional app password is only a fallback for post search, in case Bluesky starts requiring login for it. Never enter your main password – create an app password in Bluesky Settings → Privacy and security → App passwords.

**How many search results can I get?**
With `sort: latest`, the Actor pages back through time until `maxItems` is reached or the results run out. With `sort: top`, Bluesky serves only the first ~100 results anonymously; add an app password to page further.

**Why does a custom feed return `FEED_UNAVAILABLE`?**
Some feeds are personalized ("Following", "Popular with Friends") and need a logged-in viewer, or the feed's own server is offline. Public feeds like "Discover"/"What's Hot" work.

**Can I get emails or phone numbers?**
No. Bluesky profiles don't expose them and this Actor doesn't try to collect personal contact data.

**Does it get deleted or private posts?**
No. Bluesky has no private posts; deleted posts and blocked/taken-down accounts are not returned.

**Can I run it on a schedule?**
Yes – e.g. `searchPosts` with `since: "1 day"` every day, or `trending` every hour.

### Is it legal to scrape Bluesky?

This Actor only reads **public data** from Bluesky's open AT Protocol API, which Bluesky provides for third-party apps and research. It does not log in by default, does not access private data and does not collect contact details. Posts are still personal data and copyrighted content of their authors: comply with GDPR and similar laws, respect Bluesky's Terms of Service and developer guidelines, honour deletions, and **never use the data for spam, harassment or unsolicited messaging**. If unsure, consult a lawyer.

### Related actors

- **Telegram Channel Scraper – Posts, Messages & Views, No Login**
- **Hacker News Scraper – Stories, Comments & Who Is Hiring Jobs**
- **Substack Scraper – Newsletter Posts, Full Text & Comments**
- **Weibo Scraper – Hot Search, Trending Posts, Comments & Profiles**

### Support & feedback

Found a bug or need a field? Open an issue in the **Issues** tab – feature requests are welcome.

# Actor input Schema

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

<b>searchPosts</b>: posts matching <i>Search query</i>. <b>authorFeed</b>: posts of the <i>Handles</i>. <b>profiles</b>: profile details of the <i>Handles</i> (or profiles matching the <i>Search query</i>). <b>followers</b> / <b>follows</b>: who follows / is followed by the <i>Handles</i>. <b>customFeed</b>: posts of custom feeds or lists in <i>Feed URLs</i>. <b>postThread</b>: a post and all its replies from <i>Post URLs</i>. <b>trending</b>: Bluesky's current trending topics.

## `query` (type: `string`):

For <b>searchPosts</b> (and <b>profiles</b> when no handles are given). Supports Bluesky search syntax: <code>"exact phrase"</code>, <code>#hashtag</code>, <code>from:handle</code>, <code>mentions:handle</code>, <code>domain:nytimes.com</code>. Default: <code>artificial intelligence</code>.

## `handles` (type: `array`):

For <b>authorFeed</b>, <b>profiles</b>, <b>followers</b> and <b>follows</b>. Accepts <code>jay.bsky.team</code>, <code>@jay.bsky.team</code>, <code>https://bsky.app/profile/jay.bsky.team</code> or a DID. A bare name like <code>alice</code> becomes <code>alice.bsky.social</code>.

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

For <b>customFeed</b>. Custom feed or list URLs, e.g. <code>https://bsky.app/profile/bsky.app/feed/whats-hot</code>, <code>https://bsky.app/profile/bsky.app/lists/3lvpca43j5z26</code> or <code>at://</code> URIs.

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

For <b>postThread</b>. Post URLs like <code>https://bsky.app/profile/bsky.app/post/3l6oveex3ii2l</code> (or <code>at://</code> URIs). The post and all its replies (nested, with <code>threadDepth</code>) are returned.

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

Maximum results per query / handle / feed / post thread (for trending: max topics). You pay per result, so this also caps your cost.

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

For <b>searchPosts</b>: <b>latest</b> (newest first, pages back until Max items is reached) or <b>top</b> (most engaging). Without a login, Bluesky returns only the first ~100 <i>top</i> results.

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

Only posts created at or after this date. Absolute (<code>2025-01-31</code>) or relative (<code>7 days</code>, <code>24 hours</code>). Applies to searchPosts, authorFeed and customFeed.

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

Only posts created before this date. Absolute or relative, like <i>Posts since</i>.

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

For <b>searchPosts</b>: only posts in this language (ISO code such as <code>en</code>, <code>de</code>, <code>ja</code>, <code>pt</code>).

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

For <b>authorFeed</b>: which posts to include.

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

For <b>authorFeed</b>: also return posts the account reposted (marked <code>isRepost: true</code>).

## `postsPerTrend` (type: `integer`):

For <b>trending</b>: also fetch this many top posts for each trending topic (0 = topics only).

## `blueskyHandle` (type: `string`):

Optional and normally NOT needed. Only used for post search if Bluesky refuses anonymous search, or to page beyond the first ~100 <i>top</i> results. Use together with an app password.

## `blueskyAppPassword` (type: `string`):

An app password (Bluesky Settings -> Privacy and security -> App passwords), never your main password. Stored encrypted and used only for post search.

## Actor input object example

```json
{
  "mode": "searchPosts",
  "query": "#photography",
  "handles": [
    "bsky.app",
    "https://bsky.app/profile/nytimes.com"
  ],
  "feedUrls": [
    "https://bsky.app/profile/bsky.app/feed/whats-hot"
  ],
  "postUrls": [
    "https://bsky.app/profile/bsky.app/post/3l6oveex3ii2l"
  ],
  "maxItems": 200,
  "sort": "top",
  "since": "7 days",
  "until": "2025-12-31",
  "lang": "en",
  "authorFeedFilter": "posts_no_replies",
  "includeReposts": true,
  "postsPerTrend": 10,
  "blueskyHandle": "yourname.bsky.social"
}
```

# Actor output Schema

## `results` (type: `string`):

All scraped items in the default dataset (JSON, CSV, Excel and more via the dataset export).

## `files` (type: `string`):

Records saved in the default key-value store, e.g. the run summary.

# 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 = {
    "query": "artificial intelligence",
    "maxItems": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("openharvest/bluesky-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 = {
    "query": "artificial intelligence",
    "maxItems": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("openharvest/bluesky-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 '{
  "query": "artificial intelligence",
  "maxItems": 50
}' |
apify call openharvest/bluesky-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,openharvest/bluesky-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/X6U24aVd9g1VcSUsy/builds/pGL7wv5wKyTbzqdYR/openapi.json
