# Stocktwits Watchlist Sentiment Monitor (`fetchfinch/stocktwits-watchlist-sentiment-monitor`) Actor

Monitor Stocktwits watchlists for bullish and bearish sentiment shifts, reversals, and message-volume spikes. Get compact, alert-ready signals for stocks and crypto.

- **URL**: https://apify.com/fetchfinch/stocktwits-watchlist-sentiment-monitor.md
- **Developed by:** [FetchFinch](https://apify.com/fetchfinch) (community)
- **Categories:** Social media, Automation, News
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 1,000 watchlist results

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Stocktwits Watchlist Sentiment Monitor

Turn a Stocktwits watchlist into alert-ready sentiment signals instead of another raw message feed.

This Apify Actor keeps rolling state between scheduled runs, measures Stocktwits' user-tagged bullish/bearish sentiment, and emits records only when something material changes:

- `SENTIMENT_REVERSAL` — sentiment crossed from meaningfully bullish to bearish, or vice versa.
- `SENTIMENT_SHIFT` — sentiment moved by the configured number of points.
- `MENTION_SPIKE` — rolling message volume exceeded its previous level by the configured multiplier.
- `BASELINE` — the first successful snapshot for a symbol.
- `ERROR` — a symbol failed while the rest of the watchlist continued.

The Actor is designed for Apify schedules, webhooks, Make, Zapier, Slack workflows, dashboards, and research pipelines.

### Why this Actor is different

Most Stocktwits Actors export messages. This one maintains a bounded rolling observation window in a named key-value store, incrementally fetches new messages, and produces compact symbol-level events with evidence.

Sentiment is transparent and reproducible:

```text
sentimentScore = 100 × (bullish tagged messages − bearish tagged messages)
                     / all tagged messages
```

The score ranges from `-100` (all bearish) to `100` (all bullish). Untagged messages count toward volume but not toward the sentiment score. `minTaggedMessages` prevents small samples from creating sentiment alerts.

### Quick start

Use input such as:

```json
{
  "symbols": ["AAPL", "TSLA", "NVDA", "BTC.X"],
  "monitorId": "growth-and-crypto",
  "windowMinutes": 60,
  "maxMessagesPerSymbol": 300,
  "outputMode": "signalsOnly",
  "thresholds": {
    "sentimentShiftPoints": 20,
    "reversalMinScore": 15,
    "mentionSpikeMultiplier": 2,
    "minMessagesForSpike": 10,
    "minTaggedMessages": 5
  }
}
```

The first run creates state and normally emits `BASELINE`. Schedule subsequent runs every 10–15 minutes using the same `monitorId`. A run with no material changes can intentionally have an empty dataset when `outputMode` is `signalsOnly`; its `OUTPUT` key-value record still contains the run summary.

Use a different `monitorId` for each independent task or watchlist. Reusing an ID intentionally reuses its rolling history.

### Example signal

```json
{
  "eventType": "SENTIMENT_REVERSAL",
  "eventTypes": ["SENTIMENT_REVERSAL", "MENTION_SPIKE"],
  "symbol": "AAPL",
  "observedAt": "2026-08-01T10:15:00.000Z",
  "windowMinutes": 60,
  "messageCount": 84,
  "previousMessageCount": 39,
  "messageCountDelta": 45,
  "volumeRatio": 2.15,
  "newMessages": 31,
  "taggedCount": 28,
  "bullishCount": 9,
  "bearishCount": 19,
  "bullishPercent": 32.14,
  "sentimentScore": -35.71,
  "previousSentimentScore": 27.27,
  "sentimentChangePoints": -62.98,
  "uniqueContributors": 66,
  "collectionCapped": false,
  "topMessages": [],
  "sourceUrl": "https://stocktwits.com/symbol/AAPL",
  "errorMessage": null
}
```

`eventType` contains the highest-priority event for simple filters. `eventTypes` preserves every threshold crossed in the same run.

### Input guidance

- `symbols`: Up to 50 stock or crypto symbols. Remove `$`; use Stocktwits' `.X` convention where applicable for crypto.
- `monitorId`: Persistent-state namespace. Keep it stable across scheduled runs.
- `windowMinutes`: Rolling aggregation window, from 5 minutes to 7 days.
- `maxMessagesPerSymbol`: Fetch and state cap. Increase it when `collectionCapped` is `true`.
- `outputMode`: `signalsOnly` for alerts or `allSnapshots` for time-series collection.
- `topMessages`: Number of high-engagement public messages attached as evidence.
- `resetState`: Starts a new baseline for the selected `monitorId`.
- `proxyConfiguration`: Optional Apify Proxy settings.

### Persistent state

State is stored in a named key-value store:

```text
stocktwits-monitor-<monitorId>
```

Each symbol retains only normalized messages inside the current rolling window, bounded by `maxMessagesPerSymbol`. A failed symbol does not overwrite its last good state. When at least one symbol succeeds, successful state updates are committed for the next run.

Symbols removed from the watchlist are also removed from that monitor's state on the next partially or fully successful run.

### Stocktwits access and proxies

The Actor uses Stocktwits' public web data endpoint and does not require a Stocktwits API key. That endpoint is undocumented for third-party automation and may change or apply Cloudflare protection without notice.

Direct requests are attempted unless proxy use is enabled. If output contains an HTTP 403/Cloudflare error, enable Apify Proxy. Residential proxy traffic is generally more reliable, but it costs more; begin with a small watchlist and conservative concurrency.

### Local development

```bash
npm install
npm test
apify validate-schema
apify run --input-file INPUT.json
```

Local direct requests may be blocked even if cloud or residential-proxy runs work. Unit tests use deterministic fixtures and do not contact Stocktwits.

### Recommended production setup

1. Create an Apify Task with a unique `monitorId`.
2. Run it once and inspect the baseline, tagged-message sample size, and `collectionCapped` flag.
3. Tune thresholds for the watchlist's normal activity.
4. Schedule it every 10–15 minutes.
5. Connect successful-run output to an integration, or call the dataset API from your automation.

### Responsible use

Stocktwits messages are public user-generated content. Sentiment labels are self-selected by authors, are not verified, and are not trading recommendations. Comply with Stocktwits' terms, applicable law, and your data-retention obligations. This Actor is not affiliated with Stocktwits and does not provide financial advice.

# Actor input Schema

## `symbols` (type: `array`):

Stocktwits symbols without the $ prefix. Crypto symbols commonly use Stocktwits' .X suffix, for example BTC.X.

## `monitorId` (type: `string`):

Stable ID used for persistent state. Use a different ID for each independent watchlist or schedule.

## `windowMinutes` (type: `integer`):

Only messages inside this rolling time window contribute to sentiment and volume metrics.

## `maxMessagesPerSymbol` (type: `integer`):

Collection and rolling-state cap per symbol. Increase this for high-volume symbols or long windows.

## `outputMode` (type: `string`):

signalsOnly keeps scheduled-run datasets alert-ready. allSnapshots also emits unchanged symbol snapshots.

## `emitBaseline` (type: `boolean`):

Write one BASELINE item per symbol the first time this monitor runs.

## `thresholds` (type: `object`):

Thresholds are evaluated against the previous successful snapshot for the same monitor ID.

## `topMessages` (type: `integer`):

Number of high-engagement messages included as evidence in each output item.

## `maxConcurrency` (type: `integer`):

Keep this conservative to reduce rate-limit risk.

## `maxRetries` (type: `integer`):

Retry rate limits, Cloudflare blocks, and transient server failures with exponential backoff.

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

Direct HTTP is attempted by default. If Stocktwits returns Cloudflare 403 responses, enable Apify Proxy; residential IPs are the most reliable fallback.

## `resetState` (type: `boolean`):

Discard this monitor's previous snapshots and create a fresh baseline.

## `failOnSymbolError` (type: `boolean`):

When disabled, failed symbols emit ERROR records while successful symbols continue.

## Actor input object example

```json
{
  "symbols": [
    "AAPL",
    "TSLA",
    "NVDA"
  ],
  "monitorId": "default-watchlist",
  "windowMinutes": 60,
  "maxMessagesPerSymbol": 200,
  "outputMode": "signalsOnly",
  "emitBaseline": true,
  "thresholds": {
    "sentimentShiftPoints": 20,
    "reversalMinScore": 15,
    "mentionSpikeMultiplier": 2,
    "minMessagesForSpike": 10,
    "minTaggedMessages": 5
  },
  "topMessages": 3,
  "maxConcurrency": 3,
  "maxRetries": 3,
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "resetState": false,
  "failOnSymbolError": false
}
```

# Actor output Schema

## `signals` (type: `string`):

Baseline, sentiment, volume-spike, and error records from the default dataset.

## `runSummary` (type: `string`):

Counts and per-symbol outcomes written to the OUTPUT key-value record.

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("fetchfinch/stocktwits-watchlist-sentiment-monitor").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("fetchfinch/stocktwits-watchlist-sentiment-monitor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{}' |
apify call fetchfinch/stocktwits-watchlist-sentiment-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=fetchfinch/stocktwits-watchlist-sentiment-monitor",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/FOOwT0i7XxKqpN58X/builds/nXFkweHfRSLheZy3s/openapi.json
