# Malaysia Open Data Studio — Gov Data Joins (`subimpact/my-open-data-studio`) Actor

Value layer on Malaysia's official open data API (data.gov.my): cross-dataset joins (population x income x CPI by state), CSV/JSON/XLSX delivery, webhooks, AI-ready output with CC BY 4.0 attribution.

- **URL**: https://apify.com/subimpact/my-open-data-studio.md
- **Developed by:** [subimpact](https://apify.com/subimpact) (community)
- **Categories:** AI, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $20.00 / 1,000 joined pulls

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/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

## Malaysia Government Data (data.gov.my) — CPI, Population, Income → CSV

**Download any Malaysian government statistic as a clean CSV, JSON or Excel file — population, household income, CPI inflation, crime, schools, hospitals — joined and ready for Excel, Google Sheets, or your AI pipeline. No API key, no scraping.**

This Actor is a value layer on the official [data.gov.my](https://data.gov.my) API (CC BY 4.0). It does not scrape the website — it joins, cleans, and packages government open data into one flat table per run.

### What you get

Run the default preset (`economy-by-state`) and the dataset contains real rows like this:

| state | year | population\_thousands | income\_mean\_rm | income\_median\_rm | cpi\_annual\_2010\_100 |
| :--- | :--- | ---: | ---: | ---: | ---: |
| Johor | 2020 | 4009.7 | 7264.0 | 5690.0 | 123.258333 |
| Johor | 2022 | 4028.3 | 8517.0 | 6879.0 | 130.625 |
| Kedah | 2020 | 2131.4 | 5005.0 | 3829.0 | 117.358333 |
| Kedah | 2022 | 2163.1 | 5550.0 | 4402.0 | 123.475 |
| Kelantan | 2020 | 1792.5 | 4411.0 | 3010.0 | 118.783333 |

*(32 rows, 16 states × 2 years — actual output of the `economy-by-state` preset.)*

The rows land in the **Dataset** tab (exportable as CSV/JSON/Excel with one click) and the file is also stored in the key-value store in your chosen format.

### Which preset do you need?

| You want… | Use preset |
| :--- | :--- |
| Malaysian household income by state | `economy-by-state` |
| Population by state | `economy-by-state` or `demography-by-state` |
| CPI / inflation by state | `economy-by-state` or `prices-inflation` |
| Births, deaths, crime by state | `demography-by-state` |
| Employment, unemployment, labour force | `labour-market` |
| Fuel prices, headline & core CPI | `prices-inflation` |
| COVID cases, vaccines, organ pledges | `health-pandemic` |
| LRT / MRT / monorail / KTMB ridership | `public-transport` |
| Schools, teachers, enrolment by state | `education-by-state` |
| Monthly tourist arrivals | `tourism-arrivals` |
| Crops, fish landings, timber, minerals | `agriculture-commodities` |
| Crimes, prisoners, drug rehabilitation | `public-safety` |
| Hospital beds, staff, maternal health | `healthcare-capacity` |
| Your own dataset IDs, keys and filters | `custom` |

Legacy codes `B1`–`B11` are still accepted as aliases.

### Input

| field | type | default | description |
|---|---|---|---|
| `mode` | enum | `studio` | `studio` (presets/joins) or `extract` (raw pull of 1–5 datasets by ID) |
| `preset` | enum | `economy-by-state` | see table above, or `custom` |
| `datasetIds` | string list | — | extract mode: data.gov.my dataset ids (1–5) |
| `customDatasets` | string list | — | custom join: dataset ids (2–6) |
| `customKey` | string list | `["state","year"]` | custom join: key columns (`year` = date field's year) |
| `customJoinType` | enum | `inner` | `inner` / `left` |
| `customParams` | string | — | JSON object of extra API params (e.g. `{"filter": "overall@division"}`) |
| `customAgg` | enum | `none` | `none` / `sum` / `mean` |
| `format` | enum | `json` | `json` / `csv` / `xlsx` |
| `webhookUrl` | string | — | POST the result file here after the run (optional) |
| `aiReady` | bool | `true` | Emit JSON Schema + dictionary + samples |
| `includeAttribution` | bool | `true` | Attach CC BY 4.0 metadata sidecar |

### Output

- **Dataset (default)** — the actual data rows, one record per row; the final record is the run summary (mode, preset, row count, files, year range).
- **Key-value store** — `<preset>_join.<json|csv|xlsx>`, `<preset>_ai_ready.json` (schema + data dictionary + sample rows for LLM/RAG), `<preset>_attribution.json` (CC BY 4.0 source metadata), `<preset>_verification.json` (row count, year range, state coverage, source rows).

### Cost

**$0.002 per run (pay-per-event, one `joined-pull` charge ≈ $2.00 / 1,000 pulls).** The underlying data comes from Malaysia's official free API (data.gov.my, CC BY 4.0) — you pay for the join + packaging, not the data. Apify compute is billed on top of the event price (~$0.001 per run, paid by the user).

### Attribution

Malaysia Open Data — [data.gov.my](https://data.gov.my). Licensed under **CC BY 4.0**. See `<preset>_attribution.json` in every run output.

# Actor input Schema

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

studio = value layer (presets, joins, AI-ready package). extract = raw data pull of 1-5 datasets by ID (cheaper per-pull event).

## `datasetIds` (type: `array`):

REQUIRED when mode=extract: list of data.gov.my dataset ids to pull raw (1-5), e.g. population\_state, cpi\_state, hh\_income\_state. Leave empty to use studio mode.

## `preset` (type: `string`):

Which joined dataset to deliver. Legacy codes B1-B11 are still accepted (B1 = economy-by-state, B2 = demography-by-state, B3 = labour-market, B4 = prices-inflation, B5 = health-pandemic, B6 = public-transport, B7 = education-by-state, B8 = tourism-arrivals, B9 = agriculture-commodities, B10 = public-safety, B11 = healthcare-capacity). custom = bring your own dataset ids.

## `customDatasets` (type: `array`):

REQUIRED when preset=custom: list of data.gov.my catalogue ids to join (2-6), e.g. population\_state, hh\_income\_state, cpi\_state. Leave empty to use a preset.

## `customKey` (type: `array`):

For preset=custom: key parts to join on. Use 'year' (extracted from the date field) or raw field names like state, district. All datasets must share these keys.

## `customJoinType` (type: `string`):

inner keeps only keys present in every dataset; left keeps all keys from the first dataset.

## `customParams` (type: `string`):

For preset=custom: JSON object of extra API params applied to every dataset, e.g. {"filter": "overall@division"}. Must be valid JSON.

## `customAgg` (type: `string`):

none = keep raw rows (last value per key). sum = aggregate numeric values per key. mean = average numeric values per key.

## `format` (type: `string`):

File format delivered to the key-value store (dataset records are always JSON).

## `webhookUrl` (type: `string`):

POST the result file to this URL after the run (3 retries with backoff). Leave empty to skip.

## `aiReady` (type: `boolean`):

Also emit JSON Schema + data dictionary + sample rows for LLM/RAG ingestion.

## `includeAttribution` (type: `boolean`):

Attach source/licence metadata sidecar to every output.

## Actor input object example

```json
{
  "mode": "studio",
  "datasetIds": [
    "population_state",
    "cpi_state"
  ],
  "preset": "economy-by-state",
  "customDatasets": [
    "population_state",
    "hh_income_state"
  ],
  "customKey": [
    "state",
    "year"
  ],
  "customJoinType": "inner",
  "customParams": "{\"filter\": \"overall@division\"}",
  "customAgg": "none",
  "format": "json",
  "aiReady": true,
  "includeAttribution": true
}
```

# Actor output Schema

## `joinedData` (type: `string`):

The joined data rows (e.g. state, year, population\_thousands, income\_mean\_rm, income\_median\_rm, cpi\_annual\_2010\_100). The last record of each run is the run summary.

## `extractedData` (type: `string`):

Raw dataset rows, tagged with \_dataset\_id. The last record of each run is the run summary.

## `runSummary` (type: `string`):

Final dataset record per run: mode, preset, row count, format, files, year range.

## `aiReadyPackage` (type: `string`):

JSON Schema + data dictionary + sample rows for LLM/RAG ingestion.

## `attribution` (type: `string`):

Source and licence metadata (CC BY 4.0).

# 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 = {
    "datasetIds": [
        "population_state",
        "cpi_state"
    ],
    "customDatasets": [
        "population_state",
        "hh_income_state"
    ],
    "customKey": [
        "state",
        "year"
    ],
    "customParams": "{\"filter\": \"overall@division\"}"
};

// Run the Actor and wait for it to finish
const run = await client.actor("subimpact/my-open-data-studio").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 = {
    "datasetIds": [
        "population_state",
        "cpi_state",
    ],
    "customDatasets": [
        "population_state",
        "hh_income_state",
    ],
    "customKey": [
        "state",
        "year",
    ],
    "customParams": "{\"filter\": \"overall@division\"}",
}

# Run the Actor and wait for it to finish
run = client.actor("subimpact/my-open-data-studio").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 '{
  "datasetIds": [
    "population_state",
    "cpi_state"
  ],
  "customDatasets": [
    "population_state",
    "hh_income_state"
  ],
  "customKey": [
    "state",
    "year"
  ],
  "customParams": "{\\"filter\\": \\"overall@division\\"}"
}' |
apify call subimpact/my-open-data-studio --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,subimpact/my-open-data-studio"
        }
    }
}

```

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/EVHrtzSQCbCtjupSk/builds/YrB6dzaHVdlhhqstH/openapi.json
