# Bluesky Public Posts: Search, Feeds & Threads (`jpmarketdata/bluesky-public-posts-search`) Actor

Export bulk recent Bluesky keyword mentions, public account watchlists and direct post reply context: redacted text, timestamps, links and engagement. Cursor-paged feeds, bounded date-window search, no login. $0.40/1k posts. Unofficial.

- **URL**: https://apify.com/jpmarketdata/bluesky-public-posts-search.md
- **Developed by:** [h ichi](https://apify.com/jpmarketdata) (community)
- **Categories:** Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$0.40 / 1,000 unique public post delivereds

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?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Bluesky Public Posts: Search, Feeds & Threads

Collect keyword mentions, a public account watchlist, and direct post reply context in one scheduled dataset. Public text, post links, timestamps and engagement support brand monitoring and content research. This is an unofficial, bounded observation of the source search index and feeds; it does not guarantee every mention or all comments.

### Input

Use queries, handles and/or postUrls, at least one and at most five combined sources. Empty input falls back to queries=\["openai"]. If any source field is supplied, omitted queries/handles/postUrls are empty lists; handles-only or URLs-only inputs never add the keyword default. Defaults are maxPostsPerSource=100, maxItems=500, sort="latest". Latest search walks older date windows; source cursor search is unavailable in current anonymous local probes. top is a single source-ranked snapshot of at most 100 roots per query. Watchlists use source cursors. Optional UTC since/until and search language are supported. since is inclusive and until exclusive on a derived sortAt basis for every source kind: normalize source createdAt and indexedAt to UTC milliseconds, then use the earlier instant. This follows the official AppView chronology rule, rather than using indexing time alone. An imported old post keeps its old creation date; a future-created post uses its earlier indexing date. createdAt remains the source creation timestamp in the output and can lie outside this sortAt window; there is no additional createdAt filter. Direct post inputs optionally include source-visible replies to depth 1–3; search/handle roots do not each fetch replies. maxItems is a billable row ceiling including replies.

### Output

One unique public post row has postUri, sourceUrl, matched sourceInputs, contact-redacted text, createdAt, indexedAt, langs, like/repost/reply/quote counters, reply parent/root URIs when available, sanitized external URLs, hasMedia, sourceRank, coverage and observedAt. Store the postUri for downstream repeat-run de-duplication. Source ranks are preserved and may not be strict chronological order. Search coverage is non-exhaustive and moderation/deletion affects visible results. A run summary records sources, request/window counts, cap stops, skipped/blocked rows, dateWindowBasis=derived_sort_at, the derivation, oldestObservedSortAt and nextUntil search boundaries; summary is uncharged and saved to SUMMARY in key-value storage.

### Charging

$0.0004 per unique delivered post through post-exported; $0.40 per 1000. No actor-specific start charge or free trial. Direct roots and delivered replies are the same event. Collect every requested source into a bounded sanitized row buffer before any delivery or billing; if any required source acquisition fails, fail with zero rows/events. Duplicates, filtered/redacted-to-empty records and uncharged SUMMARY never create events. Sources with a genuine filtered zero receive uncharged source-evidenced SUMMARY entries only if another source delivers posts. If no posts are delivered across the run, fail without charging; abnormal empty or all-source failure also fails.

### Limits

256MB, lightweight official read-only HTTP APIs, no login, paid API, proxy, browser or downloads. At most 200 requests with 1-second spacing, 15-second request timeout, 2MiB response cap and 300-second run timeout. Global 1000-row ceiling; paginate handle feeds and bounded date-window search. Compute the next exclusive until directly from the minimum derived sortAt in a returned search window, without subtracting a microsecond. The API does not expose a sortAt field; the timestamp is derived from its required createdAt/indexedAt inputs using the documented AppView rule and millisecond normalization. Timestamp-only paging cannot recover every unseen post tied at that boundary, even when more matches exist; SUMMARY sets timestampBoundaryTiesExhaustive=false and coverageExhaustive=false. A cap stop or empty older window does not prove complete coverage. Repeated cursor/window, nondecreasing boundary or no new unique post fails. Bounded dates do not establish complete mention coverage.

### Privacy

No profile names, avatars, bios, follower lists, member IDs, precise locations, emails, phone numbers or credentials are emitted. Post URIs/links are retained only for attribution and deduplication. Text and URLs are screened for contact/secret patterns and @mentions. Moderation-labeled and private/blocked records are excluded. No sentiment or person attribute is inferred.

### Source and automation

Uses api.bsky.app public search, author-feed and thread GET endpoints. Its robots explicitly allows crawling. Official API is free; this paid Actor adds managed batching, source-specific paging, privacy guards, deduplication, schedules and export without maintaining your own client. It does not offer secret source access. Price advantage versus current direct Store products is a testable hypothesis, not proof of customer payment. Terms retain user content ownership; fresh independent creation review and private cloud default/nondefault verification are required before release.

### If something goes wrong

- **Wrong number or a failed run?** Open a ticket on the **Issues** tab. I read every one and reply within 2 business days (Japan time).
- **You never get a fake "empty" result.** If the site can't be read, the run fails and says so.
- **Delivered rows only.** One validated unique row is one billable event. Duplicates, summaries, failed acquisition and undelivered rows are not charged.
- **Checked every week.** An automatic test runs this tool weekly; if the site changes, I fix it.
- **Public source data only.** No login or member/applicant records. Contact patterns are screened before delivery; prose may still name people.

### More tools by the same author

- [Mercari Japan Sold Prices — What Items Really Sell For](https://apify.com/jpmarketdata/mercari-japan-price-checker)
- [Yahoo! Auctions Japan Sold Prices — Median, Range, Bids](https://apify.com/jpmarketdata/yahoo-auction-sold-comps)

All tools (Japan marketplaces, real estate, jobs, racing, prediction markets): <https://apify.com/jpmarketdata>

### Disclaimer

Unofficial, independent tool — **not affiliated with, endorsed by, or sponsored by Bluesky**. Product names and logos belong to their owners and only say where the data comes from. Data is read from public pages, for market research; check before you act on it.

# Actor input Schema

## `queries` (type: `array`):

One to five ordinary keyword or hashtag queries. Search is a bounded sample of the Bluesky search index, not an exhaustive archive. Combined queries, handles and direct post URLs may contain at most five sources.

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

Optional public handles for cursor-paged authored feeds; excludes profile, follower and member data.

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

Optional bsky.app/profile/HANDLE/post/RECORD URLs. Direct post roots are returned with optional source-visible reply context.

## `maxPostsPerSource` (type: `integer`):

Maximum unique posts per query or handle, 1–200; direct roots are always included within the global cap. Date paging retrieves older latest search windows because anonymous cursor search was blocked in current probes.

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

Global cap on distinct charged post rows, including replies. Duplicate source matches are emitted once.

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

latest walks older source sortAt date windows and preserves rank within each window; source order is not promised monotonic. Unseen posts sharing an oldest timestamp boundary may be omitted. top is one ranked snapshot, maximum 100 roots/query. Handle feeds retain source order.

## `lang` (type: `string`):

Optional source language code, for example en. Not a nationality or residence filter.

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

Optional ISO8601 UTC inclusive lower bound on derived sortAt: the earlier of createdAt and indexedAt after UTC millisecond normalization. The same date basis applies to search, authored feeds and direct context; createdAt output is retained separately.

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

Optional ISO8601 UTC exclusive upper bound on the same derived sortAt basis as since; must follow since. This is not a separate createdAt filter. Search timestamp ties and source index limits prevent exhaustive date-window coverage.

## `includeReplies` (type: `boolean`):

If true, direct post inputs return the source-visible reply tree to threadDepth. This is bounded context, not all historical comments. Search and handle matches do not each trigger a thread request.

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

Depth for source-visible direct-post reply context, 1–3. Blocks, deleted posts and hidden replies may be absent.

## Actor input object example

```json
{
  "queries": [
    "openai"
  ],
  "maxPostsPerSource": 100,
  "maxItems": 500,
  "sort": "latest",
  "includeReplies": false,
  "threadDepth": 2
}
```

# Actor output Schema

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

Unique public posts and bounded reply context with explicit source coverage.

# 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 = {
    "queries": [
        "openai"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("jpmarketdata/bluesky-public-posts-search").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 = { "queries": ["openai"] }

# Run the Actor and wait for it to finish
run = client.actor("jpmarketdata/bluesky-public-posts-search").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 '{
  "queries": [
    "openai"
  ]
}' |
apify call jpmarketdata/bluesky-public-posts-search --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,jpmarketdata/bluesky-public-posts-search"
        }
    }
}
```

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/OcCHmPodd2ExqoAvT/builds/AsmfB8p2RCa57O4uo/openapi.json
