# Singapore ACRA Company Register (data.gov.sg) (`knotty_mistveil/singapore-acra-entities`) Actor

Singapore companies, sole proprietorships and LLPs from the official ACRA register on data.gov.sg: UEN, status, SSIC, address. Filter by date, industry, area.

- **URL**: https://apify.com/knotty\_mistveil/singapore-acra-entities.md
- **Developed by:** [Jason](https://apify.com/knotty_mistveil) (community)
- **Categories:** Business, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.00 / 1,000 entity returneds

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?

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

## Singapore ACRA Company Register (data.gov.sg)

Get newly registered Singapore companies, sole proprietorships, partnerships, LLPs and foreign branches straight from the official ACRA register that the Accounting and Corporate Regulatory Authority publishes as open data on data.gov.sg. The register is split into 27 CSV files (one per first letter of the entity name, plus "Others"), each up to 76 MB; this Actor streams the files you choose row by row, keeps only the entities that match your date window, status, type, SSIC industry code, name keywords and postal district, and delivers clean, flat JSON with the UEN, status, incorporation date, company type, registered address and industry codes. No login, no API key and no scraping of the ACRA website: it reads the published CSVs under the Singapore Open Data Licence, so the output is safe to use commercially.

### What you get

One record per entity (`id` = UEN):

- `uen`, `entityName`, `entityType` - the register uses exactly five values: Local Company, Sole Proprietorship/ Partnership, Limited Liability Partnership, Limited Partnership, Foreign Company Branch (public accounting firms are published as Sole Proprietorship/ Partnership, not as a type of their own)
- `entityStatus` (Live, Live Company, Struck Off, Terminated, Cancelled, ...), `registrationDate` and `uenIssueDate` (ISO dates)
- `companyType` (Exempt Private Company Limited by Shares, Public Company Limited by Guarantee, ...), `businessConstitution` (Sole-Proprietor / Partnership)
- `address` as one line in Singapore format, plus the parts: `block`, `streetName`, `levelNo`, `unitNo`, `buildingName`, `postalCode`, `postalDistrict` (1-28, derived from the postal code)
- `primarySsicCode` (industry code, present on every entity) and `secondarySsicCode` (on roughly half). The matching `primarySsicDescription` / `secondarySsicDescription` labels are **usually null**: ACRA leaves them blank for recently registered entities, so a default 90-day run returns them empty essentially every time (0 of 407 rows registered in 2026 in the Q file, 0 of 446 in X, measured on the 2026-08-14 refresh), and only ~30% of a whole file has them. Filter on the codes and look the label up in the SSIC 2020 table.
- `noOfOfficers`, `accountDueDate`, `annualReturnDate`, `formerEntityNames` (array), `auditFirms` (array of `{uen, name}`, populated for well under 1% of entities)
- provenance: `source`, `url` (the data.gov.sg dataset page), `letter`, `datasetId`, `datasetLastUpdated`, `fetchedAt`

Note: ACRA does not publish paid-up capital, officers' names or contact details in this open dataset, so those fields are not available.

Five further columns exist in the source files but ACRA leaves them empty in every file we have checked, so the Actor always returns `null` for them: `primaryUserDescribedActivity`, `secondaryUserDescribedActivity`, `pafConstitution`, `otherAddressLine1` and `otherAddressLine2`. `addressType` is likewise always `LOCAL`. They are still emitted, unchanged, so nothing breaks if ACRA starts filling them - but do not plan a workflow around them.

### Input

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `letters` | array | `["A"]` | Which register files to stream: `A`-`Z` and/or `Others` (names starting with a digit or symbol). Each file is 4-76 MB and is read in full, so the default is a single letter to keep "Try it" cheap. The list also offers `all` as a shorthand for every file (686 MB in total), so `["all"]` and the explicit 27-value array do the same thing. |
| `daysBack` | integer | `90` | Keep entities registered/incorporated within this many days before today (or before `registeredTo`). The files refresh monthly around the 14th with registrations up to the end of the previous month, so the newest record can be about six weeks old; 30 days is often empty, which is why the default is 90. |
| `registeredFrom` | string | - | Earliest registration date, `YYYY-MM-DD` (inclusive). Overrides `daysBack`. |
| `registeredTo` | string | today | Latest registration date, `YYYY-MM-DD` (inclusive). |
| `allDates` | boolean | `false` | Ignore the registration date and return every matching entity in the selected files (for example every live company starting with Q). |
| `entityStatuses` | array | `["Live", "Live Company"]` | Exact status values to keep, case-insensitive. `Live` is used for businesses and LLPs, `Live Company` for companies. Empty list = all statuses (including Struck Off, Terminated, Cancelled, Dissolved ...). |
| `entityTypes` | array | `[]` | Case-insensitive substrings matched against the entity type, e.g. `["Local Company"]`. |
| `ssicCodes` | array | `[]` | SSIC codes or prefixes matched against the primary and secondary SSIC code, e.g. `["62"]` for IT services, `["56111"]` for restaurants. |
| `keywords` | array | `[]` | Case-insensitive substrings; an entity is kept when its name contains any of them. |
| `postalDistricts` | array | `[]` | Singapore postal districts 1-28 (derived from the first two digits of the postal code), e.g. `["1","2","6"]` for the CBD. |
| `postalPrefixes` | array | `[]` | Keep postal codes starting with any of these strings, e.g. `["53","54"]`. |
| `maxItems` | integer | `1000` | Stop after this many entities; you pay per entity returned. Minimum 1 - a run started through the API with `maxItems: 0` fails with a clear error instead of quietly returning 1,000 records. The "Try it" form prefills 100 so a first trial run stays cheap. |
| `apiKey` | string (secret) | - | Optional free data.gov.sg API key to raise the download rate limit when sweeping many letters. Not needed for normal use. Request one (free, no cost, email sign-up) at <https://guide.data.gov.sg/developer-guide/api-overview/how-to-request-an-api-key>. |

Filters combine with AND; values inside one list combine with OR.

Default input (live entities registered in the last 90 days whose name starts with A):

```json
{ "letters": ["A"], "daysBack": 90, "entityStatuses": ["Live", "Live Company"], "maxItems": 1000 }
```

New IT companies in the CBD across the whole register:

```json
{
  "letters": ["all"],
  "daysBack": 120,
  "entityTypes": ["Local Company"],
  "ssicCodes": ["62"],
  "postalDistricts": ["1", "2", "6"],
  "maxItems": 5000
}
```

Every live restaurant whose name starts with S or T, regardless of age:

```json
{ "letters": ["S", "T"], "allDates": true, "ssicCodes": ["56111"], "maxItems": 20000 }
```

### Output example

A real record from the August 2026 refresh (letter Q, default filters, run on 2026-09-07):

```json
{
  "id": "202631197G",
  "source": "data.gov.sg/acra",
  "url": "https://data.gov.sg/datasets/d_4130f1d9d365d9f1633536e959f62bb7/view",
  "uen": "202631197G",
  "entityName": "QIANXUN INVESTMENT PTE. LTD.",
  "entityType": "Local Company",
  "entityStatus": "Live Company",
  "registrationDate": "2026-07-09",
  "uenIssueDate": "2026-07-09",
  "businessConstitution": null,
  "companyType": "Exempt Private Company Limited by Shares",
  "pafConstitution": null,
  "address": "36 ROBINSON ROAD #20-01 CITY HOUSE, SINGAPORE 068877",
  "addressType": "LOCAL",
  "block": "36",
  "streetName": "ROBINSON ROAD",
  "levelNo": "20",
  "unitNo": "01",
  "buildingName": "CITY HOUSE",
  "postalCode": "068877",
  "postalDistrict": 1,
  "otherAddressLine1": null,
  "otherAddressLine2": null,
  "primarySsicCode": "64202",
  "primarySsicDescription": null,
  "primaryUserDescribedActivity": null,
  "secondarySsicCode": "64999",
  "secondarySsicDescription": null,
  "secondaryUserDescribedActivity": null,
  "noOfOfficers": 4,
  "accountDueDate": "2028-01-30",
  "annualReturnDate": null,
  "formerEntityNames": [],
  "auditFirms": [],
  "issuanceAgency": "ACRA",
  "letter": "Q",
  "datasetId": "d_4130f1d9d365d9f1633536e959f62bb7",
  "datasetLastUpdated": "2026-08-14T14:05:57+08:00",
  "fetchedAt": "2026-09-07T06:15:57.357Z"
}
```

ACRA writes `na` for empty cells; the Actor converts those to `null`. Note the null `primarySsicDescription` / `secondarySsicDescription` above - that is the norm, not a parsing bug: ACRA publishes the code (`64202` here) but not the label for recently registered entities, and every record in a default 90-day run looks like this. Look the code up in the SSIC 2020 table if you need the label.

### Pricing

Pay per result: **US$0.001 per entity returned**, i.e. **US$1 per 1,000 entities**. Empty runs cost nothing. A full sweep of all live entities across the 27 files is several hundred thousand records; use the status, SSIC, district and date filters plus `maxItems` to pay only for what you need. Apify platform usage (compute, storage) is billed by Apify on top of the per-result price; runs on the Apify free plan are limited by the platform's monthly free credit, so set `maxItems` and the number of letters to stay inside it.

### Data source and licence

- Source: "ACRA Information on Corporate Entities ('A')" ... ('Z') and ('Others'), managed by the Accounting and Corporate Regulatory Authority and published on data.gov.sg (for example <https://data.gov.sg/datasets/d_8575e84912df3c28995b8e6e0e05205a/view> for letter A). The Actor uses the public dataset download API (`poll-download`) and the dataset metadata API.
- Licence: Singapore Open Data Licence v1.0, <https://data.gov.sg/open-data-licence>, which permits commercial use with attribution. Please credit "Accounting and Corporate Regulatory Authority via data.gov.sg" when you republish the data.
- Personal data: an entity's registered address (`address`, `block`, `streetName`, `levelNo`, `unitNo`, `buildingName`, `postalCode`) is frequently a **residential** address for sole proprietorships and partnerships, and the entity name of a sole proprietorship can itself be an individual's name. ACRA publishes the register lawfully and data.gov.sg licenses it for commercial reuse, but that address data is personal data about an identifiable individual under Singapore's Personal Data Protection Act 2012 (PDPA).
- Downstream use is the buyer's responsibility: the PDPA's Do Not Call provisions cover marketing messages to Singapore telephone numbers, and using a registered address for direct marketing needs its own basis. The Actor returns only the published register fields and never combines them with data from another source.
- Related dataset not used: "Entities Registered with ACRA" (<https://data.gov.sg/datasets/d_3f960c10fed6145404ca7b821f263b87/view>) is a single 232 MB file with only eight columns (UEN, UEN status, name, entity type, UEN issue date, street, postal code). It has no registration date, detailed status, SSIC code or full address, so the 27 per-letter files above are both richer and cheaper to filter.

### Limits and freshness

- The files are refreshed monthly (around the 14th) and contain registrations up to the end of the previous month, so the lag between incorporation and appearance is 2-6 weeks. Each record carries `datasetLastUpdated` so you can see which batch it came from.
- Files are streamed, never buffered, and memory stays flat because rows are discarded as they are read. Measured on the largest file (S, 76 MB, 233,307 rows) with Node capped at a 256 MB heap: an end-to-end scan takes about 10 seconds and sits near 100 MB RSS, and it makes little difference whether the default 90-day window is used (only 24,975 of the 233,307 rows are parsed as CSV; the rest are rejected by a cheap substring test first) or `allDates` forces every row through the parser - the scan is bound by the download, not by parsing. A `maxItems: 3` run stops and cancels the download after 0.1 seconds. Exact seconds and megabytes move with machine and network, so treat them as a range rather than a promise; what is stable is that a full scan of the biggest file stays well inside a 256 MB cap. Runs that actually return very large result sets add the per-run de-duplication set and the push buffers on top of that - returning all 233,307 rows of S peaked at 200 MB RSS. A 27-file sweep is dominated by the download-API pacing below, not by parsing.
- data.gov.sg allows two download-API calls per 10 seconds without a key; the Actor paces its calls accordingly and retries on 429, so a 27-file sweep spends roughly 2.5 minutes on pacing. Supplying `apiKey` lifts the limit; when the input is empty the Actor also reads the `DATA_GOV_SG_API_KEY` environment variable, which is handy for local runs.
- Each letter file lists entities whose name starts with that letter; an entity appears in exactly one file. Records are de-duplicated by UEN within a run.
- Postal districts are derived only for six-digit postal codes; entities with historical four-digit codes or foreign addresses get `postalDistrict: null`.

### Use cases

- New-business lead lists: every company or sole proprietorship incorporated last month in a given industry (SSIC) or postal district, with its registered address.
- KYC and supplier checks: confirm a UEN, its status, registration date and registered address, or pull an entity's history of former names.
- Market research: count new registrations by SSIC code, entity type or district month over month using `allDates` with date ranges.

### Support

Open an issue on the Actor's Issues tab on Apify Store; replies within 14 days. Please include your run ID and input.

# Actor input Schema

## `letters` (type: `array`):

ACRA publishes one CSV per first letter of the entity name (A-Z) plus 'Others' for names starting with a digit or symbol. Each file is 4-76 MB and is streamed in full, so pick only the letters you need. Default is A (57 MB) to keep the default run cheap; add more letters, or pick 'all' (shorthand for every file, 686 MB in total) for a complete sweep.

## `daysBack` (type: `integer`):

Return entities whose registration/incorporation date is within this many days before today (or before 'Registered to'). Ignored when 'Registered from' is set. The register refreshes monthly around the 14th with registrations up to the end of the previous month, so the newest record can be about six weeks old; 90 days is a safe default, 30 days is often empty.

## `registeredFrom` (type: `string`):

Earliest registration/incorporation date to include (inclusive). Overrides 'Days back'. Use an old date such as 1900-01-01 together with 'All dates' unticked to sweep history.

## `registeredTo` (type: `string`):

Latest registration/incorporation date to include (inclusive). Defaults to today.

## `allDates` (type: `boolean`):

Tick to ignore the registration date entirely and return every entity in the selected files that matches the other filters, e.g. every live company whose name starts with Q. Combine with 'Max results' and the status/SSIC filters to control cost.

## `entityStatuses` (type: `array`):

Exact status values to keep (case-insensitive). Real values include: Live, Live Company, Struck Off, Terminated, Cancelled, Cancelled (Non-Renewal), Ceased Registration, Gazetted To Be Struck Off, Dissolved - Members Voluntary Winding Up, In Liquidation - Compulsory Winding Up (Insolvency), Amalgamated, Converted To LLP. Default keeps only active entities (Live = business/LLP, Live Company = company). Leave the list empty to return all statuses.

## `entityTypes` (type: `array`):

Keep entities whose type contains any of these strings (case-insensitive). The register only ever uses these five values: Local Company, Sole Proprietorship/ Partnership, Limited Liability Partnership, Limited Partnership, Foreign Company Branch (public accounting firms are published as Sole Proprietorship/ Partnership). Example: \["Local Company"] for incorporated companies only. Empty = all types.

## `ssicCodes` (type: `array`):

Singapore Standard Industrial Classification codes to keep. Each value is matched as a prefix against the primary and secondary SSIC code, so "62" matches all of 62011-62090 (IT services) and "56111" matches restaurants only. Empty = all industries.

## `keywords` (type: `array`):

Case-insensitive substrings; an entity is kept when its name contains any of them (OR). Example: \["logistics", "freight"]. Empty = no name filter.

## `postalDistricts` (type: `array`):

Singapore postal district numbers derived from the first two digits of the six-digit postal code (URA sector table), e.g. \["1", "2", "6"] for the CBD (Raffles Place, Tanjong Pagar, City Hall). Entities with old four-digit or foreign postal codes never match. Empty = all districts.

## `postalPrefixes` (type: `array`):

Keep entities whose postal code starts with any of these digit strings, e.g. \["53", "54"] for Hougang/Serangoon sectors or \["049422"] for one building. Empty = all postal codes.

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

Stop after this many entities. You are charged per result returned. The full register holds about two million rows (686 MB) across all 27 files.

## `apiKey` (type: `string`):

Optional. A free data.gov.sg API key raises the download rate limit (2 calls per 10 s without a key). Only useful when you sweep many letters; the Actor already paces its calls to stay within the anonymous limit. Get one at https://guide.data.gov.sg/developer-guide/api-overview/how-to-request-an-api-key

## Actor input object example

```json
{
  "letters": [
    "A"
  ],
  "daysBack": 90,
  "allDates": false,
  "entityStatuses": [
    "Live",
    "Live Company"
  ],
  "entityTypes": [],
  "ssicCodes": [],
  "keywords": [],
  "postalDistricts": [],
  "postalPrefixes": [],
  "maxItems": 100
}
```

# Actor output Schema

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

All entities returned by the run, one JSON object each. Append ?format=csv or ?format=xlsx to download a spreadsheet.

## `summary` (type: `string`):

Counts, the date window used, source freshness and any per-source warnings, written at the end of the run.

# 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 = {
    "letters": [
        "A"
    ],
    "daysBack": 90,
    "entityStatuses": [
        "Live",
        "Live Company"
    ],
    "entityTypes": [],
    "ssicCodes": [],
    "keywords": [],
    "postalDistricts": [],
    "postalPrefixes": [],
    "maxItems": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("knotty_mistveil/singapore-acra-entities").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 = {
    "letters": ["A"],
    "daysBack": 90,
    "entityStatuses": [
        "Live",
        "Live Company",
    ],
    "entityTypes": [],
    "ssicCodes": [],
    "keywords": [],
    "postalDistricts": [],
    "postalPrefixes": [],
    "maxItems": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("knotty_mistveil/singapore-acra-entities").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 '{
  "letters": [
    "A"
  ],
  "daysBack": 90,
  "entityStatuses": [
    "Live",
    "Live Company"
  ],
  "entityTypes": [],
  "ssicCodes": [],
  "keywords": [],
  "postalDistricts": [],
  "postalPrefixes": [],
  "maxItems": 100
}' |
apify call knotty_mistveil/singapore-acra-entities --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,knotty_mistveil/singapore-acra-entities"
        }
    }
}

```

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/pkC59WYuEPDtmGXWs/builds/SK9pWRZW8yQ3NEMsF/openapi.json
