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

Bluesky scraper and API: search posts by keyword, export author feeds, profiles, followers, follows and reply threads to CSV, Excel or JSON. No login needed. $1 per 1,000 results.

- **URL**: https://apify.com/deanko/bluesky-scraper.md
- **Developed by:** [dean ko](https://apify.com/deanko) (community)
- **Categories:** Social media, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 results

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, profiles, followers and search API

Scrape **Bluesky** (bsky.app) into clean, flat rows for Excel, Google Sheets, BI tools or your database. Search posts by keyword or hashtag, export any account's posts, profiles, followers and follows, or pull a full reply thread from a post URL.

It runs on Bluesky's official public AppView API, so you need no browser, no proxies and, for most uses, no login. It costs **$1 per 1,000 results**.

### What does the Bluesky Scraper do?

Pick one of six modes:

| Mode | Input | One row per |
|---|---|---|
| `search` | a search query, plus optional sort (latest/top), since, until, language and author | post |
| `authorFeed` | handles | post or repost in each account's feed |
| `profiles` | handles | profile |
| `followers` | handles | follower account |
| `follows` | handles | account they follow |
| `thread` | post URLs | the post and every reply, with its depth |

**Post rows** include the post URI and web link, author handle, display name and DID, text, created/indexed time, like, repost, reply, quote and bookmark counts, language, hashtags, mentions, links (with link card title), image URLs and alt text, video URL, quoted post, and reply parent/root.

**Profile rows** include the handle, DID, profile link, display name, bio, followers, follows and posts counts, account creation date, avatar, banner, verification status and labels.

#### Who is it for?

- **Social listening and brand monitoring.** Track mentions of your brand, product or competitors as people move from X/Twitter to Bluesky. Schedule a daily `search` run with `since` set to yesterday.
- **Researchers and data journalists.** Build datasets of posts on elections, climate or any topic, with language and date filters, for discourse and network analysis.
- **Influencer and lead discovery.** Export the followers of a niche account, then enrich them with `profiles` to rank by follower count.
- **Marketers and community managers.** Export your own posts with engagement counts, or every reply under a launch post with `thread`.
- **AI and NLP.** Collect labeled, language-tagged text for training and evaluation sets.

### Why use this Bluesky scraper?

- **Low price.** $1 per 1,000 results, and you can set a maximum charge per run.
- **Six modes in one Actor.** Search, author feeds, profiles, followers, follows and threads, with no need to chain several Actors.
- **Official API, not HTML scraping.** It doesn't break when the website changes and needs no proxies.
- **No login for most modes.** Profiles, feeds, followers, follows and threads are fully public. See the FAQ for search.
- **Flat, analysis-ready rows.** Hashtags, mentions, links and images are pulled out of the post's rich text and embeds into simple lists. Posts and Profiles table views open straight in the Console.
- **Robust.** It pages with Bluesky's cursors, retries on rate limits (429) and server errors, and skips a bad handle instead of failing the run.

### How to scrape Bluesky

1. Click **Try for free** (or **Start**) on this page.
2. Choose a **mode** and fill in the search query, handles or post URLs.
3. Set **Max results** to cap the number of rows, and with it your cost.
4. Click **Start**. A 50-post search finishes in a few seconds.
5. Download the results as **CSV, Excel, JSON, XML or HTML**, or read them via the API.

#### Input examples

Top English posts about the election since October 1:

```json
{
  "mode": "search",
  "searchQuery": "election",
  "sort": "top",
  "since": "2026-10-01",
  "lang": "en",
  "maxResults": 100
}
```

Latest posts by one account that mention a keyword:

```json
{ "mode": "search", "searchQuery": "AI", "author": "nytimes.com", "maxResults": 50 }
```

Profiles of several accounts (handles, @handles, DIDs and profile URLs all work):

```json
{ "mode": "profiles", "handles": ["bsky.app", "@jay.bsky.team", "https://bsky.app/profile/nytimes.com"] }
```

The last 500 posts of two accounts:

```json
{ "mode": "authorFeed", "handles": ["bsky.app", "nasa.gov"], "maxResults": 500 }
```

The first 2,000 followers of an account:

```json
{ "mode": "followers", "handles": ["bsky.app"], "maxResults": 2000 }
```

A post and all its replies:

```json
{ "mode": "thread", "postUrls": ["https://bsky.app/profile/bsky.app/post/3myinmg7his27"] }
```

| Field | What it does | Example |
|---|---|---|
| `mode` | What to scrape | `search`, `authorFeed`, `profiles`, `followers`, `follows`, `thread` |
| `searchQuery` | Keywords, #hashtag or phrase (search mode) | `bluesky`, `#AI`, `"climate change"` |
| `sort` | `latest` (newest first) or `top` (most engaging) | `top` |
| `since` / `until` | Date range for search | `2026-10-01`, `2026-10-01T12:00:00Z` |
| `lang` | Language code for search | `en`, `ja`, `es` |
| `author` | Only posts by this account (search) | `nytimes.com` |
| `handles` | Accounts for authorFeed, profiles, followers and follows | `bsky.app` |
| `postUrls` | Posts for thread mode | `https://bsky.app/profile/.../post/...` |
| `maxResults` | Maximum rows in total (default 100) | `1000` |
| `blueskyHandle` / `blueskyAppPassword` | Optional login, only needed for search beyond 100 posts | `you.bsky.social` |

### Output sample

A post row (search, authorFeed and thread modes):

```json
{
  "uri": "at://did:plc:bolmj5eezk7xkabiz3ueskd7/app.bsky.feed.post/3myhhgu3eic2g",
  "url": "https://bsky.app/profile/marlena-love.bsky.social/post/3myhhgu3eic2g",
  "cid": "bafyreicrqwnhuox52atfzgyrf2qb5a34di27ho6p2hmfrkyaaq46rv6tui",
  "authorHandle": "marlena-love.bsky.social",
  "authorDisplayName": "🌈Marlena🕊️",
  "authorDid": "did:plc:bolmj5eezk7xkabiz3ueskd7",
  "text": "Dramatic Sunrise on the Gulf Coast\n#bluesky #love",
  "createdAt": "2026-10-22T10:30:18.477Z",
  "indexedAt": "2026-10-22T10:30:19.641Z",
  "likeCount": 830,
  "repostCount": 103,
  "replyCount": 39,
  "quoteCount": 3,
  "bookmarkCount": 5,
  "lang": "en",
  "langs": ["en"],
  "hashtags": ["bluesky", "love"],
  "mentions": [],
  "mentionDids": [],
  "links": [],
  "linkTitle": null,
  "images": [],
  "imageAlts": [],
  "videoUrl": "https://video.bsky.app/watch/did%3Aplc%3Abolmj5eezk7xkabiz3ueskd7/bafkreihwrwtoo6xv2i6npmwep6ujavgyiqifu3ctrjb3wyis2sgrrsbpwm/playlist.m3u8",
  "videoThumbnail": "https://video.bsky.app/watch/did%3Aplc%3Abolmj5eezk7xkabiz3ueskd7/bafkreihwrwtoo6xv2i6npmwep6ujavgyiqifu3ctrjb3wyis2sgrrsbpwm/thumbnail.jpg",
  "quotedPostUri": null,
  "quotedPostUrl": null,
  "isReply": false,
  "replyToUri": null,
  "rootUri": null,
  "labels": [],
  "scrapedAt": "2026-10-23T04:30:00.000Z"
}
```

A profile row (profiles mode; followers and follows rows add `relation` and `ofHandle`):

```json
{
  "did": "did:plc:z72i7hdynmk6r22z27h6tvur",
  "handle": "bsky.app",
  "url": "https://bsky.app/profile/bsky.app",
  "displayName": "Bluesky",
  "description": "official Bluesky account (check username👆)\n\nBugs, feature requests, feedback: support@bsky.app",
  "followersCount": 35349295,
  "followsCount": 23,
  "postsCount": 895,
  "createdAt": "2023-04-12T04:53:57.057Z",
  "verified": false,
  "labels": [],
  "scrapedAt": "2026-10-23T04:30:00.000Z"
}
```

Extra fields by mode: `authorFeed` adds `feedOwner`, `repostedBy` and `isPinned`. `thread` adds `threadDepth` (0 = the post itself) and `threadRootUrl`. Follower and follow rows come from Bluesky's list endpoints, which don't include counts, so `followersCount`, `followsCount` and `postsCount` are `null` there. Run those handles through `profiles` mode to get the counts.

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

This Actor uses **pay-per-event** pricing:

- **$0.001 per result saved** ($1 per 1,000 posts or profiles)
- **$0.00005 per run start** (per GB of memory, a fraction of a cent)

**Worked example:** exporting the 5,000 latest followers of a brand account, then their 5,000 profiles with follower counts, is 10,000 results × $0.001 = **$10**, plus 2 run starts × $0.00005 × 0.25 GB, which is well under a cent. The default 50-post search costs about **$0.05**, so the $5 of monthly free credit on the Apify Free plan covers about 5,000 results.

Set **Max results** or a maximum charge per run, and the Actor stops as soon as it reaches the limit.

### Integrations and API

Call the Actor from your own code with the official `apify-client`, or connect it to Make, Zapier, n8n, Google Sheets, Slack, webhooks and more from the **Integrations** tab.

**Python**

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("deanko/bluesky-scraper").call(run_input={
    "mode": "search",
    "searchQuery": "your brand",
    "sort": "latest",
    "maxResults": 100,
})
for post in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(post["url"], post["likeCount"], post["text"][:80])
```

**JavaScript / Node.js**

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

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('deanko/bluesky-scraper').call({
    mode: 'profiles',
    handles: ['bsky.app', 'nytimes.com'],
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((p) => console.log(p.handle, p.followersCount));
```

You can also run it with one HTTP request through the `run-sync-get-dataset-items` endpoint and get the rows back in the response.

### FAQ

**Do I need a Bluesky account?**
Not for `authorFeed`, `profiles`, `followers`, `follows` and `thread`, which use the public AppView API (`public.api.bsky.app`). Search also works without login, but **Bluesky now serves only the first page (up to 100 posts) of search results to logged-out clients**. Deeper search pages need authentication. To get more than 100 search results per run, enter your Bluesky handle and an **app password** in the optional login fields. The Actor then signs in through `com.atproto.server.createSession` on bsky.social and sends every request with that session.

**Is an app password safe?**
Yes, that is what app passwords are for. Create one in Bluesky under **Settings → Privacy and security → App passwords**, and revoke it whenever you like. Never enter your main password. The input field is secret, so Apify stores it encrypted and never shows it in logs. The Actor only reads data and never posts, likes or follows.

**Where does the data come from?**
It comes from Bluesky's official AppView API, the same data the bsky.app website and apps show. Only public data is returned.

**Are there rate limits?**
Bluesky rate-limits by IP. The Actor fetches pages one at a time, 100 rows per request, and on HTTP 429 it waits for the reset time Bluesky sends before retrying. It also retries server errors with backoff.

**How do I monitor a keyword every day?**
Schedule the Actor (Console → Schedules) with `mode: search`, `sort: latest` and `since` set to yesterday, and add a webhook or Slack/Sheets integration to receive new posts.

**How does Max results work with several handles?**
It is a total across the whole run. With 3 handles and `maxResults: 300`, the first handle can use up all 300 rows if it has that many posts. Run handles separately if you need a fixed number per account.

**What happens with a wrong handle or URL?**
It is logged as a warning and skipped, and the rest of the run continues.

### Limitations

- Without login, search returns at most 100 posts per run (see the FAQ).
- Bluesky's search caps how deep any query can page, even when logged in. For big historical pulls, split the date range with `since` / `until`.
- Only public data is available. Blocked or deactivated accounts and posts that authors limit to logged-in users may be missing.
- Thread mode returns the post and its replies, not the parent posts above it.
- Likes and repost lists (who liked a post) are not included.

### Legal and privacy

The data comes from Bluesky's public API. This Actor is not affiliated with or endorsed by Bluesky Social PBC. Results can include personal data such as handles and display names. You are responsible for using it in line with Bluesky's terms, GDPR and the laws that apply to you.

### Support and feedback

Missing a field or mode you need, or found a bug? Open an issue in the **Issues** tab on this page. If the Actor saves you time, a short review helps other people find it.

# Actor input Schema

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

Search uses the search fields below. Author feed, profiles, followers and follows use Handles. Thread uses Post URLs.

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

Keywords, a #hashtag or a phrase, like in the Bluesky search box. Supports Lucene-style syntax, e.g. "climate change" -bitcoin, or from:handle.

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

Latest = newest first. Top = most engaging posts first.

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

Only posts on or after this date. YYYY-MM-DD or a full ISO datetime (2026-10-01T12:00:00Z).

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

Only posts before this date. YYYY-MM-DD or a full ISO datetime.

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

Only posts in this language, as a 2-letter code: en, ja, es, de, pt, ko...

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

Only posts by this account (handle or DID), e.g. nytimes.com.

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

Bluesky handles, DIDs or profile URLs, e.g. bsky.app, nytimes.com, https://bsky.app/profile/jay.bsky.team.

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

Post links like https://bsky.app/profile/bsky.app/post/3l6oveex3ii2l (or at:// URIs). Returns the post and all its replies, with their depth in the thread.

## `maxResults` (type: `integer`):

Maximum rows to save in total, across all handles or URLs. You pay per row, so this caps your cost (100 rows = $0.10).

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

Not needed today. Only fill this in if a run fails with 'refused the request without login'. Then use your handle plus an app password.

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

Create one at bsky.app → Settings → Privacy and security → App passwords. Never use your main password. Stored encrypted.

## Actor input object example

```json
{
  "mode": "search",
  "searchQuery": "bluesky",
  "sort": "latest",
  "handles": [],
  "postUrls": [],
  "maxResults": 50
}
```

# Actor output Schema

## `results` (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 = {
    "mode": "search",
    "searchQuery": "bluesky",
    "handles": [],
    "postUrls": [],
    "maxResults": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("deanko/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 = {
    "mode": "search",
    "searchQuery": "bluesky",
    "handles": [],
    "postUrls": [],
    "maxResults": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("deanko/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 '{
  "mode": "search",
  "searchQuery": "bluesky",
  "handles": [],
  "postUrls": [],
  "maxResults": 50
}' |
apify call deanko/bluesky-scraper --silent --output-dataset

```

## MCP server setup

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