# EU Annual Reports Search (`thesjostrandproject/eu-annual-reports-search`) Actor

Search EU listed-company annual reports (ESEF) from an open filing index by LEI, country, period end or name. One row per filing: LEI, name, period, error counts, report links. No financial figures or report text. API, pay per record.

- **URL**: https://apify.com/thesjostrandproject/eu-annual-reports-search.md
- **Developed by:** [The Sjöstrand Project](https://apify.com/thesjostrandproject) (community)
- **Categories:** Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$3.00 / 1,000 filing records

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## EU Annual Reports Search

### What this Actor returns

Search the annual financial reports that EU listed companies file in the European Single Electronic Format (ESEF), by company LEI, country, financial period end or company name, and get one clean, typed record per filing. Every record has the same 10 fields, and a value the filing index does not have is `null`, never a missing key.

- `filing_id`, `lei`, `entity_label`, `country`: which filing it is, and the company's LEI, name and country
- `period_end`, `processed_at`: the last day of the financial period, and the date the index processed the filing
- `error_count`, `warning_count`: how many validation errors and warnings the index found in the filing
- `report_package_url`, `report_json_url`: links to the report package as filed and to its processed JSON

The Actor reads the public API of one filing index and nothing else. It returns filing data and links only. It does not open the reports, so it returns no financial figures and no report text, and nothing from inside a report, such as the names of directors or auditors, ever reaches the output.

### Who it helps

- **Analysts and investors** who want a list of which listed companies filed which annual reports, and where the report files are, ready for a spreadsheet or their own pipeline.
- **Developers and compliance teams** who want to check that a company's latest annual report is in the index, or who want the link to a filing's processed JSON, through the Apify API.

### Quick start

1. Open the Input tab and leave the defaults as they are. They search the filings of one company, identified by its LEI, for financial periods ending in 2023 and 2024, so a first run returns 2 records.
2. Press Start. The default run costs about $0.006: 2 records at $0.003 each, plus Apify's tiny start event (see What it costs). Try it on one record first by setting Maximum records to 1.
3. Open the Output tab, check the records, then set your own LEIs, country, dates or company name.

### Input

| Field | What it does | Default |
|---|---|---|
| Company LEIs | Only filings of these companies, as 20-character LEI codes, one per entry. Up to 50. | `96950020QIOHAAK9V551` |
| Country | A two-letter country code like `FR`, `SE` or `NL`. | empty |
| Financial period ends from | Only reports whose period ends on or after this date. | `2023-01-01` |
| Financial period ends to | Only reports whose period ends on or before this date. | `2024-12-31` |
| Company name contains | Text the company name must contain, in any letter case. | empty |
| Maximum records | How many records to return at most, from 1 to 1000. | 10 |

All the filters you fill in are combined: a filing must match every one. Results come with the latest financial period first, as the index reports it. The index's `period_end` can be wrong, and the live index holds some dates in the future, so the first records of an unfiltered search may be bogus; set Financial period ends to a date that has passed if you want to leave them out.

### Output

One record per filing. The record below is the first one the default input returned on 13 October 2026, unchanged, except that the two link fields are described here instead of shown, because Store pages cannot hold links. In a real record they are the full links to the files.

```json
{
  "filing_id": "96950020QIOHAAK9V551-2024-12-31-ESEF-FR-0",
  "lei": "96950020QIOHAAK9V551",
  "entity_label": "SOPRA STERIA GROUP",
  "country": "FR",
  "period_end": "2024-12-31",
  "processed_at": "2025-09-24",
  "error_count": 0,
  "warning_count": 0,
  "report_package_url": "(the link to the report package, a ZIP file)",
  "report_json_url": "(the link to the processed JSON)"
}
```

### What it costs

You pay for one event, **Filing record**, at $0.003 per record returned. Apify also adds its standard run-start event, $0.00005 per start for an Actor run with up to 1 GB of memory; it is the platform's own event, not ours. The default run returns 2 records and costs about $0.006. Records the Actor leaves out (see below) are never charged.

For example, a search that returns 200 filings costs $0.60. If your Apify plan includes monthly credit, a small run draws on that credit.

If the source cannot be reached or keeps answering with errors, the run stops with a clear status message, and only the records already returned are charged.

### Limits and honest notes

- **The index is not complete.** The operator says the index does not hold every filing: a technical error can prevent the viewer and JSON from being produced, and a filing whose LEI or date cannot be worked out is left out of the index altogether. The operator also says that for some countries, Germany and Ireland among them, filings are not available in a way that lets it find and download them reliably, so those countries have gaps or nothing. A search can therefore return fewer filings than a company has filed.
- **Not only EU filings.** The index also holds filings from outside the EU, for example the United Kingdom and Ukraine. Filings of companies without an LEI are left out of your results and are not charged.
- **Filing data and links only.** No financial figures, no report text, and no names of directors or auditors. The Actor does not open the report files.
- **Dates are the index's.** `period_end` comes from the source and can be wrong: it is inferred by the index from the facts tagged in the report, and the live index holds some period ends in the future, for example 2031-03-01. The Actor does not correct or remove them. `processed_at` is when the index processed the filing, not when the company filed it. When a company has several reports for one period, such as translations or amendments, each is its own record and the index does not say how they relate.
- **Errors and warnings** are the index's own validation counts. A filing with errors may have no processed JSON, and then `report_json_url` is `null`.
- **Up to 1000 records per run.** For more, split the search by date range or country.
- **A scan reads at most 25 pages.** When most results are left out, the Actor can stop after 25 pages before it reaches your Maximum records. The status message then says `search_stopped_at_25_pages` with how many matches it did not reach; narrow the search to get the rest.
- **Pace.** The source states no rate limit and says it may add one. When the source is busy it waits as asked, up to about a minute per request, and then stops with a clear message. The Actor sends at most one request every two seconds.
- **Freshness** follows the source, which says there may be a significant delay between a company submitting a filing and the filing being added to the index.

### Questions

**Why did my run return fewer records than I asked for?**
Either fewer filings matched, or some were left out because the company has no LEI or the filing lacked a name, a country or a date. You were not charged for those.

**What does the run say when something is wrong?**
It says what it found and what to try. A message about an LEI, a country code or a date names the field to fix and shows the right shape. "Could not be reached" or "kept answering" means the source is busy; wait a few minutes and run again.

**Where do I find a company's LEI?**
An LEI is a 20-character code that identifies a legal entity, such as a listed company. Public LEI registers list it for each company.

**How do I find a company's most recent report?**
Put its LEI in the Company LEIs list, set Financial period ends to a date that has passed, and read the first records. Results come with the latest period first, but `period_end` comes from the source and can be wrong, so check the date and the filing before you rely on it.

**Can I run it on a schedule?**
Yes, with the Apify scheduler or the API. Each run is independent and charged per record returned.

### Data source and AI note

Data source: the public filing index and API run by XBRL International, which republishes the annual reports that EU listed companies file with their countries' officially appointed filing systems. The index states no licence name; its about page says that at present there are no restrictions on the ways the data can be used. The site footer reads "All Rights Reserved". The underlying filings belong to the filers and to national filing systems, and their reuse terms differ by country; check them before you reuse a report itself. The records have no source_updated_at field: `processed_at` is the date the index processed the filing.

This tool is not affiliated with, endorsed by or sponsored by XBRL International or any national filing system.

This tool was built with the help of AI coding assistants.

### Help

Questions, a record that looks wrong, or an idea for a field: please open an issue on the Issues tab. Thank you for the report; every one is read.

# Actor input Schema

## `leis` (type: `array`):

Only filings of these companies, given as 20-character LEI codes, one per entry, like 96950020QIOHAAK9V551. Up to 50. Leave empty for any company.

## `country` (type: `string`):

Only filings from this country, as a two-letter code like FR, SE or NL. Leave empty for any country.

## `periodEndFrom` (type: `string`):

Only reports whose financial period ends on or after this date (YYYY-MM-DD). Leave empty for no lower bound.

## `periodEndTo` (type: `string`):

Only reports whose financial period ends on or before this date (YYYY-MM-DD). Leave empty for no upper bound.

## `entityName` (type: `string`):

Only companies whose name contains this text, in any letter case, like steria. The characters % and \_ are not allowed. Leave empty for any name.

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

How many filings to return at most. You pay only for records returned.

## Actor input object example

```json
{
  "leis": [
    "96950020QIOHAAK9V551"
  ],
  "country": "",
  "periodEndFrom": "2023-01-01",
  "periodEndTo": "2024-12-31",
  "entityName": "",
  "maxItems": 10
}
```

# Actor output Schema

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

The filings of this run, one per filing, with the 10 fields described in the dataset schema (id, company LEI and name, country, period end, processing date, error and warning counts, report links).

# 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 = {
    "leis": [
        "96950020QIOHAAK9V551"
    ],
    "country": "",
    "periodEndFrom": "2023-01-01",
    "periodEndTo": "2024-12-31",
    "entityName": "",
    "maxItems": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("thesjostrandproject/eu-annual-reports-search").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 = {
    "leis": ["96950020QIOHAAK9V551"],
    "country": "",
    "periodEndFrom": "2023-01-01",
    "periodEndTo": "2024-12-31",
    "entityName": "",
    "maxItems": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("thesjostrandproject/eu-annual-reports-search").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 '{
  "leis": [
    "96950020QIOHAAK9V551"
  ],
  "country": "",
  "periodEndFrom": "2023-01-01",
  "periodEndTo": "2024-12-31",
  "entityName": "",
  "maxItems": 10
}' |
apify call thesjostrandproject/eu-annual-reports-search --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,thesjostrandproject/eu-annual-reports-search"
        }
    }
}
```

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/xnRXSnyQOyfyfOTbz/builds/VakDTjgV4QH2Xbona/openapi.json
