# Bluesky Scraper: Posts, Profiles, Followers & Feeds (`transparent_meteorite/bluesky-posts-profiles-scraper`) Actor

Scrape Bluesky posts by keyword or hashtag, author feeds, profiles, followers and follows from the public API. Engagement counts, links, media and dates. No login.

- **URL**: https://apify.com/transparent_meteorite/bluesky-posts-profiles-scraper.md
- **Developed by:** [Detroit Web Agent - Open Data](https://apify.com/transparent_meteorite) (community)
- **Categories:** Social media, Lead generation, News
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.56 / 1,000 record returneds

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: Posts, Profiles, Followers & Feeds

Use this to scrape Bluesky posts by keyword or hashtag, pull any account's feed, look up profiles in bulk, and export followers or follows, with full engagement counts, links, media and timestamps, from Bluesky's public API. No login, no cookies, no API key.

Every post row carries 34 fields (text, author, like / repost / reply / quote / bookmark counts, language, hashtags, links, media type and image URLs, reply and quote references, timestamps and a ready-to-open URL). Every profile row carries followers, follows, post count, bio, join date and verification status. For comparison, the most-used Bluesky post scraper on the Store returns 8 fields per post and the profile scraper with the most runs returns 9 fields per profile.

### Run in 30 seconds

1. Open the **Input** tab. The prefilled values (mode **Search posts**, term `web scraping`, 25 results) are a ready-to-run test.
2. Click **Start**. A first dataset is normally ready in under 20 seconds.
3. Open the **Posts** table in the Output tab, or download the dataset as JSON, CSV, Excel or HTML.
4. Change **What to scrape** to switch between keyword search, an account's feed, a profile list, profile search, followers or follows.

New to Apify? [Create a free account](https://console.apify.com/sign-up?fpr=rnmakg) and run this Actor in under a minute. Some links are affiliate links.

### What it does

Bluesky is an open social network built on the AT Protocol. Its public AppView serves read-only data over HTTP with no authentication. This Actor wraps that interface into one clean, pay-per-result tool with six modes:

| Mode | Input | Returns |
|---|---|---|
| **Search posts** | Keywords, exact phrases or hashtags | Posts, newest first or top by engagement |
| **Author feed** | Handles | An account's posts (optionally replies, media, reposts) |
| **Profiles** | Handles or profile URLs | One enriched profile row per account |
| **Search profiles** | Keywords | Accounts whose name or bio match |
| **Followers** | Handles | Every follower as a profile row with follower counts |
| **Follows** | Handles | Every account the handle follows, same fields |

Followers and follows are enriched: the raw list from the network carries only names, so the Actor looks each account up in batches and returns follower, follow and post counts as well, which is what you need to rank or filter an audience.

### Why it is more complete

- **34 fields per post.** Besides text and counts you get `indexedAt`, `languages`, `hashtags`, `links`, `mentionedDids`, `mediaType`, `imageUrls`, `videoPlaylistUrl`, `externalLinkUrl` and `externalLinkTitle`, `quotedPostUri`, reply parent and root, content labels, the query that found the post, and the author's DID, handle, display name, avatar and profile URL.
- **Five engagement counters.** Likes, reposts, replies, quotes and bookmarks. The most-used alternative returns two (likes and reposts).
- **Deep search without logging in.** Bluesky's public search refuses paging cursors for anonymous callers (it answers HTTP 403 after the first 100 results). This Actor pages by walking the time window backwards instead, so a 450-post keyword search returns 450 distinct posts, not 100. Top-ranked search walks one-day windows, newest first.
- **Hashtag handling that works with date filters.** The public API rejects raw `#tag` queries combined with a date range. The Actor rewrites a lone hashtag into the tag filter automatically, so `#python` with a start date just works.
- **Six modes in one Actor** instead of separate tools for posts, profiles, followers and follows, which keeps the dataset schema, billing and scheduling in one place.
- **Predictable cost.** One flat price per row returned. Posts skipped by your filters (for example minimum likes) and duplicates are never charged, and the Actor respects your "Maximum cost per run" setting exactly.

### Input

- **What to scrape**: the mode above.
- **Search terms**: keywords, `"exact phrases"` or `#hashtags`. One search per term; the result limit is shared evenly.
- **Handles**: `bsky.app`, `@apify.com` or a full `https://bsky.app/profile/...` URL.
- **Max results**: total rows to return (1 to 100,000).
- **Search order**: newest first, or top.
- **Posted since / until**: ISO date or date-time, e.g. `2026-10-01` or `2026-10-01T12:00:00Z`.
- **Language**: two-letter code (`en`, `pt`, `ja`).
- **Only from this author** and **Only posts linking to domain**: narrow a keyword search to one account or to posts that share links from a site such as `github.com`.
- **Minimum likes**: quality filter for search and feeds.
- **Author feed contents** and **Include reposts**: choose posts only, posts with replies, media posts, or own threads.

### Example input

Brand and topic monitoring:

```json
{
  "mode": "searchPosts",
  "searchTerms": ["\"web scraping\"", "#opensource"],
  "language": "en",
  "since": "2026-10-01",
  "minLikes": 2,
  "maxItems": 500
}
```

An influencer's last posts:

```json
{
  "mode": "authorFeed",
  "handles": ["bsky.app", "apify.com"],
  "feedFilter": "posts_no_replies",
  "maxItems": 200
}
```

Audience export with counts:

```json
{
  "mode": "followers",
  "handles": ["apify.com"],
  "maxItems": 1000
}
```

### Output sample

A post row (shortened):

```json
{
  "recordType": "post",
  "uri": "at://did:plc:uhu2.../app.bsky.feed.post/3mz252yquxq2a",
  "url": "https://bsky.app/profile/sasame-mcp.bsky.social/post/3mz252yquxq2a",
  "text": "A pay-per-event Apify Actor is exactly the kind of capability...",
  "createdAt": "2026-10-29T20:45:18Z",
  "authorHandle": "sasame-mcp.bsky.social",
  "authorDisplayName": "SaSame",
  "likeCount": 0, "repostCount": 0, "replyCount": 0, "quoteCount": 0, "bookmarkCount": 0,
  "languages": null,
  "isReply": true,
  "hashtags": null,
  "links": null,
  "mediaType": null,
  "query": "apify",
  "source": "search"
}
```

A profile row carries `did`, `handle`, `displayName`, `description`, `avatar`, `banner`, `followersCount`, `followsCount`, `postsCount`, `createdAt`, `verifiedStatus`, `pinnedPostUri`, `labels` and, for followers and follows, `sourceHandle`. Fields that do not apply to a row are `null`, so every row has the same shape in CSV and Excel.

### Use cases

- **Brand and PR monitoring.** Schedule a daily run on your brand name and product names, filtered to posts since yesterday, and send new mentions to Slack or a sheet.
- **Social listening and research.** Collect posts on a topic or hashtag with language and date filters for sentiment, topic modelling or academic datasets. Engagement counts let you separate noise from signal.
- **Influencer discovery.** Search profiles by niche keyword, then use follower counts and post counts to shortlist accounts; pull their feeds to check engagement per post.
- **Audience analysis.** Export a competitor's or community's followers with counts, bios and join dates, and segment by size or keywords in the bio.
- **Lead generation.** Find people asking for tools in your category ("looking for a scraper"), and capture the post URL and author for outreach done by a human.
- **Link and news tracking.** Use the link-domain filter to see who shares your site, or which articles from a publication are gaining traction on Bluesky.
- **Migration and growth tracking.** Track how many followers a handle gains by running Profiles on a schedule and comparing `followersCount` over time.

### Tips for good results

- Start with a small **Max results** (25 to 50) to check that your terms return what you expect, then scale up.
- Use quotes for exact phrases: `"machine learning"` finds the phrase, `machine learning` finds posts containing both words.
- For fresh monitoring, set **Posted since** to the previous day and run daily on a schedule. Deduplication across terms inside one run is automatic; across runs, compare on the stable `uri` field.
- **Top** order returns the most engaged posts per day, newest day first, which is ideal for "what did people react to" reports.
- The Actor runs fine with 256 MB of memory.

### Field reference

**Post rows.** `uri` and `cid` identify the record on the AT Protocol; `url` opens it on bsky.app. `text`, `createdAt` (when the author wrote it) and `indexedAt` (when the network saw it) describe the content. `authorDid`, `authorHandle`, `authorDisplayName`, `authorAvatar` and `authorProfileUrl` identify the author. `likeCount`, `repostCount`, `replyCount`, `quoteCount` and `bookmarkCount` are the engagement counters at scrape time. `languages` lists the declared language codes. `isReply`, `replyParentUri` and `replyRootUri` place a reply in its thread; `quotedPostUri` points at a quoted post. `hashtags`, `mentionedDids` and `links` are parsed from the post's rich-text facets, so they are exact rather than regex guesses. `mediaType` is one of `images`, `video`, `link` or `quote`, with `imageUrls`, `videoPlaylistUrl`, `externalLinkUrl` and `externalLinkTitle` filled when relevant. `labels` carries moderation labels, `query` the search term or handle that produced the row, and `source` says whether it came from search, an author feed or a repost.

**Profile rows.** `recordType` is `profile`, `follower` or `follow`. `did`, `handle`, `displayName`, `description`, `avatar`, `banner` and `url` identify the account; `followersCount`, `followsCount` and `postsCount` size it; `createdAt` is the join date; `verifiedStatus` and `labels` carry verification and moderation info; `pinnedPostUri` links the pinned post; `sourceHandle` tells you whose follower or follow list a row came from.

### Pricing and cost control

Pay per result: **$0.00093 per post or profile row on the free plan, falling to $0.00056 on the Gold plan** (Bronze $0.00086, Silver $0.00073), plus a $0.00005 start fee per run. There are no subscriptions, proxy fees or hidden compute add-ons: the Actor needs no proxy and runs in 256 MB. To cap spend, set **Max results** and the run-level **Maximum cost per run**; the Actor reads the remaining budget before every row and stops the moment it would be exceeded. Filtered-out posts and duplicates cost nothing.

**How this beats the other Bluesky scrapers (live Store prices read through the Apify API, 2026-10-29, free-plan rate, 1,000-row run including start fees):**

| Actor | Users | Per row | Start fee | Cost for 1,000 rows |
|---|---|---|---|---|
| This Actor | new | $0.00093 | $0.00005 | $0.93 |
| lexis-solutions/bluesky-posts-scraper | 386 | $0.0011 | $0.004 | $1.10 |
| automation-lab/bluesky-scraper (posts) | 277 | $0.001 ($0.003 per profile) | $0.005 | $1.01 |
| fatihtahta/All-In-One-Bluesky-Scraper | 230 | $0.00149 | none | $1.49 |

We are cheaper than all three at every plan level, return 34 fields per post, and charge profiles at the same rate as posts.

### Integrations and scheduling

Schedule the Actor from the Schedules tab (hourly, daily, weekly) to build a monitoring feed. Send results onward with the Apify integrations for Slack, email, Google Sheets, Make, Zapier or n8n, or call the Actor from your own code through the REST API. For incremental monitoring, store the highest `createdAt` you have seen and pass it as **Posted since** on the next run.

### Fair use and data

The Actor reads Bluesky's public, unauthenticated AppView API and returns data that is publicly visible on the network. It does not log in, does not read private data, and spaces its requests politely, backing off when the service asks it to slow down. You remain responsible for using the data in line with Bluesky's terms and applicable privacy law; handle personal data (names, bios) with care, and do not use it for spam or harassment.

### FAQ

**Do I need a Bluesky account or app password?** No. Everything comes from public endpoints.

**Why is there a limit on how far back search goes?** The public search index returns whatever matches your terms inside the time window you give. Very old or very sparse topics simply return fewer posts. Use **Posted since / until** to target a period.

**Can it get replies to a post or a full thread?** Use Author feed with the "posts and replies" option for an account, or Search posts with the author filter.

**What happens when a handle does not exist?** It is skipped, a warning is logged and noted in the run output, and nothing is charged for it.

**What happens if I hit my maximum cost per run?** The Actor stops cleanly, keeps what it already delivered, and shows a clear message in the run status.

**Why did my run return fewer rows than Max results?** The topic, date range or filters matched fewer posts than you asked for. The final status message says so.

**Does it return deleted or blocked posts?** No. It returns what the public API returns at the time of the run.

**Can I run it from code or no-code tools?** Yes: the Apify API and client libraries, plus Make, Zapier, n8n and Google Sheets integrations all work with any Actor. Open the API tab for ready-to-copy snippets.

### Alternatives to other Bluesky tools

Several Store Actors cover one slice of Bluesky: a post scraper with 8 output fields, a user scraper with 9, a follower scraper and a profile scraper as separate tools. This Actor replaces that set with one input form, one dataset schema and one bill, returns more fields per row, and handles the hashtag and deep-paging quirks of the public API for you. If you already have a workflow built on another tool, the field names here (`text`, `createdAt`, `likeCount`, `repostCount`, `authorHandle`, `url`) map directly, so switching is a column rename at most.

### Related actors

- [Threads Profile Scraper](https://apify.com/transparent_meteorite/threads-profile-scraper) for Meta Threads accounts.
- [TikTok Public Profile Scraper](https://apify.com/transparent_meteorite/tiktok-public-profile-scraper) for TikTok creators.
- [YouTube Channel Scraper](https://apify.com/transparent_meteorite/youtube-channel-scraper) for channels and subscriber counts.
- [Instagram Public Profile Scraper](https://apify.com/transparent_meteorite/instagram-public-profile-scraper) for Instagram accounts.

### Support

Found a bug or need a field? Open an issue on the Issues tab with the run URL and you will get a reply there. Feature requests are welcome.

### Changelog

- **0.2**: Deep search paging by time window (works around the anonymous cursor block), hashtag plus date-range fix, six modes, enriched followers and follows, charge-limit and soft-timeout handling.
- **0.1**: First private build.

# Actor input Schema

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

Search posts and Search profiles use Search terms. The other modes use Handles.

## `searchTerms` (type: `array`):

Keywords, phrases ("exact phrase") or hashtags (#python). One search per term. Used by Search posts and Search profiles.

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

Bluesky handles or profile URLs, e.g. bsky.app, @apify.com or https://bsky.app/profile/bsky.app. Used by Author feed, Profiles, Followers and Follows.

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

Total rows to return across all terms or handles (shared evenly). You are charged per row returned.

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

Applies to Search posts.

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

Only posts at or after this date/time, e.g. 2026-10-01 or 2026-10-01T12:00:00Z. Search posts only.

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

Only posts before this date/time. Search posts only.

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

Two-letter language code such as en, pt or ja. Search posts only.

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

Restrict a keyword search to one handle, e.g. apify.com. Search posts only.

## `linkDomain` (type: `string`):

Only posts that link to this domain, e.g. github.com. Search posts only.

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

Skip posts with fewer likes than this (skipped posts are never charged). Search posts and Author feed.

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

Applies to Author feed.

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

In Author feed, include posts the account reposted (marked source = repost).

## Actor input object example

```json
{
  "mode": "searchPosts",
  "searchTerms": [
    "web scraping"
  ],
  "maxItems": 25,
  "sort": "latest",
  "feedFilter": "posts_no_replies",
  "includeReposts": true
}
```

# Actor output Schema

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

No description

## `profiles` (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": "searchPosts",
    "searchTerms": [
        "web scraping"
    ],
    "maxItems": 25
};

// Run the Actor and wait for it to finish
const run = await client.actor("transparent_meteorite/bluesky-posts-profiles-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": "searchPosts",
    "searchTerms": ["web scraping"],
    "maxItems": 25,
}

# Run the Actor and wait for it to finish
run = client.actor("transparent_meteorite/bluesky-posts-profiles-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": "searchPosts",
  "searchTerms": [
    "web scraping"
  ],
  "maxItems": 25
}' |
apify call transparent_meteorite/bluesky-posts-profiles-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,transparent_meteorite/bluesky-posts-profiles-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/keHOiVOGCy86BtPMR/builds/MCba7DMNVXsRjewhM/openapi.json
