# Romania CUI Check & Watch: ANAF VAT, e-Factura, Inactive (`clearsource/ro-company-status`) Actor

Check Romanian companies by CUI in bulk and get notified when their status changes: VAT registration, inactive status, split VAT, e-Factura registry. Uses ANAF's public web service; source and retrieval time on every record. Pay per CUI checked and per change detected.

- **URL**: https://apify.com/clearsource/ro-company-status.md
- **Developed by:** [PPFTEC S.R.L](https://apify.com/clearsource) (community)
- **Categories:** Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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

## Romania CUI Status Check: ANAF VAT, Inactive, e-Factura

Check Romanian companies by CUI (CIF) against ANAF's public VAT-status web service (PlatitorTvaRest v9): VAT registration, VAT on cash (TVA la încasare), inactive taxpayer, struck-off date, split VAT and the RO e-Factura registry. Optional **watch mode** tells you only when one of those statuses changes.

For finance and invoicing teams, e-Factura and ERP software, and onboarding / KYB teams who need to know whether a counterparty is VAT-registered, inactive or struck off before they issue or pay an invoice.

> Status: v0.1.4, private build. Not yet published on the Apify Store (the public listing waits for a legal check, DECISIONS D6). Watch mode is built and tested but switched off on Apify until then.

### Quick start

Lookup of three companies (status today, Europe/Bucharest):

```json input
{ "mode": "lookup", "cuis": ["RO16054368", "9900000010", "9900000029"] }
```

Lookup on a past date, without addresses:

```json input
{ "mode": "lookup", "cuis": ["16054368"], "queryDate": "2026-01-15", "includeAddresses": false }
```

Watch your counterparties (schedule this daily or weekly in Apify; a run between 02:00 and 06:00 Bucharest time is a courtesy to ANAF):

```json input
{ "mode": "watch", "cuis": ["RO16054368", "9900000010"] }
```

Validate a list without sending anything to ANAF and without being charged:

```json input
{ "cuis": ["RO 16.054.368", "14841555", "9900000010"], "dryRun": true }
```

`9900000010` and `9900000029` are synthetic test codes; replace them with your own CUIs.

### Output

One record per valid CUI in lookup mode (synthetic example):

```json output
{
  "record_type": "ro-company-status",
  "cui": "9900000010",
  "input": "RO9900000010",
  "query_date": "2026-10-05",
  "found": true,
  "name": "TEST SINTETIC SRL",
  "entity_kind": "legal-entity",
  "registration_status": "INREGISTRAT din data 01.03.2004",
  "registration_date": "2004-03-01",
  "trade_register_no": "J40/0000/2004",
  "caen_code": "6201",
  "legal_form": "SOCIETATE COMERCIALA CU RASPUNDERE LIMITATA",
  "organisation_form": "PERSOANA JURIDICA",
  "ownership_form": "PROPR.PRIVATA-CAPITAL PRIVAT AUTOHTON",
  "tax_office": "TEST",
  "e_invoice_registered": true,
  "vat": { "registered": true, "periods": [{ "from": "2004-03-01", "to": null, "cancellation_recorded_on": null, "message": null }] },
  "vat_on_cash": { "active": false, "from": null, "to": null, "updated": null, "published": null, "act_type": null },
  "inactive": { "is_inactive": false, "inactivated_on": null, "reactivated_on": null, "published_on": null, "deregistered_on": null },
  "split_vat": { "active": false, "from": null, "cancelled_on": null },
  "registered_office": { "street": "Str. Test", "number": "1", "locality": "Sector 1 Mun. Bucuresti", "locality_code": "1", "county": "MUNICIPIUL BUCURESTI", "county_code": "40", "county_auto_code": "B", "country": null, "details": null, "postal_code": "012345" },
  "fiscal_domicile": null,
  "redacted_fields": [],
  "insolvency": null,
  "privacy_notice_url": "https://clearsource.ppftec.com/privacy",
  "provenance": {
    "envelope_version": "1.0.0",
    "source_id": "anaf-ws-platitor-tva-v9",
    "source_url": "https://webservicesp.anaf.ro/api/PlatitorTvaRest/v9/tva",
    "source_request": { "method": "POST", "body_sha256": "3b1f6c1fd7a1d5e0b0c1c3f4f6a7e8d9c0b1a2f3e4d5c6b7a8f9e0d1c2b3a4f5", "page": "1" },
    "source_record_url": null,
    "retrieved_at": "2026-10-05T04:12:03.512Z",
    "source_version": { "api_version": "v9", "doc_sha256": "9346a0f7435234fa9ddf73e9e72c43ae354577d6a7db3b44fa5ef54ad31ae185" },
    "producer": { "name": "ro-company-status", "version": "0.1.4", "build": null, "run_id": null, "adapter": "anaf-v9" },
    "record_hash": "sha256:5e0c2b7d3a1f4e6b8c9d0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d",
    "licence": {
      "id": "LicenseRef-ANAF-public-ws",
      "url": "https://static.anaf.ro/static/10/Anaf/Informatii_R/Servicii_web/doc_WS_V9.txt",
      "attribution": "Sursa: ANAF (Agenția Națională de Administrare Fiscală). Information only; the ANAF response is authoritative."
    },
    "personal_data": "none"
  }
}
```

A CUI that ANAF does not know comes back with `"found": false` and every status field `null`.

In watch mode the dataset holds only **change records**:

```json output
{
  "record_type": "ro-company-status-change",
  "cui": "9900000010",
  "name": "TEST SINTETIC SRL",
  "entity_kind": "legal-entity",
  "query_date": "2026-10-05",
  "checked_at": "2026-10-05T04:12:03.512Z",
  "watch_status": "changed",
  "changed_fields": ["inactive", "inactivated_on"],
  "previous": { "found": true, "registration_status": "INREGISTRAT din data 01.03.2004", "vat_registered": true, "vat_period_from": "2004-03-01", "vat_period_to": null, "vat_on_cash": false, "inactive": false, "inactivated_on": null, "reactivated_on": null, "deregistered_on": null, "split_vat": false, "e_invoice_registered": true },
  "current": { "found": true, "registration_status": "INREGISTRAT din data 01.03.2004", "vat_registered": true, "vat_period_from": "2004-03-01", "vat_period_to": null, "vat_on_cash": false, "inactive": true, "inactivated_on": "2026-10-02", "reactivated_on": null, "deregistered_on": null, "split_vat": false, "e_invoice_registered": true },
  "previously_checked_at": "2026-10-04T04:11:58.020Z",
  "privacy_notice_url": "https://clearsource.ppftec.com/privacy",
  "provenance": {
    "envelope_version": "1.0.0",
    "source_id": "anaf-ws-platitor-tva-v9",
    "source_url": "https://webservicesp.anaf.ro/api/PlatitorTvaRest/v9/tva",
    "retrieved_at": "2026-10-05T04:12:03.512Z",
    "source_version": { "api_version": "v9", "doc_sha256": "9346a0f7435234fa9ddf73e9e72c43ae354577d6a7db3b44fa5ef54ad31ae185" },
    "producer": { "name": "ro-company-status", "version": "0.1.4", "adapter": "anaf-v9" },
    "record_hash": "sha256:5e0c2b7d3a1f4e6b8c9d0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d",
    "licence": { "id": "LicenseRef-ANAF-public-ws", "attribution": "Sursa: ANAF (Agenția Națională de Administrare Fiscală). Information only; the ANAF response is authoritative." },
    "personal_data": "none"
  }
}
```

A sole trader in a watch list produces a status-free record with `"watch_status": "not-watched-natural-person-business"`, `name`, `previous` and `current` set to `null`, and no charge.

Run-level files in the default key-value store: `INVALID_INPUTS` (list index and reason; 13-digit values are never stored), `FAILED_INPUTS`, `RUN_SUMMARY` (counts only), `DRIFT_REPORT`, `CLASSIFICATION_STATS` (legal forms and counts, no names, no CUIs), `DRY_RUN`.

### Fields

| Field | Meaning (ANAF source field) |
| --- | --- |
| `found` | `false` when ANAF lists the CUI as not found |
| `registration_status`, `registration_date` | `stare_inregistrare`, `data_inregistrare` |
| `vat.registered`, `vat.periods[]` | VAT registration under art. 316 Codul fiscal (`scpTVA`, `perioade_TVA`) |
| `vat_on_cash` | VAT on cash system (`inregistrare_RTVAI`) |
| `inactive` | Inactive taxpayer; `deregistered_on` is the struck-off date (`dataRadiere`) |
| `split_vat` | Split VAT (`inregistrare_SplitTVA`) |
| `e_invoice_registered` | RO e-Factura registry (`statusRO_e_Factura`) |
| `registered_office`, `fiscal_domicile` | Address blocks; reduced for sole traders |
| `entity_kind` | `legal-entity`, `natural-person-business` or `unknown` (treated like a sole trader) |
| `insolvency` | always `null`: no lawful open source is used |

All statuses are **as at `query_date`**, as ANAF answers for that date.

### Watch mode

- State is kept **in your own Apify account**, in a key-value store with the fixed name `ro-company-status-watch`. Per CUI it holds only: status hash, last check time, schema version and the status fields listed above. No name, address, phone, fax, IBAN, trade register number or CAEN code.
- One watch list per account, at most **5,000 CUIs**. Larger lists are refused before any request, without charge.
- Each **company** (legal entity) is queried at most **once per calendar day** (Europe/Bucharest); a second run the same day skips it free of charge. Sole traders and unclassifiable forms are not stored (see below), so they are queried again on every run, but never charged and never reported with status data.
- The first run stores a baseline and emits nothing. CUIs you remove from the list are deleted from the store on the next run; entries not checked for 90 days are deleted. `"resetWatch": true` deletes the store; it always works, even while the actor is switched off by the maintainer, because it only deletes your own state (no ANAF request, no charge).
- A company that disappears from ANAF is re-checked once in the same run before a change is reported.
- Delivery is at least once: a crash between output and state write can repeat one change on the next run. Do not start two watch runs at once (the second one stops).

### Sole traders (PFA, II, IF, liberal professions)

In lookup records the street, number, details and postal code of their addresses are removed (county and locality stay) and `provenance.personal_data` is `public-register-natural-person`. Phone, fax and IBAN are never output for anyone. Sole traders are **not watched**; check them in lookup mode instead. Objections: contact the operator (below); an accepted objection removes the CUI from all output within 24 hours.

### Pricing (pay per event)

| Event | Price | Charged when |
| --- | --- | --- |
| `company-check` | $0.003 | lookup: per CUI answered by ANAF (found or not found) |
| `watch-check` | $0.001 | watch: per company answered by ANAF (baseline, unchanged or changed) |
| `status-change` | $0.01 | watch: per change record |

Not charged: invalid, duplicate or unavailable CUIs, failed requests, dry runs, skipped (already checked today) and sole-trader records. Examples: 500 CUIs looked up once = $1.50. 500 companies watched daily ≈ $15 per month plus $0.01 per change; weekly ≈ $2.15 per month.

### Limits

ANAF allows one request per second per client. All users of this actor share one queue spaced 1.5 s apart (100 CUIs per request): at most about 4,000 CUIs per minute **for everyone together**. Your run may wait when others are running. Per run: 10,000 CUIs in lookup, 5,000 in watch, 150 ANAF requests, 55 minutes, 256 MB.

A queue slot is used only if asking for it took at most 400 ms; a slower answer is discarded and a new slot is requested, and after 3 slow answers the run stops without calling ANAF. If the shared rate-limit service is unavailable the run stops **before** calling ANAF, lists the unchecked CUIs in `FAILED_INPUTS` and charges nothing for them.

### Data source and licence

ANAF web service PlatitorTvaRest v9, documentation `https://static.anaf.ro/static/10/Anaf/Informatii_R/Servicii_web/doc_WS_V9.txt` (sha256 `9346a0f7…ae185`). Attribution on every record: "Sursa: ANAF (Agenția Națională de Administrare Fiscală). Information only; the ANAF response is authoritative." This actor is not affiliated with or endorsed by ANAF. DISCLAIMER: this output is not official, not certified and not OGL-licensed; it is not real-time, does not contain insolvency information and keeps no status history.

### Privacy

Privacy notice (Art. 14 GDPR, Romanian and English; source, purposes, legitimate interests, retention, objection channel): https://clearsource.ppftec.com/privacy — linked from every record as `privacy_notice_url`.

We (the developer) keep no copy of your CUIs or results. The only data that leaves your run besides the ANAF request is a request to our rate-limit service, which carries a hash of the run id and the build number, never a CUI. Apify's "Share run data with developers" is opt-in; if you turn it on we can see your input and output, use them only for support, and never copy them. You control what you store.

### Use with AI assistants (MCP)

This actor can be called by AI assistants through Apify's MCP server (run by Apify; this actor is an independent product of PPFTEC S.R.L., not affiliated with or endorsed by ANAF). Add it to an MCP client such as Claude, VS Code or Cursor:

```
{
  "mcpServers": {
    "clearsource-ro-company-status": {
      "url": "https://mcp.apify.com?tools=clearsource/ro-company-status"
    }
  }
}
```

On first use your browser asks you to sign in to Apify (OAuth); or send your token as `Authorization: Bearer <APIFY_TOKEN>`. The assistant reads the input schema, runs a lookup and then fetches the dataset items. Billing is per CUI answered (see Pricing). The answer is the register's status on the query date; it is not legal or tax advice. Details: https://docs.apify.com/platform/integrations/mcp (checked 2026-10-10). Guide: https://clearsource.ppftec.com/guides/romania-cui-vat-check

### Guide

Step-by-step guide, including the free manual way: https://clearsource.ppftec.com/guides/romania-cui-vat-check

### Terms of use

This actor is offered by PPFTEC S.R.L. under Apify's terms plus our terms of use: https://clearsource.ppftec.com/terms

### Stop / takedown

ANAF, a data subject or anyone with a concern can ask us to stop processing a CUI or to stop the actor altogether: write to **support@ppftec.com**. We answer within **24 hours**. An accepted objection removes the CUI from all output and from watch stores (on their next run) within 24 hours; the maintainer kill switch stops all runs before their next ANAF request.

### Reliability

Every response is checked against the expected ANAF shape; a missing or retyped field stops the run with `SCHEMA DRIFT` and a `DRIFT_REPORT`. Retries back off 5, 10, 20, 40 s and take a new queue slot each time. A maintainer kill switch stops runs before the next ANAF request. See `CHANGELOG.md`.

### Maintainer setup (not for users)

- Secrets live in the Apify secret store and are referenced from `.actor/actor.json` (`@pdaLimiterSecret`, `@pdaSuppressionPepper`, `@pdaHealthToken`), never as plain values: `apify secrets add pdaLimiterSecret <value>` before `apify push`. Environment variables are frozen into a build; the instant kill switch is the limiter's `ANAF_DISABLED` (see `workers/anaf-limiter/README.md`).
- The central rate limiter is `PDA_LIMITER_URL` = `https://clearsource.ppftec.com/limiter` (set in `.actor/actor.json`; the actor refuses any other host or path and never follows redirects). The run log names the limiter endpoint in use.
- Pay-per-event pricing must be configured before any run: on Apify the actor refuses to run without it ("pay-per-event pricing is not configured", no ANAF request). For a **private** test build only, `PDA_ALLOW_NO_PPE=1` lifts this; never set it on the public build.
- Health mode needs `PDA_HEALTH_ENABLED=1`, the secret `PDA_HEALTH_TOKEN` (≥ 32 characters) and the same value as the secret input `healthToken` in the maintainer's scheduled task; optionally `PDA_MAINTAINER_USER_ID` pins it to our Apify user id. Without all of them the run answers "Invalid input".

### Calling via API

```bash
curl -X POST "https://api.apify.com/v2/acts/<owner>~ro-company-status/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H 'content-type: application/json' -d '{"mode":"lookup","cuis":["RO16054368"]}'
```

Operator: PPFTEC S.R.L., https://ppftec.com. Information only, not legal or tax advice.

# Changelog

This Actor's version history is a separate document: https://apify.com/clearsource/ro-company-status/changelog.md

# Actor input Schema

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

Lookup returns one record per CUI. Watch keeps a small status store in your own Apify account and emits records only when a status changes.

## `cuis` (type: `array`):

Romanian fiscal codes of companies, with or without the RO prefix. Lookup: up to 10,000 per run. Watch: up to 5,000 (your whole watch list). Personal numeric codes (CNP, 13 digits) are refused.

## `queryDate` (type: `string`):

Default: today (Europe/Bucharest). Not allowed in watch mode.

## `includeAddresses` (type: `boolean`):

Sole-trader addresses are always reduced to county and locality.

## `resetWatch` (type: `boolean`):

Deletes the watch store in your account and stops. No ANAF call, no charge.

## `dryRun` (type: `boolean`):

Validates the input and plans the requests. Nothing is sent to ANAF and nothing is charged.

## `internalMode` (type: `string`):

Reserved for the maintainer's monitor. Allowed values: none, health (validated by the actor).

## `healthToken` (type: `string`):

Reserved for the maintainer's monitor.

## Actor input object example

```json
{
  "mode": "lookup",
  "cuis": [
    "RO16054368"
  ],
  "includeAddresses": true,
  "resetWatch": false,
  "dryRun": false,
  "internalMode": "none"
}
```

# Actor output Schema

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

Lookup records, or change records in watch mode, with provenance and the privacy notice link.

## `runFiles` (type: `string`):

Key-value store records such as INVALID_INPUTS, FAILED_INPUTS or DRIFT_REPORT when present.

# 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 = {
    "cuis": [
        "RO16054368"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("clearsource/ro-company-status").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 = { "cuis": ["RO16054368"] }

# Run the Actor and wait for it to finish
run = client.actor("clearsource/ro-company-status").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 '{
  "cuis": [
    "RO16054368"
  ]
}' |
apify call clearsource/ro-company-status --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,clearsource/ro-company-status"
        }
    }
}
```

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/2QdK34QsB3JizDlgc/builds/s23FaULO1pEzFl5Ls/openapi.json
