# AnnualReports.com Glossy Annual Report PDF Scraper (`jungle_synthesizer/annualreports-com-annual-report-pdf-scraper`) Actor

Scrape glossy annual report PDFs from AnnualReports.com, a free aggregator covering NYSE, NASDAQ, AMEX, OTC, TSX, LSE, AIM, and ASX issuers, including internationally headquartered firms. Extracts CEO/CFO/chairman names, financial highlights, and employee counts for equity research and IR/comp work.

- **URL**: https://apify.com/jungle\_synthesizer/annualreports-com-annual-report-pdf-scraper.md
- **Developed by:** [BowTiedRaccoon](https://apify.com/jungle_synthesizer) (community)
- **Categories:** Business, Other, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $8.00 / 1,000 record scrapeds

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

## AnnualReports.com Glossy Annual Report PDF Scraper

Scrape and parse glossy, designer-laid-out annual report PDFs from AnnualReports.com — a free aggregator covering NYSE, NASDAQ, AMEX, OTC, TSX, TSX-V, ASX, AIM, and LSE issuers, including internationally headquartered companies. Unlike SEC EDGAR (US-only, plain-text 10-Ks), AnnualReports.com hosts the marketing-piece annual report public companies send to shareholders — CEO letters, financial-highlights infographics, segment breakdowns, and (when disclosed) ESG detail.

### What it does

The actor discovers companies via one of four modes, visits each company's profile page for identity and header data (ticker, exchange, sector, industry, headquarters, employee count), then downloads and parses each qualifying report PDF to extract structured fields: CEO/CFO/chairman names (from the letter-to-shareholders signature), financial highlights (revenue, net income, diluted EPS, dividend per share), reported business segments, fiscal year end, and page count.

PDF text extraction uses `pdf-parse` for born-digital PDFs (the vast majority of reports on this source), with OCR fallback for the rare scanned/legacy document.

### Use cases

- **Equity research** — CEO/CFO changes, financial highlights, and segment mix across a ticker watchlist
- **IR / comp benchmarking** — pull headquarters, employee count, and financial highlights for peer-set analysis
- **Global coverage for non-SEC issuers** — companies headquartered outside the US (e.g. Ireland, UK, Australia) that don't file 10-Ks
- **Finance LLM training data** — designer-laid-out, magazine-style annual reports as a distinct document shape from plain-text filings

### Input

| Field | Type | Description | Default |
|-------|------|-------------|---------|
| `mode` | string | Discovery mode: `by_ticker`, `by_sector`, `by_country`, `recent` | `by_ticker` |
| `tickers` | array | Stock ticker symbols to look up (mode=`by_ticker`) | `["AAPL", "MSFT", "BRK.A"]` |
| `sectors` | array | Sectors to crawl (mode=`by_sector`). One of: Basic Materials, Conglomerates, Consumer Goods, Energy, Financial, Healthcare, Industrial Goods, Non Profits, Real Estate, Services, Technology, Utilities | `["Technology"]` |
| `countries` | array | Headquarters countries to filter for (mode=`by_country`), matched against each company's parsed HQ location | `["United States"]` |
| `yearFrom` | integer | Earliest report year to include (inclusive) | 2022 |
| `yearTo` | integer | Latest report year to include (inclusive) | 2025 |
| `includeFullText` | boolean | Include the extracted letter-to-shareholders text (a few KB) in each record | false |
| `maxItems` | integer | Maximum number of report records to return | 15 |

`by_country` routes through the site's exchange filters when the country maps to one (United States, Canada, United Kingdom, Australia); other countries fall back to a `maxItems`-bounded scan since the source has no direct country filter.

### Output

Each record represents one company's one report year:

| Field | Description |
|-------|-------------|
| `ticker` | Stock ticker symbol |
| `company_name` | Issuer legal/trading name |
| `exchange` | Primary listing exchange |
| `sector` | Business sector |
| `industry` | Business industry (finer-grained than sector) |
| `country` | Headquarters country |
| `report_year` | Fiscal/report year of this annual report |
| `report_fiscal_year_end` | Fiscal year end date as stated in the report (e.g. "December 31"), when found |
| `pdf_url` | Direct URL to the annual report PDF |
| `pdf_page_count` | Page count of the report PDF |
| `cover_image_url` | Report cover thumbnail (most recent report only) or company logo as fallback |
| `report_type` | Always "annual" for this source |
| `ceo_name` | CEO name, best-effort extracted from the letter-to-shareholders signature |
| `cfo_name` | CFO name, best-effort extracted from the letter-to-shareholders signature |
| `chairman_name` | Board chairman name, best-effort extracted (often the same person as CEO) |
| `headquarters_address` | Best-effort `{city, region, country}` — the source does not publish full street addresses |
| `financial_highlights` | Best-effort `{revenue, net_income, eps_diluted, dividend_per_share, unit}` parsed from the report's income statement |
| `segments` | Best-effort list of reported business segment names, when disclosed in a simple list form |
| `employee_count` | Employee count band from the company profile (e.g. 10000 for "10,000+ Employees") |
| `full_letter_to_shareholders` | Letter-to-shareholders excerpt (only populated when `includeFullText: true`) |

### Example

```json
{
  "mode": "by_ticker",
  "tickers": ["ETN", "WCN", "HRL"],
  "yearFrom": 2023,
  "yearTo": 2025,
  "maxItems": 20
}
```

Returns up to 20 report records across Eaton, Waste Connections, and Hormel Foods for report years 2023–2025.

### Notes

- Designer-laid-out, magazine-style reports have inconsistent text layout — financial-highlights infographics and multi-column pages don't always extract in reading order. `ceo_name`/`cfo_name`/`chairman_name`, `financial_highlights`, and `segments` are best-effort: populated when the source PDF exposes the underlying text in a recognisable shape, left empty otherwise. Measured fill rates on a 10-report sample across 4 companies: `ceo_name` 8/10, `cfo_name` 1/10 (most letters are CEO/Chairman-signed only, not CFO), `segments` 2/10 (most reports disclose segments in a financial-statement table rather than a simple list), `full_letter_to_shareholders` 5/10 when `includeFullText: true` (some report formats have no distinct letter section in the extracted text).
- Ticker symbols are not globally unique across exchanges (e.g. `WCN` matches both a NYSE waste-management company and an unrelated ASX mining company) — `by_ticker` mode keeps only the profile whose own displayed ticker exactly matches what you requested.
- Only the annual report PDF is in scope. Standalone sustainability/ESG reports and 10-K HTML filings, when the company publishes them separately from the annual report, are not included.

# Actor input Schema

## `sp_intended_usage` (type: `string`):

What will this data feed? E.g. lead lists, KYB checks, price tracking.

## `sp_improvement_suggestions` (type: `string`):

Provide any feedback or suggestions for improvements.

## `sp_contact` (type: `string`):

We'll personally help with your use case. No spam.

## `mode` (type: `string`):

How to discover companies. by\_ticker/by\_sector use the site's own filters; by\_country falls back to a full alphabetical scan when the country has no matching exchange.

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

Stock ticker symbols to look up (mode=by\_ticker). Matched against AnnualReports.com's own search.

## `sectors` (type: `array`):

Sectors to crawl (mode=by\_sector). Multiple sectors run in the same crawl.

## `countries` (type: `array`):

Headquarters countries to filter for (mode=by\_country). Matched against the company's parsed HQ location, not a site-side filter — free text since AnnualReports.com covers issuers worldwide with no fixed country list.

## `yearFrom` (type: `integer`):

Earliest report year to include (inclusive).

## `yearTo` (type: `integer`):

Latest report year to include (inclusive).

## `includeFullText` (type: `boolean`):

When true, includes the extracted letter-to-shareholders text (a few KB) in each record.

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

Maximum number of report records to return.

## Actor input object example

```json
{
  "sp_intended_usage": "Describe your intended use...",
  "sp_improvement_suggestions": "Share your suggestions here...",
  "sp_contact": "Share your email here...",
  "mode": "by_ticker",
  "tickers": [
    "AAPL",
    "MSFT",
    "BRK.A"
  ],
  "sectors": [
    "Technology"
  ],
  "countries": [
    "United States"
  ],
  "yearFrom": 2022,
  "yearTo": 2025,
  "maxItems": 15
}
```

# Actor output Schema

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

No description

# 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 = {
    "sp_intended_usage": "Describe your intended use...",
    "sp_improvement_suggestions": "Share your suggestions here...",
    "sp_contact": "Share your email here...",
    "mode": "by_ticker",
    "tickers": [
        "AAPL",
        "MSFT",
        "BRK.A"
    ],
    "sectors": [
        "Technology"
    ],
    "countries": [
        "United States"
    ],
    "yearFrom": 2022,
    "yearTo": 2025,
    "includeFullText": false,
    "maxItems": 15
};

