Bluesky Post Monitor (unofficial) avatar

Bluesky Post Monitor (unofficial)

Pricing

from $1.00 / 1,000 posts

Go to Apify Store
Bluesky Post Monitor (unofficial)

Bluesky Post Monitor (unofficial)

Unofficial monitor for public Bluesky author feeds, keywords, and hashtags. Drops deleted posts and can return only new posts across runs. Not affiliated with Bluesky.

Pricing

from $1.00 / 1,000 posts

Rating

0.0

(0)

Developer

Olalekan Johnson

Olalekan Johnson

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Share

Watch public Bluesky posts from an author feed, a keyword, or a hashtag, and keep only the posts that are still on the network when the run fetches them. Optional new-post tracking skips URIs this Actor already saved.

This Actor is not affiliated with, endorsed by, or maintained by Bluesky, Bluesky Social, PBC, or the AT Protocol team. It is an independent client of Bluesky's public AppView. It never posts, likes, follows, replies, or sends a notification.

What you can do with it

  • Snapshot a public profile feed, starting with the default handle bsky.app, with no Bluesky account.
  • Search recent posts for a phrase, or collect a hashtag.
  • Run several handles or phrases in one go. Each value is requested on its own, then rows are de-duplicated by post URI.
  • Schedule New posts only so the next run returns posts you have not already saved for that state namespace.
  • Drop posts whose text contains an exclude keyword.

Social listening, PR checks, and research exports are the intended use. The Actor is read-only.

What works without a login

Checked against the live AppView on 23 October 2026:

  • GET https://public.api.bsky.app/xrpc/app.bsky.feed.getAuthorFeed returns posts with no token. Author mode is the default, and it is what the Apify daily test runs.
  • GET https://public.api.bsky.app/xrpc/app.bsky.feed.searchPosts currently returns an HTML 403 from the edge. That response is not a JSON "please log in" error.
  • GET https://api.bsky.app/xrpc/app.bsky.feed.searchPosts returns search results with no token. Search requires the q parameter. Hashtags are sent as q plus tag.

