# SourceBottle Journalist Requests Scraper (`automation-lab/sourcebottle-journalist-requests`) Actor

Export public SourceBottle journalist call-outs, topics and deadlines for PR expert-source matching. Filter categories and keywords without accessing private contacts.

- **URL**: https://apify.com/automation-lab/sourcebottle-journalist-requests.md
- **Developed by:** [Automation Lab](https://apify.com/automation-lab) (community)
- **Categories:** News, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.40 / 1,000 item extracteds

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

## SourceBottle Journalist Requests Scraper

Export public **SourceBottle journalist requests** for recurring PR expert-source matching. Collect call-out titles, summaries, publication labels, countries, posting dates, deadlines and request-page links. Choose editorial categories and optionally match topic keywords.

This Actor reads SourceBottle only. It does not pitch journalists, retrieve private contact information, submit responses, or collect Bluesky/Mastodon posts.

### Who is this for?

- PR consultants building a daily shortlist for clients.
- Subject-matter experts checking relevant public media opportunities.
- Communications teams comparing request IDs in their own spreadsheet or database.

### Why use it?

Keep public call-outs in structured rows instead of manually copying individual pages. Category selection narrows the source listing; keyword matching reduces the delivered shortlist. Request-ID deduplication prevents the same call-out appearing twice when it belongs to multiple selected categories.

The output preserves source dates rather than guessing timezones or silently discarding expired entries. A public publication label can be an agency, anonymous requester, or named outlet; it is not independently verified by this Actor.

### Quick start

1. Select one or more categories, or omit categories to scan all eleven editorial categories.
2. Choose a country listing. Australia is the default.
3. Optionally add topic keywords.
4. Set the global request limit and page cap.
5. Run and open the Requests dataset tab.
6. Export JSON, CSV, Excel, or connect your normal Apify integration.

Example input:

```json
{
  "categories": ["Business & Finance"],
  "country": "Australia",
  "maxItems": 3,
  "maxPagesPerCategory": 2
}
```

### Input parameters

| Parameter | Default | Behavior |
|---|---|---|
| `categories` | All eleven | Editorial categories, exact supported names below. Duplicates are removed. Empty selection is invalid. |
| `country` | Australia | Selects the public SourceBottle country listing, not a strict output-country text filter. |
| `keywords` | No filter | Up to 20 nonempty strings. ANY case-insensitive substring over title and summary; surrounding whitespace trimmed. |
| `maxItems` | 100 | Global accepted unique request limit, 1–1000, after filtering and deduplication. |
| `maxPagesPerCategory` | 10 | Listing-page cap per selected category, 1–50; stops sooner at exhaustion or global item limit. |

There is no zero/unlimited option. Limits are upper bounds, not promised output counts. Keyword matching has no word boundaries: `tax` can match a longer word containing those letters. Multiple keywords use ANY, not ALL. No title-only, linguistic stemming, exact-field equality, or hidden AI matching is applied.

Supported categories: Business & Finance; Environment; General; Health & Wellbeing; Lifestyle, Food & Fashion; Parenting & Education; Professional Services; Property; PR, Media & Marketing; Technology; Travel & Leisure.

Country listings: Australia, New Zealand, UK & Republic of Ireland, Canada, United States, Singapore. A request's own country text is preserved even when a regional listing contains international requests.

### Output fields

| Field | Meaning |
|---|---|
| `requestId` | Stable public SourceBottle call-out ID. |
| `title` | Request title. |
| `summary` | Public summary; null when absent. |
| `publication` | Public outlet/requester label; null when absent. |
| `country` | Country shown on the request; null when absent. |
| `postingDate` | Listing posting date in source text; null when absent. |
| `deadline` | Request deadline in source text, including timezone when supplied; null when absent. |
| `category` | First selected category where this request was found, not its exhaustive category membership. |
| `requestUrl` | Public request-page URL, not a private pitching/contact endpoint. |
| `scrapedAt` | UTC extraction timestamp. |

The default dataset contains one row per unique request ID. The SUMMARY key-value record contains request, byte, retry, scanned and saved counters. A partial dataset can remain if a later upstream request fails; a failed run is not a complete successful export.

### Example result

A current public call-out returned by the Actor:

```json
{
  "requestId": "163169",
  "title": "SMALL BUSINESSES to comment on tax changes + surcharge ban",
  "summary": "Small business owners in Australia who would be keen: - to comment on changes to trusts, tax and the surcharge ban - to pose up for a photo - happy to provide specifics (how much money will be lost through changes, how much more you will charge, expense cost increases etc)",
  "publication": "The Australian",
  "country": "Australia",
  "deadline": "24 October 2026 @ 5pm Eastern Daylight Saving Time",
  "postingDate": "23 October 2026",
  "category": "Business & Finance",
  "requestUrl": "https://www.sourcebottle.com/query.asp?qid=163169",
  "scrapedAt": "2026-10-25T15:07:17.196Z"
}
```

Public availability changes. This example is not a promise that the request is still accepting responses.

### How much does it cost to export SourceBottle journalist requests?

Pay-per-event pricing charges a **$0.002 start fee** plus each unique exported request. Filtered-out, duplicate and failed requests have no item charge. A run finding no matches still incurs the start fee.

| Apify Store spend tier | Price per request |
|---|---:|
| FREE | $0.0026795 |
| BRONZE | $0.00233 |
| SILVER | $0.0018174 |
| GOLD | $0.001398 |
| PLATINUM | $0.001398 |
| DIAMOND | $0.001398 |

At BRONZE, examples are $0.00433 for one request, $0.0253 for ten, and $0.235 for one hundred, including one start fee. Spend tiers follow your qualifying aggregate monthly Apify Store spend, not volume within this Actor. Check the Pricing tab for the tier applicable to your account. These examples estimate Actor event charges, not guaranteed final invoices.

### Pagination and coverage

The Actor visits selected categories in input order and listing pages in source order. It stops when the global accepted-result limit is reached. Each source page currently contains up to twelve requests. When category coverage or page caps stop collection, increasing maxItems alone will not add more results.

The source can list expired call-outs. This Actor does not infer deadline expiry, normalize source timezone strings, promise historical completeness, or guarantee every country listing is exclusive to that country. Samples, prizes and sponsorship requests are excluded from the category selector.

### Integrations

Export the dataset to Google Sheets for an editorial shortlist, send rows through Make or Zapier for team review, or load JSON into your CRM/data warehouse. Use requestId as the stable key when comparing scheduled runs.

Apify schedules can repeat the collection. Built-in only-new persistence, alerts and cross-run change detection are not provided; maintain those in your downstream system. Every scheduled invocation is independently billed for the rows it exports.

### API usage

Use your Apify token privately. Start the Actor once, retain the returned run ID, then inspect that same run until terminal status. Read results only after checking success.

```bash
curl -X POST 'https://api.apify.com/v2/acts/automation-lab~sourcebottle-journalist-requests/runs' \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"categories":["Technology"],"maxItems":10}'
```

### JavaScript client example

```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/sourcebottle-journalist-requests')
  .call({ categories: ['Technology'], maxItems: 10 });
if (run.status !== 'SUCCEEDED') throw new Error(run.status);
const { items } = await client.dataset(run.defaultDatasetId).listItems({ limit: 10 });
console.log(items);
```

Python:

```python
import os
from apify_client import ApifyClient
client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('automation-lab/sourcebottle-journalist-requests').call(
    run_input={'categories': ['Technology'], 'maxItems': 10})
if run['status'] != 'SUCCEEDED':
    raise RuntimeError(run['status'])
print(client.dataset(run['defaultDatasetId']).list_items(limit=10).items)
```

### MCP integration

For Claude Code:

```bash
claude mcp add --transport http apify \
  'https://mcp.apify.com?tools=automation-lab/sourcebottle-journalist-requests'
```

Claude Desktop, Cursor, and VS Code: equivalent desktop/editor configuration:

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

Discover tool names and schemas using scoped tools/list. Actor selection also adds run/dataset/key-value-store/abort helpers, not exactly one tool. Discovery is read-only and does not authorize execution.

Example prompt: “Collect up to ten SourceBottle Technology requests and show their titles, outlets, deadlines and links.” For an authorized call, start once and retain run/status/storage IDs: IDs are metadata, not result rows. call-actor waits at most waitSecs 0–45 seconds (default 30); allow client transport margin beyond the chosen wait. If pending, follow get-actor-run for the SAME ID with 2/4/8-second backoff capped at 10 seconds and a total 120-second deadline. At the deadline report pending and the ID, rather than starting again. On timeout recover the known ID; if unknown, report uncertainty without blind retry.

After success, use get-dataset-items with defaultDatasetId, explicit limit, offset and needed fields. Example consumer budget: 20-row pages, at most 100 source rows and 64 KiB serialized UTF-8 content admitted to model context, whichever comes first. Enforce bytes host-side before injection, disclose omissions/oversized rows, and track source offsets rather than transformed row counts. If the client cannot intercept oversized responses, do not claim a hard byte guarantee. Report continuation offset and partial status when incomplete; keep full exports outside model context. This context budget does not change the Actor's collection limits. Discovery caching and model-visible token behavior depend on the client; no numeric savings are promised.

### Legality and privacy

This independent tool is not affiliated with or endorsed by SourceBottle. It uses no AI at runtime. For support, open an issue on this Actor's Apify Store page.

Results and counters are retained in your Apify storage under your account's retention settings; delete datasets/KV records and runs from Console when no longer needed. The Actor has no persistent cookies, private contact cache, or external data recipient beyond Apify hosting and the disclosed private error service.

Use public request information only for lawful, relevant source matching. Respect the source's terms and applicable privacy rules. Respond through SourceBottle using its normal account workflow; the Actor does not bypass signup or submit pitches. Do not spam, infer private identities from anonymous labels, or treat publication labels as verified contact information.

Failed operations send sanitized diagnostic input, exceptions and Actor/build/run IDs to our private GlitchTip service for repair. Secret fields and URL queries are removed; reports are retained for 30 days.

### Troubleshooting and FAQ

**Why fewer records than maxItems?** Source exhaustion, keyword filtering, duplicate IDs, or the per-category page cap may limit results. Try a broader category or remove keywords.

**Why an expired deadline?** Source listings may retain old requests. Deadline strings are exported faithfully; assess current suitability before responding.

**Does it send email or retrieve contacts?** No. It exports public call-out fields and the public request link only.

**Can I provide arbitrary URLs?** No. This Actor accepts the documented categories and country listings only.

**What happens on a source error?** Network, 429 and server failures receive up to three total attempts with backoff. Permanent HTTP errors and unrecognized pages fail honestly. No paid proxy or browser fallback runs automatically.

### Related workflows

This is intentionally a standalone SourceBottle collection tool. General contact/domain finders do not replace public journalist requests. Use your own dataset comparison and CRM integrations for follow-up rather than assuming contact enrichment is included.

# Changelog

This Actor's version history is a separate document: https://apify.com/automation-lab/sourcebottle-journalist-requests/changelog.md

# Actor input Schema

## `categories` (type: `array`):

Public editorial categories to scan. Omit for all eleven. Duplicate requests across categories are returned once, labelled with the first selected category.

## `country` (type: `string`):

Selects the SourceBottle country listing via its public scid parameter. A request’s displayed country is preserved; this is not a strict country-text filter.

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

Optional ANY case-insensitive substring matching over title and summary. Whitespace is trimmed. No word boundaries or exact-field matching. Empty array means no keyword filter.

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

Global accepted unique request limit across selected categories, after keyword filtering. No zero or unlimited option. Pagination caps and source availability may yield fewer.

## `maxPagesPerCategory` (type: `integer`):

Safety cap on listing pages per category. Stops sooner at source exhaustion or global maxItems. Each listing page currently contains up to 12 requests.

## Actor input object example

```json
{
  "categories": [
    "Business & Finance"
  ],
  "country": "Australia",
  "maxItems": 3,
  "maxPagesPerCategory": 2
}
```

# Actor output Schema

## `dataset` (type: `string`):

Unique public SourceBottle request rows.

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

Request, transfer, retry and output counters.

# 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 = {
    "categories": [
        "Business & Finance"
    ],
    "maxItems": 3,
    "maxPagesPerCategory": 2
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/sourcebottle-journalist-requests").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 = {
    "categories": ["Business & Finance"],
    "maxItems": 3,
    "maxPagesPerCategory": 2,
}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/sourcebottle-journalist-requests").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 '{
  "categories": [
    "Business & Finance"
  ],
  "maxItems": 3,
  "maxPagesPerCategory": 2
}' |
apify call automation-lab/sourcebottle-journalist-requests --silent --output-dataset

```

## MCP server setup

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

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/yBIen7f0ibwL98Q8B/builds/nFqp25PUvXQdFL9Ua/openapi.json
