# Bluesky Scraper: Mentions, Hashtags, Link Tracker (`pradio/bluesky-mentions`) Actor

Track every public Bluesky post that mentions your keywords, hashtags or account, or links to your domain, over the last 1 to 36 hours. Bluesky posts are read from Bluesky's own public stream, one row per post with live likes, reposts and replies.

- **URL**: https://apify.com/pradio/bluesky-mentions.md
- **Developed by:** [Pradio Actors](https://apify.com/pradio) (community)
- **Categories:** Social media, Marketing
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.58 / 1,000 post returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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 Scraper: Mentions, Hashtags, Link Tracker

### What does Bluesky Scraper do?

Bluesky Scraper returns the public Bluesky posts that match your topics over the last 1 to 36 hours. A topic is a keyword, a hashtag, an account people mention, or a domain people link to. It reads Bluesky's own public stream of posts, so every matching post in the window is found, not a ranked sample, apart from posts deleted since and posts by authors who opted out of logged-out viewing or asked to be excluded. The newest come back, up to your `maxItems` cap of 1,000 by default. Each row carries the post's text, author, link and live likes, reposts and replies.

A day of Bluesky is about 3.4 million public posts, read in about 15 minutes. You pay $0.004 for each hour read and $0.00088 for each post returned.

### Who uses Bluesky Scraper

| Who | What they run it for |
|---|---|
| Brand and communications teams | Every post that names the brand or links to its site, once a day |
| Publishers and marketers | Which posts share their articles, and how much engagement each got |
| Developer relations and open-source maintainers | Mentions of a project, its hashtag or its docs domain |
| Researchers | Every post on a topic over a fixed window, up to 5,000 a run |

### Features

- **Complete over the window.** Every public post in the window is read, so a quiet mention is found as surely as a popular one. The newest matching posts come back, up to `maxItems` (1,000 by default); raise `maxItems` to get all of a busy topic, up to 5,000 posts a run. The only posts never found are those deleted since and those whose authors opted out of logged-out viewing or asked to be excluded.
- **Four kinds of topic in one run.** Keywords and phrases, hashtags, mentions of an account, and links to a domain or any of its subdomains.
- **Link tracking.** A post matches your domain through its links, its link card or a URL in its text.
- **Live engagement.** Likes, reposts, replies, quotes and bookmarks are read when the row is written.
- **Which topic matched.** Every row names the topic or topics it matched, such as `domain:example.com`.
- **Images, link cards and threads.** Every row carries the post's image addresses with their alt text, its link card with title and description, the post it quotes, and the reply and thread it belongs to.
- **Engagement minimums.** Set a minimum number of likes or reposts and only posts that reached it are returned and charged.
- **An answer for every topic.** A topic with no post in the window gets one free row that says so.
- **Built for a schedule.** Run it daily with a 24-hour look back, or hourly with 1: either way each hour is read once.

### What you can count on

- Every post row is charged only after it is written to your dataset. A row you cannot see is never billed.
- A run that finds nothing returns one NO\_POSTS\_FOUND row that says so, never an empty dataset.
- A spending limit stops the run cleanly with a STOPPED\_EARLY row saying how many rows were returned and how many were not.
- Every run writes a RUN\_SUMMARY to its key-value store with rowsFetched, rowsPushed, rowsCharged and duplicatesDropped, so a short run and a broken one are told apart.
- If Bluesky's stream changes what it answers, the run fails with the error in the log. It never returns rows full of nulls and calls it success.
- No value is invented: a field the Bluesky firehose does not show is null. That is `author_name`, `langs`, `images`, `external_link`, `quote_of`, `reply_to` or `reply_root` when the post has none.

### Why this one

- **Measured on topics it had never seen.** Over 24 hours and 100 open-source project domains nobody tuned it on, every domain got an answer. 14 had posts, and 86 had none and said so.
- **Cheaper on a busy topic, dearer on a quiet one.** The most-used alternative on this platform charges a $0.004 fee per run and $0.0011 a post. This Actor charges $0.00088 a post plus $0.004 for each hour it reads. At 1,000 posts a day this Actor costs about $0.98, the lower of the two. At 60 posts a day this Actor costs about $0.15, and the scan makes it the dearer of the two. What the scan buys is a read of every post in the window, not a search's pick of them.
- **Lighter runs.** It runs at 1024 MB where the most-used alternative runs at 4096 MB.
- **Links, not just words.** Search a domain and you get the posts that link to it, whether or not they name it.

### What data does Bluesky Scraper return?

One row per post. This is a real row from a run:

```json
{
  "status": "ok",
  "url": "https://bsky.app/profile/msittechnews.bsky.social/post/3mwdqjbfsc32b",
  "uri": "at://did:plc:ksq7lyouf5ycik4izcmfe4ro/app.bsky.feed.post/3mwdqjbfsc32b",
  "text": "\"New FinOps for AI capabilities: Control spend, measure value, and optimize for impact\" techcommunity.microsoft.com/blog/ai-fino... #Microsoft #techcommunity",
  "created_at": "2026-09-25T12:11:38.732Z",
  "author_name": "MS IT Tech News",
  "author_handle": "msittechnews.bsky.social",
  "author_did": "did:plc:ksq7lyouf5ycik4izcmfe4ro",
  "langs": null,
  "type": "post",
  "matched_by": [
    "domain:microsoft.com"
  ],
  "links": [
    "https://techcommunity.microsoft.com/blog/ai-finops-blog/new-finops-for-ai-capabilities-control-spend-measure-value-and-optimize-for-impa/4559660"
  ],
  "images": null,
  "external_link": {
    "uri": "https://techcommunity.microsoft.com/blog/ai-finops-blog/new-finops-for-ai-capabilities-control-spend-measure-value-and-optimize-for-impa/4559660",
    "title": "New FinOps for AI capabilities: Control spend, measure value, and optimize for impact | Microsoft Community Hub",
    "description": "Explore new FinOps for AI capabilities that help organizations control spend, measure value, and optimize AI investments for greater business impact."
  },
  "quote_of": null,
  "reply_to": null,
  "reply_root": null,
  "reply_count": 0,
  "repost_count": 0,
  "like_count": 0,
  "quote_count": 0,
  "bookmark_count": 0,
  "cid": "bafyreihpm6gr5cq3sonejrhh2mn7pkit3fm5zcuz2b4r7loibq5ehmqgd4",
  "row_type": "ROW"
}
```

Every field, and where it comes from:

| Field | Type | What it is |
|---|---|---|
| `url` | string | The post's page on bsky.app, with the author's handle. |
| `uri` | string | The post's at:// address, the identifier Bluesky's own API uses. |
| `text` | string | The post's text as written. Empty (`""`) when the post is only a link card, such as an article shared from another site. |
| `created_at` | string | When the author posted it, by the author's own clock, in one ISO 8601 format: UTC with milliseconds, such as `2026-09-25T09:26:15.779Z`. |
| `author_name` | string | The author's display name. Null when they set none. |
| `author_handle` | string | The author's handle. |
| `author_did` | string | The author's permanent account identifier. A handle can change; this does not. |
| `langs` | array | The languages the author tagged the post with. Null when they tagged none. |
| `type` | string | `post`, `reply` or `quote`. |
| `matched_by` | array | Which of your topics the post matched, for example `keyword:apify` or `domain:example.com`. |
| `links` | array | Every link the post carries: link facets, the link card and URLs in the text. |
| `images` | array | The post's images, each as `{ url, alt }`: the full-size address on Bluesky and the author's alt text. Includes a quote post's own images. Null when the post has none. Images are not downloaded. |
| `external_link` | object | The post's link card as `{ uri, title, description }`. Null when it has none; `title` and `description` are null when the card carries only a link. |
| `quote_of` | string | The at:// address of the post this one quotes. Null when it quotes nothing, or when that post's author opted out of logged-out viewing, is excluded, or could not be checked. |
| `reply_to` | string | On a reply, the at:// address of the post it answers. Null on a top-level post, or when that post's author opted out of logged-out viewing, is excluded, or could not be checked. |
| `reply_root` | string | On a reply, the at:// address of the thread's first post. Null on a top-level post, or when that post's author opted out of logged-out viewing, is excluded, or could not be checked. |
| `reply_count` | integer | Replies when the row was read. |
| `repost_count` | integer | Reposts when the row was read. |
| `like_count` | integer | Likes when the row was read. |
| `quote_count` | integer | Quote posts when the row was read. |
| `bookmark_count` | integer | Bookmarks when the row was read. |
| `cid` | string | The content hash of the version of the post that was read. |
| `status` | string | `ok` on a post. On a free row without a post, the reason: see Output. |
| `row_type` | string | `ROW` for a post, `ITEM_STATUS` for a free row about one topic, `NO_POSTS_FOUND` or `STOPPED_EARLY` for the run. |
| `query` | string | On a free row, the topic it is about. |
| `reason` | string | On a free row, what happened. |
| `rowsFetched` | integer | On a run-level row, the rows the run had to write: posts within `maxItems` and free topic rows. |
| `rowsReturned` | integer | On a run-level row, how many rows this run wrote before this row, free topic rows included. |
| `rowsRemaining` | integer | On a run-level row, the rows not written because the charge limit was reached. |

### How much does it cost?

Three prices cover everything:

- **$0.004 per hour of Bluesky read** (`hour-scanned`). Charged after the read, for the hours actually read. A 24-hour look back is 24 events whatever your topics match.
- **$0.00088 per post returned** (`post-returned`). Charged after the row is written to your dataset.
- **$0.00005 per `apify-actor-start` event.** The platform's own start charge, one event per GB of memory. The default 1024 MB bills one.

| A daily run over 24 hours | Scan | Posts | Total |
|---|---|---|---|
| A niche brand, 60 posts | $0.096 | $0.0528 | about $0.15 |
| A busy topic, 1,000 posts | $0.096 | $0.88 | about $0.98 |

Run daily for a month, the scan costs $2.88 plus the posts. Paid Apify plans pay less per post: $0.00074 on Bronze, $0.0007 on Silver and $0.00058 on Gold and above.

What is never charged: the free row for a topic with no posts, the NO\_POSTS\_FOUND and STOPPED\_EARLY rows, a topic the run cannot read, and posts beyond your `maxItems` cap.

To keep a run cheap: pick the shortest look back your schedule needs. An hourly run with a 1-hour look back reads each hour once; so does a daily run with 24. A daily run with 25 reads one hour twice: that hour of overlap covers a late schedule, at the price of one extra `hour-scanned` event ($0.004) a day.

### How do I use Bluesky Scraper?

1. Open the Actor and go to the Input tab.
2. Type your topics: keywords, hashtags, accounts people mention, or domains people link to. One is enough.
3. Set how many hours to look back, from 1 to 36.
4. Press **Start**. The posts appear in the Output tab and in the run's dataset.

Example input:

```json
{
  "keywords": ["apify", "web scraping"],
  "hashtags": ["python"],
  "domains": ["github.com"],
  "lookbackHours": 24,
  "maxItems": 1000
}
```

Through the API:

```bash
curl -X POST "https://api.apify.com/v2/acts/Pradio~bluesky-mentions/runs?token=<YOUR_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"keywords": ["apify"], "domains": ["apify.com"], "lookbackHours": 24}'
```

### Input

| Field | Type | Default | What it does |
|---|---|---|---|
| `keywords` | array | none | Words or phrases, matched as whole words, case-insensitive. |
| `hashtags` | array | none | Hashtags, with or without the #. |
| `mentions` | array | none | Handles whose @mentions to track. Returns posts by anyone that mention the account. |
| `domains` | array | none | Domains whose links to track. Subdomains match too. |
| `lookbackHours` | integer | 24 | How far back to read, from 1 to 36 hours. |
| `languages` | array | none | Only posts tagged with one of these language codes. Empty returns every language. |
| `includeReplies` | boolean | true | Return replies as well as top-level posts and quotes. |
| `excludeAuthors` | array | none | Handles or account identifiers whose posts this run must not return. |
| `maxItems` | integer | 1000 | The most posts one run returns, newest first. At most 5000. |
| `maxItemsPerTopic` | integer | none | The most posts one topic returns, so a busy topic cannot use up the run's cap. |
| `minLikes` | integer | none | Only posts with at least this many likes when the row is read. |
| `minReposts` | integer | none | Only posts with at least this many reposts when the row is read. |

A post matching any topic in any field is returned once, with every topic it matched in `matched_by`. A domain you paste as a full URL is read as its domain. A value the run cannot read comes back as a free `bad_input` row naming it.

#### Mentions

A mention filter finds posts by anyone that tag the account with @. It is meant for the accounts of organisations, brands and projects, and for public accounts talking about their work. It is not for watching or profiling a private person, and there is no way to ask for one account's own posts.

#### Engagement minimums

`minLikes` and `minReposts` keep only the posts that reached a number of likes or reposts when the row is read. Set both and a post must meet both. A post under the minimum is not returned and not charged. It also does not count toward `maxItems` or `maxItemsPerTopic`: the run keeps reading matched posts, newest first, until the cap is filled or the matched posts run out. The scanned hours are charged either way, because the whole window is read before any post is counted.

```json
{
  "keywords": ["web scraping"],
  "lookbackHours": 24,
  "minLikes": 10,
  "maxItems": 200
}
```

When every post a topic matched falls under the minimums, the topic's free `no_posts` row says so.

### Output

- **Post rows**, newest first, each with `row_type: "ROW"` and every field above.
- **A free row for each topic with no post** in the window: `status: "no_posts"`, with the topic in `query`. It is written only when the whole window was read, so it means nothing matched, not that the run gave up. When a topic's only posts were withheld (their authors opted out, asked to be excluded, or deleted them), the row says that instead.
- **A free `bad_input` row** for a topic the run cannot read, and a free `not_found` row for a handle that does not exist.
- **NO\_POSTS\_FOUND**, one free row, when no topic matched anything.
- **STOPPED\_EARLY**, one free row, when your spending limit ends the run.
- **RUN\_SUMMARY** in the run's key-value store. Its counts are `rowsFetched`, `rowsPushed` (every row written, free rows included), `rowsCharged` (posts billed), `rowsUncharged` (free topic rows) and `duplicatesDropped`. The run-level rows in the dataset carry `rowsFetched`, `rowsReturned` and `rowsRemaining`, so you can read the totals without opening the store.

### Worked examples

Use it to track brand mentions once a day. A 24-hour look back on a daily schedule reads each hour once and leaves no gap.

```json
{
  "keywords": ["acme analytics"],
  "mentions": ["acme.bsky.social"],
  "domains": ["acme.com"],
  "lookbackHours": 24
}
```

Use it to collect a hashtag's best posts: every English post in the last day with at least 20 likes.

```json
{
  "hashtags": ["buildinpublic"],
  "languages": ["en"],
  "lookbackHours": 24,
  "minLikes": 20
}
```

Use it to see who shares your articles. Every top-level post linking to your site in the last 6 hours comes back with its link card.

```json
{
  "domains": ["example-news.com"],
  "includeReplies": false,
  "lookbackHours": 6
}
```

Use it to follow several topics at once without one crowding out the rest: at most 100 posts each.

```json
{
  "keywords": ["rust", "golang", "typescript"],
  "lookbackHours": 12,
  "maxItemsPerTopic": 100,
  "maxItems": 300
}
```

### What can you do with the data?

**Track a launch.** A product team runs it every morning with the product's name, its hashtag and its domain, and reads what people said yesterday.

**Measure an article's reach.** A publisher tracks its domain and sorts the day's rows by likes and reposts to see which stories travelled.

**Watch a project's mentions.** An open-source maintainer tracks the project's account and docs domain, and answers questions before they pile up.

**Study a topic.** A researcher collects the posts on a hashtag over 36 hours, with the time each was posted. The set is complete when the hashtag has no more posts than the `maxItems` cap, 5,000 at most per run.

### Use it with AI agents

Connect it to an assistant that speaks MCP:

```bash
claude mcp add --transport http apify "https://mcp.apify.com?tools=Pradio/bluesky-mentions"
```

Then ask for the posts that mentioned your brand yesterday, and the assistant runs it for you.

### Personal data

- **Transparency.** The rows are public Bluesky posts, read without logging in from Bluesky's public stream of posts and its public API. They identify people: the author's handle, display name, account identifier and the text they wrote.

- **Purpose.** Topic, brand, keyword and link monitoring and research. Not for watching, profiling or building a picture of a private individual.

- **You are the controller.** You decide what to collect and why. You need your own lawful basis, you owe the authors notice (or rely knowingly on the exemption for disproportionate effort), you must honour an author's request to delete their data, and you should keep rows no longer than your purpose needs.

- **Sensitive content.** Post text can reveal sensitive things about the author or others. Do not use the rows to infer or classify people by health, politics, religion, sexuality or similar. The Actor adds no such classification.

- **What is left out.** An author whose profile asks Bluesky to hide them from logged-out viewers is left out. A post deleted during the window, or no longer shown by Bluesky when the row is read, is not returned. No images or video are downloaded: a row carries an image's address and the author's alt text, as the post shows them. The link card is the one embedded in the author's post, with its address, title and short description as the post carries them; the Actor never opens the linked page. No profile details beyond the handle and display name are read. The reply and quote fields carry only the at:// address of another public post. That address includes the other author's account identifier, but no name, handle or text. It is null when that author opted out, is excluded, or could not be checked.

- **Opting out, and deletion on objection.** An author who objects can ask to be left out through this Actor's Issues tab in the Apify Console. The turnaround is the next release: the request is added to the Actor's exclusion list, which is applied on every run from its start. The `excludeAuthors` input does the same for your own list.

- **Nothing kept.** The Actor keeps nothing between runs. Your rows live in your Apify account.

### Release notes

- **0.1** (2026-09-24, updated 2026-09-25): first build, with images, link cards, quote and reply fields on every post, and minimum likes and reposts. Since 2026-09-25 `created_at` always comes in one format, UTC with milliseconds and a `Z`.

### Limits

- **36 hours back, no further.** Bluesky's public stream keeps about 36 hours. Older posts are out of reach.
- **Whole words, not meaning.** A keyword matches the word or phrase you type. It does not find synonyms or misspellings.
- **Languages as tagged.** The language filter reads the tags authors set. A post with no tag is dropped when you filter by language.
- **Engagement at read time.** Counts are read once, when the row is written. They keep changing after the run.
- **No author filter.** You cannot ask for one account's posts. This is deliberate.
- **The scan is the cost.** A run reads every post in the window whatever your topics match. A shorter look back costs less.

### Troubleshooting

**My topic got a `no_posts` row.** Nobody posted about it in the window. The whole window was read, so this is the answer, and it is free. Try a longer look back or a broader keyword.

**A post I can see on Bluesky is missing.** It may be older than your look back, its author may have opted out of logged-out viewing, its text may not contain your keyword as a whole word, or it may have fewer likes or reposts than your minimums.

**The run took about 15 minutes.** A 24-hour look back reads about 3.4 million posts. A 1-hour look back takes under a minute.

**The run ended with a STOPPED\_EARLY row.** Your spending limit was reached before every row was written. The row says how many rows were returned and how many were not. Raise the limit or shorten the look back.

### FAQ

**Can I use integrations with Bluesky Scraper?**
Yes. Send the rows to Google Sheets, Slack, Zapier, Make or a webhook from the Integrations tab, for example after every scheduled run.

**Can I use Bluesky Scraper with the Apify API?**
Yes. Start a run with the API call above and read the dataset through the API or a client library.

**Can I use Bluesky Scraper through an MCP server?**
Yes. Add it with the command in the AI agents section and an assistant can run it for you.

**Is it legal to collect Bluesky posts?**
The Actor reads public posts from Bluesky's own public stream and API, logged out, at the pace Bluesky asks for. Bluesky's terms leave authors in charge of their content, and the posts are personal data, so read the Personal data section above and check your own use.

### More from this publisher

- [Website Email Phone Contacts Scraper](https://apify.com/pradio/website-email-phone): the emails, phones and social profiles on the pages of the sites that mention you.
- [Broken Link Checker](https://apify.com/pradio/broken-link): every link on a page, checked, for the pages your mentions point to.
- [OCR Image to Text API](https://apify.com/pradio/image-text): the text inside images, for the pictures posts share.

### Not affiliated

Bluesky Scraper is an independent tool. It is not affiliated with, endorsed by or sponsored by Bluesky Social PBC. You choose the topics; it reads Bluesky's public posts on your behalf.

# Actor input Schema

## `keywords` (type: `array`):

Words or phrases to track, matched as whole words, case-insensitive. A post matching any topic in any field is returned.

## `hashtags` (type: `array`):

Hashtags to track, with or without the #.

## `mentions` (type: `array`):

Handles whose @mentions to track (brand.bsky.social). Returns posts by anyone that mention the account, never the account's own history.

## `domains` (type: `array`):

Domains whose links to track (example.com). A post linking to the domain or any subdomain matches, through its links, link card or text.

## `lookbackHours` (type: `integer`):

How far back to read, from 1 to 36 hours (the firehose keeps about 36). For a daily schedule, use 24: it reads each hour once. 25 adds an hour of overlap for a late schedule, at the price of one extra hour-scanned event.

## `languages` (type: `array`):

Only posts tagged with one of these language codes (en, de, ja). Empty returns every language.

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

Return replies as well as top-level posts and quotes.

## `excludeAuthors` (type: `array`):

Handles or DIDs whose posts this run must not return, one per line.

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

The most posts one run returns, newest first. Up to 5000.

## `maxItemsPerTopic` (type: `integer`):

The most posts one topic returns, newest first, so a busy topic cannot use up the run's cap. Empty means only maxItems applies.

## `minLikes` (type: `integer`):

Only return posts with at least this many likes when the row is read. A post under the minimum is not returned and not charged, and does not count toward Maximum items. Empty returns every post.

## `minReposts` (type: `integer`):

Only return posts with at least this many reposts when the row is read. Works with Minimum likes: a post must meet both. A post under the minimum is not returned and not charged.

## Actor input object example

```json
{
  "keywords": [
    "apify",
    "web scraping"
  ],
  "hashtags": [
    "python"
  ],
  "domains": [
    "github.com"
  ],
  "lookbackHours": 1,
  "includeReplies": true,
  "maxItems": 50
}
```

# Actor output Schema

## `rows` (type: `string`):

The post rows this run returned, one per post, plus any uncharged status rows.

## `summary` (type: `string`):

The run's counts: rows fetched, pushed, charged and uncharged, duplicates dropped, and whether it stopped early.

# 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 = {
    "keywords": [
        "apify",
        "web scraping"
    ],
    "hashtags": [
        "python"
    ],
    "domains": [
        "github.com"
    ],
    "lookbackHours": 1,
    "maxItems": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("pradio/bluesky-mentions").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 = {
    "keywords": [
        "apify",
        "web scraping",
    ],
    "hashtags": ["python"],
    "domains": ["github.com"],
    "lookbackHours": 1,
    "maxItems": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("pradio/bluesky-mentions").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 '{
  "keywords": [
    "apify",
    "web scraping"
  ],
  "hashtags": [
    "python"
  ],
  "domains": [
    "github.com"
  ],
  "lookbackHours": 1,
  "maxItems": 50
}' |
apify call pradio/bluesky-mentions --silent --output-dataset

```

## MCP server setup

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

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/Fq9ESinVc4Aw444Jk/builds/fPlKMaW2jTzdd5NsT/openapi.json
