# Bluesky Historical Archive Scraper - Old Posts by Date & Author (`logiover/bluesky-historical-archive-scraper`) Actor

Scrape the full public post history of any Bluesky account by date, author, and thread via the keyless AT-Protocol API. Posts, replies, reposts, profiles, thread trees, and list feeds. No API key, no login, no cookies.

- **URL**: https://apify.com/logiover/bluesky-historical-archive-scraper.md
- **Developed by:** [Logiover](https://apify.com/logiover) (community)
- **Categories:** Social media, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 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.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js/docs.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python/docs.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## Bluesky Historical Archive Scraper — Old Posts by Date, Author & Thread

![Apify Actor](https://img.shields.io/badge/Apify-Actor-00A67E?logo=apify&logoColor=white) ![No API key](https://img.shields.io/badge/No%20API%20key-required-2ea44f) ![Pay per result](https://img.shields.io/badge/Pricing-Pay%20per%20result-1C7ED6) ![Social Media](https://img.shields.io/badge/Category-Social%20Media-1185FE) ![Export](https://img.shields.io/badge/Export-JSON%20%7C%20CSV%20%7C%20Excel-F59E0B)

Scrape the **full public post history** of any **Bluesky** account — years of old posts, replies and reposts, **by date and by author** — content that Bluesky's own on-app search and search API can no longer reach. This Bluesky historical scraper walks each account's **AT-Protocol author feed** page by page, all the way back to the account's very first post, so you can pull a deep archive, expand entire **thread and reply trees**, dump curated **list feeds** and enrich every row with the author's **follower, following and post counts**. Point it at handles, DIDs, profile URLs, post URLs or Bluesky list URIs and get clean, flat rows. **Fast, no browser, no Bluesky login, no app password, no API key, no OAuth.**

> ### 🦋 Why this Bluesky historical scraper?
> **Full account history in flat rows** · **thousands of posts per run** · reply-tree reconstruction · date-bounded windows back to an account's first post · author profile enrichment · export to JSON / CSV / Excel. The unofficial **Bluesky / AT-Protocol API alternative** for archival, research, AI training corpora, brand monitoring and lead-gen.

---

### ✨ What does the Bluesky Historical Archive Scraper do?

This Actor is a keyless historical harvester for **Bluesky** built directly on the public **AT-Protocol XRPC API** (`public.api.bsky.app`). Its backbone is `app.bsky.feed.getAuthorFeed`, which it paginates via the returned `cursor` until the cursor runs out — reaching an account's **entire archive** of original posts, replies and reposts, far past what Bluesky's app shows before it stops loading. Each post is flattened into a single tidy row with engagement counts (likes, reposts, replies, quotes), media detection, reply/thread references and ISO timestamps. Optional modes fetch full **reply trees** (`getPostThread`), pull **curated list feeds** (`getListFeed`) and enrich every author once via `getProfile`. Date filtering is applied client-side on `createdAt`, and because feeds are newest-first, pagination stops early on time-bounded windows. It runs with **no app password, no OAuth, no session and no cookies** — only public endpoints.

### 👥 Who is it for?

- **Academic researchers & social scientists** studying how a community, topic or account evolved over time on Bluesky.
- **AI / NLP & ML teams** building domain-specific training corpora from years of real, public conversational text.
- **Brand, PR & reputation analysts** archiving everything an account (or a curated list of accounts) has ever posted.
- **Data journalists & OSINT analysts** reconstructing timelines, threads and reply networks around an event or figure.
- **Growth & lead-gen teams** collecting active, follower-ranked authors and their posting cadence for outreach.

### 🎯 Use cases

- **Full-account backfill** — pull every post an account has ever made into a spreadsheet or warehouse in one run.
- **Date-bounded historical windows** — grab a news outlet's posts from a specific month or year with `afterDate` / `beforeDate`.
- **Thread & conversation reconstruction** — expand any post into its complete flat reply tree for discourse analysis.
- **List-wide monitoring** — scrape every post surfaced by a curated Bluesky list (a community, a beat, a competitor set).
- **Influence & audience mapping** — enrich each post with the author's follower/following/post counts to rank reach.

### 🏆 Why use the Bluesky Historical Archive Scraper?

- 🔑 **Fully keyless** — no app password, no OAuth token, no session, no cookies, no developer account. It reads only public AT-Protocol endpoints.
- 📚 **Whole-archive depth** — cursor pagination reaches an account's first-ever post, well beyond what the Bluesky app or search will surface.
- 🧾 **24 clean fields per row** — text, timestamps, engagement counts, author enrichment, media type, reply/thread links and a ready-to-open post URL.
- 🧵 **Multi-mode targeting** — mix author history, individual post threads and curated list feeds in a single run.
- 🗓️ **Precise date filters + caps** — `afterDate`, `beforeDate`, `maxPostsPerActor` and a global `maxItems` give you exact control over volume and cost.
- 📤 **Export anywhere** — download as JSON, CSV, Excel, HTML, XML or JSONL, or stream via the Apify API, webhooks and integrations. Simple **pay-per-result** pricing.

### 📊 What data can you extract?

Every post, reply and repost is returned as one **flat row**. The `type` field tells you whether it is an original `post`, a `reply`, or a `repost`. Here is the complete field reference:

| Field | Type | Description |
|-------|------|-------------|
| `uri` | string | The post's AT-URI (`at://did:plc:.../app.bsky.feed.post/<rkey>`) — the stable global identifier. |
| `cid` | string | Content identifier (IPLD CID) of the post record. |
| `url` | string | Ready-to-open web URL: `https://bsky.app/profile/<handle>/post/<rkey>`. |
| `type` | string | `post`, `reply`, or `repost`. |
| `text` | string | The full post text (plain UTF-8, emojis included). |
| `createdAt` | date | ISO timestamp the author set when the post was created. |
| `indexedAt` | date | ISO timestamp Bluesky's AppView indexed the post. |
| `lang` | string | Primary declared language tag (e.g. `en`, `ja`), when present. |
| `authorDid` | string | The author's decentralized identifier (DID). |
| `authorHandle` | string | The author's handle (e.g. `nytimes.com`). |
| `authorDisplayName` | string | The author's display name. |
| `authorFollowers` | number | Author's follower count (when profile enrichment is on). |
| `authorFollowing` | number | Number of accounts the author follows. |
| `authorPostsCount` | number | Author's lifetime post count. |
| `replyCount` | number | Replies to this post. |
| `repostCount` | number | Reposts of this post. |
| `likeCount` | number | Likes on this post. |
| `quoteCount` | number | Quote-posts of this post. |
| `isReply` | boolean | `true` if the post is a reply to another post. |
| `replyToUri` | string | AT-URI of the immediate parent post (if a reply). |
| `rootUri` | string | AT-URI of the thread root (if a reply). |
| `hasMedia` | boolean | `true` if the post embeds images or video. |
| `mediaType` | string | `image`, `video`, `external` (link card), `quote`, or empty. |
| `linkUrl` | string | First media URL (image/video) or external link-card URL. |
| `threadRepliesFetched` | number | How many replies were pulled for this post in thread mode. |

#### Example output row

```json
{
  "uri": "at://did:plc:eclio37ymobqex2ncko63h4r/app.bsky.feed.post/3mr7upflb242n",
  "cid": "bafyreib2r...",
  "url": "https://bsky.app/profile/nytimes.com/post/3mr7upflb242n",
  "type": "post",
  "text": "Japan's Prime Minister Sanae Takaichi has ...",
  "createdAt": "2026-07-22T08:10:04.551Z",
  "indexedAt": "2026-07-22T08:10:05.012Z",
  "lang": "en",
  "authorDid": "did:plc:eclio37ymobqex2ncko63h4r",
  "authorHandle": "nytimes.com",
  "authorDisplayName": "The New York Times",
  "authorFollowers": 1336639,
  "authorFollowing": 120,
  "authorPostsCount": 40057,
  "replyCount": 42,
  "repostCount": 68,
  "likeCount": 86,
  "quoteCount": 9,
  "isReply": false,
  "replyToUri": null,
  "rootUri": null,
  "hasMedia": false,
  "mediaType": "external",
  "linkUrl": "https://www.nytimes.com/2026/07/22/world/asia/japan-takaichi.html",
  "threadRepliesFetched": 0
}
````

### 🚀 How to use

You can target Bluesky in three ways — combine them freely in one run.

#### Option A — Full account history (by handle, DID or profile URL)

The most common mode. List one or more accounts in **`actors`**. Each is paginated back to its first post. Add a date window and caps to control depth.

```json
{
  "actors": ["nytimes.com", "did:plc:z72i7hdynmk6r22z27h6tvur"],
  "feedFilter": "posts_no_replies",
  "afterDate": "2024-01-01",
  "beforeDate": "2024-12-31",
  "maxPostsPerActor": 2000,
  "includeProfiles": true
}
```

1. Paste handles (`nytimes.com`), DIDs (`did:plc:...`) or profile URLs (`https://bsky.app/profile/nytimes.com`) into **`actors`** — a leading `@` and any URL wrapper are stripped for you.
2. Optionally set **`feedFilter`**, a date window and **`maxPostsPerActor`**.
3. Click **Start**. Watch rows stream into the **Output** tab.

#### Option B — Individual posts with their reply trees

Pass post URLs or AT-URIs to **`postUrls`** and turn on **`expandThreads`** to pull each post's full conversation as flat rows.

```json
{
  "postUrls": [
    "https://bsky.app/profile/bsky.app/post/3mqc36slinc2m"
  ],
  "expandThreads": true,
  "threadDepth": 10
}
```

#### Option C — A curated Bluesky list

Pass list AT-URIs to **`lists`** to harvest every post surfaced by that list.

```json
{
  "lists": ["at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.graph.list/3abc..."],
  "maxPostsPerActor": 5000,
  "maxItems": 20000
}
```

### ⚙️ Input parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `actors` | array | `["nytimes.com","bsky.app"]` | Handles, DIDs or profile URLs to pull full history for. |
| `lists` | array | `[]` | Bluesky list AT-URIs (list-feed mode). |
| `postUrls` | array | `[]` | Individual post URLs or AT-URIs to fetch with their thread. |
| `feedFilter` | string | `posts_with_replies` | `posts_with_replies`, `posts_no_replies`, `posts_with_media`, or `posts_and_author_threads`. |
| `afterDate` | string | `null` | Only posts created after this date (`YYYY-MM-DD` or ISO). Stops pagination early. |
| `beforeDate` | string | `null` | Only posts created before this date. |
| `maxPostsPerActor` | integer | `1000` | Hard cap on posts per account / list. |
| `expandThreads` | boolean | `false` | Also fetch each post's reply tree and emit replies as rows. |
| `threadDepth` | integer | `6` | Reply levels to walk when `expandThreads` is on (0–1000). |
| `includeProfiles` | boolean | `true` | Enrich rows with author follower/following/post counts (cached per author). |
| `maxItems` | integer | `0` | Global cap across everything. `0` = unlimited. |
| `proxyConfiguration` | object | Apify Proxy on | Proxy settings; each retry rotates to a fresh IP. |

#### Full input example

```json
{
  "actors": ["nytimes.com", "bsky.app"],
  "lists": [],
  "postUrls": [],
  "feedFilter": "posts_with_replies",
  "afterDate": null,
  "beforeDate": null,
  "maxPostsPerActor": 1000,
  "expandThreads": false,
  "threadDepth": 6,
  "includeProfiles": true,
  "maxItems": 0,
  "proxyConfiguration": { "useApifyProxy": true }
}
```

### 💡 Tips for best results

- **Reach the full archive** — set `maxPostsPerActor` high (e.g. `50000`) and leave `maxItems` at `0` to pull an account's entire history.
- **Bound by time cheaply** — set `afterDate`; because feeds are newest-first, the scraper stops paging as soon as it crosses that date, saving results and time.
- **Drop the noise** — use `feedFilter: "posts_no_replies"` when you only want an account's original posts, not its replies to others.
- **Media-only slices** — `feedFilter: "posts_with_media"` returns just posts that carry images or video.
- **Reply trees cost more** — `expandThreads` multiplies volume; keep `threadDepth` modest (6–10) unless you truly need deep nesting.
- **Handles vs DIDs** — both work; DIDs are permanent, so prefer them for long-running scheduled jobs (handles can change).
- **Strip nothing** — you can paste raw `@handle` or a full `https://bsky.app/profile/...` URL; the Actor normalizes it.
- **Cache is automatic** — each author's profile is fetched only once per run, so enrichment adds little overhead even on big feeds.
- **Reposts are included** — in `posts_with_replies` an account's reposts appear as `type: "repost"` with the original author's handle.
- **Scale with the global cap** — for multi-account jobs, set `maxItems` to bound total cost regardless of how big any one account is.

### 🔌 Integrations

Push your Bluesky archive anywhere. From the Apify platform you can:

- **Google Sheets** — sync the dataset straight into a spreadsheet for analysts.
- **Slack / Discord** — post run summaries or new-post alerts to a channel.
- **Zapier / Make / n8n** — trigger downstream workflows on each finished run.
- **Webhooks** — POST the dataset to your own endpoint the moment a run completes.
- **Schedule** — run daily or hourly to keep a rolling archive of an account, list or beat fresh.

### 🛠️ API usage

Run the Actor programmatically with the Apify API or clients.

**cURL**

```bash
curl -X POST "https://api.apify.com/v2/acts/logiover~bluesky-historical-archive-scraper/runs?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"actors":["nytimes.com"],"maxPostsPerActor":500,"afterDate":"2025-01-01"}'
```

**Node.js (apify-client)**

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: 'YOUR_TOKEN' });

