# Bluesky Scraper — No Login, 7-in-1, Posts, Profiles & Followers (`wowlab/bluesky-scraper`) Actor

Scrape Bluesky without login: search posts by keyword or #hashtag, profile posts, profiles, followers/following, reply threads and user search. Engagement counts, media, links, language and date filters, plus 'only new posts' monitoring. $0.80 per 1,000 results.

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

## Pricing

from $0.80 / 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 — No Login, 7-in-1, Posts, Profiles & Followers

**Bluesky Scraper** collects public data from **Bluesky (bsky.app)** — **posts by keyword or #hashtag, all posts of any profile, profile details, followers and following lists, full reply threads and account search** — in one Actor, through Bluesky's **official public AT Protocol API**. **No login, no Bluesky account, no cookies, no browser.**

Every post comes as one clean row: text, author, date, language, **likes, reposts, replies, quotes**, hashtags, mentions, links, images, video and quoted post. Export to CSV, Excel, JSON or Google Sheets, schedule it, or call it from an AI agent.

**Price: $0.80 per 1,000 results** (plus a $0.00005 start event); failed sources are free.

### Why this Bluesky scraper?

- ✅ **7 modes in one Actor** — search posts, profile posts, profiles, followers, following, replies, user search. No need to buy four different scrapers.
- ✅ **Low, transparent pricing** — $0.0008 per result, a tiny $0.00005 start event, and nothing for failed handles or empty searches.
- ✅ **No login** — uses only Bluesky's public, unauthenticated AppView API. Your account is never at risk.
- ✅ **Monitoring built in** — turn on *Only new posts* and schedule the Actor: each run returns just the posts you have not seen yet (per search term / profile).
- ✅ **Filters that save money** — date range, language, minimum likes, posts linking to a domain, posts by one author, media-only feeds. Filtered-out posts are not charged.
- ✅ **Analysis-ready output** — flat fields, `engagement` total, `bsky.app` links for every post and profile, hashtags and links parsed from rich-text facets.
- ✅ **AI-agent ready** — simple input, JSON output; works through the Apify MCP server in Claude, ChatGPT, Cursor and other agents.

### Use cases

- **Brand & keyword monitoring** — track mentions of your brand, product or competitors on Bluesky every hour or day.
- **Hashtag research** — collect posts for a #hashtag or campaign with engagement numbers.
- **Influencer discovery** — search users by topic, get follower counts, export followers of key accounts.
- **Audience analysis** — download followers/following of a profile with bios and stats.
- **Social listening & sentiment datasets** — feed posts into an LLM or sentiment model.
- **Academic research & journalism** — archive a public conversation thread with all replies.
- **Link tracking** — find who shares links to your domain (`domain` filter).

### Modes and input

