Bluesky Keyword & Mention Alerts — Only New Posts
Pricing
from $3.00 / 1,000 new-posts
Bluesky Keyword & Mention Alerts — Only New Posts
Bluesky monitor for keyword, mention, hashtag, and brand alerts. Run social listening on a schedule and receive only new public posts as JSON or via webhooks; no login required. Unofficial and not affiliated with Bluesky Social PBC.
Pricing
from $3.00 / 1,000 new-posts
Rating
0.0
(0)
Developer
Telemark Digital
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
1
Monthly active users
11 days ago
Last modified
Categories
Share
Bluesky Keyword & Mention Alerts monitors keywords, mentions, handles, hashtags, and brands on Bluesky. Run it on an Apify Schedule to receive only new matched public posts as structured JSON or optional webhooks, with no Bluesky login required.
Unofficial and independent. This Actor is not affiliated with, endorsed by, or sponsored by Bluesky Social PBC.
What does Bluesky Keyword & Mention Alerts do?
- Watches keywords, phrases, hashtags, and handles (a handle is watched two ways — its own new posts and new posts that mention it), all in one task, one merged deduplicated feed.
- Per-post charges apply only to delivered filtered posts. Every run still incurs the $0.002 Actor start event; each post delivered after deduplication, exclusion, and language filtering incurs $0.003. Seen IDs are retained for 30 days, subject to the documented state cap.
- Runs headless on a Schedule and pushes structured JSON to a dataset (download as JSON, CSV, Excel, or HTML) + an optional webhook payload for Slack / n8n / Make / any endpoint / an AI agent.
Why use it?
- No app password required for keyword/hashtag/mention monitoring.
- Durable, edit-safe state. History is keyed by your Task, not by a hash of your query — so adding or changing a keyword never resets or orphans what you've already seen.
- Arrays, not one string. Watch many keywords, handles, and hashtags in a single task with a single merged feed, plus
excludeTermsand language filters. - Engineered failover. A 3-path data chain (see Reliability below) instead of a single brittle endpoint.
- Webhooks and automation-ready JSON. Fire-on-new webhooks for Slack / n8n / Make / any endpoint, or poll the dataset from your own agent workflow.
How to use it (2-minute quickstart)
- Click Try for free.
- Add at least one keyword, handle, or hashtag (e.g. keyword
bluesky monitor). - Click Start. The first run looks back 24 hours; every later run returns only what's new since the last one.
- Create a Task (top-right Create task) and attach a Schedule (e.g. every hour) — this turns the Actor into an isolated recurring monitor. Ad-hoc runs share one persistent per-user state scope, so Tasks are strongly recommended.
- (Optional) Paste a Webhook URL to get pushed the new posts the moment they appear.
Worked schedule example
Create a Task with this input, then add an hourly Schedule:
{"keywords": ["british museum", "bayeux tapestry"],"handles": ["bsky.app"],"hashtags": ["photography"],"excludeTerms": ["giveaway"],"langs": ["en"],"onlyNew": true,"maxPostsPerRun": 100,"webhookUrl": "https://hooks.example.com/your-endpoint"}
- Console: open the Task → Actions ▸ Schedule Actor → Every hour → Save.
- Each run charges
$0.002to start plus$0.003per new post; a quiet hourly watch idles at roughly $1.44/month of start fees and a few cents of new-post charges.
Integration recipes
The webhook fires only when there are new posts. Generic endpoints receive { "summary": {...}, "posts": [...up to 50 newest...] }; Slack Incoming Webhooks receive formatted text.
- n8n: add a Webhook node (HTTP POST), copy its URL into
webhookUrl.{{$json.body.posts}}is your array of new posts;{{$json.body.summary.newPosts}}is the count. - Make (Integromat): create a scenario starting with Webhooks ▸ Custom webhook, paste the address into
webhookUrl, then mapposts[]. - Slack: use a Slack Incoming Webhook URL directly as
webhookUrlfor a concise message with up to 10 post links, or route the generic payload through Make/n8n for custom formatting. - Generic / any endpoint / your own code: POST body shape:
{"summary": {"newPosts": 3,"matchedPosts": 3,"dataSourcePath": "P1","saturatedP1": false,"capped": false,"droppedCount": 0,"chargeLimitReached": false,"failureCount": 0,"charged": { "actorStart": 0.002, "newPost": 0.009 },"chargeAttempted": { "newPost": 0.009 },"finishedAt": "..."},"posts": [{"uri": "...","url": "...","author": { "handle": "..." },"text": "...","matchedTerms": ["..."],"source": "keyword","isNew": true}]}
You can also skip the webhook and pull results from the dataset via the Apify API after each scheduled run.
Use it from an AI agent
Call the Actor from your agent workflow through the Apify API, a saved Task, or an Apify MCP server that you explicitly configure. For example, an agent can request:
"Watch Bluesky for mentions of
acme.bsky.socialand the keywordproduct recall, English only, and return only posts I haven't seen before."
which maps to { "handles": ["acme.bsky.social"], "keywords": ["product recall"], "langs": ["en"], "onlyNew": true }. The only-new semantics fit agent polling because scheduled Tasks avoid duplicate context on every poll.
Input
Provide at least one of keywords, handles, or hashtags. All other fields are optional.
| Field | Type | Default | Description |
|---|---|---|---|
keywords | string[] | – | Full-text terms/phrases to watch. |
handles | string[] | – | Handles (± @). Each watched for its own posts and mentions of it. |
hashtags | string[] | – | Hashtags (± #). |
excludeTerms | string[] | [] | Drop posts containing any of these (case-insensitive), before billing. |
langs | string[] | [] | ISO 639-1 codes; empty = all. |
onlyNew | boolean | true | Return/charge only posts new to this task. Off = plain search (still charged per post). |
maxPostsPerRun | integer | 100 | Hard billing ceiling per run (1–800). |
webhookUrl | string | – | POSTed run summary + up to 50 newest posts, only when there are new posts. |
resetState | boolean | false | Wipe this task's seen-set + cursor and start fresh. |
Output
One dataset item per new matched post. Download as JSON, CSV, Excel, or HTML.
{"uri": "at://did:plc:example/app.bsky.feed.post/example123","cid": "bafyexamplecid","url": "https://bsky.app/profile/example.test/post/example123","author": {"did": "did:plc:example","handle": "example.test","displayName": "Example Account","avatar": "https://cdn.bsky.app/img/avatar/plain/example"},"text": "Example public post matching the configured keyword.","createdAt": "2026-07-10T18:42:11.000Z","indexedAt": "2026-07-10T18:42:13.512Z","langs": ["en"],"likeCount": 0,"repostCount": 0,"replyCount": 0,"quoteCount": 0,"matchedTerms": ["example keyword"],"source": "keyword","isNew": true}
| Field | Meaning |
|---|---|
uri | Stable post identity used to dedupe. |
cid | Content version identifier returned by Bluesky. |
url | Human-clickable post link. |
matchedTerms | Every input term that returned this post (emitted once even if multiple matched). |
source | keyword | hashtag | handle | mention. |
isNew | Whether this post was new to the task this run. |
Pricing
Pay-per-event, billed by Apify:
| Event | Price |
|---|---|
| Actor start (per run) | $0.002 |
| New post delivered | $0.003 ($3.00 / 1,000 new) |
After deduplication and filtering, the Actor journals the attempted post IDs, writes the dataset without an automatic event, and durably commits Task state plus the terminal journal. It then immediately requests one new-post charge before attempting best-effort lease release, summary persistence, or webhook delivery. A failed or partial post-commit charge is never retried, so the summary distinguishes the amount attempted from the amount actually charged. Excluded, off-language, and already-seen posts are free. maxPostsPerRun and the buyer's maximum total charge cap the worst case of any single run.
State semantics
- "Only new" means: not present in this task's active seen-set. Dedup is by post
uri. - Task-keyed: state is stored per Apify Task, so editing keywords keeps your history. Ad-hoc (un-scheduled) runs are keyed per user — create a Task for real monitoring.
- Cursor: tracked by the server-assigned
indexedAttime (clamped to now), so future-dated spam posts can't break the monitor. First run looks back 24h. resetState: truewipes the seen-set and cursor once; turn it back off afterward.- TTL/cap: seen post IDs older than 30 days are pruned automatically, and the oldest IDs are evicted above 50,000 per task. A post can be delivered again after expiry or eviction.
Reliability & failover
Bluesky Keyword & Mention Alerts uses a 3-path data chain and reports the active path as dataSourcePath in every run summary:
- P1 — no-login public API (default). Delivers up to 100 posts per term per poll. Every full 100-post page (
SATURATED_P1) routes that watch to the authenticated path to page out the backlog, even when the page includes previously seen overlap records. Choose a schedule frequent enough that fewer than 100 matching posts arrive between runs. - P2 — authenticated fallback (best-effort). Used for saturation/backlog and if P1 is unavailable. This path is served through an operator account, creates run-local sessions to avoid exposing derived credentials in caller storage, and is subject to Bluesky's service and rate-limit policies. There is no capacity guarantee above the P1 ceiling.
- P3 — degraded replay mode. If both search paths are unavailable, the Actor falls back to Jetstream with an up-to-five-minute replay cursor (keyword/hashtag only). Gaps longer than five minutes are not backfilled. In this degraded path, the DID is used as the author handle fallback and a CID may be unavailable.
If a recurring run is capped (capped: true), it delivers the oldest contiguous prefix and leaves its cursor where later posts remain queryable. droppedCount reports the deferred remainder; raise the ceiling or tighten the schedule to drain it faster. Plain discovery mode remains newest-first.
FAQ
- Do I need a Bluesky account or app password? No, not for keyword, hashtag, or mention monitoring.
- What happens on the first run? It looks back 24 hours and delivers matches, then tracks forward from there.
- What if more than 100 posts match in one poll? The data path auto-pages when needed, then recurring mode delivers the oldest bounded prefix so later posts remain queryable. Use a more frequent schedule for very active terms.
- Is this legal? It reads only public Bluesky posts via the public AT Protocol API and stores no private data. Respect Bluesky's Terms and applicable law for your use case.
- Why is there a per-run start fee? It's cost-recovery on runs with zero new posts (an hourly schedule idles at ~$1.44/month) and it prevents empty-schedule spam.
- Found a bug / want a feature? Open an issue on the Actor's Issues tab.