# Bluesky Scraper & API: Export Posts, Profiles & Search (`s_actors/bluesky-scraper`) Actor

Scrape Bluesky posts without login: search by keyword, hashtag, date, language, author, mention or domain, every post of any profile, custom feeds and lists. Likes, reposts, replies, quotes, author followers, media and links, only new posts since the last run, and a profiles table with contacts.

- **URL**: https://apify.com/s_actors/bluesky-scraper.md
- **Developed by:** [Superior Actors](https://apify.com/s_actors) (community)
- **Categories:** Social media, Marketing, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.59 / 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 & API: Export Posts, Profiles & Search

Scrape **Bluesky posts** to CSV, Excel or JSON: **search by keyword or hashtag** (latest or top, date range, language, author, mentions, linked domain), **every post of any profile** down to the first one, **custom feeds** (Discover and any other) and **lists**, or single posts by link. Each row has the text, **likes, reposts, replies, quotes and saves**, the **author's follower count** and engagement rate, the posting date, **images, video, link card and quoted post**, hashtags, mentions and links. Get **only new posts** since the last run for brand and keyword monitoring, and a free **profiles table**: followers, bio contacts, average likes and reposts, posts per week, top post.

```json
{ "searchQueries": ["artificial intelligence"], "usernames": ["nytimes.com"], "maxPostsPerInput": 0 }
```

No Bluesky account, app password or login needed. Use it as a **Bluesky search API** and **Bluesky posts API**: one call returns clean JSON, 100 posts per request. `0` = all: every search result within your dates (up to 100,000 per query), every post of a profile (all 6,408 posts and threads of @pfrazee.com in 107 requests).

#### What you get

One row per post. Real example, October 2026 (the newest post of each input):

| authorHandle | mediaType | likeCount | repostCount | replyCount | quoteCount | bookmarkCount | authorFollowerCount | engagementRate | createdAt |
|---|---|---|---|---|---|---|---|---|---|
| bsky.app | quote | 6,578 | 1,307 | 205 | 138 | 134 | 35,292,873 | 0.023 | 2026-10-17 |
| theonion.com | image | 1,650 | 135 | 23 | 10 | 10 | 1,358,649 | 0.134 | 2026-10-17 |
| nytimes.com | video | 246 | 40 | 5 | 8 | 29 | 1,352,179 | 0.022 | 2026-10-18 |
| pfrazee.com | image | 76 | 12 | 14 | 6 | 23 | 309,376 | 0.035 | 2026-10-17 |

| Field | Description |
|---|---|
| `postUrl`, `text`, `createdAt`, `indexedAt` | Link to the post on bsky.app, full text, posting time and when Bluesky indexed it (UTC) |
| `likeCount`, `repostCount`, `replyCount`, `quoteCount`, `bookmarkCount` | Likes, reposts, replies, quotes and saves |
| `authorFollowerCount`, `engagementRate` | The author's followers and (likes + reposts + replies + quotes) / followers, in % (Bluesky has no view counts) |
| `authorHandle`, `authorName`, `authorUrl`, `authorVerified`, `authorAvatar`, `authorDid` | The author; `authorVerified` = Bluesky's blue check |
| `mediaType`, `imageUrl`, `images`, `imageCount` | `text`, `image`, `video`, `link` or `quote`; every image in full size with its alt text and size |
| `videoUrl`, `videoThumbnail` | The video stream (HLS `.m3u8`) and its cover |
| `externalUrl`, `externalTitle`, `externalDescription` | The link card of the post |
| `quotedPostUrl`, `quotedAuthor`, `quotedText` | The post this one quotes |
| `hashtags`, `mentions`, `links`, `languages` | #hashtags, mentioned handles, links in the text (full addresses), the post's languages |
| `postType`, `isReply`, `replyToPostUrl`, `threadRootUrl` | `post`, `reply` or `repost`; for replies the post answered and the first post of the thread |
| `isPinned`, `repostedBy`, `repostedAt` | For profiles: the pinned post; for reposts the account and when it reposted (the row is the original post) |
| `labels`, `uri`, `cid` | Moderation labels and the AT Protocol identifiers of the post |
| `input`, `inputType`, `profile`, `scrapedAt` | Which query, profile, feed or link the row came from |

**Profiles table** (run's **Output** tab → *Profiles (CSV)*, also as JSON: key-value store records `profiles.csv` and `PROFILES`), one row per profile you entered, free. Real example, October 2026, over the 20 latest posts:

| handle | followersCount | postsCount | email | website | averageLikes | medianLikes | averageReposts | postsPerWeek |
|---|---|---|---|---|---|---|---|---|
| bsky.app | 35,292,873 | 880 | support@bsky.app | | 8,536.9 | 2,798 | 1,237.5 | 2.41 |
| theonion.com | 1,358,649 | 8,802 | | theonion.com/newsletters | 807.9 | 733.5 | 78 | 114.12 |
| nytimes.com | 1,352,179 | 44,457 | | nytimes.onelink.me | 77.2 | 54 | 21.8 | 394.47 |

| Column | Description |
|---|---|
| `handle`, `displayName`, `did`, `profileUrl`, `description`, `avatar`, `banner`, `createdAt`, `isVerified` | The account and its bio |
| `followersCount`, `followsCount`, `postsCount` | Followers, following and posts, exact |
| `feedsCount`, `listsCount`, `starterPacksCount`, `isLabeler`, `pinnedPostUrl` | Custom feeds, lists and starter packs the account made |
| `email`, `emails`, `website`, `handleDomain`, `linkInBio`, `instagram`, `youtube`, `x`, `facebook`, `linkedin`, `telegram`, `twitch` | Contacts written in the bio; `handleDomain` = the custom domain of the handle (nytimes.com) |
| `postsScraped`, `repliesScraped`, `repostsScraped`, `newestPostAt`, `oldestPostAt`, `postsPerWeek`, `averageLikes`, `medianLikes`, `averageReposts`, `averageReplies`, `averageQuotes`, `averageEngagementRate`, `topPostUrl`, `topPostLikes`, `topPostReposts` | Over the account's own posts of the run (your limit and dates) |
| `error` | Why a profile failed (not found) |

#### How it works

1. Add **search queries**, **profiles**, **feed or list links** and/or **post links** (any mix)
2. Set **Max posts per query, profile or feed** (`0` = all), the order and optional filters
3. Run, then download the dataset as CSV, Excel, JSON or HTML

| Input | What it does |
|---|---|
| **Search posts** | Words, a `#hashtag` or a `"quoted phrase"`; Bluesky operators like `from:`, `mentions:`, `domain:`, `lang:` work too |
| **Profiles** | `name.bsky.social`, a custom-domain handle (`nytimes.com`), `@handle`, a bsky.app link or a DID |
| **Custom feeds and lists** | `bsky.app/profile/<handle>/feed/<name>` (Discover, What's Hot, any custom feed) or `.../lists/<id>` |
| **Post links** | Single posts with all stats |
| **Max posts** | Per query, profile and feed, default 100, `0` = all |
| **Search order** | Latest = every match, newest first. Top = most popular: 100 per query, or 100 for each day with *Posted after* |
| **Language, From this account, Mentioning this account, Linking to this website, With these hashtags** | Search filters, applied by Bluesky itself |
| **Replies / Reposts of the profiles** | Add the replies a profile wrote to others and the posts it reposted |
| **Posted after / Posted before** | A date (`2026-01-01`) or a period (`24 hours`, `7 days`, `6 months`) |
| **Minimum likes** | Only posts with at least this many likes |
| **Author follower counts** | On by default: followers and engagement rate for search and feed results |
| **Only new posts since the previous run** | For schedules: only what was posted since the last check |

A missing profile, feed or post is reported in the run status and not charged. Posts left out by a filter are not charged. Posts of accounts that asked apps not to show their content to logged-out people (Bluesky's `!no-unauthenticated` setting) are left out and not charged; the run status says how many.

#### Why this Actor

| | |
|---|---|
| 🔍 **Search with no 100-post wall** | Without login Bluesky gives one page of search results; this Actor walks back in time and returns every match, newest first |
| 📈 **Top posts per day** | Top order with a date range gives the 100 most popular posts of every day: trend and campaign reports in one run |
| 📜 **Every post of a profile** | Down to the account's first post, with its own threads, and optionally replies and reposts |
| 👥 **Author reach in every row** | Follower count and engagement rate of the author, also for search and feed results |
| 🔔 **Brand and keyword alerts** | Schedule it hourly: each run returns only new mentions, a run with nothing new costs only the start |
| 📇 **Profiles table, free** | Followers, contacts from the bio, average and median likes, posts per week, top post |
| 💰 **$0.91 per 1,000 posts** | Search filters, follower counts, the profiles table and monitoring included, no add-on fees |

#### Monitor brand mentions and keywords: only new posts

1. Add your brand, product or competitor names as **search queries** (or use **Mentioning this account**), and the profiles you follow
2. Turn on **Only new posts since the previous run**, save as a task and add a schedule, e.g. every hour
3. **First run** returns the latest posts and remembers the newest one per query, profile and feed. **Every later run** returns only posts published since then

The newest post seen is kept in your account (key-value store `bluesky-scraper-state`) and is shared by all your runs and tasks with the same query and filters. Connect [Google Sheets](https://apify.com/s_actors/google-sheets-import-export), Slack or email with an integration to be notified.

#### Pricing

Pay per result, no subscription needed. Apify Scale and Business plans pay less:

| Event | Free and Starter plans | Scale plan | Business plan |
|---|---|---|---|
| Run start | $0.001 | $0.001 | $0.001 |
| Post in the results (post, reply or repost) | $0.00091 | $0.00077 | $0.00059 |

| Task | Cost |
|---|---|
| All 6,408 posts and threads of @pfrazee.com | ~$5.83 |
| Latest 20 posts of 100 accounts (2,000 posts) | ~$1.82 |
| 10,000 posts for a keyword | ~$9.10 |
| Hourly brand-mention alert for a month | ~$0.72 + $0.00091 per new post |

Profiles, feeds and posts that are not found, posts left out by filters and the profiles table are not charged.

#### Ready-made tasks

Open it, change the query, and run:

| Task | What you get |
|---|---|
| [Search Bluesky Posts by Keyword and Date](https://apify.com/s_actors/bluesky-scraper/examples/search-bluesky-posts-by-keyword-and-date) | Every post with a keyword in a date range, with likes and the author's followers |

#### FAQ

**Can it search more than 100 posts?** Yes. Without login Bluesky returns only the first page of a search; the Actor asks again for posts older than the last one and goes on until your limit, your *Posted after* date or the end of the results.

**Does it get all posts of a profile?** Yes, every post Bluesky lists on the profile, newest first, down to the account's first one, including its own threads. Turn on **Replies of the profiles** for its replies to others and **Reposts of the profiles** for reposts.

**Why are some posts missing?** Some accounts turn on Bluesky's setting that asks apps not to show their posts to logged-out people. The Actor respects it and leaves those posts out, free; the run status shows how many.

**Do I need a Bluesky account or an app password?** No. The Actor reads the public Bluesky API, the same data the bsky.app website shows to any visitor.

**Are there view counts?** No: Bluesky does not count or show views. The engagement rate uses the author's followers instead.

**Can I download the videos?** `videoUrl` is Bluesky's HLS stream (`playlist.m3u8`); tools like ffmpeg or yt-dlp save it as MP4. Images come in full size.

**Where do the emails come from?** From what the account wrote in its bio. The Actor does not guess emails.

#### Use with the API and AI agents

Run it via the [Apify API](https://docs.apify.com/api/v2) from Python, Node.js or any HTTP client, or connect it to Claude, ChatGPT and other AI agents through the [Apify MCP server](https://mcp.apify.com): "What did people on Bluesky say about our launch this week, and which posts got the most reposts?"

#### Other Bluesky tools

| Tool | What it does |
|---|---|
| [Threads Scraper](https://apify.com/s_actors/threads-scraper) | All posts, views and profiles of Threads accounts |
| [TikTok Profile Scraper](https://apify.com/s_actors/tiktok-profile-scraper) | All videos and stats of TikTok profiles |
| [Google Sheets Import & Export](https://apify.com/s_actors/google-sheets-import-export) | Send the posts to a Google Sheet |

#### Is it legal?

The Actor collects only public posts, profiles and stats that Bluesky shows to any visitor, without logging in, and leaves out accounts that asked not to be shown to logged-out people. Posts are their authors' content: respect copyright when you reuse texts and images, and use personal data such as handles and emails in line with GDPR and other privacy laws.

# Actor input Schema

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

One query per line: words, a #hashtag or a "quoted phrase", as in the Bluesky search box. Bluesky operators work too (from:handle, mentions:handle, domain:example.com, lang:en). Every matching post, newest first, as far back as you need.

## `usernames` (type: `array`):

One per line: a handle (name.bsky.social or a custom domain like nytimes.com), @handle, a profile link (bsky.app/profile/...) or a DID. Every post of the profile down to the first one. Followers, bio and contacts of each account go to the profiles table once, free.

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

Optional: links to custom feeds (bsky.app/profile/<handle>/feed/<name>, e.g. Discover) or user lists (bsky.app/profile/<handle>/lists/<id>). The posts in the feed's order.

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

Optional: single posts by link (bsky.app/profile/<handle>/post/<id>), with all stats.

## `maxPostsPerInput` (type: `integer`):

How many posts to take from each search query, profile and feed. 0 = all: every post of a profile, every search result within the dates (up to 100 000 per query), a whole feed.

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

Latest = every matching post, newest first. Top = Bluesky's most popular posts: up to 100 per query, or up to 100 for each day when Posted after is set.

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

Only posts in this language: a two-letter code such as en, de, fr, es, ja, pt.

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

Only posts written by this account (handle). Leave the query as a word, or use a profile in Profiles to get all its posts.

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

Only posts that mention this account (handle), e.g. your brand.

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

Only posts with a link to this domain, e.g. nytimes.com.

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

Only posts with all of these hashtags (without #).

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

Also output the replies the profile wrote under other people's posts (replyToPostUrl shows which post). Off = the profile's own posts and its own threads.

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

Also output the posts the profile reposted: the original post with its author and stats, plus repostedBy and repostedAt.

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

Only posts published after this: a date like 2026-01-01 or a period like 24 hours, 7 days, 2 weeks, 6 months (ago). Profiles stop reading at the first older post.

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

Only posts published before this: a date like 2026-03-01 or a period like 30 days (ago).

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

Only posts with at least this many likes. Leave empty for all.

## `includeAuthorFollowers` (type: `boolean`):

Add each author's follower count (authorFollowerCount) and the engagement rate to search and feed results. One extra light request per 25 new authors.

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

Output only posts newer than the newest one an earlier run in your account saw for the same query, profile or feed (key-value store bluesky-scraper-state). The first run outputs posts as usual and remembers the newest. A run with no new posts costs only the start.

## `maxConcurrency` (type: `integer`):

How many queries, profiles and feeds are scraped in parallel (100 posts a request).

## `proxyConfiguration` (type: `object`):

Requests go direct to the Bluesky API and switch to Apify datacenter proxies only if it refuses them. The default works for most runs.

## `debugLog` (type: `boolean`):

Log every retried request (for troubleshooting).

## Actor input object example

```json
{
  "searchQueries": [
    "artificial intelligence"
  ],
  "usernames": [
    "nytimes.com"
  ],
  "maxPostsPerInput": 20,
  "sort": "latest",
  "includeReplies": false,
  "includeReposts": false,
  "includeAuthorFollowers": true,
  "onlyNewPosts": false,
  "maxConcurrency": 5,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "debugLog": false
}
```

# Actor output Schema

## `overview` (type: `string`):

Every post: text, likes, reposts, replies, quotes, saves, the author's followers, engagement, date, media.

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

One row per profile you entered: followers, following, posts, bio, contacts (email, website, Instagram, YouTube, X), average likes and reposts, engagement, posts per week, top post.

## `summary` (type: `string`):

The same profiles table as JSON.

## `all` (type: `string`):

Every field of every row.

# 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": [
        "artificial intelligence"
    ],
    "usernames": [
        "nytimes.com"
    ],
    "maxPostsPerInput": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("s_actors/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": ["artificial intelligence"],
    "usernames": ["nytimes.com"],
    "maxPostsPerInput": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("s_actors/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": [
    "artificial intelligence"
  ],
  "usernames": [
    "nytimes.com"
  ],
  "maxPostsPerInput": 20
}' |
apify call s_actors/bluesky-scraper --silent --output-dataset

```

## MCP server setup

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