# Bluesky Profiles, Posts & Threads Scraper (`brimfold/bluesky-public-data`) Actor

Get public Bluesky profiles, account posts, people-search results and post threads with replies from Bluesky's public API. No login or API key. Follower counts, likes, reposts, quotes, hashtags, links, images and more.

- **URL**: https://apify.com/brimfold/bluesky-public-data.md
- **Developed by:** [Brimfold Studio](https://apify.com/brimfold) (community)
- **Categories:** Social media
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.35 / 1,000 item saveds

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 Profiles, Posts and Threads Scraper (No Login)

Collect public **Bluesky** data without a login, API key or browser: **account profiles**, each account's **recent posts**, **people search** results, and **post threads with their replies**. Every row comes from Bluesky's official public API (`public.api.bsky.app`), the cached read-only host Bluesky provides for public web use.

Use it for brand and competitor monitoring, influencer research, audience analysis, community tracking, and archiving your own accounts' posts.

### What you get

**Profile rows:** handle, DID, display name, bio, follower, following and post counts, verification status, join date, labels, pinned post, avatar and banner, profile URL.

**Post rows:** text, created and indexed time, languages, likes, reposts, replies, quotes and bookmarks, hashtags, mentioned accounts, links, embed type (images, video, external link, quote), image URLs with alt text, quoted post, reply parent and root, repost and pinned flags, thread depth, moderation labels, and the bsky.app post URL.

### Features

- **No login.** Nothing to connect, nothing that can get your account limited.
- **Four sources in one run:** accounts, author feeds, people search, threads.
- **Accepts anything:** `bsky.app`, `@name.bsky.social`, a bare name, a DID, or a bsky.app profile or post URL.
- **Author-feed filters:** posts only, posts and replies, media posts, or the account's own threads. Optional reposts and a "posts since" date.
- **Thread replies** collected breadth-first up to your limit.
- **Contact masking on by default:** email addresses and phone numbers in bios and posts become `[email]` and `[phone]`.
- **Polite:** about 3 requests a second, with automatic backoff on Bluesky rate-limit replies.
- **Pay per item.** Not-found accounts, invalid inputs and skipped rows are free.

### Input example

```json
{
  "handles": ["bsky.app", "https://bsky.app/profile/python.org"],
  "maxPostsPerAuthor": 20,
  "feedFilter": "posts_no_replies",
  "peopleSearch": ["data journalism"],
  "postUrls": ["https://bsky.app/profile/bsky.app/post/3l6oveex3ii2l"],
  "maxRepliesPerThread": 50,
  "maxResults": 200
}
```

With an empty input it saves the `bsky.app` profile and its 20 newest posts.

### Output example (post)

```json
{
  "type": "post",
  "source": "authorFeed:bsky.app",
  "url": "https://bsky.app/profile/bsky.app/post/3abc...",
  "authorHandle": "bsky.app",
  "text": "v1.137 is live! ...",
  "createdAt": "2026-10-30T17:02:11.000Z",
  "likeCount": 4210, "repostCount": 512, "replyCount": 380, "quoteCount": 41,
  "hashtags": [], "links": [], "embedType": "images",
  "images": [{ "url": "https://cdn.bsky.app/img/feed_fullsize/...", "alt": "Screenshot of the new widget" }],
  "isRepost": false, "isReply": false
}
```

`RUN-SUMMARY` in the key-value store lists counts, not-found accounts or posts, skipped inputs and how many items were skipped for privacy.

### What it does not do

- **No keyword search of posts.** Bluesky turns off post search for logged-out users on purpose, and this Actor respects that. Search people instead, or list the accounts or threads you care about.
- No private data: no DMs, no follower lists, no likes by a user, nothing behind a login.

### Pricing

**$0.50 per 1,000 items** ($0.0005 per profile or post row), pay-per-event. Rows that are not saved are not charged.

### Privacy

Public Bluesky posts and profiles are still personal data about real people. This Actor:

- reads only what Bluesky shows logged-out visitors;
- **skips accounts, posts and replies whose authors asked not to be shown to logged-out viewers** (the `!no-unauthenticated` label), and counts them in `skippedOptedOut`;
- masks email addresses and phone numbers in text by default;
- does not collect DMs, follower lists or anything that needs a login.

You are responsible for how you use the data: have a lawful basis (for example under GDPR or UK GDPR), collect only what you need, delete it when you are done, respect deletions (a deleted post should be removed from your copy), and follow Bluesky's Terms of Service and developer guidelines. Don't use it to harass, track or profile individuals, or to train models on people's posts without a legal basis.

### FAQ

**An account shows up under notFound.** The handle does not exist, was mistyped, or the account is deactivated, suspended or hidden from logged-out viewers.

**Why are some reposts from other authors?** A repost row keeps the original author in `authorHandle`; `repostedBy` holds the account that reposted it.

**How many posts can I get from one account?** Up to the whole public feed. Set "Posts per account" and "Max items" high enough.

# Actor input Schema

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

Bluesky handles (bsky.app, @name.bsky.social), DIDs or profile URLs. A bare name gets .bsky.social added. If you leave every source empty, the Actor uses bsky.app as an example.

## `includeProfiles` (type: `boolean`):

Save one profile row per account (bio, follower and post counts, verification).

## `includePosts` (type: `boolean`):

Save each account's recent posts.

## `maxPostsPerAuthor` (type: `integer`):

Newest posts to save per account.

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

Bluesky's author-feed filter.

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

Also save posts the account reposted (marked isRepost; author is the original poster).

## `postsSince` (type: `string`):

Optional date (YYYY-MM-DD). Stop at older posts.

## `peopleSearch` (type: `array`):

Keywords to find accounts by name, handle or bio. Saves full profiles. (Keyword search of posts is not available logged out.)

## `maxProfilesPerSearch` (type: `integer`):

Accounts to save per people search.

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

bsky.app post URLs or at:// URIs. Saves the post and its replies.

## `maxRepliesPerThread` (type: `integer`):

Replies to save per thread, breadth-first (0 = just the post).

## `maskContactInfo` (type: `boolean`):

Replace email addresses and phone numbers in bios and posts with \[email] and \[phone].

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

Stop after this many rows (profiles plus posts).

## Actor input object example

```json
{
  "handles": [
    "bsky.app"
  ],
  "includeProfiles": true,
  "includePosts": true,
  "maxPostsPerAuthor": 20,
  "feedFilter": "posts_no_replies",
  "includeReposts": false,
  "maxProfilesPerSearch": 25,
  "maxRepliesPerThread": 50,
  "maskContactInfo": true,
  "maxResults": 50
}
```

# Actor output Schema

## `results` (type: `string`):

No description

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

// Run the Actor and wait for it to finish
const run = await client.actor("brimfold/bluesky-public-data").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("brimfold/bluesky-public-data").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 brimfold/bluesky-public-data --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,brimfold/bluesky-public-data"
        }
    }
}
```

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/Qd8Eo0PMgaNKPCzhV/builds/NtYlc9gazD9gvL7Sl/openapi.json
