# Bluesky Scraper - Posts, Search, Profiles & Hashtags API (`teddythebuilder/bluesky-scraper`) Actor

Search Bluesky posts by keyword, hashtag, author, language and date, read any account's posts, look up profiles and followers counts, or fetch posts by URL. Text, links, hashtags, engagement counts, images. Official public API, no login, no API key.

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

## Pricing

from $0.83 / 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

### What does Bluesky Scraper do?

Bluesky Scraper **collects public posts and profiles from Bluesky** through the network's own public API. Search posts by keyword, phrase or #hashtag, filter by language, date range, author or a linked website, read the latest posts of any account, look up profiles with follower counts, find accounts by topic, or fetch specific posts by URL. Each post comes back with its text, timestamp, author, like/repost/reply/quote counts, hashtags, links, images or video and reply or quote relations.

It talks directly to `api.bsky.app`, the public AppView that Bluesky provides for reading public content. Its `robots.txt` states that crawling the public parts of the API is allowed and that HTTP 429 is used for rate limiting; the Actor respects that with a handful of concurrent requests and automatic back-off. There is no login, no browser and no API key.

### Why use Bluesky Scraper?

- **Official public API, no login.** No cookies, no app passwords, no accounts at risk. Only public content is read.
- **Real search.** Keywords, exact phrases, #hashtags, language, "latest" or "top" ordering, date range, a single author, or every post that links to a given website. A keyword search returns up to 100 posts per query and date window (see the FAQ); schedule it hourly or slice by date for brand monitoring, topic research and link tracking.
- **Accounts and profiles.** Read an account's recent posts (originals, with replies, or media only) and get a profile row with display name, bio, followers, following, posts count, join date and verification status.
- **Find accounts.** Search for accounts by name or topic, for example "data journalism", and get profiles with follower counts.
- **Fast and cheap.** One request returns up to 100 posts, so a run takes seconds and costs about $0.15 per 100 posts.
- **Pipeline friendly.** One flat JSON object per row with a `type` field (`post` or `profile`), ready for sheets, databases, LLMs and dashboards.
- **Pay only for results.** Pay per post or profile returned. Unknown accounts and empty searches are free.

### What data can you extract?

**Post rows** (`type: "post"`):

| Field | Example | Description |
|---|---|---|
| `text` | `Brave sued for scraping web content…` | Post text |
| `createdAt` / `indexedAt` | `2026-11-04T12:37:44.640Z` | When it was written and when Bluesky indexed it |
| `url` / `uri` | `https://bsky.app/profile/bsky.app/post/3mzh…` | Link to the post and its AT-protocol URI |
| `authorHandle` / `authorDisplayName` / `authorDid` | `seonews.bsky.social` | Public account identifiers |
| `likeCount` / `repostCount` / `replyCount` / `quoteCount` | `37` / `12` / `3` / `1` | Engagement counts |
| `hashtags` / `links` | `["ai", "brave"]` | Hashtags and URLs in the post |
| `languages` | `["en"]` | Language tags set by the author |
| `embedType` | `images` / `video` / `link` / `quote` | Kind of attachment |
| `imageUrls` / `videoUrl` / `externalLink` | | Image URLs, video playlist, shared link with title and description |
| `isReply` / `replyParentUri` / `replyRootUri` | | Reply structure |
| `quotedPostUri` | | The quoted post, if any |
| `repostedBy` | `bsky.app` | Set when an account's feed shows a repost |
| `source` | `search:web scraping` | Which search, account or URL produced the row |

**Profile rows** (`type: "profile"`): `handle`, `did`, `displayName`, `description` (bio), `followersCount`, `followsCount`, `postsCount`, `createdAt`, `avatar`, `banner`, `verified`, `labels`, `url`.

### How to use Bluesky Scraper (step by step)

1. Click **Try for free**.
2. For a topic search, put your keywords in **Search posts for keywords**, one search per line. Use quotes for phrases and `#` for hashtags. Pick **Latest** or **Top**, and optionally a language, a date range or an author.
3. To read accounts, add handles or profile URLs under **Accounts to read** and choose which posts you want. A profile row is added for each account unless you switch that off.
4. To discover accounts, enter topics under **Search for accounts**. To fetch single posts, paste their URLs under **Post URLs**.
5. Set **Max results per search or account** and **Max results per run**. You pay per result.
6. Click **Start** and open the **Posts** or **Profiles** view, or export JSON, CSV or Excel.