// Run the Actor and wait for it to finish
const run = await client.actor("jungle_synthesizer/annualreports-com-annual-report-pdf-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 = {
    "sp_intended_usage": "Describe your intended use...",
    "sp_improvement_suggestions": "Share your suggestions here...",
    "sp_contact": "Share your email here...",
    "mode": "by_ticker",
    "tickers": [
        "AAPL",
        "MSFT",
        "BRK.A",
    ],
    "sectors": ["Technology"],
    "countries": ["United States"],
    "yearFrom": 2022,
    "yearTo": 2025,
    "includeFullText": False,
    "maxItems": 15,
}

# Run the Actor and wait for it to finish
run = client.actor("jungle_synthesizer/annualreports-com-annual-report-pdf-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 '{
  "sp_intended_usage": "Describe your intended use...",
  "sp_improvement_suggestions": "Share your suggestions here...",
  "sp_contact": "Share your email here...",
  "mode": "by_ticker",
  "tickers": [
    "AAPL",
    "MSFT",
    "BRK.A"
  ],
  "sectors": [
    "Technology"
  ],
  "countries": [
    "United States"
  ],
  "yearFrom": 2022,
  "yearTo": 2025,
  "includeFullText": false,
  "maxItems": 15
}' |
apify call jungle_synthesizer/annualreports-com-annual-report-pdf-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,jungle_synthesizer/annualreports-com-annual-report-pdf-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/ce9SKftK4R0WGmgGC/builds/JKbhpdFXOJRM1btDS/openapi.json
