# Bluesky Scraper ⚡ Posts, Profiles, Search, Followers, Trending (`pulselab/bluesky-scraper`) Actor

Scrape Bluesky without login or API key: search posts by keyword or hashtag, profile posts with a free summary (posts/week, avg likes, engagement rate), followers, post threads with replies, likers, reposters and trending topics. Empty results and failures are never charged.

- **URL**: https://apify.com/pulselab/bluesky-scraper.md
- **Developed by:** [Pulse Lab](https://apify.com/pulselab) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 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, Profiles, Search, Followers, Threads, Trending

Scrape **Bluesky** through its official public API – **no login, no API key, no proxy**. One run can:

- **Search posts** by keyword or `#hashtag`, newest or top, with date range and language filters
- **Scrape profiles**: bio, follower / following / post counts, the latest posts, and a **free summary row** (posts per week, average likes / reposts / replies, engagement rate, reply & media share, top 3 posts)
- **Followers and following** of any profile
- **Post threads**: a post plus all its **replies** (nested, with depth), and who **liked** or **reposted** it
- **Trending topics** on Bluesky right now

Typical run: 2 searches + 2 profiles + 1 thread + trending = **about 3 seconds**. Empty results and failures are **never charged**.

### Input

| Field | Description |
|---|---|
| `searchQueries` | Keywords or hashtags, one per line (`chatgpt`, `#ai`). |
| `sort` / `since` / `until` / `lang` | Search options: `latest` or `top`, date range `YYYY-MM-DD`, language code (`en`, `ko`, `ja`…). |
| `maxPostsPerQuery` | Posts per search (default 50). |
| `profiles` | Handles or profile URLs: `bsky.app`, `@user.bsky.social`, `https://bsky.app/profile/nytimes.com`. |
| `maxPostsPerProfile` | Latest posts per profile (default 50, 0 = summary only). `includeReplies`, `includeReposts` to widen. |
| `maxFollowers` / `maxFollowing` | Accounts to list per profile (default 0 = off). |
| `postUrls` | `https://bsky.app/profile/<handle>/post/<id>` links. Returns the post and its replies. |
| `maxRepliesPerPost` | Replies per post URL (default 100). `maxLikersPerPost`, `maxRepostersPerPost` list who liked / reposted (default 0 = off). |
| `includeTrending` | Save the current trending topics. |
| `includeProfileSummary` | Summary row per profile (default on). |

```json
{
  "searchQueries": ["chatgpt", "#ai"],
  "profiles": ["bsky.app", "https://bsky.app/profile/nytimes.com"],
  "postUrls": ["https://bsky.app/profile/bsky.app/post/3l6oveex3ii2l"],
  "includeTrending": true,
  "maxPostsPerQuery": 100,
  "since": "2026-10-01",
  "lang": "en"
}
```

### Output

Every row has a `type`: `post`, `reply`, `profile`, `follower`, `following`, `liker`, `reposter` or `trending`. Each type has its own tab in the Output view and its own export (`?view=posts`, `?view=profiles`, …).

**Post**

```json
{
  "type": "post",
  "url": "https://bsky.app/profile/bsky.app/post/3lzqk5v2vqk2a",
  "authorHandle": "bsky.app",
  "authorName": "Bluesky",
  "text": "Now you can opt out of having your posts shown to non-followers…",
  "createdAt": "2026-08-27T19:03:40.000Z",
  "likes": 9641, "reposts": 3365, "replies": 344, "quotes": 1617,
  "isReply": false, "isRepost": false,
  "images": ["https://cdn.bsky.app/img/feed_fullsize/…"], "imageCount": 1,
  "externalUrl": null, "quotedAuthor": null,
  "hashtags": [], "mentions": [], "links": [], "langs": ["en"],
  "query": null, "profile": "bsky.app"
}
```

**Profile summary** (calculated from the posts fetched in the same run)

```json
{
  "type": "profile",
  "handle": "bsky.app", "displayName": "Bluesky",
  "followers": 35221525, "following": 17, "posts": 867,
  "postsSampled": 20, "postsPerWeek": 1.3,
  "avgLikes": 5357, "avgReposts": 1040, "avgReplies": 244,
  "engagementRate": 0.0204, "replyShare": 0, "mediaShare": 45,
  "latestPostDate": "2026-10-09T15:53:59.000Z",
  "topPosts": [{ "text": "Bluesky launched in February 2023…", "url": "https://bsky.app/profile/bsky.app/post/…", "likes": 29872 }]
}
```

Reply rows add `rootUri`, `parentUri` and `depth`; follower / liker rows carry the account's handle, display name, bio and which profile or post they belong to; trending rows have `rank`, `topic`, `description` and a feed `url`.

### Pricing (pay per event)

| Event | Charged for |
|---|---|
| `post` | one post row (search result, profile post or the post behind a post URL) |
| `reply` | one reply in a thread |
| `account` | one follower / following / liker / reposter row |
| `profile` | one profile summary row |
| `trending` | the trending-topics list (once per run) |

Searches with no results, unknown handles, missing posts and errors are **not charged**. Platform usage is included.

### Notes

- Uses only Bluesky's public AppView API (AT Protocol). Only public data; accounts that hide themselves from logged-out viewers are skipped by the API.
- Hashtag searches use Bluesky's tag filter, so `#ai` matches posts tagged *ai*, not just the text.
- Engagement rate = average (likes + reposts + replies + quotes) per post ÷ followers × 100.
- Large accounts: Bluesky caps reply threads and like lists at what its API returns; counts on the post itself are always exact.

# Actor input Schema

## `searchQueries` (type: `array`):

Keywords or hashtags. One search per line. Results are newest first (or top) and can be limited by dates and language below.

## `profiles` (type: `array`):

Handles (bsky.app, @user.bsky.social) or profile URLs (https://bsky.app/profile/…). Returns the profile's posts plus a summary row.

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

Post links like https://bsky.app/profile/user.bsky.social/post/3abc… Returns the post, its replies and optionally who liked / reposted it.

## `includeTrending` (type: `boolean`):

Also save the topics trending on Bluesky right now (event: trending, once per run).

## `maxPostsPerQuery` (type: `integer`):

Per search line.

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

Order of search results.

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

YYYY-MM-DD. Only search results posted on/after this date.

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

YYYY-MM-DD. Only search results posted on/before this date.

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

Language code such as en, ko, ja, de. Empty = all languages.

## `maxPostsPerProfile` (type: `integer`):

Posts to fetch from each profile (newest first). 0 = profile summary only.

## `includeReplies` (type: `boolean`):

Also include the profile's replies to other people in its post list.

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

Reposts are marked isRepost=true with the original author.

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

0 = do not fetch. Event: account.

## `maxFollowing` (type: `integer`):

0 = do not fetch. Event: account.

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

Replies (whole thread, nested) for each Post URL. Event: reply.

## `maxLikersPerPost` (type: `integer`):

Accounts that liked each Post URL. 0 = do not fetch. Event: account.

## `maxRepostersPerPost` (type: `integer`):

Accounts that reposted each Post URL. 0 = do not fetch. Event: account.

## `includeProfileSummary` (type: `boolean`):

One row per profile with follower counts, posts per week, average likes/reposts/replies, engagement rate and top posts. Event: profile.

## Actor input object example

```json
{
  "searchQueries": [
    "chatgpt",
    "#ai"
  ],
  "profiles": [
    "bsky.app"
  ],
  "postUrls": [],
  "includeTrending": false,
  "maxPostsPerQuery": 50,
  "sort": "latest",
  "since": "",
  "until": "",
  "lang": "",
  "maxPostsPerProfile": 50,
  "includeReplies": false,
  "includeReposts": false,
  "maxFollowers": 0,
  "maxFollowing": 0,
  "maxRepliesPerPost": 100,
  "maxLikersPerPost": 0,
  "maxRepostersPerPost": 0,
  "includeProfileSummary": true
}
```

# Actor output Schema

## `posts` (type: `string`):

No description

## `replies` (type: `string`):

No description

## `profiles` (type: `string`):

No description

## `accounts` (type: `string`):

No description

## `trending` (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 = {
    "searchQueries": [
        "chatgpt",
        "#ai"
    ],
    "profiles": [
        "bsky.app"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("pulselab/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 = {
    "searchQueries": [
        "chatgpt",
        "#ai",
    ],
    "profiles": ["bsky.app"],
}

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

```

## MCP server setup

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