### Input example

```json
{
  "searchQueries": ["web scraping", "\"open data\"", "#rstats"],
  "sort": "latest",
  "language": "en",
  "since": "2026-10-01",
  "profiles": ["bsky.app", "https://bsky.app/profile/apify.com"],
  "profileFeed": "posts_no_replies",
  "includeProfiles": true,
  "maxItems": 100,
  "maxItemsTotal": 1000
}
```

### Output example

```json
{
  "type": "post",
  "uri": "at://did:plc:embrbvwkrwf6ymtvwwrn3ucs/app.bsky.feed.post/3mziemqn5lq27",
  "url": "https://bsky.app/profile/seonews.bsky.social/post/3mziemqn5lq27",
  "text": "Brave sued for scraping web content and selling to AI firms …",
  "createdAt": "2026-11-04T12:37:44.640Z",
  "languages": ["en"],
  "authorHandle": "seonews.bsky.social",
  "authorDisplayName": "SEO News",
  "likeCount": 0,
  "repostCount": 0,
  "replyCount": 0,
  "quoteCount": 0,
  "hashtags": ["brave", "ai"],
  "links": ["https://www.example.com/article"],
  "embedType": "video",
  "isReply": false,
  "source": "search:web scraping",
  "scrapedAt": "2026-11-04T13:07:09Z"
}
```

### How much does it cost?

Bluesky Scraper uses pay-per-event pricing.

| Event | Price (Free plan) | When it is charged |
|---|---|---|
| Actor start | $0.0005 | Once per run |
| Post | $0.0015 ($1.50 per 1,000 posts) | Every post row returned |
| Profile | $0.002 ($2 per 1,000 profiles) | Every profile row returned |

Unknown accounts, deleted posts, searches without results and error rows are **free**. Paid Apify plans get Store discounts (Bronze −15%, Silver −30%, Gold and up −45%).

Worked examples (Free plan prices):

- **The 100 latest posts for each of 10 keywords:** 1,000 posts ≈ **$1.50**.
- **The last 200 posts of 10 accounts plus their profiles:** 2,000 posts + 10 profiles ≈ **$3.02**.
- **500 accounts found for "data journalism" with follower counts:** ≈ **$1.00**.

### Use cases

- **Brand and topic monitoring.** Track what is said about your company, product or topic, in a language you choose, on a schedule.
- **Link tracking.** Find every post that shares links to your site or a competitor's, and see engagement per link.
- **Research.** Collect posts for discourse analysis, sentiment studies or datasets, with timestamps and engagement.
- **Influencer and community discovery.** Search accounts by topic and rank them by followers; read what they post.
- **News and media monitoring.** Follow journalists, outlets and official accounts that have moved to Bluesky.
- **LLM and RAG pipelines.** Feed fresh posts into models and agents.

### Use with AI agents, MCP and the API

```bash
curl -X POST "https://api.apify.com/v2/acts/teddythebuilder~bluesky-scraper/run-sync-get-dataset-items?token=<TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"searchQueries":["web scraping"],"maxItems":20}'
```

Python with `apify-client`:

```python
from apify_client import ApifyClient

client = ApifyClient("<TOKEN>")
run = client.actor("teddythebuilder/bluesky-scraper").call(run_input={
    "searchQueries": ["#opendata"],
    "sort": "top",
    "since": "2026-10-01",
    "maxItems": 100,
})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(row["likeCount"], row["authorHandle"], row["text"][:100])
```

**MCP:** available as a tool in the Apify MCP server (`https://mcp.apify.com`) for Claude, ChatGPT, Cursor and other MCP clients.

### Integrations

Send results to Google Sheets, Airtable, Notion, Slack, email, a database, Make, Zapier, n8n or a webhook. Schedule the Actor to monitor a keyword or account every hour or day.

### Data source and legality

Bluesky is an open network and its AppView API is published for reading public content. This Actor reads only what that API returns without login: public posts and public profile information. It respects the API's published robots.txt and rate limits, identifies itself in the user agent, does not log in, and does not read private or follower-only content. Direct messages are not accessible.

