# Bluesky Starter Pack Scraper (`devilscrapes/bluesky-starter-pack`) Actor

Export full member lists from any Bluesky Starter Pack via the AT Protocol API — pack metadata and member profiles with follower counts — to JSON or CSV. No login needed; we page through every member so none are missed.

- **URL**: https://apify.com/devilscrapes/bluesky-starter-pack.md
- **Developed by:** [DevilScrapes](https://apify.com/devilscrapes) (community)
- **Categories:** Social media
- **Stats:** 2 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#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.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — 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

<div align="center">
  <img src="https://apify.com/.actor/icon.svg" width="160" alt="Devil Scrapes mark" />

## Bluesky Starter Pack Scraper

**💰 $2.20 / 1 000 members**  ·  pay only for results  ·  no credit card to try

*We do the dirty work so your dataset stays clean.* 😈

Export the complete member graph of any Bluesky Starter Pack. One pack by URI or every pack owned by a creator handle — we handle pagination, retries, rate-limit pacing, and fingerprint rotation. You get pack metadata plus full member profiles (follower counts, post counts, handles) in one clean self-contained dataset.

</div>

***

### 🎯 What this scrapes

Bluesky Starter Packs are curated community lists that drove 43% of all follows during Bluesky's 2024 growth surge (EurekAlert, 2024). This Bluesky starter pack scraper reaches the AT Protocol AppView API (`https://public.api.bsky.app/xrpc/`) to export every member of any starter pack — by URI or by creator handle. We do the heavy lifting against three chained endpoints; you get one clean denormalized dataset.

Target endpoints:

- `app.bsky.graph.getStarterPack` — single pack metadata and embedded list URI
- `app.bsky.graph.getActorStarterPacks` — all packs published by a creator, with cursor pagination
- `app.bsky.graph.getList` — member profiles with follower and post counts

### 🔥 Features

- **Two collection modes** — scrape a single pack by URI/URL, or bulk-export every pack owned by a creator handle up to 100 packs per run.
- **Denormalized output** — every row carries pack metadata and the member profile in one flat record. No joins needed when you drop it into a spreadsheet or CRM.
- **Bluesky web URLs accepted** — paste `https://bsky.app/starter-pack/handle/rkey` directly; we normalize it to AT URI form.
- **Follower counts on every row** — `member_followers_count`, `member_following_count`, and `member_posts_count` are denormalized onto each member row.
- **Configurable member cap** — 1–5,000 members per pack; keep test runs cheap.
- **PPE pricing** — you pay only for rows emitted, not for idle compute.

### 💡 Use cases

- **B2B growth marketing** — identify niche audiences (e.g. "AI researchers on Bluesky") and build targeted outreach lists from curated community packs.
- **Bluesky audience research** — map which accounts are recommended by influential pack curators in your industry before you invest in the platform.
- **Bluesky followers list export** — enumerate the full member graph of a peer group and compare follower counts, post activity, and engagement ratios.
- **Academic research** — map community topology and influencer networks; starter packs are the AT Protocol's primary community-formation mechanism.
- **Competitive intelligence** — track which handles appear across multiple packs in your niche.
- **Journalism and OSINT** — document community formation around a topic or event.

### ⚙️ How to use it

**Single-pack mode** — paste the pack URI or web URL:

```json
{
  "starterPackUri": "at://did:plc:z72i7hdynmk6r22z3wouymf/app.bsky.graph.starterpack/ohX7HZkOlFj",
  "maxMembersPerPack": 500,
  "proxyConfiguration": {"useApifyProxy": true}
}
```

Or use a Bluesky web URL directly:

```json
{
  "starterPackUri": "https://bsky.app/starter-pack/alice.bsky.social/abc123rkey",
  "maxMembersPerPack": 500,
  "proxyConfiguration": {"useApifyProxy": true}
}
```

**Creator mode** — scrape every pack owned by a Bluesky handle:

```json
{
  "creatorHandle": "pfrazee.com",
  "maxPacks": 10,
  "maxMembersPerPack": 200,
  "proxyConfiguration": {"useApifyProxy": true}
}
```

Exactly one of `starterPackUri` or `creatorHandle` must be set — providing both or neither returns a validation error before any network call is made.

### 📥 Input

| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| `starterPackUri` | string | one-of | — | AT URI (`at://...`) or Bluesky web URL (`https://bsky.app/starter-pack/...`) of a single pack to scrape. Mutually exclusive with `creatorHandle`. |
| `creatorHandle` | string | one-of | — | Bluesky handle (e.g. `pfrazee.com`) or DID of a creator. Exports every pack owned by that user. Mutually exclusive with `starterPackUri`. |
| `maxPacks` | integer | no | 10 | Max packs to process in creator mode (1–100). Ignored in single-pack mode. |
| `maxMembersPerPack` | integer | no | 500 | Max member rows emitted per pack (1–5,000). Pagination stops when the limit is reached. |
| `proxyConfiguration` | object | no | Apify Proxy | Apify Proxy configuration. We route requests through Apify Proxy to sustain throughput and rotate exit IPs when the API rate-limits us. |

### 📤 Output

One row per pack member. Pack metadata is denormalized into every row so a single CSV or JSON export is self-contained.

| Field | Type | Nullable | Description |
|---|---|---|---|
| `pack_uri` | string | no | AT URI of the starter pack |
| `pack_name` | string | no | Display title of the pack |
| `pack_description` | string | yes | Pack description, if set |
| `pack_creator_handle` | string | no | Bluesky handle of the pack creator |
| `member_did` | string | no | Decentralized identifier of the member |
| `member_handle` | string | no | Bluesky handle of the member |
| `member_display_name` | string | yes | Display name (may be blank) |
| `member_followers_count` | integer | yes | Follower count |
| `member_following_count` | integer | yes | Following count |
| `member_posts_count` | integer | yes | Post count |
| `member_indexed_at` | string | yes | ISO 8601 datetime the member was indexed |
| `scraped_at` | string | no | ISO 8601 UTC datetime this row was written |

Example record:

```json
{
  "pack_uri": "at://did:plc:abc123/app.bsky.graph.starterpack/xyz789",
  "pack_name": "AI Researchers on Bluesky",
  "pack_description": "Curated list of ML/AI researchers who migrated from Twitter.",
  "pack_creator_handle": "alice.bsky.social",
  "member_did": "did:plc:def456",
  "member_handle": "bob.bsky.social",
  "member_display_name": "Bob Smith",
  "member_followers_count": 1204,
  "member_following_count": 380,
  "member_posts_count": 841,
  "member_indexed_at": "2024-11-14T09:22:01.000Z",
  "scraped_at": "2026-05-16T12:00:00.000Z"
}
```

### 💰 Pricing

This Actor uses **Pay-Per-Event** (PPE) pricing — you pay only for what you use, not for idle compute time.

| Event | Price | When |
|---|---|---|
| Actor start | $0.05 | Once per run at boot |
| Member row emitted | $0.002 | Per row written to the dataset |

**Effective cost**: ~$2.20 per 1,000 members scraped ($0.05 start + $0.002 × 1,000 rows).

At 5,000 members (maximum per pack): ~$10.05.

### 🚧 What we handle for you

The AT Protocol AppView is a live, rate-limited, paginated API — not a static file. Here is what this Actor absorbs so you don't have to:

- **Fingerprint rotation** — we cycle browser TLS profiles (Chrome/Firefox/Safari impersonation via curl-cffi) so every request presents a real-browser handshake, not a plain Python script.
- **Proxy rotation** — we route through Apify Proxy and request a fresh session on every block or rate-limit response, rotating the exit IP automatically.
- **Retries with exponential backoff** — on `408 / 429 / 503` and transient network errors we back off (2 s → 4 s → 8 s → 16 s → 30 s cap, 5 attempts) and honour `Retry-After` headers when present.
- **Rate-limit pacing** — cursor pagination across `getActorStarterPacks` + `getList` is throttled to stay within the API's sustained throughput; we never hammer it.
- **Clean typed rows** — every row is Pydantic-validated before it lands in the dataset. ISO 8601 timestamps, stable DID fields, null-safe nullable columns. Dirty rows never reach your export.
- **Pay-per-result** — the `actor-start` warmup fee is $0.05; beyond that you pay only for rows that successfully land.

### ❓ FAQ

**Do I need a Bluesky account to use this?**
No. The AT Protocol public AppView API (`https://public.api.bsky.app/xrpc/`) supports unauthenticated access. No login, no API key, no OAuth required from you — we handle authentication with the platform on our end.

**Can I scrape multiple packs in one run?**
Yes — use creator mode (`creatorHandle`) to export every pack published by a Bluesky user, up to 100 packs per run.

**How do I find a pack's AT URI?**
Paste the Bluesky web URL (`https://bsky.app/starter-pack/<handle>/<rkey>`) directly into `starterPackUri`. We normalize it to AT URI form automatically.

**Why are some follower counts null?**
The AT Protocol API returns profile viewer stats inline in list member objects. If a profile is new or those fields are absent from the API response, we emit the row with those fields set to `null` rather than dropping the row — so your row count stays accurate.

**Is this a bluesky followers list for any user?**
No — this Actor scopes to starter pack membership. It exports who is *in a pack*, not the full follower graph of an arbitrary user. For Bluesky feed and post data, see our companion Bluesky Feed Posts Actor.

**Is scraping Bluesky starter packs compliant with the Terms of Service?**
Yes. The AT Protocol is an open protocol explicitly designed for interoperability and data portability. Bluesky has publicly proposed a scraping standard for AI training datasets (Slashdot, 2025). The public AppView API is the official mechanism for unauthenticated access to pack data.

**What happens if I provide both starterPackUri and creatorHandle?**
The Actor raises a validation error before making any network call and exits with a non-zero status and a clear error message.

**Can I use this for bluesky audience research before investing in the platform?**
Yes — that is a core use case. Export a pack curated by someone active in your niche, analyse follower counts and post cadence, and decide whether the audience is worth engaging before you build a presence.

### 💬 Your feedback

Found a bug, a broken pack URI, or a missing field? Open an issue or contact DevilScrapes at https://apify.com/DevilScrapes. Feature requests and star ratings on the Apify Store help us prioritise the roadmap.

# Actor input Schema

## `starterPackUri` (type: `string`):

AT URI (<code>at://did:plc:.../app.bsky.graph.starterpack/...</code>) or Bluesky web URL (<code>https://bsky.app/starter-pack/handle/rkey</code>) of the pack to scrape. Use this for single-pack mode. Mutually exclusive with <strong>Creator handle</strong>.

## `creatorHandle` (type: `string`):

Bluesky handle (e.g. <code>pfrazee.com</code>) or DID of a starter-pack creator. The Actor lists every pack owned by that user via <code>app.bsky.graph.getActorStarterPacks</code> and scrapes each. Mutually exclusive with <strong>Starter pack URI</strong>.

## `maxPacks` (type: `integer`):

Maximum number of starter packs to process when using creator mode. Ignored in single-pack mode. Default 10.

## `maxMembersPerPack` (type: `integer`):

Maximum number of member rows emitted per starter pack. Acts as a client-side cap; pagination stops when this limit is reached. Default 500.

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

Optional Apify Proxy configuration. The AT Protocol public API does not block datacenter IPs — leave disabled unless you are behind a restrictive ISP.

## Actor input object example

```json
{
  "starterPackUri": "at://did:plc:ragtjsm2j2vknwkz3zp4oxrd/app.bsky.graph.starterpack/3l2stmy4ote2b",
  "creatorHandle": "pfrazee.com",
  "maxPacks": 10,
  "maxMembersPerPack": 500,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `datasetItems` (type: `string`):

All dataset items as JSON.

## `datasetItemsCsv` (type: `string`):

Same data exported to CSV.

## `datasetView` (type: `string`):

Open the run dataset in the Console.

# 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 = {
    "starterPackUri": "at://did:plc:ragtjsm2j2vknwkz3zp4oxrd/app.bsky.graph.starterpack/3l2stmy4ote2b",
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("devilscrapes/bluesky-starter-pack").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 = {
    "starterPackUri": "at://did:plc:ragtjsm2j2vknwkz3zp4oxrd/app.bsky.graph.starterpack/3l2stmy4ote2b",
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("devilscrapes/bluesky-starter-pack").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 '{
  "starterPackUri": "at://did:plc:ragtjsm2j2vknwkz3zp4oxrd/app.bsky.graph.starterpack/3l2stmy4ote2b",
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call devilscrapes/bluesky-starter-pack --silent --output-dataset

```

## MCP server setup

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

```

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/TvAWONFOgBgyEqzjP/builds/6Xfg9QwJr83GPH58D/openapi.json
