# Simply Wall St Stock Analysis Scraper (`scrapyx/simplywallst-stocks-scraper`) Actor

Fundamental stock analysis from Simply Wall St: Snowflake score, valuation ratios (PE/PB/PEG/ROE/ROA), dividend data, ~30 fundamental checks and peer comparison. Look up exact stock URLs or screen ~177,700 companies by market cap and exchange.

- **URL**: https://apify.com/scrapyx/simplywallst-stocks-scraper.md
- **Developed by:** [Ibnu Adzim](https://apify.com/scrapyx) (community)
- **Categories:** Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.10 / 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/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

## Simply Wall St Stock Analysis Scraper

Scrapes fundamental stock analysis from [Simply Wall St](https://simplywall.st):
the **Snowflake score** (Value/Future/Past/Health/Income), valuation ratios
(P/E, P/B, PEG, ROE, ROA), dividend data, ~30-170 individual **fundamental
checks** with plain-English explanations, and peer comparisons — for any of
the ~177,700 companies the site covers.

Two ways in: paste exact stock URLs, or screen the whole universe by market
cap and exchange.

***

### What you get

One `STOCK` row per company. A `SEARCH_SUMMARY` row per screener query
(none for `stockUrls` — those are direct single-company fetches, not
searches).

#### Identity (always)

`id` · `company_id` · `name` · `slug` · `ticker_symbol` · `exchange_symbol` ·
`unique_symbol` · `canonical_url` · `primary_canonical_url` · `isin_symbol`

#### Full analysis (`stockUrls` always; screener rows when `includeFullAnalysis`, on by default)

`sector` · `country` · `currency` · `sharePrice` · `marketCap` · `peRatio` ·
`pbRatio` · `pegRatio` · `roe` · `roa` · `eps` · `debtToEquity` ·
`intrinsicDiscountPct` · `dividendCurrentYield` · `dividendFutureYield` ·
`snowflakeTotal` and the five named sub-scores (`snowflakeValue`,
`snowflakeFuture`, `snowflakePast`, `snowflakeHealth`, `snowflakeIncome`) ·
`snowflakeSummary` (one-sentence verdict, e.g. *"Outstanding track record
with excellent balance sheet."*) · `fundamentalChecks` (the full list of
individual checks — name, area, pass/fail, plain-English description) ·
`peerComparison` (the peer set Simply Wall St itself compares this company
against) — plus the raw `info` / `score` / `analysis` blocks these are
flattened from.

***

### Input

```jsonc
{
  "stockUrls": [
    "https://simplywall.st/stocks/us/tech/nasdaq-aapl/apple"
  ],
  "screenerQueries": [
    { "marketCapMinMillion": 1000000, "exchangeSymbol": "NasdaqGS" }
  ],
  "includeFullAnalysis": true,
  "includeFundamentalChecks": true,
  "maxItemsPerQuery": 20
}
```

#### `stockUrls` — the URL slug is NOT cosmetic

Copy exact URLs from simplywall.st or from a previous run's
`canonical_url`. Unlike several other paste-URL actors in this portfolio,
the trailing slug **is validated**: `.../nasdaq-aapl/apple` works,
`.../nasdaq-aapl/wrong-slug` answers a genuine 404 — it never silently
resolves to a different (wrong) company.

#### `screenerQueries` — market cap and exchange only

Each query supports two fields, and they're the only two confirmed to work:

- `marketCapMinMillion` / `marketCapMaxMillion` — in **millions of the
  listing currency** (not always USD).
- `exchangeSymbol` — an exact, case-sensitive string as Simply Wall St
  spells it: `NasdaqGS`, `NasdaqGM`, `NasdaqCM`, `NYSE`, `LSE`, `TSE`,
  `ASX`, `SEHK`, and others not listed here. There's no lookup endpoint to
  validate against — but an unrecognised value is **honest** (0 rows), not
  a silent fallback to the whole universe, so it's safe to experiment.

Every other filter name we tried — `market`, `country`, `sector`,
`industry`, `pe_ratio`, `dividend_yield`, `ticker_symbol`, `isin_symbol` —
answered `400 Invalid Rule`. This is a narrow discovery grid, not the full
screener behind the Simply Wall St app.

**`name` looks like a filter but does nothing at all.** Every operator we
tried (`like`, `contains`, `match`, `ilike`) returned the exact unfiltered
total. It is not offered as an input — sending it would look like a working
company-name search and silently return the whole catalogue instead.

#### Two toggles

- `includeFullAnalysis` (default **on**) — for screener-discovered
  companies, whether to also fetch the full analysis page. Off gives a fast
  identity-only list.
- `includeFundamentalChecks` (default **on**) — the checks list is the
  single largest field per row (up to ~75 KB on a heavily-covered mega-cap).
  It costs no extra request either way (it's part of the same page fetch);
  this toggle only controls whether it's kept in the output.

***

### Read this before trusting the numbers

**The 10,000-row result window is shared across the WHOLE ~177,700-company
index, not per query.** Bisected exactly: `offset=9999` still returns a row,
`offset=10000` returns zero — honest, not a clamp back to page 1. A scoped
query with its own total under that ceiling (e.g. `exchangeSymbol: "NYSE"`,
\~3,500 companies) can be paged to its real end. A broad, unfiltered query
never can — `totalMatches` can report 177,703 while only 10,000 rows are
ever reachable. `resultWindowCapped` and `reachableWithCurrentWindow` on the
summary row make the gap visible instead of silently truncating.

**`marketCap` is in the listing currency, not always USD.** Check the
`currency` field on the same row (only present with the full analysis pass)
before comparing market caps across markets.

***

### Known limits

**1. `analysis.extended` is deliberately dropped.** It carries the
underlying line-item financial-statement/valuation-model data behind the
curated ratios — measured at **~900 KB on Apple alone**, two to three
orders of magnitude larger than everything else on the row combined. Real
data, a different-shaped product; not half-parsed into this actor.

**2. News/events, individual analyst names, and full price-history are not
fetched at all.** Simply Wall St's detail page carries these too (as
separate cache entries in the same hydration blob), but they're
editorial/time-series data rather than fundamentals — a different product
from "this company's numbers at a glance."

**3. Same-page id collisions can happen.** One measured run: a 100-row
screener page yielded only 97 new ids (a handful were already seen, or
repeated within the page). The actor tops up rather than under-delivering —
`maxItemsPerQuery: 150` returns exactly 150 unique companies, occasionally
costing one extra request.

**4. Dual listings resolve to two different URLs.** A company listed on more
than one exchange (e.g. SimCorp on both LSE and Copenhagen) has a
`canonical_url` (the listing you asked about) and a `primary_canonical_url`
(its home listing) that can differ. This actor fetches exactly the URL you
gave it or the one the screener returned — it does not silently swap one
for the other.

***

### Anti-bot posture

**Per-surface TLS allowlist**, ladder-tested across 8 profiles on both
surfaces this actor fetches:

- Search/listing pages: 6/8 profiles clean.
- Stock detail pages: 4/8 profiles clean (a narrower subset of the above).
- The `/api/grid/filter` screener: all profiles tried came back clean —
  behaves like an ungated API surface, unlike the two HTML surfaces.

The actor uses the 4-profile intersection (`firefox133`, `safari17_0`,
`safari17_2_ios`, `safari184`) everywhere, since it is proven safe on every
surface fetched.

### Policy

`robots.txt` names no `ClaudeBot` / `anthropic-ai` / `GPTBot` group — only
generic `User-agent: *` and `User-agent: Googlebot-News`. It **disallows**
`/api/*` in general, `/_next/static/chunks/` (bundle JS — this actor never
fetches it at runtime, only used during recon), `/compare/*`, `/business`,
`/survey/`, `/playground/*` and `/narratives/new*` — none of which overlap
what this actor fetches. Notably, it carries an **explicit narrower
`Allow: /api/grid/filter`** inside the broader `/api/*` disallow — the site
itself is inviting exactly the screener route this actor uses.

# Actor input Schema

## `stockUrls` (type: `array`):

Exact simplywall.st company page URLs, e.g. 'https://simplywall.st/stocks/us/tech/nasdaq-aapl/apple'. Copy these from simplywall.st itself or from 'canonicalUrl' in a previous run's output — the last URL segment is NOT cosmetic on this site (unlike some other actors in this portfolio): a wrong slug answers a genuine 404, it does not silently resolve to the right company. Each URL always gets the full fundamentals pass (Snowflake score, valuation ratios, financial checks).

## `screenerQueries` (type: `array`):

Discover companies by market cap and/or exchange instead of (or as well as) pasting URLs. Each item: {"marketCapMinMillion": 100000, "marketCapMaxMillion": null, "exchangeSymbol": "NasdaqGS"}. All three fields are optional — an empty object screens the whole ~177,700-company universe, bounded by 'Max stocks per query'. marketCap is in MILLIONS of the LISTING currency (not always USD). exchangeSymbol is an exact, case-sensitive string as Simply Wall St spells it (examples: NasdaqGS, NasdaqGM, NYSE, LSE, TSE, ASX, SEHK) — there is no lookup endpoint to validate against, but an unknown value is honest (0 results), it does not silently return the wrong companies.

## `includeFullAnalysis` (type: `boolean`):

For companies found via 'Screener queries', also fetch the full analysis page (Snowflake score, valuation ratios, financial statement checks, peer comparison) — one extra request per company on a restricted TLS pool, each page 1-2 MB. Turn off for a fast identity-only list (name, ticker, exchange, canonical URL). Stock URLs you paste directly always get the full pass regardless of this setting.

## `includeFundamentalChecks` (type: `boolean`):

Simply Wall St runs ~30 individual pass/fail checks per company (experienced management, seasoned board, dividend coverage, debt levels, and more), each with a plain-English explanation — this is fetched as part of the same page as everything else (no extra request), but it is the single largest field per row. Turn off to drop it from the output and keep rows smaller; the Snowflake score totals and valuation ratios are unaffected either way.

## `maxItemsPerQuery` (type: `integer`):

Stop each screener query after this many companies. Simply Wall St's screener shares one Elasticsearch-style result window across the WHOLE ~177,700-company index: offset+size cannot exceed 10,000, regardless of how narrow your own filter's total is (verified by bisection). A broad, unfiltered query can therefore never be crawled to completion — the summary row reports 'resultWindowCapped' and 'reachableWithCurrentWindow' when that happens. Set to 0 to fetch as many as the window allows.

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

How many requests may be in flight at once. This absorbs latency variance; it is NOT the throttle — the crawl rate is set by 'Minimum interval between requests' below.

## `minRequestInterval` (type: `number`):

The actual speed control: the shortest gap between two requests, across all workers. Detail pages are 1-2 MB each on a TLS pool that is clean but narrow (4 of 8 profiles ladder past both surfaces), so this actor paces more cautiously than a pure-JSON-API one by default.

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

Residential with no country pin is the cloud default. Neither surface's data depends on the exit IP's geography (the screener filters and each company's canonical URL do all the scoping), so a wide, unpinned pool is kept for recovering a block rather than narrowed to a region.

## Actor input object example

```json
{
  "stockUrls": [
    "https://simplywall.st/stocks/us/tech/nasdaq-aapl/apple"
  ],
  "screenerQueries": [
    {
      "marketCapMinMillion": 1000000,
      "exchangeSymbol": "NasdaqGS"
    }
  ],
  "includeFullAnalysis": true,
  "includeFundamentalChecks": true,
  "maxItemsPerQuery": 20,
  "maxConcurrency": 4,
  "minRequestInterval": 0.4,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `items` (type: `string`):

One row per scraped record. See the dataset's default view for field definitions.

# 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 = {
    "stockUrls": [
        "https://simplywall.st/stocks/us/tech/nasdaq-aapl/apple"
    ],
    "screenerQueries": [
        {
            "marketCapMinMillion": 1000000,
            "exchangeSymbol": "NasdaqGS"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapyx/simplywallst-stocks-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 = {
    "stockUrls": ["https://simplywall.st/stocks/us/tech/nasdaq-aapl/apple"],
    "screenerQueries": [{
            "marketCapMinMillion": 1000000,
            "exchangeSymbol": "NasdaqGS",
        }],
}

# Run the Actor and wait for it to finish
run = client.actor("scrapyx/simplywallst-stocks-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 '{
  "stockUrls": [
    "https://simplywall.st/stocks/us/tech/nasdaq-aapl/apple"
  ],
  "screenerQueries": [
    {
      "marketCapMinMillion": 1000000,
      "exchangeSymbol": "NasdaqGS"
    }
  ]
}' |
apify call scrapyx/simplywallst-stocks-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapyx/simplywallst-stocks-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/1xCpRZbd0Em9bF8OB/builds/ofn0hwbL3fKep8H1n/openapi.json