Posts and profiles are public but can contain personal data (names, handles, opinions, bios). If you collect them, you are responsible for a lawful basis under rules such as GDPR, for honouring deletion requests and for how you use the data. This Actor does not collect e-mail addresses, phone numbers or other private contact data.

### FAQ

**Do I need a Bluesky account or API key?**
No. The Actor reads public data without any login.

**How many posts does a keyword search return?**
Up to 100 per query and date window. Bluesky refuses to page through keyword search results for requests without a login (it answers 403 to the next page), and this Actor does not work around that. To collect more, run the same query for consecutive date windows with **Posted after** and **Posted before** (for example one day each), or schedule a run every hour with the "Latest" order. Account feeds are not limited this way: they page through the account's posts up to your maximum.

**Why does a search for "everything from an account in a date range" need a keyword?**
Bluesky's search API does not accept an empty query together with a date filter. To read an account, use **Accounts to read**, which returns the account's latest posts directly. To filter by date and domain, the Actor uses the domain name as the search term.

**Can it get followers or following lists?**
Not in this version. Profiles include the follower and following counts.

**Can it get replies to a post?**
Reply relations are included (`replyParentUri`, `replyRootUri`) and you can search for replies to a topic. Fetching a full thread is not part of this version.

**Are deleted or private posts returned?**
No. Deleted posts are reported as "post_not_found_or_deleted" (free); private content is not available through the public API.

**How fresh is the data?**
Bluesky indexes new posts within seconds, so searches return posts from the last minute.

**Questions or bugs?**
Open an issue on the actor page. We reply within 24 hours. If the actor helps you, a rating on the Store page helps others find it.

### Related actors

- [Hacker News Scraper](https://apify.com/teddythebuilder/hacker-news-scraper): Who is hiring posts and stories from Hacker News.
- [Website to Markdown](https://apify.com/teddythebuilder/website-to-markdown): clean Markdown of the articles that posts link to.
- [Website Contact Scraper](https://apify.com/teddythebuilder/website-contact-scraper): company contact details from websites.
- [Audio & Video to Text](https://apify.com/teddythebuilder/speech-to-text-transcriber): transcribe audio and video files.

### Changelog and support

- **1.0 (November 2026):** first release. Post search with language, date, author and linked-domain filters; account feeds; profiles; account search; post URLs.

Questions or feature requests? Open an issue on the actor page. We reply within 24 hours.

# Actor input Schema

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

One search per line. Words, phrases in quotes and #hashtags work, e.g. web scraping, "open data", #rstats. Posts matching all words are returned (up to 100 per query and date window).

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

Latest first, or the most engaged posts first.

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

Only posts tagged with this language, as a two-letter code such as en, sv, de, ja, pt. Empty = all languages.

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

Limit the keyword search to one account (handle such as apify.com).

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

Only posts after this date or date-time (YYYY-MM-DD or ISO 8601, UTC).

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

Only posts before this date or date-time.

## `linkedDomain` (type: `string`):

Find posts that share links to this website, e.g. nytimes.com. Can be used without keywords.

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

Handles or profile URLs, e.g. bsky.app or https://bsky.app/profile/apify.com. Returns each account's latest posts and its profile row.

## `profileFeed` (type: `string`):

Original posts only, posts with replies, or only posts with images or video.

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

For every account in 'Accounts to read', also output its profile (display name, bio, followers, following, posts count, join date).

## `findAccounts` (type: `array`):

Find accounts by name or topic, e.g. data journalism. Returns profile rows.

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

Fetch specific posts by URL (https://bsky.app/profile/<handle>/post/<id>) or at:// URI.

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

Posts or profiles to return for each account or account search. Keyword searches return up to 100 posts per query and date window (Bluesky does not page keyword search without login): use Posted after / before to collect more. You pay per result.

## `maxItemsTotal` (type: `integer`):

Stop the whole run after this many results.

## Actor input object example

```json
{
  "searchQueries": [
    "web scraping"
  ],
  "sort": "latest",
  "profiles": [],
  "profileFeed": "posts_no_replies",
  "includeProfiles": true,
  "findAccounts": [],
  "postUrls": [],
  "maxItems": 100,
  "maxItemsTotal": 1000
}
```

# Actor output Schema

## `results` (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": [
        "web scraping"
    ]
};

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

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

```

## MCP server setup

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