const run = await client.actor('logiover/bluesky-historical-archive-scraper').call({
    actors: ['nytimes.com', 'bsky.app'],
    feedFilter: 'posts_no_replies',
    maxPostsPerActor: 1000,
    includeProfiles: true,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

**Python (apify-client)**

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_TOKEN")

run = client.actor("logiover/bluesky-historical-archive-scraper").call(run_input={
    "actors": ["nytimes.com"],
    "afterDate": "2025-01-01",
    "maxPostsPerActor": 1000,
})

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["authorHandle"], item["likeCount"], item["text"][:80])
```

### 🤖 Use with AI agents (MCP)

This Actor works as a tool for AI agents through the **Apify MCP Server**, so an LLM agent can pull Bluesky history on demand. Connect the Apify MCP server to your agent, then prompt it naturally — for example: *"Use the Bluesky Historical Archive Scraper to fetch the last 500 posts from `nytimes.com` created after 2025-01-01, then summarize the main themes."* The agent calls the Actor, receives the flat dataset and reasons over it.

### ❓ Frequently Asked Questions

#### Do I need a Bluesky API key, app password or login?

No. The scraper reads only public AT-Protocol endpoints on `public.api.bsky.app`, so no app password, OAuth token, session or cookie is ever required to pull old posts, threads or author profiles.

#### How far back can it scrape a Bluesky account?

All the way to the account's first-ever post. The Actor paginates the author feed via its `cursor` until the cursor is exhausted, which reaches the entire public archive — far deeper than the Bluesky app loads before it stops.

#### How many posts can I get per run?

Thousands. A single account can yield tens of thousands of rows; multiple accounts or a big list scale further. Use `maxPostsPerActor` and the global `maxItems` to control volume and cost.

#### Can I scrape Bluesky posts by date?

Yes. Set `afterDate` and/or `beforeDate` (`YYYY-MM-DD` or full ISO). Because feeds are newest-first, pagination stops early once posts fall before `afterDate`, so time-bounded windows are fast and cheap.

#### Does it work by handle or by DID?

Both. Paste a handle (`nytimes.com`), a DID (`did:plc:...`) or a full profile URL. Handles are resolved automatically. DIDs are permanent, so they are the safer choice for scheduled jobs.

#### Can it fetch replies and full threads?

Yes. Turn on `expandThreads` and set `threadDepth` to walk each post's reply tree via `getPostThread`. Every reply is returned as its own flat row, so you can reconstruct entire conversations.

#### Why not use Bluesky's search endpoint?

Bluesky's `searchPosts` endpoint is edge-restricted and returns errors to server-side callers, so this Actor deliberately builds on the reliable, keyless author-feed, thread and list endpoints instead — which also reach far deeper history than search does.

#### The run returned zero results — what went wrong?

Check that the handle/DID/URL is correct and the account is public, and that any date window actually contains posts. A private, deactivated or misspelled account returns nothing; the run still exits cleanly with a warning in the log.

#### Why are some fields null?

Not every post has media, a language tag, or reply references, so `linkUrl`, `lang`, `replyToUri` and `rootUri` can be null. Author enrichment fields are null when `includeProfiles` is off or a profile can't be fetched.

#### How do I export old Bluesky posts to CSV or JSON?

Run the scraper, then download the dataset as CSV, JSON, Excel, HTML, XML or JSONL straight from the Apify Console, or pull it through the API. Every post is a flat row, so it imports cleanly into spreadsheets and databases.

#### How do I download a Bluesky account's entire post history?

Add the handle or DID to `actors`, set `maxPostsPerActor` high and leave `maxItems` at `0`. The Actor paginates the full author feed and returns every post, reply and repost.

#### How often is the data updated?

It's live. Each run reads Bluesky's current public AppView, so newly published posts appear within moments of going live. Schedule the Actor to keep a rolling, always-fresh archive.

### ⚖️ Is it legal?

This Actor collects only **publicly available** Bluesky content served by the open AT-Protocol AppView. It requires no login and accesses nothing private. You are responsible for using the data in compliance with **Bluesky's Terms of Service**, the **AT-Protocol** usage norms, applicable privacy laws such as **GDPR** and **CCPA**, and any local regulations. Do not use scraped personal data for spam, harassment or unlawful profiling. Scrape responsibly and respect rate limits.

### 🔗 Related scrapers by logiover

Building a wider social dataset? Pair this with the rest of the logiover social-media suite:

- [Reddit Historical Archive Scraper](https://apify.com/logiover/reddit-historical-archive-scraper) — old Reddit posts & comments by date.
- [X Tweet Scraper](https://apify.com/logiover/x-tweet-scraper) — profiles, tweets and posts from X / Twitter.
- [Threads Scraper](https://apify.com/logiover/threads-scraper) — posts and profiles from Meta's Threads.
- [Mastodon Scraper](https://apify.com/logiover/mastodon-scraper) — posts from the wider fediverse.

👉 Browse all **[logiover scrapers on Apify Store](https://apify.com/logiover)** — 180+ actors across social media, real estate, jobs, crypto & B2B data.

***

### 📝 Changelog

#### 2026-07-22

- 🦋 Initial release. Full public Bluesky account history via the keyless AT-Protocol API: author-feed pagination to the first post, thread/reply-tree expansion, curated list feeds, author profile enrichment, and precise `afterDate` / `beforeDate` windows. 24 flat fields per row, deduplicated by URI, with fresh-proxy retry and exponential backoff.

# Actor input Schema

## `actors` (type: `array`):

Bluesky accounts to pull the full public post history for. Accepts handles (e.g. `nytimes.com`, `bsky.app`), DIDs (e.g. `did:plc:...`), or full profile URLs (e.g. `https://bsky.app/profile/nytimes.com`). A leading `@` and any URL prefix are stripped automatically. Each account is paginated all the way back to reach its entire archive.

## `lists` (type: `array`):

Curated Bluesky list AT-URIs to pull posts from (list-feed mode). Format: `at://did:plc:.../app.bsky.graph.list/...`. Every post surfaced by the list is returned as a flat row, just like an author feed. Great for scraping a whole community or topic list at once.

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

Individual Bluesky posts to fetch together with their thread (reply tree). Accepts post URLs (e.g. `https://bsky.app/profile/<handle>/post/<rkey>`) or raw AT-URIs (`at://did:plc:.../app.bsky.feed.post/<rkey>`). Each post and, when `Expand threads` is on, its replies are returned as flat rows.

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

Which slice of each account's history to pull. `Posts with replies` (default) returns everything: original posts, the account's replies, and reposts. `Posts, no replies` drops the account's replies. `Posts with media` returns only posts that contain images or video. `Posts and author threads` returns posts plus the account's own self-reply threads.

## `afterDate` (type: `string`):

Only return posts created AFTER this date. Format: `YYYY-MM-DD` or a full ISO timestamp. Because feeds are newest-first, pagination stops early once posts are older than this date, so time-bounded windows are fast and cheap. Example: `2024-01-01`.

## `beforeDate` (type: `string`):

Only return posts created BEFORE this date. Combine with `After Date` for a precise historical window. Format: `YYYY-MM-DD` or a full ISO timestamp. Example: `2024-12-31`.

## `maxPostsPerActor` (type: `integer`):

Hard cap on the number of posts pulled per account / list. Prevents one huge account (a news outlet can have tens of thousands of posts) from eating the whole budget. Set high to reach the full archive.

## `expandThreads` (type: `boolean`):

For each scraped post, also fetch its full reply tree (comments) via getPostThread and emit every reply as its own flat row. Multiplies result volume and lets you reconstruct entire conversations. Off by default to keep runs lean.

## `threadDepth` (type: `integer`):

How many reply levels deep to walk when `Expand threads` is on (0-1000). Higher values capture nested sub-replies but cost more. 6 is a sensible default for most conversations.

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

Enrich every row with the author's follower count, following count, and total post count (getProfile is fetched once per author and cached). Adds valuable audience-size context for lead-gen and influence analysis. On by default.

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

Total hard cap across ALL accounts, lists, posts, and expanded threads combined. `0` = unlimited (fetch everything within the per-account caps).

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

Apify Proxy (ON by default). The public Bluesky AT-Protocol API responds cleanly to Apify's datacenter IPs, so datacenter proxy is the reliable default. Each retry rotates to a fresh proxy session (new IP) to sail past transient rate limits. If you ever see persistent throttling on a very large run, switch the group to RESIDENTIAL here.

## Actor input object example

```json
{
  "actors": [
    "nytimes.com",
    "bsky.app"
  ],
  "lists": [],
  "postUrls": [],
  "feedFilter": "posts_with_replies",
  "afterDate": null,
  "beforeDate": null,
  "maxPostsPerActor": 1000,
  "expandThreads": false,
  "threadDepth": 6,
  "includeProfiles": true,
  "maxItems": 0,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

All Bluesky posts, replies, and reposts extracted by this run. Open the Dataset tab to browse, filter, and export as CSV, JSON, or Excel.

# 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 = {
    "actors": [
        "nytimes.com",
        "bsky.app"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("logiover/bluesky-historical-archive-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 = { "actors": [
        "nytimes.com",
        "bsky.app",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("logiover/bluesky-historical-archive-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "actors": [
    "nytimes.com",
    "bsky.app"
  ]
}' |
apify call logiover/bluesky-historical-archive-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=logiover/bluesky-historical-archive-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Bluesky Historical Archive Scraper - Old Posts by Date & Author",
        "description": "Scrape the full public post history of any Bluesky account by date, author, and thread via the keyless AT-Protocol API. Posts, replies, reposts, profiles, thread trees, and list feeds. No API key, no login, no cookies.",
        "version": "1.0",
        "x-build-id": "7QkcKBIekesbQrKlk"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/logiover~bluesky-historical-archive-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-logiover-bluesky-historical-archive-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/logiover~bluesky-historical-archive-scraper/runs": {
            "post": {
                "operationId": "runs-sync-logiover-bluesky-historical-archive-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/logiover~bluesky-historical-archive-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-logiover-bluesky-historical-archive-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "actors": {
                        "title": "Accounts (handles / DIDs / profile URLs)",
                        "type": "array",
                        "description": "Bluesky accounts to pull the full public post history for. Accepts handles (e.g. `nytimes.com`, `bsky.app`), DIDs (e.g. `did:plc:...`), or full profile URLs (e.g. `https://bsky.app/profile/nytimes.com`). A leading `@` and any URL prefix are stripped automatically. Each account is paginated all the way back to reach its entire archive.",
                        "default": [],
                        "items": {
                            "type": "string"
                        }
                    },
                    "lists": {
                        "title": "Bluesky Lists (AT-URIs)",
                        "type": "array",
                        "description": "Curated Bluesky list AT-URIs to pull posts from (list-feed mode). Format: `at://did:plc:.../app.bsky.graph.list/...`. Every post surfaced by the list is returned as a flat row, just like an author feed. Great for scraping a whole community or topic list at once.",
                        "default": [],
                        "items": {
                            "type": "string"
                        }
                    },
                    "postUrls": {
                        "title": "Individual Post URLs / AT-URIs",
                        "type": "array",
                        "description": "Individual Bluesky posts to fetch together with their thread (reply tree). Accepts post URLs (e.g. `https://bsky.app/profile/<handle>/post/<rkey>`) or raw AT-URIs (`at://did:plc:.../app.bsky.feed.post/<rkey>`). Each post and, when `Expand threads` is on, its replies are returned as flat rows.",
                        "default": [],
                        "items": {
                            "type": "string"
                        }
                    },
                    "feedFilter": {
                        "title": "Feed Filter",
                        "enum": [
                            "posts_with_replies",
                            "posts_no_replies",
                            "posts_with_media",
                            "posts_and_author_threads"
                        ],
                        "type": "string",
                        "description": "Which slice of each account's history to pull. `Posts with replies` (default) returns everything: original posts, the account's replies, and reposts. `Posts, no replies` drops the account's replies. `Posts with media` returns only posts that contain images or video. `Posts and author threads` returns posts plus the account's own self-reply threads.",
                        "default": "posts_with_replies"
                    },
                    "afterDate": {
                        "title": "After Date (ISO)",
                        "type": "string",
                        "description": "Only return posts created AFTER this date. Format: `YYYY-MM-DD` or a full ISO timestamp. Because feeds are newest-first, pagination stops early once posts are older than this date, so time-bounded windows are fast and cheap. Example: `2024-01-01`.",
                        "default": null
                    },
                    "beforeDate": {
                        "title": "Before Date (ISO)",
                        "type": "string",
                        "description": "Only return posts created BEFORE this date. Combine with `After Date` for a precise historical window. Format: `YYYY-MM-DD` or a full ISO timestamp. Example: `2024-12-31`.",
                        "default": null
                    },
                    "maxPostsPerActor": {
                        "title": "Max Posts Per Account",
                        "minimum": 1,
                        "type": "integer",
                        "description": "Hard cap on the number of posts pulled per account / list. Prevents one huge account (a news outlet can have tens of thousands of posts) from eating the whole budget. Set high to reach the full archive.",
                        "default": 1000
                    },
                    "expandThreads": {
                        "title": "Expand Threads (fetch reply trees)",
                        "type": "boolean",
                        "description": "For each scraped post, also fetch its full reply tree (comments) via getPostThread and emit every reply as its own flat row. Multiplies result volume and lets you reconstruct entire conversations. Off by default to keep runs lean.",
                        "default": false
                    },
                    "threadDepth": {
                        "title": "Thread Depth",
                        "minimum": 0,
                        "maximum": 1000,
                        "type": "integer",
                        "description": "How many reply levels deep to walk when `Expand threads` is on (0-1000). Higher values capture nested sub-replies but cost more. 6 is a sensible default for most conversations.",
                        "default": 6
                    },
                    "includeProfiles": {
                        "title": "Enrich With Author Profiles",
                        "type": "boolean",
                        "description": "Enrich every row with the author's follower count, following count, and total post count (getProfile is fetched once per author and cached). Adds valuable audience-size context for lead-gen and influence analysis. On by default.",
                        "default": true
                    },
                    "maxItems": {
                        "title": "Max Items (Global)",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Total hard cap across ALL accounts, lists, posts, and expanded threads combined. `0` = unlimited (fetch everything within the per-account caps).",
                        "default": 0
                    },
                    "proxyConfiguration": {
                        "title": "Proxy",
                        "type": "object",
                        "description": "Apify Proxy (ON by default). The public Bluesky AT-Protocol API responds cleanly to Apify's datacenter IPs, so datacenter proxy is the reliable default. Each retry rotates to a fresh proxy session (new IP) to sail past transient rate limits. If you ever see persistent throttling on a very large run, switch the group to RESIDENTIAL here.",
                        "default": {
                            "useApifyProxy": true
                        }
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
