# Journalist Request Finder: SourceBottle, Bluesky, Mastodon (`magenta_waterwheel/journalist-request-finder`) Actor

Fresh journalist and media requests from SourceBottle call-outs plus Bluesky and Mastodon #journorequest / #prrequest posts. Emails, deadlines, outlet and topic filters. HARO-style digest for PR. $0.003 per request.

- **URL**: https://apify.com/magenta_waterwheel/journalist-request-finder.md
- **Developed by:** [Magenta Waterwheel](https://apify.com/magenta_waterwheel) (community)
- **Categories:** News, Lead generation, Social media
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 journalist requests

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

## Journalist Request Finder: SourceBottle, Bluesky & Mastodon

**Journalist Request Finder** pulls fresh journalist and media requests into one table from:

- **SourceBottle** public call-outs (title, summary, outlet, country, deadline)
- **Bluesky** `#journorequest` / `#prrequest` / `#mediarequest` posts
- **Mastodon** public tag timelines for the same hashtags

Each row has the request text, author or outlet, link, any contact emails, links and a deadline hint. Filter by topic, schedule it daily, and turn on **Skip requests returned in earlier runs** so you only get (and pay for) new ones.

This is a lightweight **HARO-style** digest for PR freelancers and founders. Volume is modest: SourceBottle usually has a handful of open call-outs at a time, Bluesky adds roughly **5–15** genuine requests per day, and Mastodon `#journorequest` is often quiet (sometimes days without a new post). There is **no X / Twitter** source (X would need a paid API and is not included).

### Pricing

Pay per result:

- **$0.003 per request** saved to the dataset (after your filters).
- A tiny start fee of **$0.00005** per run.
- Filtered-out posts (not a request, wrong topic, already seen) are free.
- Example: a daily run that returns 20 new requests costs about **$0.06**.

### Who it's for

- PR agencies and freelancers pitching experts to journalists
- Founders and consultants looking for free press mentions and backlinks
- Anyone who used HARO-style digests and wants SourceBottle + social requests as they're posted

### What you get

Every row is one request:

- `source` (`sourcebottle` | `bluesky` | `mastodon`)
- `postedAt`, `author`, `handle`, `text`, optional `title`
- `outlet`, `country` (SourceBottle); `emails`, `links`, `hashtags`, `deadlineHint`
- `url`, `matchedTerms`, `isRequest`
- `likes`, `reposts`, `replies` (social sources)

A `RUN_SUMMARY` record in the key-value store lists sources enabled, posts scanned per source, unique posts and how many results were pushed.

### How to use it

1. Click **Try for free**.
2. Keep the default sources (SourceBottle + Bluesky + Mastodon), or drop any you don't need.
3. Keep the default search terms for Bluesky/Mastodon, or add your own.
4. Optionally set **Topic keywords** and **Only posts from the last N hours** to 24 for a daily schedule.
5. Turn on **Skip requests returned in earlier runs** so scheduled runs only bill for new requests.
6. Click **Start**, then download the results from the **Output** tab.

### Input example

```json
{
    "sources": ["bluesky", "mastodon", "sourcebottle"],
    "searchTerms": ["journorequest", "prrequest", "mediarequest", "journo request"],
    "sinceHours": 24,
    "topicKeywords": ["saas", "marketing"],
    "onlyRequests": true,
    "skipSeen": true,
    "maxResults": 200
}
```

### Output example

```json
{
    "source": "sourcebottle",
    "matchedTerms": ["sourcebottle"],
    "url": "https://www.sourcebottle.com/query.asp?qid=163216",
    "postUri": "sourcebottle:qid:163216",
    "author": "National news publication",
    "handle": "sourcebottle:163216",
    "postedAt": "2026-10-26T12:00:00.000Z",
    "title": "Shark expert on NSW's new attack prevention methods",
    "text": "Shark expert on NSW's new attack prevention methods\n\nLooking for a shark expert...",
    "outlet": "National news publication",
    "country": "Australia",
    "emails": [],
    "links": ["https://www.sourcebottle.com/query.asp?qid=163216"],
    "hashtags": [],
    "deadlineHint": "27 October 2026 @ 5pm Eastern Daylight Saving Time",
    "isRequest": true,
    "likes": 0,
    "reposts": 0,
    "replies": 0
}
```

### Tips and limits

- Bluesky public search returns up to the **100 latest posts per term** (first page only), so run at least daily.
- Mastodon volume for `#journorequest` is often thin; a run can correctly return **zero** Mastodon rows when nothing new is in your time window.
- SourceBottle call-outs are public listing pages; responding usually needs a free SourceBottle account. Contact emails are rarely in the public text.
- Many social posts say "DM me" instead of listing an email; leave **Only requests with an email** off unless you specifically need emails in the text.
- This Actor does **not** scrape X or paid HARO/Qwoted feeds.
- Use public content in line with applicable laws and each platform's terms.

### FAQ

#### Is this a HARO alternative?

It covers the same job — finding journalists who are asking for sources — from **SourceBottle**, **Bluesky** and **Mastodon**. It is not a full replacement for HARO or Qwoted.

#### Do I need accounts or API keys?

No. The Actor uses public SourceBottle pages, Bluesky's public search API and Mastodon public tag timelines.

#### Why so few Mastodon results?

The `#journorequest` tag on major instances is low-traffic, and public tag timelines often show posts that are weeks or months old. With the default **48-hour** window a run can correctly return **zero** Mastodon rows. Set **Only posts from the last N hours** to `0` (no limit) or a large value (for example `720`) if you want those older tags included. When `maxResults` is tight, the Actor reserves a fair share of rows per enabled source so fresher SourceBottle/Bluesky posts do not crowd Mastodon out entirely.

#### Can I monitor only new requests?

Yes. Turn on **Skip requests returned in earlier runs** and schedule the Actor daily in Apify Console.

If this saved you time, a quick rating on the Store helps a lot: [leave a review](https://apify.com/magenta_waterwheel/journalist-request-finder/reviews).

### More tools from the same developer

| Actor                                                                                       | What it does                                                                       | Price                     |
| ------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | ------------------------- |
| [Career Site Jobs API](https://apify.com/magenta_waterwheel/career-site-jobs-api)           | Jobs from Greenhouse, Lever, Ashby and SmartRecruiters career sites, with salaries | $2 / 1,000 jobs           |
| [App Store Reviews Scraper](https://apify.com/magenta_waterwheel/app-store-reviews-details) | Apple App Store reviews and app details in any country                             | $0.25 / 1,000 reviews     |
| [Website Screenshot & PDF API](https://apify.com/magenta_waterwheel/website-screenshot-pdf) | Full-page PNG/JPEG screenshots and web page to PDF in bulk                         | $2.50 / 1,000 screenshots |
| [Document & PDF to Markdown](https://apify.com/magenta_waterwheel/document-to-markdown)     | PDF, Word, PowerPoint, Excel and HTML to LLM-ready Markdown, with OCR              | $3 / 1,000 pages          |

# Actor input Schema

## `sources` (type: `array`):

Which sources to query. Defaults to SourceBottle + Bluesky + Mastodon. Use a subset to save time (e.g. only \["sourcebottle"]).

## `searchTerms` (type: `array`):

Bluesky search terms and Mastodon hashtags (without #). Ignored for SourceBottle. Defaults cover #journorequest, #prrequest and media requests.

## `sinceHours` (type: `integer`):

0 means no time limit. Bluesky returns up to 100 latest posts per term; Mastodon tag timelines and SourceBottle listing pages are scanned newest-first until this window is exhausted.

## `topicKeywords` (type: `array`):

Keep only requests mentioning at least one of these words, e.g. finance, nutrition, SaaS. Leave empty for all topics.

## `excludeKeywords` (type: `array`):

Drop requests mentioning any of these words.

## `onlyRequests` (type: `boolean`):

Drop Bluesky/Mastodon posts that mention the hashtag but don't read like a request. SourceBottle call-outs are always treated as requests.

## `requireEmail` (type: `boolean`):

Keep only requests that include an email address in the text. Many social posts say "DM me" instead; SourceBottle usually hides contact behind login.

## `skipSeen` (type: `boolean`):

Use with a daily schedule so you only get (and pay for) new requests.

## `maxResults` (type: `integer`):

Maximum number of requests to save in this run (after filters).

## Actor input object example

```json
{
  "sources": [
    "bluesky",
    "mastodon",
    "sourcebottle"
  ],
  "searchTerms": [
    "journorequest",
    "prrequest",
    "mediarequest",
    "journo request"
  ],
  "sinceHours": 48,
  "onlyRequests": true,
  "requireEmail": false,
  "skipSeen": false,
  "maxResults": 200
}
```

# Actor output Schema

## `requests` (type: `string`):

No description

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

No description

# 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 = {
    "sources": [
        "bluesky",
        "mastodon",
        "sourcebottle"
    ],
    "searchTerms": [
        "journorequest",
        "prrequest",
        "mediarequest",
        "journo request"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("magenta_waterwheel/journalist-request-finder").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 = {
    "sources": [
        "bluesky",
        "mastodon",
        "sourcebottle",
    ],
    "searchTerms": [
        "journorequest",
        "prrequest",
        "mediarequest",
        "journo request",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("magenta_waterwheel/journalist-request-finder").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 '{
  "sources": [
    "bluesky",
    "mastodon",
    "sourcebottle"
  ],
  "searchTerms": [
    "journorequest",
    "prrequest",
    "mediarequest",
    "journo request"
  ]
}' |
apify call magenta_waterwheel/journalist-request-finder --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,magenta_waterwheel/journalist-request-finder"
        }
    }
}
```

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/b7QoYmtkiCQbM13Aj/builds/PddCg3sFkPKeuE6aD/openapi.json
