# Brand Monitoring – X, Reddit, News & Bluesky (`sfoln/brand-mentions-monitor`)

Monitor brand mentions on X (Twitter), Reddit, Google News, the web, Bluesky, Hacker News and your RSS feeds in one run: one row per mention with text, URL, author, date, engagement, language and sentiment, plus a per-brand summary. $1 per 1,000 mentions + $0.01 per brand.

- Family: Social media data
- Categories: Social media, News, Automation
- Availability: on request (private on Apify). Email contact@sfoln.com with subject "Access request: Brand Monitoring – X, Reddit, News & Bluesky (sfoln/brand-mentions-monitor)" before calling; calls fail until it is public.
- JSON: https://www.sfoln.com/actors/brand-mentions-monitor.json
- MCP: https://mcp.apify.com?tools=sfoln/brand-mentions-monitor
- REST (sync): POST https://api.apify.com/v2/actors/sfoln~brand-mentions-monitor/run-sync-get-dataset-items
- REST (async): POST https://api.apify.com/v2/actors/sfoln~brand-mentions-monitor/runs

Brand Monitoring – X, Reddit, News & Bluesky tracks who is talking about your brand, product or keyword across seven channels in one run: X (Twitter) posts, Reddit posts, Google News articles, Google web results, Bluesky posts, Hacker News stories and comments, and the RSS/Atom feeds you list. Every mention becomes one normalized row (source, title/text, URL, author, publish date, engagement) with its language and sentiment; the same mention found by two sources is delivered once. Each brand also gets a summary row: counts per source, sentiment split, volume by day, top domains and repeat authors. With onlyNew, scheduled runs deliver (and charge) only mentions you have not received before. Built for PR and comms teams, social listening, competitor tracking, reputation alerts and agency reporting.

## Price per delivered record

| Event | Price | Per 1,000 | Charged for |
| --- | --- | --- | --- |
| Brand mention (`brand-mention`) | $0.001 | $1 / 1,000 | Charged once per mention row delivered (an X post, Reddit post, news article, web page, Bluesky post, Hacker News story or comment, or feed item mentioning the brand). |
| Brand monitor query (`brand-monitor-query`) | $0.01 | $10 / 1,000 | Charged once per brand whose sources were queried; covers the per-call minimums of the first page of each paid source (also when nothing is found). |

Pay-per-event, USD, billed by Apify; no platform usage charge. Only records with status `ok` are charged. Cap a run with `maxTotalChargeUsd`. Apify free plan: up to 10 per run.

## Run

```bash
curl -X POST "https://api.apify.com/v2/actors/sfoln~brand-mentions-monitor/run-sync-get-dataset-items" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"brands":["Apify"]}'
```

MCP client configuration:

```json
{
  "mcpServers": {
    "apify": {
      "type": "http",
      "url": "https://mcp.apify.com?tools=sfoln/brand-mentions-monitor"
    }
  }
}
```

Example prompt: Find this week's mentions of the brand "Apify" on X, Reddit, Google News, the web, Bluesky and Hacker News, and summarize what people are saying and how positive it is.

## Input

List brand names or keywords. Each brand produces one row per mention found plus one summary row.

| Field | Type | Default | Meaning |
| --- | --- | --- | --- |
| `brands` (required) | array | — | Brands or keywords: Brand names or keywords, one per line. Multi-word names are searched as exact phrases; advanced search syntax (quotes, OR, -word) is passed through as written. Accounts on the Apify free plan can process up to 10 brands per run. |
| `sources` | array | `["x","reddit","news","web","bluesky","hackernews"]` | Sources: Where to look: x (X / Twitter posts), reddit (Reddit posts), news (Google News), web (Google web results), bluesky (Bluesky posts), hackernews (Hacker News stories and comments), rss (the feeds listed below). Default: all but rss; rss is added whenever feeds are given. |
| `feeds` | array | — | RSS / Atom feeds: Feed URLs (RSS, Atom, JSON Feed) or website / newsletter URLs whose feed is discovered automatically (up to 20). Their items that mention the brand become mentions (source rss). Each feed is read once per run for all brands. |
| `onlyNew` | boolean | `false` | Only new mentions: Deliver (and charge) only mentions that earlier runs did not deliver for the same brand. Built for schedules: the first run returns the current mentions, later runs only what is new. The memory lives in a key-value store named brand-mentions-monitor-memory in your account (30 days). |
| `maxPerSource` | integer | `20` | Max mentions per source: Upper limit of mentions per brand and source (10–100), newest / most relevant first. |
| `timeRange` | string | `"week"` | Time range: Only mentions from the last day, week, month or year (any = no limit). One of `"day"`, `"week"`, `"month"`, `"year"`, `"any"`. |
| `country` | string | `"us"` | Country (news & web): 2-letter country code for Google News and web results (gl), e.g. us, gb, de. |
| `language` | string | `"en"` | Language (news & web): Interface language for Google News and web results (hl), e.g. en, de. |
| `excludeDomains` | array | — | Exclude domains: Domains to leave out (e.g. your own site: apify.com). Applied to all sources and as -site: filters on web search; posts are never dropped for their own platform's domain (x.com, reddit.com, bsky.app, news.ycombinator.com: use Sources to leave a platform out). |
| `excludeRetweets` | boolean | `true` | Exclude reposts on X: Skip plain reposts (retweets) in X results. |
| `maxItems` | integer | `100` | Max brands: Safety cap on how many brands are processed in this run; the rest are ignored and counted in the OUTPUT record. |
| `concurrency` | integer | `2` | Concurrency: Brands processed in parallel (1–4). Sources of one brand are always queried in parallel. |
| `itemTimeoutSecs` | integer | `600` | Per-brand timeout (seconds): Time limit per brand; paging stops early when it is near. |
| `includeRaw` | boolean | `false` | Include raw upstream response: Attach the unmodified upstream item (tweet, Reddit post, search result, Bluesky post, Hacker News hit or feed item) to each mention row under `raw`. |