Search and hashtag modes call api.bsky.app directly so they do not depend on the blocked public host. If Bluesky later refuses those calls with HTTP 401 or 403, add your handle and an app password (created in the Bluesky app's settings, not your main password). The password field is secret. It is not printed in the log, not saved on the post, and not written into the seen-URI store. Leave both login fields empty for the public author feed.

The Actor follows Bluesky's developer guidelines for a read-only client: it does not generate interactions, and it does not keep a copy of a post the AppView no longer returns as a live record. Deleted, missing, and blocked post views are dropped. Past dataset rows from earlier runs stay where they are, because Apify datasets are append-only; this run simply will not write that post again.

Input

Author feed, no login:

{
"mode": "author",
"queries": ["bsky.app"],
"maxPosts": 5,
"newPostsOnly": false
}

Keyword watch across runs:

{
"mode": "search",
"queries": ["apify", "web scraping"],
"excludeKeywords": ["hiring"],
"since": "2026-10-01T00:00:00Z",
"newPostsOnly": true,
"stateNamespace": "brand-watch",
"maxPosts": 50
}

Hashtag:

{
"mode": "hashtag",
"queries": ["python"],
"maxPosts": 20
}

Optional login, only if search is refused:

{
"mode": "search",
"queries": ["bluesky"],
"identifier": "yourname.bsky.social",
"appPassword": "xxxx-xxxx-xxxx-xxxx"
}
FieldNotes
Modeauthor, search, or hashtag
QueriesHandles, keywords, or tags. The @ and a leading # are optional
Exclude keywordsCase-insensitive, applied to post text
Posted sinceISO date or datetime. Also sent to search
New posts onlySkip URIs already saved. Default off, so the daily test stays non-empty
State namespaceSeparate watches in one account
Maximum posts1 to 200, default 5
Maximum charge (USD)Extra cap, with the Apify run cap. The lower one wins
Handle and app passwordOptional. Both or neither

A new-posts run that finds nothing new succeeds. Any other run that saves nothing fails, including a login failure or a handle the AppView does not know.

Output

{
"uri": "at://did:plc:example/app.bsky.feed.post/3abc",
"cid": "bafyrei...",
"url": "https://bsky.app/profile/bsky.app/post/3abc",
"authorHandle": "bsky.app",
"authorDid": "did:plc:example",
"authorDisplayName": "Bluesky",
"text": "Post text as returned by the AppView.",
"createdAt": "2026-10-22T18:00:00.000Z",
"indexedAt": "2026-10-22T18:00:01.000Z",
"langs": ["en"],
"tags": ["example"],
"likeCount": 12,
"repostCount": 2,
"replyCount": 1,
"quoteCount": 0,
"replyTo": null,
"embedType": null,
"matchedQuery": "bsky.app"
}

SUMMARY in the default key-value store has the post count, the estimated charge, whether the run authenticated, and errors. It does not contain the app password or the access token.

Pricing

Pay per event. The only custom event is post. Do not price apify-default-dataset-item, or each saved post would be billed twice. Set post as the primary event.

EventPriceWhen it fires
apify-actor-start$0.005Once per run, by the platform (memory is capped at 1 GB, so once). The Actor does not call Actor.charge for it.
post$0.001One live public post was saved

One hundred posts cost $0.005 + 100 × $0.001 = $0.105. Platform usage is not billed to you. The Actor stops when the next post would pass the run maximum charge or maxChargeUsd. Posts that were deleted, blocked, excluded by keyword, older than since, or already seen are not charged.

FAQ

Is this an official Bluesky product? No. It calls Bluesky's public HTTP API the same way any other read-only client can. The listing must not imply a partnership.

Why does the default use an author feed instead of search? The daily test has to return data with the prefilled input and no secrets. Author feeds on public.api.bsky.app do that today. Search is available as a mode, on the host that currently allows it without a token.

Do I have to create a Bluesky account? No. Add a handle and app password only if a search or hashtag request comes back unauthorized. The password input is marked secret and is not required, so the daily test is not skipped.

What happens to deleted posts? If the AppView returns a not-found or blocked view, or a record that is no longer a post, that post is not saved. The seen store holds URIs, not text, so this Actor is not a private archive of deleted posts. Rows written by an earlier run remain in that run's dataset.

Can one run watch several keywords? Yes. Put them in queries. A post that matches two queries is saved once.

Does it respect Bluesky's developer guidelines? It is a read-only monitor: no automated follows, likes, replies, or other interactions that would notify someone. It drops posts the API says are gone. Broader duties in those guidelines (in-app reporting tools, a public contact address) apply to products that publish user-generated content of their own. This Actor does not.

Limits

  • Public AppView reads only. No private profiles, no mutes, no DMs.
  • Search availability can change. The 23 October 2026 check found unauthenticated search on api.bsky.app and an edge block on public.api.bsky.app. The optional app password is the fallback.
  • At most five pages per query, and at most 200 saved posts per run.
  • Seen URIs are capped at 10,000 per namespace.
  • Counts (likes, reposts, replies) are whatever the AppView returned at fetch time.
  • The client waits half a second between requests to the same host and retries server errors. It does not try to look like a browser.
  • Platform usage is not billed to you; you pay only the events above.

Run it locally

From this directory, with monitor_core vendored beside the package:

mkdir -p storage/key_value_stores/default
printf '%s\n' '{"mode":"author","queries":["bsky.app"],"maxPosts":3}' > storage/key_value_stores/default/INPUT.json
python -m bluesky_post_monitor

Publish notes

Use limited permissions. Copy .actor/pay_per_event.json into pay-per-event monetization, with post as the primary event, and leave apify-default-dataset-item at $0 or remove it. Keep "unofficial" in the title.