| Mode (`mode`) | Uses | Returns |
|---|---|---|
| `searchPosts` | `queries` (keywords, "phrases", #hashtags, `from:handle`, `lang:en`) | posts |
| `profilePosts` | `handles` | the profile's posts (optionally replies, reposts, media-only) |
| `profiles` | `handles` | profile details with followers / following / posts counts |
| `followers` / `following` | `handles` | accounts (add `enrichProfiles` for their counts) |
| `postReplies` | `postUrls` | the post and its whole reply tree with `depth` |
| `searchUsers` | `queries` | matching accounts |

Handles can be `jay.bsky.team`, `@jay.bsky.team`, a DID or a `https://bsky.app/profile/...` URL.

| Option | What it does | Default |
|---|---|---|
| `maxItems` | Max results per search term / profile / post | 50 |
| `maxTotalItems` | Hard cap for the whole run | 1000 |
| `sort` | `latest` or `top` (post search) | latest |
| `since` / `until` | Date range: `2026-10-01`, ISO timestamp, or days ago (`7`) | — |
| `language` | e.g. `en`, `de`, `ja` | — |
| `minLikes` | Skip posts with fewer likes | 0 |
| `author`, `domain` | Post search: only one author / only posts linking to a domain | — |
| `feedFilter` | Profile posts: no replies, with replies, media, video, self-threads | no replies |
| `includeReposts` | Profile posts: include reposts | false |
| `replyDepth` | Reply levels in `postReplies` | 6 |
| `onlyNewPosts` | Skip posts returned in earlier runs (monitoring) | false |

```json
{
  "mode": "searchPosts",
  "queries": ["web scraping", "#buildinpublic"],
  "sort": "latest",
  "since": "7",
  "language": "en",
  "maxItems": 100
}
```

### Output

Post example (real data, shortened):

```json
{
  "type": "post",
  "url": "https://bsky.app/profile/bsky.app/post/3myx7vphq3s2j",
  "text": "getting our Halloween decorations set up",
  "createdAt": "2026-10-28T16:58:02.827Z",
  "indexedAt": "2026-10-28T16:58:05.900Z",
  "languages": [
    "en"
  ],
  "authorHandle": "bsky.app",
  "authorName": "Bluesky",
  "authorDid": "did:plc:z72i7hdynmk6r22z27h6tvur",
  "authorAvatar": "https://cdn.bsky.app/img/avatar/plain/did:plc:z72i7hdynmk6r22z27h6tvur/bafkreihwihm6kpd6zuwhhlro75p5qks5qtrcu55jp3gddbfjsieiv7wuka",
  "authorProfileUrl": "https://bsky.app/profile/bsky.app",
  "authorVerified": false,
  "likeCount": 3637,
  "repostCount": 387,
  "replyCount": 225,
  "quoteCount": 126,
  "bookmarkCount": 207,
  "engagement": 4375,
  "hashtags": [],
  "mentionedDids": [],
  "links": [],
  "images": [
    {
      "url": "https://cdn.bsky.app/img/feed_fullsize/plain/did:plc:z72i7hdynmk6r22z27h6tvur/bafkreienhu3nspbm2f2v5q74arjtlvltdlgruzjvo6ndlggksp6l6zlf5a",
      "thumbnail": "https://cdn.bsky.app/img/feed_thumbnail/plain/did:plc:z72i7hdynmk6r22z27h6tvur/bafkreienhu3nspbm2f2v5q74arjtlvltdlgruzjvo6ndlggksp6l6zlf5a",
      "alt": "Gravestone reading:\n\nHere lies StumbleUpon\n2001 - 2018\n\"Remember websites?\""
    },
    {
      "url": "https://cdn.bsky.app/img/feed_fullsize/plain/did:plc:z72i7hdynmk6r22z27h6tvur/bafkreifpa3vyizusbzwgmnpr4wgm4nhsztxolgvjr4gmzspmpwysltrnre",
      "thumbnail": "https://cdn.bsky.app/img/feed_thumbnail/plain/did:plc:z72i7hdynmk6r22z27h6tvur/bafkreifpa3vyizusbzwgmnpr4wgm4nhsztxolgvjr4gmzspmpwysltrnre",
      "alt": "Gravestone reading:\n\nHere lies Vine\n2013 - 2017\n\"Look at all those chickens\""
    },
    {
      "url": "https://cdn.bsky.app/img/feed_fullsize/plain/did:plc:z72i7hdynmk6r22z27h6tvur/bafkreienq5z3pmk3h6yiqyfmuf7lvux6mknnfol4akfrn4fh5l7qngkwo4",
      "thumbnail": "https://cdn.bsky.app/img/feed_thumbnail/plain/did:plc:z72i7hdynmk6r22z27h6tvur/bafkreienq5z3pmk3h6yiqyfmuf7lvux6mknnfol4akfrn4fh5l7qngkwo4",
      "alt": "Gravestone reading:\n\nHere lies AIM\n1997 - 2017\n\"BRB LOL\""
    },
    {
      "url": "https://cdn.bsky.app/img/feed_fullsize/plain/did:plc:z72i7hdynmk6r22z27h6tvur/bafkreicfsv73pv6l5az2ktk4ltxi5izmnuuakhhiufmr2hrj4yjp2htuyq",
      "thumbnail": "https://cdn.bsky.app/img/feed_thumbnail/plain/did:plc:z72i7hdynmk6r22z27h6tvur/bafkreicfsv73pv6l5az2ktk4ltxi5izmnuuakhhiufmr2hrj4yjp2htuyq",
      "alt": "Gravestone reading:\n\nHere lies Myspace\n2003 - ?\n\"Please add me to your top 8\""
    }
  ],
  "videos": [],
  "externalLink": null,
  "quotedPost": null,
  "isReply": false,
  "replyToUrl": null,
  "threadRootUrl": null,
  "labels": [],
  "uri": "at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post/3myx7vphq3s2j",
  "cid": "bafyreibaaieigry2etw7p6k74hvdz53i5x6myssdqhlcaoyd3edalgiuzm",
  "profile": "bsky.app",
  "isRepost": false,
  "repostedAt": null,
  "scrapedAt": "2026-10-30T16:05:42.019Z"
}
```

Profile example:

```json
{
  "type": "profile",
  "handle": "bsky.app",
  "displayName": "Bluesky",
  "did": "did:plc:z72i7hdynmk6r22z27h6tvur",
  "profileUrl": "https://bsky.app/profile/bsky.app",
  "description": "official Bluesky account (check username👆)\n\nBugs, feature requests, feedback: support@bsky.app",
  "avatar": "https://cdn.bsky.app/img/avatar/plain/did:plc:z72i7hdynmk6r22z27h6tvur/bafkreihwihm6kpd6zuwhhlro75p5qks5qtrcu55jp3gddbfjsieiv7wuka",
  "banner": "https://cdn.bsky.app/img/banner/plain/did:plc:z72i7hdynmk6r22z27h6tvur/bafkreichzyovokfzmymz36p5jibbjrhsur6n7hjnzxrpbt5jaydp2szvna",
  "followersCount": 35453171,
  "followsCount": 32,
  "postsCount": 901,
  "listsCount": 18,
  "feedsCount": 7,
  "starterPacksCount": 15,
  "isLabeler": false,
  "verified": false,
  "trustedVerifier": true,
  "pinnedPostUrl": "https://bsky.app/profile/bsky.app/post/3l6oveex3ii2l",
  "accountCreatedAt": "2023-04-12T04:53:57.057Z",
  "labels": [],
  "scrapedAt": "2026-10-30T16:05:43.911Z"
}
```

A `RUN_SUMMARY` record in the key-value store lists results per source and any failed handles/URLs with the reason.

### Pricing

Pay per result: **$0.0008 per dataset item ($0.80 per 1,000)** plus $0.00005 per run start. Free Apify plan credits are enough for thousands of posts. Posts skipped by filters, duplicates and failed sources are not charged. Set *Max results in total* or a maximum charge per run to control cost.

### Limits — please read

- **Post search without login returns at most 100 posts per search term per run.** Bluesky's public API blocks paging of search results for anonymous clients. To get more: split by date range (`since`/`until`), use several narrower terms, or schedule the Actor with *Only new posts* to collect continuously. Profile posts, followers, following, replies and user search have **no such limit**.
- Only public data is available. Posts or profiles hidden by their owner for logged-out viewers, deleted posts and blocked content are not returned.
- Counts (likes, reposts…) are a snapshot at scrape time.

### Is it legal to scrape Bluesky?

This Actor reads only **public data** through Bluesky's **official public API** (`public.api.bsky.app` / `api.bsky.app`), which Bluesky provides for anyone to use without authentication, and it never logs in, posts, likes or follows. Bluesky's developer guidelines prohibit spam and automated interactions — this Actor does neither. Results can contain personal data (names, bios); you are responsible for using them lawfully (e.g. GDPR) and in line with [Bluesky's Terms](https://bsky.social/about/support/tos). If unsure, ask a lawyer.

### FAQ

**Do I need a Bluesky account or API key?** No.

**Why did my search stop at 100 posts?** See *Limits* above — that is Bluesky's limit for anonymous search; use date ranges or monitoring.

**Can I monitor a hashtag and get only new posts?** Yes: `searchPosts` + `onlyNewPosts: true`, then create a Schedule (e.g. every hour). Use a different `stateStoreName` per monitor.

**Can I get all followers of a big account?** Yes, followers/following are paged without limit; use `maxItems` to cap cost (e.g. 100,000 followers ≈ $80).

**Found a bug or need a field?** Open an issue on the Actor's Issues tab — fixes are usually quick.

# Actor input Schema

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

Choose the data you want. Search modes use "Search terms"; profile modes use "Handles"; replies use "Post URLs".

## `queries` (type: `array`):

For "Search posts" and "Search users". Keywords, "exact phrases" or #hashtags, one per line. Bluesky search syntax works too (e.g. from:handle, lang:en).

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

For profile, followers and following modes. Accepts handle (bsky.app or @jay.bsky.team), DID, or https://bsky.app/profile/... URL. A bare name like "alice" becomes alice.bsky.social.

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

For "Replies to posts". https://bsky.app/profile/<handle>/post/<id> links or at:// URIs.

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

Limit per source. Note: post search without login returns at most 100 posts per search term per run (Bluesky limit).

## `maxTotalItems` (type: `integer`):

Hard cap across all sources, to control cost.

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

For "Search posts": newest first or most popular.

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

Only posts on/after this date. YYYY-MM-DD, ISO timestamp or number of days ago (e.g. 7). Works in post search and profile posts.

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

Post search only: posts before this date (YYYY-MM-DD, ISO timestamp or days ago). Use several date ranges to collect more than 100 posts for one term.

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

Two-letter language code (en, de, ja, pt...). Filters posts by the author-declared language.

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

Skip posts with fewer likes (not charged).

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

Optional handle to restrict post search to one account.

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

Optional, e.g. nytimes.com — find who shares links to a site.

## `feedFilter` (type: `string`):

For "Posts from profiles".

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

Profile posts: also include posts the profile reposted (marked isRepost).

## `replyDepth` (type: `integer`):

Replies mode: how many reply levels to collect.

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

Replies mode: also output the post itself (depth 0).

## `enrichProfiles` (type: `boolean`):

Adds followers, following and post counts to each account in followers/following/user search (extra API calls, same price).

## `onlyNewPosts` (type: `boolean`):

Remember posts already returned and skip them in the next runs. Schedule the Actor to get only fresh posts each time.

## `stateStoreName` (type: `string`):

Named key-value store used by "Only new posts". Use different names for different monitors.

## Actor input object example

```json
{
  "mode": "searchPosts",
  "queries": [
    "web scraping"
  ],
  "handles": [
    "bsky.app"
  ],
  "maxItems": 50,
  "maxTotalItems": 1000,
  "sort": "latest",
  "minLikes": 0,
  "feedFilter": "posts_no_replies",
  "includeReposts": false,
  "replyDepth": 6,
  "includeRootPost": true,
  "enrichProfiles": false,
  "onlyNewPosts": false,
  "stateStoreName": "bluesky-scraper-seen"
}
```

# Actor output Schema

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

Posts with text, author, engagement counts, hashtags, links and media.

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

Profiles, followers, following or user-search results.

## `runSummary` (type: `string`):

Results per source and failed sources with error messages (failures are not charged).

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

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

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

```

## MCP server setup

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