# Bluesky Scraper (`blueskyscraper/bluesky-scraper`) Actor

Scrape Bluesky without a login: posts of any account, keyword and hashtag search, profiles with followers and bio contacts, followers and following, comments under a post, likers, reposters, quotes, custom feeds and starter packs. Export JSON, CSV or Excel, or call it by API.

- **URL**: https://apify.com/blueskyscraper/bluesky-scraper.md
- **Developed by:** [BlueskyScraper](https://apify.com/blueskyscraper) (community)
- **Categories:** Social media, Agents, MCP servers
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $0.70 / 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** turns Bluesky into clean rows without a login or an API key: **posts of any account**, **keyword and hashtag search** that goes back in time, **profiles** with follower counts and the e-mails and links written in the bio, **followers and following**, **every comment under a post**, **likers, reposters and quote posts**, **custom feeds**, **lists and starter packs**, and **account search** by keyword. Every post carries text, time, author, likes, reposts, replies, quotes, images, video, link card, hashtags, mentions and the reply context. **No start fee, error rows are free** — $1 per 1,000 posts.

Paste handles, keywords or bsky.app links, click Start, and download JSON, CSV or Excel — or pull the rows through the Apify API into Google Sheets, n8n, Make, Zapier, a database or an AI agent.

### Tested head-to-head — 28 September 2026

![Bluesky scrapers tested head-to-head: this Actor returns search results in 2.8 s, 300 of 300 posts, 43 fields per post, follower counts included, $1 per 1,000 posts and $0.50 per 1,000 followers](https://api.apify.com/v2/key-value-stores/tZJ3TnhQJ1jqUQVXq/records/bluesky-tested-2026-09-28.png)

The same requests went to this Actor and to the six most-used Bluesky scrapers on the Apify Store: 20 latest posts for `coffee`, 20 posts of @nytimes.com, 20 followers of @nytimes.com, and a 300-post deep search.

| | This Actor | The six others |
|---|---|---|
| Keyword search, 20 posts | **2.8 s** | 3.3–28.9 s |
| 300 posts asked for one keyword | **300** | 50–300 (three tested) |
| Fields in a post row | **43** | 18–32 |
| Followers export with each person's follower count | **yes, at no extra cost** | 2 of 3 tested |
| Price per 1,000 followers | **$0.50** | $1.25–3.00 |
| Price per 1,000 posts | **$1.00** | $1.00–3.00 |
| Start fee per run | **none** | 3 of 6 charge $0.004–0.005 |

### What is Bluesky Scraper?

Bluesky is built on the open AT Protocol, and its public data can be read by anyone. Reading it at scale still takes work: search refuses to page for visitors who are not logged in, big comment threads come back trimmed, follower lists come without counts, and every endpoint returns a different nested shape. This Actor does that work and hands back flat rows with the same field names in every mode.

It is a **Bluesky API alternative** for social listening and brand monitoring, audience and competitor research, lead lists from bios, trend and sentiment analysis, academic research, archiving, and AI agents asked "what are people on Bluesky saying about X". It needs **no Bluesky account, no app password and no cookies**, so no account of yours can ever be limited.

Twelve modes, one row shape per type:

| Mode | You give | You get |
|---|---|---|
| **Posts** | handles | the account's posts, own threads, replies or media posts, reposts marked, pinned post flagged |
| **Search** | keywords, #hashtags, phrases | public posts, Latest paged back in time or Top ranked; language, date, author, mention, domain and hashtag filters |
| **Profiles** | handles | followers, following, posts count, bio, e-mails, phones and links from the bio, blue check, latest post |
| **Followers / Following** | handles | everyone who follows the account, or whom it follows |
| **Comments** | post URLs | the post and every reply under it, with depth and parent, big threads opened branch by branch |
| **Quotes** | post URLs | posts that quote the post |
| **Likers / Reposters** | post URLs | people who liked or reposted it, with the time of the like |
| **Account search** | keywords | people and brands matching a keyword |
| **Custom feed** | feed URL | the posts a feed shows right now |
| **List or starter pack** | list or starter pack URL | its members |

### What data does Bluesky Scraper extract?

#### Post rows

| Field | Example |
|---|---|
| `url`, `uri`, `id`, `cid` | https://bsky.app/profile/bsky.app/post/3l6oveex3ii2l · at://did:plc:…/app.bsky.feed.post/3l6oveex3ii2l |
| `text`, `language`, `languages` | "👋 Bluesky is an open social network…" · en · \["en"] |
| `createdAt`, `indexedAt` | 2024-10-17T07:06:51.491Z |
| `handle`, `did`, `displayName`, `avatar`, `authorVerified`, `authorProfileUrl` | bsky.app · did:plc:z72i7hdynmk6r22z27h6tvur · Bluesky |
| `likeCount`, `repostCount`, `replyCount`, `quoteCount`, `engagement` | 63700 · 9533 · 8596 · 708 · 82537 |
| `mediaType`, `images[]`, `video`, `externalLink` | image · \[{url, thumbnail, alt, width, height}] · {playlistUrl, thumbnail} · {url, title, description} |
| `hashtags`, `mentions`, `links` | \["coffee"] · \[{handle, did}] · \["https://…"] |
| `isReply`, `replyToUrl`, `replyToHandle`, `replyToDid`, `rootUrl` | the conversation a reply belongs to |
| `isQuote`, `quotedPost` | {url, handle, text, createdAt, likeCount} |
| `isRepost`, `repostedBy`, `repostedAt`, `isPinned`, `labels` | reposts and pins in an account's feed |
| `threadDepth`, `parentUri` | Comments mode: 1 = direct reply, 2 = reply to a reply |
| `containsKeyword`, `query`, `source`, `sourceInput` | which input produced the row |

#### Profile rows

`handle`, `did`, `displayName`, `description`, `followersCount`, `followsCount`, `postsCount`, `emails`, `phones`, `links` (written in the bio), `verified`, `isTrustedVerifier`, `isLabeler`, `listsCount`, `feedsCount`, `starterPacksCount`, `acceptsDMs`, `avatar`, `banner`, `pinnedPostUrl`, `createdAt`, `latestPostAt`, `latestPostUrl`, `latestPostText`.

#### Account rows (followers, following, likers, reposters, members, account search)

`handle`, `did`, `displayName`, `description`, `emails`, `links`, `verified`, `avatar`, `createdAt`, `followersCount`, `followsCount`, `postsCount`, `relation` (follower, following, liked, reposted, list member, search match), `relatedTo`, `actionAt` (when the like was given). Counts are included at the account price; switch on **Full profiles** for the complete profile row.

Every row has `type` and `scrapedAt`. An account, post or URL that cannot be read comes back as an `error` row with the reason — `account not found`, `not found or deleted` — never silently missing, and never on the bill.

### How much does it cost to scrape Bluesky?

Pay per row, no subscription, no start fee, no charge for errors:

| Row | Price | For |
|---|---|---|
| **Post** | **$0.001** — $1 per 1,000 | Posts, Search, Comments, Quotes, Custom feed |
| **Profile** | $0.002 | Profiles mode, the optional profile row, Full profiles for followers |
| **Account** | $0.0005 — $0.5 per 1,000 | Followers, Following, Likers, Reposters, list members, account search |

There is no browser and no proxy in this Actor, so platform usage stays close to zero. On the **Apify free plan ($5 of credit a month)** that is thousands of posts every month for free. Set **Maximum rows per run** and a run can never cost more than that number of rows.

### How to scrape Bluesky

1. **Pick a mode** — Posts, Search, Profiles, Followers, Comments and the rest.
2. **Paste inputs** — handles (`bsky.app`, `@nytimes.com`, a profile URL), keywords (`coffee`, `#bookstodon`), post URLs, or any bsky.app link in **Bluesky URLs**, which is routed to the right mode by itself.
3. **Set the cap** — **Results per account, keyword or URL**, and **Maximum rows per run** for the whole run.
4. **Add filters if you need them** — date window, languages, minimum likes / reposts / replies, domain, mentions, hashtags.
5. **Start.** Rows appear in the dataset as they are collected; download JSON, CSV, Excel or HTML, or read them by API.

### ⬇️ Input

The last 500 posts of two accounts, reposts left out, with their profiles:

```json
{
    "mode": "posts",
    "handles": ["nytimes.com", "theonion.com"],
    "maxResultsPerInput": 500,
    "includeReposts": false,
    "includeProfile": true
}
```

Brand monitoring — English posts from the last week that mention a brand, with at least one like, new posts only on every scheduled run:

```json
{
    "mode": "search",
    "keywords": ["notion"],
    "languages": ["en"],
    "postedAfter": "2026-09-21",
    "minLikes": 1,
    "strictKeywordMatch": true,
    "maxResultsPerInput": 1000,
    "monitoringStoreName": "notion-watch"
}
```

Everyone who liked a post, with full profiles:

```json
{
    "mode": "likers",
    "postUrls": ["https://bsky.app/profile/bsky.app/post/3l6oveex3ii2l"],
    "maxResultsPerInput": 2000,
    "includeProfileDetails": true
}
```

### ⬆️ Output

```json
{
    "type": "post",
    "url": "https://bsky.app/profile/bsky.app/post/3l6oveex3ii2l",
    "text": "👋  Bluesky is an open social network that gives creators independence from platforms, developers the freedom to build, and users a choice in their experience…",
    "createdAt": "2024-10-17T07:06:51.491Z",
    "language": "en",
    "handle": "bsky.app",
    "displayName": "Bluesky",
    "likeCount": 63700,
    "repostCount": 9533,
    "replyCount": 8596,
    "quoteCount": 708,
    "mediaType": "text",
    "hashtags": [],
    "links": [],
    "isReply": false,
    "isPinned": true,
    "source": "posts",
    "sourceInput": "bsky.app"
}
```

### Use cases

#### Social listening and brand monitoring

Search your brand, product or competitor by keyword, mention or linked domain, keep English posts with engagement, and schedule the run with **Monitoring mode** so every run delivers only new posts.

#### Lead generation and outreach lists

Export the followers of a competitor, the members of a starter pack in your niche, or everyone who liked a launch post — with bios, and the e-mails and websites people wrote in them.

#### Creator, audience and competitor research

Profiles with follower, following and post counts plus the date of the latest post show who is active and growing; the Posts mode gives the full publishing history and engagement.

#### Research, archiving and AI

Every conversation as a tree (`threadDepth`, `parentUri`), stable IDs (`did`, `uri`) that survive handle changes, and clean text for sentiment analysis, topic modelling and RAG pipelines.

### Integrations

- **API** — start runs and read datasets from any language; Python and JavaScript clients from Apify.
- **No-code** — n8n, Make, Zapier, Google Sheets, Airtable, Slack, webhooks.
- **Schedules** — hourly or daily runs; with **Monitoring mode** each run returns only what is new.
- **AI agents and MCP** — call the Actor as a tool from Claude, ChatGPT or any MCP client through the Apify MCP server.

### Troubleshooting

- **Search Top stops at 100 posts** — that is all Bluesky shows a visitor who is not logged in. Choose **Latest**: it pages back in time as far as you ask.
- **A big post returns fewer replies than its counter** — Bluesky sends large threads trimmed; the Actor opens every reply that has its own replies. Replies that were deleted, or hidden by the author, cannot be read.
- **An account returns an error row** — the handle does not exist, was renamed, or the account asked Bluesky not to be shown to logged-out visitors. Such accounts are respected and skipped.

### ❓ FAQ

#### Is it legal to scrape Bluesky?

This Actor reads only public data that Bluesky serves to anyone without a login, the same data its open AT Protocol publishes. It does not log in, does not bypass any access control, and skips accounts that asked not to be shown to logged-out visitors. Personal data such as names and e-mails may be protected by laws like the GDPR — make sure you have a lawful reason to process it, and consult a lawyer if unsure.

#### Do I need a Bluesky account or app password?

No. Nothing is logged in and nothing is asked of you except the inputs.

#### How far back can it go?

Posts mode pages through an account's whole history. Search in Latest order pages back by date as far as Bluesky's index goes.

#### Can I get only new posts every day?

Yes — give **Monitoring mode** a name and schedule the run; each run returns only posts not delivered before under that name.

#### Can I use it from Python, n8n or an AI agent?

Yes — every run is available through the Apify API, integrations and the MCP server.

### Your feedback

Something missing, a field you need, a result that looks wrong? Open an issue on the Actor's Issues tab — it is read and answered.

### You might also like

| Actor | What it does |
|---|---|
| [Bluesky Posts Scraper](https://apify.com/blueskyscraper/bluesky-posts-scraper) | Keyword, hashtag and phrase search, account posts and custom feeds |
| [Bluesky Profile Scraper](https://apify.com/blueskyscraper/bluesky-profile-scraper) | Profiles with follower counts, bio e-mails and links, latest post |
| [Bluesky Followers Scraper](https://apify.com/blueskyscraper/bluesky-followers-scraper) | Followers, following, likers, reposters, list and starter pack members |
| [Bluesky Comments Scraper](https://apify.com/blueskyscraper/bluesky-comments-scraper) | Every reply under a post as a tree, plus quote posts |

# Actor input Schema

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

<b>Posts</b> — an account's own posts. <b>Search</b> — public posts for a keyword, hashtag or phrase. <b>Profiles</b> — followers count, bio, links, emails. <b>Followers / Following</b> — an account's audience. <b>Comments</b> — a post and every reply under it. <b>Quotes / Likers / Reposters</b> — who reacted to a post. <b>Account search</b> — people and brands by keyword. <b>Custom feed</b> and <b>List or starter pack</b> — paste the URL into <i>Bluesky URLs</i>.

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

For Posts, Profiles, Followers and Following. Handle, @handle, profile URL or DID: <code>bsky.app</code>, <code>@nytimes.com</code>, <code>https://bsky.app/profile/jay.bsky.team</code>, <code>alice</code> (read as alice.bsky.social).

## `keywords` (type: `array`):

For Search and Account search. <code>coffee</code>, <code>#bookstodon</code>, <code>"climate policy"</code> (exact phrase). Bluesky search operators work too: <code>from:nytimes.com</code>, <code>lang:de</code>, <code>domain:github.com</code>.

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

For Comments, Quotes, Likers and Reposters: <code>https://bsky.app/profile/bsky.app/post/3l6oveex3ii2l</code>. An <code>at://</code> URI works too.

## `startUrls` (type: `array`):

Any bsky.app link, routed automatically: profiles, posts, <b>custom feeds</b> (<code>…/profile/\<handle>/feed/\<name></code>), <b>lists</b> (<code>…/lists/\<id></code>), <b>starter packs</b> (<code>bsky.app/starter-pack/…</code>), search and hashtag pages.

## `maxResultsPerInput` (type: `integer`):

Cap for each input, not for the whole run. Search pages back in time for Latest; Top returns up to 100 posts per keyword (all Bluesky shows a visitor who is not logged in).

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

Search: <b>Latest</b> goes back as far as you ask; <b>Top</b> is Bluesky's ranking, first 100 posts per keyword.

## `postedAfter` (type: `string`):

<code>YYYY-MM-DD</code>. Older posts are dropped; Posts and Search stop paging once they reach them.

## `postedBefore` (type: `string`):

<code>YYYY-MM-DD</code>. With <i>Only posts after</i> this gives a date window.

## `languages` (type: `array`):

Keep only posts written in these languages, ISO codes: <code>en</code>, <code>de</code>, <code>ja</code>, <code>pt</code>. Bluesky apps tag the language of almost every post. Empty = all.

## `hashtags` (type: `array`):

Search: only posts carrying all of these hashtags (without #).

## `fromAuthor` (type: `string`):

Search: limit results to one account's posts.

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

Search: posts that mention this account — brand monitoring in one field.

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

Search: posts that link to a website, e.g. <code>nytimes.com</code> — who shares your content.

## `linkUrl` (type: `string`):

Search: posts sharing one exact page.

## `strictKeywordMatch` (type: `boolean`):

Search: Bluesky also returns posts that match a keyword by stem or by a link preview. On keeps only posts whose text, hashtags or link title contain it. Every row carries <code>containsKeyword</code> either way.

## `filterKeywords` (type: `array`):

Any mode: a post is kept when its text, hashtags or link title contain any of these words. Empty = keep everything.

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

Drop posts with fewer likes.

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

Drop posts with fewer reposts.

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

Drop posts with fewer replies.

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

Posts mode: the same tabs a profile shows.

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

Posts mode: keep posts the account reposted (marked <code>isRepost</code>, with <code>repostedAt</code>).

## `includeProfile` (type: `boolean`):

Posts mode: also save one profile row per account — followers, bio, links, emails.

## `includeLatestPost` (type: `boolean`):

Profiles mode: fill <code>latestPostAt</code>, <code>latestPostUrl</code> and <code>latestPostText</code> — shows at a glance whether the account is active. One extra request per profile, no extra charge.

## `includeProfileDetails` (type: `boolean`):

Followers, Following, Likers, Reposters, Account search, Lists: every person already comes with followers, following and posts counts. On opens the full profile row instead — phones, banner, pinned post, lists and feeds counts, latest post fields — billed at the profile price.

## `includeRootPost` (type: `boolean`):

Comments mode: the first row is the post itself, then its replies.

## `replySort` (type: `string`):

Comments mode: order of replies under each post. Every reply keeps <code>threadDepth</code> and <code>parentUri</code>, so the tree can always be rebuilt.

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

Stop after this many rows in the whole run — a hard ceiling on cost.

## `monitoringStoreName` (type: `string`):

Give the run a name (e.g. <code>brand-watch</code>) and every later run with the same name returns <b>only posts it has not delivered before</b> — schedule it hourly and get a clean feed of new posts, nothing billed twice. Empty = normal run.

## Actor input object example

```json
{
  "mode": "posts",
  "handles": [
    "bsky.app"
  ],
  "maxResultsPerInput": 50,
  "sort": "latest",
  "strictKeywordMatch": false,
  "minLikes": 0,
  "minReposts": 0,
  "minReplies": 0,
  "authorFeedFilter": "posts_and_author_threads",
  "includeReposts": true,
  "includeProfile": false,
  "includeLatestPost": true,
  "includeProfileDetails": false,
  "includeRootPost": true,
  "replySort": "likes",
  "maxItems": 10000
}
```

# Actor output Schema

## `rows` (type: `string`):

One row per post, profile or account; error rows explain inputs that could not be read.

# 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 = {
    "handles": [
        "bsky.app"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("blueskyscraper/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 = { "handles": ["bsky.app"] }

# Run the Actor and wait for it to finish
run = client.actor("blueskyscraper/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 '{
  "handles": [
    "bsky.app"
  ]
}' |
apify call blueskyscraper/bluesky-scraper --silent --output-dataset

```

## MCP server setup

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