Example input:

```json
{
  "brands": [
    "Apify"
  ]
}
```

## Output

One record per input item with `status` (`ok` is charged; `not_found`, `error`, `skipped_*` are free) and `charged`. The run stores an `OUTPUT` summary record.

| Field | Type | Meaning |
| --- | --- | --- |
| `input` | string | object | Input: The input item this record corresponds to. |
| `status` | string | Status: ok (mention row charged brand-mention; summary row charged brand-monitor-query) \| error (all sources failed or nothing could be queried; not charged) \| skipped_budget \| skipped_free_plan |
| `charged` | boolean | Charged: Whether this record incurred a pay-per-event charge. |
| `chargedEvent` | string | Charged event: Pay-per-event event charged for this record. |
| `source` | string | Source: Mention rows: platform (x \| reddit \| news \| web \| bluesky \| hackernews \| rss). Summary rows: multi-source. |
| `fetchedAt` | string | Fetched at: ISO timestamp. |
| `error` | object | Error: code / message when status is not ok (or a partial failure). |
| `warnings` | array | Warnings: Non-fatal notes (partial data, stopped early, free-plan caps). |
| `recordType` | string | Record type: mention (one per mention found) or summary (one per brand). |
| `brand` | string | null | Brand: Brand / keyword the row belongs to. |
| `provider` | string | null | Provider: Mention rows: upstream (twitterapi, scrapecreators, serper, bluesky, hackernews, feeds). |
| `position` | integer | null | Position: Mention rows: 1-based position within its source for this brand. |
| `mentionId` | string | null | Mention id: X post id, Reddit post id, Bluesky at:// URI, Hacker News item id or feed item guid. |
| `title` | string | null | Title: Reddit post title, news headline, web page title, Hacker News story title (for comments: the story they belong to) or feed item title. |
| `text` | string | null | Text: Post text (X, Bluesky), self-text (Reddit), comment or story text (Hacker News), snippet (news / web) or feed summary / snippet around the brand (RSS); ≤ 1000 chars. |
| `url` | string | null | URL: Link to the mention. |
| `domain` | string | null | Domain: x.com, reddit.com, bsky.app, news.ycombinator.com or the publishing site. |
| `author` | string | null | Author: X handle, Reddit username, Bluesky handle, Hacker News user, news publisher or feed item author. |
| `authorName` | string | null | Author name: X or Bluesky display name. |
| `authorFollowers` | integer | null | Author followers: X author's followers. |
| `publishedAt` | string | null | Published: ISO timestamp. |
| `publishedAtIsEstimate` | boolean | null | Date is estimate: True when derived from a relative label ("3 hours ago"). |
| `language` | string | null | Language: ISO 639-1 language of the mention: the platform's own tag when it has one, else detected from the text. |
| `community` | string | null | Community: Subreddit (Reddit) or Hacker News item type (story, comment, ask_hn, show_hn, launch_hn, tell_hn). |
| `rank` | integer | null | Rank: Position in Google News / web results. |
| `engagement` | object | null | Engagement: { likes, reposts, replies, quotes, views } (X, Bluesky) or { score, comments } (Reddit, Hacker News). |
| `engagementTotal` | integer | null | Engagement total: likes + reposts + replies + quotes (X, Bluesky) or score + comments (Reddit, Hacker News); null for news, web and RSS. |
| `sentiment` | string | null | Sentiment: Mention rows: positive \| neutral \| negative (VADER, English text); null for other languages. |
| `sentimentScore` | number | null | Sentiment score: Mention rows: VADER compound score from -1 (most negative) to 1 (most positive). |
| `feedUrl` | string | null | Feed URL: RSS mentions: the feed the item came from. |
| `feedTitle` | string | null | Feed title: RSS mentions: the feed's title. |
| `sources` | array | Sources: Summary rows: sources queried. |
| `timeRange` | string | null | Time range: Summary rows: day \| week \| month \| year \| any. |
| `totalMentions` | integer | null | Total mentions: Summary rows: mention rows delivered for the brand. |
| `mentionsBySource` | object | null | Mentions by source: Summary rows: { x, reddit, news, web, bluesky, hackernews, rss } counts. |
| `topDomains` | array | Top domains: Summary rows: up to 10 { domain, count }. |
| `topAuthors` | array | Repeat authors: Summary rows: up to 10 { author, source, count } with more than one mention. |
| `newestMentionAt` | string | null | Newest mention: Summary rows: ISO. |
| `oldestMentionAt` | string | null | Oldest mention: Summary rows: ISO. |
| `totalEngagement` | integer | null | Total engagement: Summary rows: sum of engagementTotal. |
| `sourceStatus` | object | null | Source status: Summary rows: per source ok \| error \| skipped. |
| `stopReasons` | object | null | Stop reasons: Summary rows: why paging stopped per source (limit, end, budget, deadline, caught_up, error…). |
| `sentimentCounts` | object | null | Sentiment counts: Summary rows: { positive, neutral, negative, unscored } mentions (unscored: not English). |
| `averageSentiment` | number | null | Average sentiment: Summary rows: mean sentiment score of the scored mentions (-1 … 1). |
| `sentimentBySource` | object | null | Sentiment by source: Summary rows: sentiment counts per source with mentions. |
| `volumeByDay` | array | null | Volume by day: Summary rows: [{ date, count }] by UTC publish day, oldest first. |
| `topLanguages` | array | null | Top languages: Summary rows: up to 5 [{ language, count }]. |
| `duplicatesSkipped` | integer | null | Duplicates skipped: Summary rows: mentions found again (same canonical URL) by another source or page; not charged. |
| `nearDuplicatesSkipped` | integer | null | Near-duplicates skipped: Summary rows: the same article under another URL from another source (news, web, RSS); not charged. |
| `unrelatedSkipped` | integer | null | Unrelated hits skipped: Summary rows: Bluesky / Hacker News hits whose text does not contain the brand; not charged. |
| `onlyNew` | boolean | null | Only new: Summary rows: true when onlyNew was on. |
| `firstRun` | boolean | null | First run: Summary rows (onlyNew): no earlier run had remembered this brand. |
| `alreadyDeliveredSkipped` | integer | null | Already delivered: Summary rows (onlyNew): mentions met that an earlier run delivered (not charged; newest-first sources stop at the first one). |
| `raw` | object | null | Raw: Mention rows with includeRaw: the unmodified upstream item. |

