# Bluesky API scraper — public Bluesky profiles, posts, threads, replies, likes, reposts, quotes, followers/following, lists, custom feeds and starter packs, plus authenticated post search and timeline (AT Protocol / bsky.app)

> actor profile: did/handle/displayName/desc/followers/follows/posts counts/avatar/banner/labels (+ batch via 'actors' <=25). Requires actor OR actors (engine-enforced; required_params=() so the actors-only batch isn't rejected by the gateway literal-key validator).
> ReefAPI engine `bluesky` · 23 endpoints · clean JSON, no scraping or browsers to manage.

## How to call
- **Endpoint:** `POST https://api.reefapi.com/bluesky/v1/<action>` with a JSON body.
- **Auth:** header `x-api-key: <YOUR_REEFAPI_KEY>` — create one free (1,000 credits, no card): https://reefapi.com/signup
- **Response (every call):** `{ ok: boolean, data: ..., meta: { record_count, credits, ... }, error: { code, message } }` — branch on `ok`. Failed or blocked calls are free.
- **One key + one shared credit pool** across every ReefAPI API. Per-call credits are listed on each endpoint below.
- **Use it from an AI agent (MCP):** connect `https://api.reefapi.com/mcp` (remote streamable-http, `Authorization: Bearer <key>`) and your assistant can call these actions directly.

## Endpoints

### POST /bluesky/v1/profile — 1 credit
actor profile: did/handle/displayName/desc/followers/follows/posts counts/avatar/banner/labels (+ batch via 'actors' <=25). Requires actor OR actors (engine-enforced; required_params=() so the actors-only batch isn't rejected by the gateway literal-key validator).

**Parameters:**
- `actor` (string, optional) — A Bluesky account — its handle (e.g. bsky.app), a did:plc:… DID, or a bsky.app/profile/<…> URL. The AppView resolves all three.
- `actors` (array, optional) — Batch mode: up to 25 handles/DIDs (array or comma-separated string). When given, returns a profiles[] batch instead of a single profile.

**Returns:** single: did, handle, display_name, description, followers_count, follows_count, posts_count, avatar, banner, labels, verified, pinned_post — OR (with `actors`) profiles[] + count

**Example request body:**
```json
{
  "actor": "bsky.app"
}
```

### POST /bluesky/v1/author_feed — 1 credit
an actor's posts (full record+embed+counts), cursor-paginated; filter=posts_no_replies|posts_with_replies|posts_with_media|posts_and_author_threads

**Parameters:**
- `actor` (string, required) — A Bluesky account — its handle (e.g. bsky.app), a did:plc:… DID, or a bsky.app/profile/<…> URL. The AppView resolves all three.
- `filter` (enum, optional, default "posts_no_replies") — Which of the author's posts to include. [one of: posts_no_replies, posts_with_replies, posts_with_media, posts_and_author_threads]
- `limit` (integer, optional, default 30) — Max records to return (1-200, default 30). Auto-paginated; larger values are clamped to 200.
- `cursor` (string, optional) — Pagination cursor from a previous response's `cursor`. Omit for page one.

**Returns:** posts[] (uri, text, author, like/repost/reply/quote counts, embed, tags), count, cursor

**Example request body:**
```json
{
  "actor": "bsky.app",
  "limit": 30
}
```

### POST /bluesky/v1/search_actors — 1 credit
search users by name/handle/bio (cursor); typeahead=true for fast suggest

**Parameters:**
- `q` (string, required) — Search query (keywords; also accepted as 'query').
- `typeahead` (boolean, optional) — true = fast as-you-type suggestions (no pagination); false = full search.
- `limit` (integer, optional, default 25) — Max records to return (1-100, default 25). Auto-paginated; larger values are clamped to 100.
- `cursor` (string, optional) — Pagination cursor from a previous response's `cursor`. Omit for page one.

**Returns:** actors[] (did, handle, display_name, description, avatar, verified), count, cursor

**Example request body:**
```json
{
  "q": "news",
  "limit": 25
}
```

### POST /bluesky/v1/thread — 1 credit
a post + its reply tree (recursive) + parent

**Parameters:**
- `uri` (string, required) — The post's at:// AT-URI (or a bsky.app/profile/<did>/post/<rkey> URL). Get it from a post's `uri` field.
- `depth` (integer, optional, default 6) — How many levels of replies to include below the post (0-100).
- `parent_height` (integer, optional, default 0) — How many parent posts above the target to include (0-100).

**Returns:** root{post, replies[] (recursive)}, parent, reply_total

**Example request body:**
```json
{
  "uri": "at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post/3l6oveex3ii2l",
  "depth": 3
}
```

### POST /bluesky/v1/post_detail — 1 credit
hydrate one or more posts by at:// URI (<=25): record/embed/like+repost+reply+quote counts

**Parameters:**
- `uris` (array, required) — One or more post at:// AT-URIs (array or comma-separated string, up to 25). A single 'uri' is also accepted.

**Returns:** posts[] (full normalized post each), count, requested

**Example request body:**
```json
{
  "uris": [
    "at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post/3l6oveex3ii2l"
  ]
}
```

### POST /bluesky/v1/likes — 1 credit
actors who liked a post (cursor); cid auto-used if given

**Parameters:**
- `uri` (string, required) — The post's at:// AT-URI (or a bsky.app/profile/<did>/post/<rkey> URL). Get it from a post's `uri` field.
- `cid` (string, optional) — Optional post content-id (a post's `cid`). Speeds up the lookup; auto-resolved from the URI when omitted.
- `limit` (integer, optional, default 50) — Max records to return (1-500, default 50). Auto-paginated; larger values are clamped to 500.
- `cursor` (string, optional) — Pagination cursor from a previous response's `cursor`. Omit for page one.

**Returns:** likes[] (actor did/handle/display_name + liked_at), count, cursor

**Example request body:**
```json
{
  "uri": "at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post/3l6oveex3ii2l",
  "cid": "bafyreicnt42y6vo6pfpvyro234ac4o6ijug6adwwrh7awflgrqlt4zibxq",
  "limit": 25
}
```

### POST /bluesky/v1/reposts — 1 credit
actors who reposted a post (cursor)

**Parameters:**
- `uri` (string, required) — The post's at:// AT-URI (or a bsky.app/profile/<did>/post/<rkey> URL). Get it from a post's `uri` field.
- `cid` (string, optional) — Optional post content-id (a post's `cid`). Speeds up the lookup; auto-resolved from the URI when omitted.
- `limit` (integer, optional, default 50) — Max records to return (1-500, default 50). Auto-paginated; larger values are clamped to 500.
- `cursor` (string, optional) — Pagination cursor from a previous response's `cursor`. Omit for page one.

**Returns:** reposted_by[] (actor did/handle/display_name), count, cursor

**Example request body:**
```json
{
  "uri": "at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post/3l6oveex3ii2l",
  "cid": "bafyreicnt42y6vo6pfpvyro234ac4o6ijug6adwwrh7awflgrqlt4zibxq",
  "limit": 25
}
```

### POST /bluesky/v1/followers — 1 credit
actors following the subject (cursor)

**Parameters:**
- `actor` (string, required) — A Bluesky account — its handle (e.g. bsky.app), a did:plc:… DID, or a bsky.app/profile/<…> URL. The AppView resolves all three.
- `limit` (integer, optional, default 100) — Max records to return (1-2000, default 100). Auto-paginated; larger values are clamped to 2000.
- `cursor` (string, optional) — Pagination cursor from a previous response's `cursor`. Omit for page one.

**Returns:** subject, followers[] (actor objects), count, cursor

**Example request body:**
```json
{
  "actor": "bsky.app",
  "limit": 50
}
```

### POST /bluesky/v1/follows — 1 credit
actors the subject follows (cursor)

**Parameters:**
- `actor` (string, required) — A Bluesky account — its handle (e.g. bsky.app), a did:plc:… DID, or a bsky.app/profile/<…> URL. The AppView resolves all three.
- `limit` (integer, optional, default 100) — Max records to return (1-2000, default 100). Auto-paginated; larger values are clamped to 2000.
- `cursor` (string, optional) — Pagination cursor from a previous response's `cursor`. Omit for page one.

**Returns:** subject, follows[] (actor objects), count, cursor

**Example request body:**
```json
{
  "actor": "bsky.app",
  "limit": 10
}
```

### POST /bluesky/v1/quotes — 1 credit
Accounts/posts that QUOTE a given post (quote-posts), cursor-paginated. The other half of a post's engagement alongside likes and reposts.

**Parameters:**
- `uri` (string, required) — The post's at:// AT-URI (or a bsky.app/profile/<did>/post/<rkey> URL). Get it from a post's `uri` field.
- `cid` (string, optional) — Optional post content-id (a post's `cid`). Speeds up the lookup; auto-resolved from the URI when omitted.
- `limit` (integer, optional, default 50) — Max records to return (1-200, default 50). Auto-paginated; larger values are clamped to 200.
- `cursor` (string, optional) — Pagination cursor from a previous response's `cursor`. Omit for page one.

**Returns:** posts[] (each quoting post, full normalized), count, cursor

**Example request body:**
```json
{
  "uri": "at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post/3l6oveex3ii2l",
  "cid": "bafyreicnt42y6vo6pfpvyro234ac4o6ijug6adwwrh7awflgrqlt4zibxq",
  "limit": 25
}
```

### POST /bluesky/v1/suggested_follows — 1 credit
Accounts suggested from a given account — Bluesky's 'who to follow next' / similar-accounts set. Not paginated (a fixed suggestion set, ~10-50 actors).

**Parameters:**
- `actor` (string, required) — A Bluesky account — its handle (e.g. bsky.app), a did:plc:… DID, or a bsky.app/profile/<…> URL. The AppView resolves all three.
- `limit` (integer, optional, default 25) — Max records to return (1-100, default 25). Auto-paginated; larger values are clamped to 100.

**Returns:** suggestions[] (actor objects), count, is_fallback

**Example request body:**
```json
{
  "actor": "bsky.app"
}
```

### POST /bluesky/v1/lists — 1 credit
The curated/moderation lists an account CREATED (user-curated lists + mute/block mod-lists), cursor-paginated.

**Parameters:**
- `actor` (string, required) — A Bluesky account — its handle (e.g. bsky.app), a did:plc:… DID, or a bsky.app/profile/<…> URL. The AppView resolves all three.
- `limit` (integer, optional, default 50) — Max records to return (1-500, default 50). Auto-paginated; larger values are clamped to 500.
- `cursor` (string, optional) — Pagination cursor from a previous response's `cursor`. Omit for page one.

**Returns:** lists[] (uri, name, purpose, description, list_item_count, creator), count, cursor

**Example request body:**
```json
{
  "actor": "nytimes.com",
  "limit": 10
}
```

### POST /bluesky/v1/list — 1 credit
A single list's metadata plus its MEMBERS (the accounts on the list), members cursor-paginated.

**Parameters:**
- `list` (string, required) — A list's at:// AT-URI (e.g. at://did:plc:…/app.bsky.graph.list/<rkey>), or a bsky.app/profile/<did>/lists/<rkey> URL. Get it from the `lists` action.
- `limit` (integer, optional, default 100) — Max records to return (1-2000, default 100). Auto-paginated; larger values are clamped to 2000.
- `cursor` (string, optional) — Pagination cursor from a previous response's `cursor`. Omit for page one.

**Returns:** list (metadata), members[] (actor objects), count, cursor

**Example request body:**
```json
{
  "list": "at://did:plc:eclio37ymobqex2ncko63h4r/app.bsky.graph.list/3lbl2hyzmfy2u",
  "limit": 20
}
```

### POST /bluesky/v1/list_feed — 1 credit
The feed of posts authored by the members of a list (a list used as a custom timeline), cursor-paginated.

**Parameters:**
- `list` (string, required) — A list's at:// AT-URI (e.g. at://did:plc:…/app.bsky.graph.list/<rkey>), or a bsky.app/profile/<did>/lists/<rkey> URL. Get it from the `lists` action.
- `limit` (integer, optional, default 30) — Max records to return (1-200, default 30). Auto-paginated; larger values are clamped to 200.
- `cursor` (string, optional) — Pagination cursor from a previous response's `cursor`. Omit for page one.

**Returns:** posts[] (from list members, full normalized), count, cursor

**Example request body:**
```json
{
  "list": "at://did:plc:eclio37ymobqex2ncko63h4r/app.bsky.graph.list/3lbl2hyzmfy2u",
  "limit": 15
}
```

### POST /bluesky/v1/actor_feeds — 1 credit
The custom feed generators an account CREATED (its published feeds), cursor-paginated.

**Parameters:**
- `actor` (string, required) — A Bluesky account — its handle (e.g. bsky.app), a did:plc:… DID, or a bsky.app/profile/<…> URL. The AppView resolves all three.
- `limit` (integer, optional, default 50) — Max records to return (1-500, default 50). Auto-paginated; larger values are clamped to 500.
- `cursor` (string, optional) — Pagination cursor from a previous response's `cursor`. Omit for page one.

**Returns:** feeds[] (uri, display_name, description, like_count, creator), count, cursor

**Example request body:**
```json
{
  "actor": "bsky.app",
  "limit": 10
}
```

### POST /bluesky/v1/feed_generator — 1 credit
One custom feed generator's metadata plus its live online/valid status (is the feed service up).

**Parameters:**
- `feed` (string, required) — A feed-generator's at:// URI (…/app.bsky.feed.generator/<rkey>) or its bsky.app/profile/<id>/feed/<rkey> share URL. Get it from `actor_feeds` or `popular_feeds`.

**Returns:** feed-generator view + is_online, is_valid

**Example request body:**
```json
{
  "feed": "at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/whats-hot"
}
```

### POST /bluesky/v1/popular_feeds — 1 credit
Discover popular custom feed generators — pass `q` to search feeds by name/topic, or omit for the platform-wide popular list. Cursor-paginated.

**Parameters:**
- `q` (string, optional) — Optional search query to filter popular feeds by name/topic. Omit for the platform-wide popular list.
- `limit` (integer, optional, default 50) — Max records to return (1-100, default 50). Auto-paginated; larger values are clamped to 100.
- `cursor` (string, optional) — Pagination cursor from a previous response's `cursor`. Omit for page one.

**Returns:** feeds[] (feed-generator views), count, cursor

**Example request body:**
```json
{
  "q": "news",
  "limit": 10
}
```

### POST /bluesky/v1/get_feed — 1 credit
The posts served BY a custom feed generator (e.g. a Discover/What's-Hot/topic feed) by its at:// URI, cursor-paginated. Public — no account required.

**Parameters:**
- `feed` (string, required) — A feed-generator's at:// URI (e.g. a custom/discover feed). Get it from a profile's associated feedgens or a feed's share URL.
- `limit` (integer, optional, default 30) — Max records to return (1-200, default 30). Auto-paginated; larger values are clamped to 200.
- `cursor` (string, optional) — Pagination cursor from a previous response's `cursor`. Omit for page one.

**Returns:** feed, posts[] from that feed generator, count, cursor

**Example request body:**
```json
{
  "feed": "at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/whats-hot",
  "limit": 20
}
```

### POST /bluesky/v1/starter_packs — 1 credit
The starter packs an account CREATED (shareable onboarding bundles of accounts + feeds), cursor-paginated.

**Parameters:**
- `actor` (string, required) — A Bluesky account — its handle (e.g. bsky.app), a did:plc:… DID, or a bsky.app/profile/<…> URL. The AppView resolves all three.
- `limit` (integer, optional, default 50) — Max records to return (1-500, default 50). Auto-paginated; larger values are clamped to 500.
- `cursor` (string, optional) — Pagination cursor from a previous response's `cursor`. Omit for page one.

**Returns:** starter_packs[] (uri, name, description, creator, list_item_count, join counts), count, cursor

**Example request body:**
```json
{
  "actor": "bsky.app",
  "limit": 10
}
```

### POST /bluesky/v1/starter_pack — 1 credit
One starter pack's full detail: its underlying list, sample member profiles and any bundled feeds.

**Parameters:**
- `starter_pack` (string, required) — A starter-pack at:// URI (…/app.bsky.graph.starterpack/<rkey>) or a bsky.app/starter-pack/<id>/<rkey> URL. Get it from the `starter_packs` action.

**Returns:** starter-pack view + list, members[] (sample), feeds[]

**Example request body:**
```json
{
  "starter_pack": "at://did:plc:ragtjsm2j2vknwkz3zp4oxrd/app.bsky.graph.starterpack/3m4j33zq7sz2m"
}
```

### POST /bluesky/v1/search_posts — 1 credit
Search Bluesky posts by keyword or hashtag — filter by sort order, date range, author, language, domain or tag. Requires an app-password; returns a clear error if none is configured.

**Parameters:**
- `q` (string, required) — Search query (keywords; also accepted as 'query').
- `sort` (enum, optional, default "top") — Result ordering for post search. [one of: top, latest]
- `limit` (integer, optional, default 25) — Max records to return (1-100, default 25). Auto-paginated; larger values are clamped to 100.
- `cursor` (string, optional) — Pagination cursor from a previous response's `cursor`. Omit for page one.
- `since` (string, optional) — Only posts at/after this time (ISO-8601 or YYYY-MM-DD).
- `until` (string, optional) — Only posts at/before this time (ISO-8601 or YYYY-MM-DD).
- `author` (string, optional) — Restrict to posts by this account (handle or DID).
- `mentions` (string, optional) — Restrict to posts mentioning this account (handle or DID).
- `lang` (string, optional) — Restrict to posts in this language (BCP-47 code, e.g. en, tr, ja).
- `domain` (string, optional) — Restrict to posts linking to this domain.
- `url` (string, optional) — Restrict to posts linking to this exact URL.
- `tag` (string, optional) — Restrict to posts with this hashtag (without the #).

**Returns:** posts[] (full normalized post each), hits_total, count, cursor

**Example request body:**
```json
{
  "q": "bluesky",
  "sort": "top",
  "limit": 25
}
```

### POST /bluesky/v1/get_actor_likes — 1 credit
posts an actor liked (authed; the AppView serves the authenticated account's own likes). Defaults to the logged-in handle.

**Parameters:**
- `actor` (string, optional) — A Bluesky account — its handle (e.g. bsky.app), a did:plc:… DID, or a bsky.app/profile/<…> URL. The AppView resolves all three.
- `limit` (integer, optional, default 30) — Max records to return (1-200, default 30). Auto-paginated; larger values are clamped to 200.
- `cursor` (string, optional) — Pagination cursor from a previous response's `cursor`. Omit for page one.

**Returns:** actor, posts[] the account liked, count, cursor (authed)

**Example request body:**
```json
{
  "limit": 20
}
```

### POST /bluesky/v1/timeline — 1 credit
the authenticated account's home timeline (authed)

**Parameters:**
- `limit` (integer, optional, default 30) — Max records to return (1-200, default 30). Auto-paginated; larger values are clamped to 200.
- `algorithm` (string, optional) — Optional timeline algorithm key (server-defined; omit for the default).
- `cursor` (string, optional) — Pagination cursor from a previous response's `cursor`. Omit for page one.

**Returns:** posts[] from the account's home timeline, count, cursor (authed)

**Example request body:**
```json
{
  "limit": 20
}
```

## More
- Try it live, no code: https://reefapi.com/playground?engine=bluesky
- Human docs page: https://reefapi.com/docs/bluesky
- Every ReefAPI API in one file (for your AI): https://reefapi.com/llms-full.txt
