# News Sentiment Mini (`publicmoney/sentiment-mini`) Actor

Score the news around any ticker and set it against the price move: label, score, story counts, and every story's own score published so you can re-aggregate or feed your own model. Export data, run via API, schedule and monitor runs, or integrate with other tools.

- **URL**: https://apify.com/publicmoney/sentiment-mini.md
- **Developed by:** [Public Money](https://apify.com/publicmoney) (Apify)
- **Categories:** Business
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $1.00 / 1,000 records

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?

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

A sentiment score you cannot audit is a number you cannot use. This Actor scores the news around a ticker, sets the result against the actual price move in `agreesWithPrice`, and publishes every individual story's score rather than only the aggregate, so you can re-weight it, drop a publisher you distrust, or feed the raw scores to your own model.

### What it does

- Publishes **every story's own score**, not just the aggregate, so the number is auditable and re-aggregatable.
- Sets sentiment **against the actual price move** in `agreesWithPrice`, which is the check that tells you whether the score is saying anything.
- Returns the counts behind the label: `storyCount`, `positive` and `negative`, so a 0.2 from two stories is distinguishable from a 0.2 from forty.
- Gives a plain `label` next to the numeric `score`, so a dashboard can use one and a model the other.
- Reads the news through the fleet's own news Actors, so the story set is the same one the other Actors return.
- Takes **several tickers in one run**, with the depth per ticker under your control.

### Use cases

| You need to | How this Actor does it |
| --- | --- |
| Check whether sentiment is saying anything | Read `agreesWithPrice` before you trust `score` |
| Re-weight by publisher | Take the per-story scores and apply your own weights |
| Filter out thin coverage | Require `storyCount` above your threshold |
| Track a name over time | Schedule the run and chart `score` against the price |
| Feed your own model | Use the per-story scores as features rather than the aggregate |
| Feed a trading agent | Call the Actor over MCP and let the model ask for what it needs |

### Quick start

1. Click **Try for free**.
2. Add your tickers, one per line: `AAPL`, `MSFT`, `NVDA`.
3. Set **News per ticker**, which is how many stories are scored. More stories means a steadier score and a slower run.
4. Click **Start**. Rows appear within seconds.
5. Export as JSON, CSV, Excel or XML, or read the dataset over the API.

### Input

| Field | Type | Default | What it controls |
| --- | --- | --- | --- |
| `tickers` | array | `AAPL` | Tickers to score |
| `newsPerTicker` | integer | `10` | How many stories to score per ticker |
| `maxItems` | integer | `0` | Caps how many ticker records are returned |

```json
{
    "tickers": [
        "AAPL",
        "MSFT",
        "NVDA"
    ],
    "newsPerTicker": 10,
    "maxItems": 0
}
```

### Output

One dataset item per ticker, with the individual story scores nested inside it. A ticker with no news in range comes back with `storyCount: 0` rather than a fabricated neutral score.

| Field group | Fields |
| --- | --- |
| Instrument | `status`, `tickerSymbol`, `name` |
| Aggregate | `label`, `score`, `storyCount`, `positive`, `negative` |
| Cross-check | `changePercent`, `agreesWithPrice` |
| Per story | `stories` |

```json
{
    "status": "ok",
    "tickerSymbol": "AAPL",
    "name": "Apple Inc.",
    "label": "slightly negative",
    "score": -0.18,
    "storyCount": 10,
    "positive": 3,
    "negative": 5,
    "changePercent": -2.51,
    "agreesWithPrice": true
}
```

### Integrations

Run it over the API and get the rows back in one call:

```bash
curl -X POST "https://api.apify.com/v2/acts/publicmoney~sentiment-mini/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"tickers": ["AAPL", "MSFT", "NVDA"], "newsPerTicker": 10, "maxItems": 0}'
```

From Python:

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_TOKEN")
run = client.actor("publicmoney/sentiment-mini").call(run_input={"tickers": ["AAPL", "MSFT", "NVDA"], "newsPerTicker": 10, "maxItems": 0})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["tickerSymbol"], item["name"])
```

Give an AI agent the Actor over MCP:

```json
{
    "mcpServers": {
        "apify": {
            "url": "https://mcp.apify.com/?actors=publicmoney/sentiment-mini"
        }
    }
}
```

Schedules run it on any cron, webhooks fire when a run finishes, and platform integrations push the
dataset to Google Sheets, Slack, Airtable, Zapier or your own endpoint.

### Cost

Pay per event, so you pay for records rather than compute time.

| Event | Free tier | Top volume tier |
| --- | --- | --- |
| Instrument record | $0.01 | $0.004 |
| Actor start | $0.00005 per GB | Same |

Two things are charged. The **instrument record** above is this Actor's own event. On top of that it runs the fleet's news Actors as **child runs on your account**, and each charges its own per-record price, which will not appear in this Actor's usage total. **News per ticker** is the lever. A record that returned no data is never charged.

### Troubleshooting

| Issue | Solution |
| --- | --- |
| `storyCount` is 0 | No news in range for that ticker. The Actor reports the zero rather than inventing a neutral score. |
| `agreesWithPrice` is false and I expected true | That is the useful case. It means the news and the tape disagree, which is worth a look rather than a bug. |
| The score looks jumpy between runs | A small `storyCount` moves a lot on one story. Raise **News per ticker** for a steadier number. |
| The run costs more than the record price suggests | The child news Actors charge their own per-record price on your account, outside this Actor's usage total. |
| I disagree with a story's score | Every story's own score is published, so drop it and re-aggregate. That is why they are in the record. |

### FAQ

#### How is the score calculated?

Each story is scored, then the scores are aggregated into `score` and a plain `label`. The point of this Actor is that the per-story scores are published too, so you never have to take the aggregate on trust.

#### What does agreesWithPrice mean?

Whether the sentiment and the day's price move point the same way. It is the fastest check on whether a score is telling you anything, and a false is often more interesting than a true.

#### Can I use my own model instead?

Yes, and that is the intended use for anyone serious. Take the per-story scores as features and ignore the aggregate.

#### Why is it priced above the single-source Actors?

One record fans out into child runs against the news Actors, and those charge their own per-record price on your account.

#### Does it return historical sentiment?

No. Each record is current. Schedule it and let the dataset accumulate.

#### Do I need a the news it reads API key?

No. You need an Apify token to call the Actor over the API. No the news it reads credential is involved anywhere.

#### Can I get this data in Python?

Yes, with the `apify-client` package as shown above. It returns parsed JSON, so there is no HTML or response handling on your side.

#### Can I get the data into Excel or Google Sheets?

Yes. Export the dataset as XLSX or CSV, or connect the Google Sheets integration so each run appends to a sheet.

#### Can an AI agent call this Actor?

Yes. Add it to an MCP client with the config above and the model can request what it needs on its own. Every record is flat JSON with named fields, so no post-processing is needed.

#### Is it legal to scrape the news it reads?

This Actor reads public the news it reads data that needs no login and collects no personal data. Scraping public data is generally lawful, and how you store, redistribute or act on market data is governed by your own agreements and local rules. Take your own legal advice for your use case.

### Changelog

- **0.0.1** First release. Aggregate and per-story scores with a price cross-check.

### Feedback

Found a field the news it reads publishes that this Actor misses, or an input it rejects? Open an issue on the Issues tab with the input and what you expected. A daily test runs every Actor in the fleet against live sources, so parser fixes ship fast.

# Actor input Schema

## `tickers` (type: `array`):

Symbols to score, one per line. Examples: AAPL, TSLA, NVDA.

## `newsPerTicker` (type: `integer`):

How many recent stories to score per ticker.

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

Stop after this many tickers. 0 means all of them.

## Actor input object example

```json
{
  "tickers": [
    "AAPL",
    "TSLA"
  ],
  "newsPerTicker": 10,
  "maxItems": 0
}
```

# Actor output Schema

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

One item per requested input, in the default dataset.

# 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 = {
    "tickers": [
        "AAPL",
        "TSLA"
    ],
    "newsPerTicker": 10,
    "maxItems": 0
};

// Run the Actor and wait for it to finish
const run = await client.actor("publicmoney/sentiment-mini").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 = {
    "tickers": [
        "AAPL",
        "TSLA",
    ],
    "newsPerTicker": 10,
    "maxItems": 0,
}

# Run the Actor and wait for it to finish
run = client.actor("publicmoney/sentiment-mini").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 '{
  "tickers": [
    "AAPL",
    "TSLA"
  ],
  "newsPerTicker": 10,
  "maxItems": 0
}' |
apify call publicmoney/sentiment-mini --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,publicmoney/sentiment-mini"
        }
    }
}

```

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/fTkdkyK4EmPlOGnr4/builds/lL8m1BCcMHQXfQmLv/openapi.json
