# Journalist Requests Scraper (`axiomworks/journalist-requests-scraper`) Actor

Scrape current SourceBottle call-outs and public journalist requests from Bluesky and Mastodon. Get titles, request text, outlet, requester role, country, deadlines and social author details. Social results can be filtered by hashtag, keyword or age.

- **URL**: https://apify.com/axiomworks/journalist-requests-scraper.md
- **Developed by:** [Axiom Works](https://apify.com/axiomworks) (community)
- **Categories:** Social media, News
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.98 / 1,000 results

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

**Returns:** one row per journalist call-out with `source`, `title`, `text`, `outlet`, `requesterRole`, `country`, `deadlineDate` and `sourceUrl`.
**Input:** `{"sources": ["sourcebottle", "bluesky"], "hashtags": ["journorequest"], "maxItems": 50}`
**Best for:** PR people looking for media opportunities, expert sources answering journalist requests, and newsroom or agency monitoring of who is asking for quotes.

```json
{
    "source": "sourcebottle",
    "id": "sourcebottle:163038",
    "sourceUrl": "https://www.sourcebottle.com/query.asp?qid=163038",
    "title": "Anyone have ticket to fly out of Western Sydney Airport?",
    "outlet": "The Daily Telegraph",
    "requesterRole": "Journalist",
    "country": "Australia",
    "deadlineDate": "2026-10-18",
    "postedAt": "2026-10-16",
    "text": "The Daily Telegraph is hoping to speak to someone who has bought tickets to fly from Western Sydney Airport and is happy..."
}
```

### What does Journalist Requests Scraper do?

Journalist Requests Scraper is a journalist request scraper and call-out feed. It collects public requests from journalists, podcasters and PR professionals who are looking for expert sources, interviewees, case studies or product samples. It reads three public places: the SourceBottle call-out listings, the Bluesky hashtag search (for example `#journorequest`) and the Mastodon hashtag timeline on mastodon.social. No login is needed and nothing behind a login is read.

Every result is one call-out. SourceBottle call-outs come with a title, a summary, the media outlet, the requester role, the country, the response deadline and the date posted. Bluesky and Mastodon posts come with the post text, the date, the author name, handle and profile link, the hashtags, the language and the like, reply and repost counts. All sources are merged into one list, sorted newest first, and the same request is returned once even when it was cross-posted.

People use this journalist request API to answer call-outs in their niche before the deadline, to build a daily feed of media opportunities for a PR agency, to find which outlets are asking for sources on a topic, and to feed an AI agent that drafts pitches. Schedule it and turn on the "only new" option to get just the call-outs you have not seen before.

### What data can you get from SourceBottle, Bluesky and Mastodon?

The table lists every output field. Fields that belong to one network are null for the others: SourceBottle rows have no author or counters, social posts have no outlet or deadline.

| Field | Description | Example |
|---|---|---|
| `source` | Network the call-out came from: sourcebottle, bluesky or mastodon. | bluesky |
| `id` | Stable unique id: sourcebottle:{qid}, the Bluesky at:// URI, or mastodon:{status uri}. | sourcebottle:163038 |
| `sourceUrl` | Page of the call-out or post (SourceBottle call-out page, bsky.app post, Mastodon status). | https://www.sourcebottle.com/query.asp?qid=163038 |
| `title` | Headline of the SourceBottle call-out. Null for social posts. | Anyone have ticket to fly out of Western Sydney Airport? |
| `text` | What the requester is asking for: SourceBottle summary or the full post text. | The Daily Telegraph is hoping to speak to someone who has bought tickets... |
| `details` | Extra details paragraph of a SourceBottle call-out (only when details are included and the call-out has any). | Please also let us know your age. |
| `category` | SourceBottle industry the call-out is listed under. Null for social posts. | General |
| `outlet` | Media outlet or organisation of the SourceBottle requester. | The Daily Telegraph |
| `requesterRole` | Role shown for the SourceBottle requester, e.g. Journalist, Podcaster, PR Pro. | Journalist |
| `country` | Country of the SourceBottle call-out (needs details). | Australia |
| `verifiedMedia` | True when SourceBottle marks the requester as verified media (needs details). | true |
| `needsLocal` | Whether the source must be local, as stated on SourceBottle (needs details). | false |
| `deadline` | Response deadline text as shown by SourceBottle, with time and zone when details are included. | 18 October 2026 @ 5pm Eastern Daylight Saving Time |
| `deadlineDate` | Deadline date as YYYY-MM-DD, parsed from the deadline text. | 2026-10-18 |
| `postedAt` | When the call-out was posted: a date for SourceBottle, an ISO timestamp for social posts. | 2026-10-17T16:28:13.999Z |
| `authorName` | Display name of the social post author. | Elizabeth Blackstock |
| `authorHandle` | Handle of the social post author. | elizablackstock.bsky.social |
| `authorUrl` | Profile link of the social post author: the contact link for social call-outs. | https://bsky.app/profile/elizablackstock.bsky.social |
| `hashtags` | Hashtags used in the social post, without #. | \["journorequest", "USGP"] |
| `language` | Language code the post is tagged with. | en |
| `likeCount` | Likes (Bluesky) or favourites (Mastodon) when scraped. | 2 |
| `replyCount` | Replies when scraped. | 1 |
| `repostCount` | Reposts (Bluesky) or boosts (Mastodon) when scraped. | 4 |
| `matchedTag` | The hashtag whose search returned the post. | journorequest |
| `topicCategory` | AI-assigned SourceBottle-style category, or "not a media request". Only when AI enrichment is on. | Technology |
| `deadlineFromText` | AI-extracted deadline (YYYY-MM-DD) stated in a social post. Only when AI enrichment is on. | 2026-10-20 |
| `scrapedAt` | UTC time of the run. | 2026-10-18T00:11:57.404Z |

SourceBottle never shows the name of the requester, only the outlet and the role, so `authorName` and `authorHandle` are empty for SourceBottle rows. For social posts, `authorUrl` is the contact link. For SourceBottle rows, `sourceUrl` is the call-out page, where responding needs a free SourceBottle account.

### How to use Journalist Requests Scraper

1. Open the Actor in Apify Console and leave the defaults to get the 50 newest call-outs from SourceBottle, Bluesky and Mastodon, or pick the sources you want.
2. Choose hashtags for the social networks (without the `#`) and, for SourceBottle, the categories such as General, Technology, Property or Health & Wellbeing. You can also paste start URLs: a SourceBottle category page, a call-out page, a bsky.app hashtag page or a mastodon.social tag page.
3. Set the filters you need: keywords, the maximum age of social posts in days, and "only open" to drop SourceBottle call-outs whose deadline has passed.
4. Set Maximum results and start the run. The newest call-outs across all sources are kept until that number is reached.
5. Open the Output tab and export the dataset as JSON, CSV, Excel, XML or RSS, or read it through the API.

Turning off SourceBottle details saves one request per result, but omits country, details and verified media. Mastodon-only runs may return few results. Unsupported start URLs are skipped with a warning.

### Input

All fields are optional. Start URLs replace sources, hashtags and categories when given.

| Field | Type | Required | Description |
|---|---|---|---|
| `sources` | array | No | Networks to read: `sourcebottle`, `bluesky`, `mastodon`. Default: all three. |
| `hashtags` | array of strings | No | Hashtags without `#` for Bluesky and Mastodon. Default: journorequest, prrequest, mediarequest. |
| `sourceBottleCategories` | array of strings | No | SourceBottle category names or numeric ids. Default: all 12 categories. |
| `startUrls` | array of URLs | No | SourceBottle category or call-out pages, bsky.app hashtag pages, mastodon.social tag pages. |
| `keywords` | array of strings | No | Keep only call-outs whose title or text contains one of these words. |
| `maxAgeDays` | integer | No | Drop social posts older than this many days. Default 30. |
| `onlyOpen` | boolean | No | Drop SourceBottle call-outs whose deadline date is before today. Default false. |
| `includeDetails` | boolean | No | Open each SourceBottle call-out page for country, details, verified media and local requirement. Default true. |
| `onlyNew` | boolean | No | Skip call-outs returned by earlier runs, remembered in a named key-value store. Default false. |
| `enrichWithAI` | boolean | No | Add `topicCategory` and `deadlineFromText` with a third-party AI processor. Default false. |
| `excludeKeywords` | array of strings | No | Case-insensitive phrases to remove from title, text or details; default empty. |
| `monitorName` | string | No | Separate onlyNew history; default empty uses the original shared history. |
| `maxItems` | integer | No | Maximum results across all sources, 1-10000. Default 50. |
| `proxyConfiguration` | object | No | Proxy settings. Default: no proxy. |

```json
{
    "sources": ["sourcebottle", "bluesky", "mastodon"],
    "hashtags": ["journorequest", "prrequest"],
    "sourceBottleCategories": ["General", "Technology"],
    "keywords": ["startup"],
    "maxAgeDays": 14,
    "onlyOpen": true,
    "includeDetails": true,
    "maxItems": 100
}
```

The optional `excludeKeywords` array removes requests containing any supplied phrase in their title, text or details, ignoring case. For example, `"excludeKeywords": ["gift guide", "sponsorship"]` removes those topics from a broader industry feed. Exclusions are checked on the final record, including fetched SourceBottle details when available, before that record is pushed or charged. Excluded rows do not consume `maxItems`; the Actor continues through the collected candidates, although restrictive filters can yield fewer results than requested.

The optional `monitorName` string separates histories when `onlyNew` is enabled. A finance schedule can use `"monitorName": "finance-pr"`, while a health schedule uses `"monitorName": "health-pr"`. A request delivered by one named monitor can still be delivered by the other. Reusing the same name shares that monitor's history. Leaving the name empty preserves the original shared history, so existing schedules keep their behavior. Changing the name begins a separate history; it does not erase earlier histories. With `onlyNew` disabled the name has no effect. Histories retain the most recent 50,000 emitted IDs per monitor.

### Output

Two real items from a run, one per kind of source:

```json
[
{
    "source": "sourcebottle",
    "id": "sourcebottle:163038",
    "sourceUrl": "https://www.sourcebottle.com/query.asp?qid=163038",
    "title": "Anyone have ticket to fly out of Western Sydney Airport?",
    "outlet": "The Daily Telegraph",
    "requesterRole": "Journalist",
    "country": "Australia",
    "deadlineDate": "2026-10-18",
    "postedAt": "2026-10-16",
    "text": "The Daily Telegraph is hoping to speak to someone who has bought tickets to fly from Western Sydney Airport and is happy..."
},
{
    "source": "bluesky",
    "id": "at://did:plc:es3krp3lkhh475unifmgxwgc/app.bsky.feed.post/3my3j4bgjws2w",
    "sourceUrl": "https://bsky.app/profile/elizablackstock.bsky.social/post/3my3j4bgjws2w",
    "text": "wild #journorequest:\n\nDoes anyone have an image of Taylor Swift performing at the 2016 #USGP that I could use for a stor",
    "postedAt": "2026-10-17T16:28:13.999Z",
    "authorHandle": "elizablackstock.bsky.social",
    "authorUrl": "https://bsky.app/profile/elizablackstock.bsky.social",
    "hashtags": [
        "journorequest",
        "USGP"
    ],
    "likeCount": 2,
    "matchedTag": "journorequest"
}
]
```

The dataset can be downloaded as JSON, CSV, Excel, XML or RSS from the Output tab, or read with the API.

### How much does Journalist Requests Scraper cost?

The Actor is charged per result returned. You pay only for the results that end up in the dataset, and `maxItems` caps how many that can be. If you turn on AI enrichment, each result that was successfully enriched is charged as a separate event. See the Pricing tab for the current prices.

### Use with AI agents (MCP)

Connect Journalist Requests Scraper to Claude, Cursor or any MCP client through the Apify MCP server:

```json
{
    "mcpServers": {
        "apify": {
            "url": "https://mcp.apify.com/?tools=axiomworks/journalist-requests-scraper"
        }
    }
}
```

Example prompts:

1. "Get the 20 newest journalist requests from SourceBottle and Bluesky that mention startups and summarise who is asking for what."
2. "Find open SourceBottle call-outs in the Technology category and list the outlet, the deadline and the call-out link."
3. "Check #prrequest on Bluesky from the last week and list the posts that ask for expert sources, with the author profile links."

### Integrations and API

Run Journalist Requests Scraper from your own code with the Apify API. Set `APIFY_TOKEN` to your Apify API token (send it in the `Authorization` header, never in the URL).

**Python**

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_API_TOKEN>")
run = client.actor("axiomworks/journalist-requests-scraper").call(run_input={"sources": ["sourcebottle", "bluesky"], "hashtags": ["journorequest"], "maxItems": 50})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)
```

**JavaScript**

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_API_TOKEN>' });
const run = await client.actor('axiomworks/journalist-requests-scraper').call({ sources: ['sourcebottle', 'bluesky'], hashtags: ['journorequest'], maxItems: 50 });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

**cURL**

```bash
curl -X POST "https://api.apify.com/v2/acts/axiomworks~journalist-requests-scraper/run-sync-get-dataset-items" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"sources": ["sourcebottle", "bluesky"], "hashtags": ["journorequest"], "maxItems": 50}'
```

The Integrations tab connects the dataset to Make, Zapier, n8n, Google Sheets and webhooks. Combine a daily schedule with `onlyNew` to push new call-outs to Slack or email.

### FAQ

**Why did my run return fewer results than maxItems?**
Only as many call-outs exist as the filters allow. SourceBottle lists its current call-outs only, social posts older than `maxAgeDays` are dropped, and the same text posted on several networks is returned once. Raise `maxAgeDays` or add hashtags and categories.

**How fresh is the data?**
The Actor reads the live pages and APIs at run time. SourceBottle shows current call-outs, including some that expired a day or two ago; use `onlyOpen` to drop those. Social posts are as fresh as the network search index.

**Why is Mastodon almost empty?**
The Actor reads the hashtag timeline of mastodon.social, which only holds posts that reached that server. Volume on these hashtags is low compared with Bluesky.

**Why are `country` or `verifiedMedia` null?**
Those fields come from the SourceBottle call-out page. They are null when `includeDetails` is off and for social posts.

**Can I get the requester's email or phone number?**
No. SourceBottle requires a free login to respond and does not show contact data publicly, and this Actor never logs in. For social posts, `authorUrl` links to the author's public profile.

**What happens with a bad category, hashtag or URL?**
The run still succeeds with 0 or fewer results and a message in the log and the status line explaining what to change. Only a malformed URL or an invalid option value fails the run.

**Do I need a proxy?**
The sources are public and normally work without one. If SourceBottle blocks the run, enable Apify Proxy in the proxy settings; blocked requests are retried on a new IP automatically.

**How do I run it on a schedule?**
Create a schedule in Apify Console and enable `onlyNew`. Each run then returns only call-outs it has not returned before, tracked in the named key-value store `journalist-requests-seen`.

**Does the AI option change the other fields?**
No. It only adds `topicCategory` and `deadlineFromText`, which use a third-party AI processor and are billed separately per enriched result.

### Is it legal to scrape SourceBottle, Bluesky and Mastodon?

The Actor collects only publicly available data, with no login: public SourceBottle pages and the public search of Bluesky and Mastodon. Social posts are written by named people, so the author fields can be personal data. You are responsible for using them in line with the rules that apply to you, such as GDPR, and with each network's terms. This is not legal advice.

Journalist Requests Scraper is an independent tool. It is not affiliated with, endorsed by or sponsored by SourceBottle, Bluesky or Mastodon.

### Related Actors

| Actor | What it does |
|---|---|
| [Bluesky Profile Scraper](https://apify.com/axiomworks/bluesky-profile-scraper) | Collects public Bluesky profile data for the authors you find here. |
| [Reddit Lead Miner](https://apify.com/axiomworks/reddit-lead-miner) | Finds people asking for help or recommendations on Reddit. |

### Feedback

Found a bug or need a field that isn't here? Open an issue on the Issues tab and we'll look at it.

# Actor input Schema

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

Networks to read: 'sourcebottle' (call-outs by industry), 'bluesky' and 'mastodon' (hashtag search). Default: all three. Ignored when startUrls is set.

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

Hashtags without '#' searched on Bluesky and Mastodon, case-insensitive, e.g. 'journorequest', 'prrequest', 'mediarequest'. Each is searched separately and posts are returned once. Default: journorequest, prrequest, mediarequest.

## `sourceBottleCategories` (type: `array`):

SourceBottle industries to read, as names (e.g. 'General', 'Technology', 'Property', 'Health & Wellbeing') or numeric iids. An unknown name is skipped with a warning. Leave empty for all 12 industries.

## `startUrls` (type: `array`):

Optional. Replaces sources, hashtags and categories. Accepts SourceBottle category pages (industry-list-results.asp?iid=N), call-out pages (query.asp?qid=N), Bluesky hashtag pages (bsky.app/hashtag/tag) and mastodon.social tag pages (mastodon.social/tags/tag). Other URLs are skipped with a warning.

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

Keep only call-outs whose title or text contains any of these words (case-insensitive), e.g. 'startup', 'nurse'. For SourceBottle the match uses the listing title and summary. Leave empty to keep everything.

## `maxAgeDays` (type: `integer`):

Drop Bluesky and Mastodon posts older than this many days, 1-36500, default 30. SourceBottle shows only its current call-outs, so this does not apply to it.

## `onlyOpen` (type: `boolean`):

Drop SourceBottle call-outs whose deadline date is before today (UTC). Call-outs without a parseable deadline are kept. Default false.

## `includeDetails` (type: `boolean`):

Open each SourceBottle call-out page (about 1 request per second) to add country, details, verifiedMedia, needsLocal and the full deadline. If false, those fields are null and runs are faster. Default true.

## `onlyNew` (type: `boolean`):

Remember returned ids in the named key-value store 'journalist-requests-seen' and skip them on later runs. Use it for scheduled runs. Default false.

## `enrichWithAI` (type: `boolean`):

Adds topicCategory (a SourceBottle-style category, or 'not a media request') and, for social posts, deadlineFromText, using a third-party AI processor. Each enriched result is billed as a separate event. Default false.

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

Maximum number of call-outs returned in total across all sources, 1-10000, default 50. The newest are kept. You are charged only for results returned.

## `proxyConfiguration` (type: `object`):

Proxy for requests. Default: no proxy. Enable Apify Proxy only if SourceBottle blocks the run.

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

Skip requests whose title, text or details contain any of these phrases, case-insensitive. Applied before charging; leave empty to exclude nothing. Example: gift guide.

## `monitorName` (type: `string`):

Keep a separate onlyNew history for this named feed, e.g. finance-pr. Used only with onlyNew=true. Empty preserves the existing shared history; use different names for independent schedules.

## Actor input object example

```json
{
  "sources": [
    "sourcebottle",
    "bluesky"
  ],
  "hashtags": [
    "journorequest"
  ],
  "sourceBottleCategories": [
    "General"
  ],
  "keywords": [],
  "maxAgeDays": 30,
  "onlyOpen": false,
  "includeDetails": true,
  "onlyNew": false,
  "enrichWithAI": false,
  "maxItems": 10,
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "excludeKeywords": [],
  "monitorName": ""
}
```

# Actor output Schema

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

Dataset with one item per result, in the order found.

# 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": [
        "sourcebottle",
        "bluesky"
    ],
    "hashtags": [
        "journorequest"
    ],
    "sourceBottleCategories": [
        "General"
    ],
    "maxItems": 10,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("axiomworks/journalist-requests-scraper").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": [
        "sourcebottle",
        "bluesky",
    ],
    "hashtags": ["journorequest"],
    "sourceBottleCategories": ["General"],
    "maxItems": 10,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("axiomworks/journalist-requests-scraper").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": [
    "sourcebottle",
    "bluesky"
  ],
  "hashtags": [
    "journorequest"
  ],
  "sourceBottleCategories": [
    "General"
  ],
  "maxItems": 10,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call axiomworks/journalist-requests-scraper --silent --output-dataset

```

## MCP server setup

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

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/djlVCaHZksxSpUUdl/builds/AOSvENq2vsW2ft4df/openapi.json
