# KLSE / Bursa Malaysia Stock Fundamentals Scraper (`lewxiangang/klse-fundamentals`) Actor

Scrape KLSE (Bursa Malaysia) stock fundamental data including P/E ratio, EPS, dividend yield, and financial metrics for Malaysian-listed companies.

- **URL**: https://apify.com/lewxiangang/klse-fundamentals.md
- **Developed by:** [lewxiangang](https://apify.com/lewxiangang) (community)
- **Categories:** Other
- **Stats:** 25 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $1.00 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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

Scrape fundamental financial data for **1,000+ Malaysian stocks** listed on Bursa Malaysia (KLSE). Get quarterly results, annual summaries, dividends, and financial ratios in machine-readable JSON or CSV format.

**The first and only Apify actor dedicated to Malaysian stock fundamentals.**

### What data does it collect?

| Data Type | Fields |
|-----------|--------|
| **Company Info** | Name, sector, market (Main/ACE/LEAP), current price, market cap |
| **Quarterly Results** | Revenue, net profit, EPS (sen), profit margin per quarter |
| **Annual Summary** | Revenue, net profit, EPS, PE, ROE, NTA, dividend yield, DPS per year |
| **Dividends** | Ex-date, payment date, dividend per share (sen), type (interim/final/special) |
| **Financial Ratios** | PE, PB, ROE, ROA, debt-to-equity, current ratio, dividend yield |

### Why use this actor?

| Alternative | Cost | Coverage | Machine-readable? |
|-------------|------|----------|-------------------|
| Bloomberg Terminal | ~$25,000/year | Full | Yes |
| EODHD API | $19.99-$99.99/month | Partial KLSE | Yes |
| KLSE Screener | RM5/month | Full KLSE | No (web only) |
| **This Actor** | **Pay-per-use** | **Full KLSE** | **Yes (JSON/CSV)** |

No subscriptions. No minimums. Pay only for what you scrape.

### Use Cases

- **Retail investors**: Screen all 1,000+ Bursa stocks by PE, ROE, dividend yield
- **Quant trading**: Feed fundamentals into algorithmic trading models
- **Fintech apps**: Power stock screeners, portfolio trackers, robo-advisors
- **Academic research**: Analyze Malaysian market trends across decades
- **Financial advisors**: Generate client reports with up-to-date fundamentals

### Input

```json
{
    "stockCodes": ["MAYBANK", "CIMB", "TENAGA"],
    "market": "all",
    "dataTypes": ["quarterly_results", "annual_summary", "dividends", "ratios"],
    "yearsHistory": 5,
    "maxConcurrency": 3
}
```

#### Input Parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `stockCodes` | array | `[]` | Stock codes to scrape (e.g., `["MAYBANK", "CIMB"]`). Leave empty for all stocks. |
| `stockNumbers` | array | `[]` | Alternative: Bursa stock numbers (e.g., `["1155", "1023"]`) |
| `market` | string | `"all"` | Filter by market: `all`, `main`, `ace`, `leap` |
| `dataTypes` | array | all | Data to collect: `quarterly_results`, `annual_summary`, `dividends`, `ratios` |
| `yearsHistory` | integer | `5` | Years of historical data (1-10) |
| `outputFormat` | string | `"json"` | Output format: `json` or `csv` |
| `maxConcurrency` | integer | `3` | Max concurrent requests (1-10). Keep low to avoid blocks. |
| `proxyConfiguration` | object | Apify proxy | Proxy settings. Recommended for large scrapes. |

### Output Example

```json
{
    "stockCode": "MAYBANK",
    "stockNumber": "1155",
    "companyName": "Malayan Banking Berhad",
    "market": "Main Market",
    "sector": "Financial Services",
    "currentPrice": 9.45,
    "marketCap": 112000,
    "currency": "MYR",
    "quarterly": [
        {
            "quarter": "Q3 2025",
            "date": "2025-09-30",
            "revenue": 18234000,
            "netProfit": 2456000,
            "eps": 21.5,
            "margin": 13.47
        },
        {
            "quarter": "Q2 2025",
            "date": "2025-06-30",
            "revenue": 17890000,
            "netProfit": 2380000,
            "eps": 20.8,
            "margin": 13.30
        }
    ],
    "annual": [
        {
            "year": 2024,
            "revenue": 70123000,
            "netProfit": 9234000,
            "eps": 80.5,
            "pe": 11.7,
            "roe": 10.8,
            "nta": 7.45,
            "dividendYield": 5.82,
            "dps": 55.0
        }
    ],
    "dividends": [
        {
            "exDate": "2025-03-15",
            "paymentDate": "2025-04-10",
            "dividendPerShare": 30.0,
            "type": "interim"
        },
        {
            "exDate": "2024-09-20",
            "paymentDate": "2024-10-15",
            "dividendPerShare": 25.0,
            "type": "final"
        }
    ],
    "ratios": {
        "pe": 11.7,
        "pb": 1.27,
        "roe": 10.8,
        "roa": 1.05,
        "debtToEquity": null,
        "currentRatio": null,
        "dividendYield": 5.82
    },
    "scrapedAt": "2026-03-09T08:30:00.000Z"
}
```

#### Notes on Data Units

- **EPS and DPS** are in **sen** (1 MYR = 100 sen)
- **Revenue and net profit** are in **MYR thousands** (as reported by source)
- **Market cap** is in **MYR millions**
- **Percentages** (ROE, ROA, margin, dividend yield) are raw numbers (e.g., `10.8` means 10.8%)
- **Currency**: Most companies report in MYR. Offshore companies reporting in USD are flagged with `"currency": "USD"`

### Supported Stocks

The actor includes a built-in mapping of **100+ top KLSE stocks** covering:

- All 30 FBMKLCI component stocks
- Major stocks across all sectors: banking, plantations, technology, property, energy, healthcare, REITs, telecommunications
- Selected ACE Market stocks

For stocks not in the built-in list, simply pass the stock code as shown on i3investor — the actor will attempt to scrape it.

### How It Works

1. Accepts stock codes (or numbers) as input
2. Fetches stock overview, quarterly financials, annual financials, and dividend pages from i3investor.com
3. Parses HTML tables using Cheerio (fast, no browser needed)
4. Merges data per stock into a single comprehensive record
5. Outputs to Apify dataset in JSON or CSV format

Data is sourced from i3investor.com, which aggregates **public Bursa Malaysia regulatory filings**.

### Performance

| Scenario | Estimated Time | Estimated Cost |
|----------|---------------|----------------|
| 10 stocks, all data | ~2 minutes | ~$0.01 |
| 100 stocks, all data | ~15 minutes | ~$0.05 |
| All 1,000+ stocks | ~2 hours | ~$0.50 |

Estimates based on Apify platform pricing. Actual costs depend on proxy usage and retries.

### Tips

- **Start small**: Test with 3-5 stocks before running full market scans
- **Use proxy**: Enable Apify proxy for reliable scraping, especially for 100+ stocks
- **Rate limiting**: Keep `maxConcurrency` at 3 or below to avoid being blocked
- **Schedule runs**: Set up weekly/monthly schedules for regular data updates
- **Combine with other actors**: Pair with Yahoo Finance actors for cross-referencing price data

### Cost Estimation

| Results | Estimated Cost |
|---------|---------------|
| 100 results | ~$0.10 |
| 1,000 results | ~$1.00 |
| 5,000 results | ~$5.00 |
| 10,000 results | ~$10.00 |

Costs include Apify platform usage fees. Actual cost depends on proxy usage, retries, and data volume per stock. Check the [Apify pricing page](https://apify.com/pricing) for current platform rates.

### Integrations

Access your scraped data through multiple channels:

- **API**: Programmatic access to datasets via [Apify API](https://docs.apify.com/api/v2)
- **Webhooks**: Trigger external services when a scrape completes
- **Zapier**: Connect to 5,000+ apps via the [Apify Zapier integration](https://zapier.com/apps/apify)
- **Google Sheets**: Export results directly to Google Sheets from the Apify console
- **Make.com**: Build automated workflows with the [Apify Make integration](https://www.make.com/en/integrations/apify)

### FAQ

**How fresh is the data?**
Data is sourced from public regulatory filings via i3investor.com. Financial results are typically available 1-2 trading days after companies file with Bursa Malaysia.

**Can I scrape all 1,000+ stocks at once?**
Yes. Leave the `stockCodes` and `stockNumbers` fields empty to scrape all available stocks. This takes approximately 2 hours and costs around $0.50.

**What format is the output?**
JSON by default. You can export to CSV, Excel, or Google Sheets directly from the Apify console or via the API.

**Is this real-time stock data?**
No. This actor collects fundamental data from regulatory filings (quarterly results, annual reports, dividends, ratios). It does not provide real-time or delayed stock prices. For price data, pair this with a dedicated stock price actor.

### Is scraping KLSE data legal?

This actor scrapes publicly available data from i3investor.com. All financial data originates from **public regulatory filings** submitted to Bursa Malaysia.

- This is **not** real-time data — all prices and financials are delayed
- This is **not** investment advice — always do your own research
- This actor does **not** scrape bursamalaysia.com directly
- Users are responsible for complying with the terms of service of data sources
- Data is provided "as-is" without warranty of accuracy or completeness

### Related actors

Looking for Malaysian marketplace data? Check out [Mudah.my Scraper](https://apify.com/lewxiangang/mudah-search-scraper) for car prices, property listings, and electronics deals.

# Actor input Schema

## `stockCodes` (type: `array`):

Array of KLSE stock codes to scrape (e.g., \['MAYBANK', 'CIMB', 'TENAGA']). Leave empty and set stockNumbers instead, or leave both empty to scrape top stocks.

## `stockNumbers` (type: `array`):

Alternative: array of Bursa stock numbers (e.g., \['1155', '1023', '5347']). Used if stockCodes is empty.

## `market` (type: `string`):

Filter by market segment.

## `dataTypes` (type: `array`):

Select which data types to collect. Default collects all.

## `yearsHistory` (type: `integer`):

Number of years of historical data to collect (1-10).

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

Maximum number of concurrent requests. Keep low (1-3) to avoid being blocked.

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

Proxy configuration for the crawler. Recommended for large-scale scraping.

## Actor input object example

```json
{
  "stockCodes": [
    "MAYBANK",
    "CIMB",
    "TENAGA",
    "PETRONM"
  ],
  "stockNumbers": [
    "1155",
    "1023",
    "5347"
  ],
  "market": "all",
  "dataTypes": [
    "quarterly_results",
    "annual_summary",
    "dividends",
    "ratios"
  ],
  "yearsHistory": 5,
  "maxConcurrency": 2,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# 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 = {
    "stockCodes": [
        "MAYBANK",
        "CIMB",
        "TENAGA"
    ],
    "market": "all",
    "dataTypes": [
        "quarterly_results",
        "annual_summary",
        "dividends",
        "ratios"
    ],
    "yearsHistory": 5,
    "maxConcurrency": 2,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("lewxiangang/klse-fundamentals").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 = {
    "stockCodes": [
        "MAYBANK",
        "CIMB",
        "TENAGA",
    ],
    "market": "all",
    "dataTypes": [
        "quarterly_results",
        "annual_summary",
        "dividends",
        "ratios",
    ],
    "yearsHistory": 5,
    "maxConcurrency": 2,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("lewxiangang/klse-fundamentals").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 '{
  "stockCodes": [
    "MAYBANK",
    "CIMB",
    "TENAGA"
  ],
  "market": "all",
  "dataTypes": [
    "quarterly_results",
    "annual_summary",
    "dividends",
    "ratios"
  ],
  "yearsHistory": 5,
  "maxConcurrency": 2,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call lewxiangang/klse-fundamentals --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=lewxiangang/klse-fundamentals",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

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