Example record:

```json
{
  "input": "Apify",
  "recordType": "mention",
  "brand": "Apify",
  "source": "bluesky",
  "provider": "bluesky",
  "position": 1,
  "mentionId": "at://did:plc:example1/app.bsky.feed.post/3m1abcdxyz",
  "title": null,
  "text": "Moved our price monitoring to Apify last week and it has been fantastic so far.",
  "url": "https://bsky.app/profile/dataops-team1.bsky.social/post/3m1abcdxyz",
  "domain": "bsky.app",
  "author": "dataops-team1.bsky.social",
  "authorName": "Data Ops Team 1",
  "authorFollowers": null,
  "publishedAt": "2026-10-14T05:00:00.000Z",
  "publishedAtIsEstimate": false,
  "language": "en",
  "community": null,
  "rank": null,
  "engagement": {
    "likes": 14,
    "reposts": 3,
    "replies": 2,
    "quotes": 1,
    "views": null,
    "score": null,
    "comments": null
  },
  "engagementTotal": 20,
  "sentiment": "positive",
  "sentimentScore": 0.5574,
  "feedUrl": null,
  "feedTitle": null,
  "status": "ok",
  "charged": true,
  "chargedEvent": "brand-mention",
  "fetchedAt": "2026-10-14T10:00:02.000Z"
}
```

## FAQ

**How do I get only new mentions on a schedule?**

Turn on onlyNew and schedule the Actor (for example daily). The first run returns the current mentions; later runs deliver and charge only mentions that no earlier run delivered for the same brand.

**How is sentiment calculated?**

Locally with VADER, a lexicon built for English social-media text, so there is no AI fee. English mentions get positive, neutral or negative and a score from -1 to 1; other languages get null (their language is still detected).

**Can I monitor blogs and newsletters?**

Yes: list their RSS/Atom feeds or website URLs in feeds. Every item that mentions the brand in its title, summary or content becomes a mention with source rss.


## Related

- [Bluesky Scraper – Posts, Profiles & Search](https://www.sfoln.com/actors/bluesky-scraper.md) (`sfoln/bluesky-scraper`)
- [Hacker News Scraper – Stories & Comments](https://www.sfoln.com/actors/hacker-news-scraper.md) (`sfoln/hacker-news-scraper`)
- [RSS Feed Reader – Blogs, News & Newsletters](https://www.sfoln.com/actors/rss-feed-reader.md) (`sfoln/rss-feed-reader`)
- [Google News Scraper](https://www.sfoln.com/actors/google-news-search.md) (`sfoln/google-news-search`)
- [Reddit Posts Scraper (Subreddits)](https://www.sfoln.com/actors/reddit-subreddit-posts.md) (`sfoln/reddit-subreddit-posts`)

Publisher: SFOLN LLC, contact@sfoln.com. Catalogue: https://www.sfoln.com/catalog.json. Agent guide: https://www.sfoln.com/agents.md.
