# API documentation

The API is REST and answers in JSON. The full description, in OpenAPI 3.1 format, is at [https://api.apifirme.dev/openapi.json?lang=en](https://api.apifirme.dev/openapi.json?lang=en); this page is generated from it. Field names and values are those of the Romanian registries (`denumire` is the name, `judet` the county, `stare` the status).

## Authentication

Every request, except the validation ones, needs an API key in the `Authorization` header:

```
curl https://api.apifirme.dev/rest/v1/companies/13548146 \
  -H "Authorization: Bearer af_live_..."
```

## Limits

Every response says how much is left of the limits: `x-ratelimit-limit`, `x-ratelimit-remaining` and `x-ratelimit-reset` for the requests per minute, `x-quota-limit` and `x-quota-remaining` for those of the month. A request over the limit gets `429` with a `Retry-After` header and is not counted. Beyond the plan's monthly number of requests another 5 % are allowed, at no cost; only after those are requests refused.

## Conditional requests

Every `200` response has an `ETag` header. A request that sends it back in `If-None-Match` gets `304` without a body if the response has not changed; it counts as a request too.

## Errors

Errors follow RFC 7807: the content type is `application/problem+json`, and the body has `type`, `title`, `status` and `detail`. `type` identifies the kind of error, for example `https://apifirme.dev/problems/invalid-cui`.

## For AI assistants

The same data is available as tools through the Model Context Protocol, at `https://api.apifirme.dev/mcp` (Streamable HTTP transport): `lookup_company`, `check_invoice`, `get_financials`, `get_company_events`, `get_fx_rate`, `count_new_companies` and `list_new_companies`. An assistant sends its user to sign in (OAuth 2.1); a program can send the same API key in the `Authorization` header. Only tool calls are counted, each as one request; connecting and listing the tools are not. The steps for each assistant are at https://apifirme.dev/en/ai.

## Sources

Every company and every financial statement has a `sources` field: which registry the information comes from, which dataset, and when it was first and last retrieved.

## Companies

A company's data, by its CUI (the Romanian unique registration code).

### GET /rest/v1/companies/{cui}

A company's data. Returns the company with this CUI. If the company is not in the database yet, it is looked up at ANAF (the Romanian tax administration) right away: the answer comes in the same request or, if the source is slow, as `202` with a `Retry-After` header. For a natural person (a sole trader: PFA, individual or family enterprise) the answer is `404`.

| Parameter | Where | Description |
|---|---|---|
| `cui` | in the path, required | The unique registration code (CUI), with or without the `RO` prefix, for example `13548146`. |

```
curl https://api.apifirme.dev/rest/v1/companies/13548146 \
  -H "Authorization: Bearer af_live_..."
```

Responses:

| Code | What it means |
|---|---|
| `200` | The company. |
| `202` | The company is being looked up at ANAF and did not arrive in time. Repeat the request after the number of seconds in `Retry-After`. Does not count as a request. |
| `400` | A parameter is missing or not valid; `detail` says which. |
| `401` | The API key is missing or not valid. |
| `404` | Does not exist: an unregistered CUI, a natural person, or no data for the day or year requested. |
| `429` | The limit per minute or the month's number of requests has been exceeded. Does not count as a request. |
| `503` | The company is not in the database and ANAF could not be asked right now. Does not count as a request. |

The `200` response is a `Company` object:

| Field | Type | Description |
|---|---|---|
| `cui` | integer | The unique registration code (CUI). |
| `denumire` | string or null | The name. |
| `nr_reg_com` | string or null | The registration number in the trade register. |
| `euid` | string or null | The European unique identifier (source: ONRC, the trade register). |
| `sediu_secundar` | boolean | Whether it is the secondary seat of a company, registered at ANAF with a CUI of its own. |
| `forma_juridica` | string or null | The legal form as ANAF writes it. |
| `forma_juridica_cod` | string or null | The legal form from the trade register: `SRL`, `SA`, ... |
| `stare` | string or null | The status, in one word: `FUNCTIUNE` (in operation), `SUSPENDARE` (suspended), `REORGANIZARE JUDICIARA` (judicial reorganisation), `INSOLVENTA` (insolvency), `DIZOLVARE` (dissolution), `LICHIDARE` (liquidation), `FALIMENT` (bankruptcy), `RADIERE` (struck off). |
| `stari_registru` | array | All statuses and remarks from the trade register, each with `cod` (code) and `denumire` (name). |
| `caen` | integer or null | The CAEN code of the main activity (source: ANAF). CAEN is the Romanian classification of economic activities, aligned with NACE. |
| `caen_denumire` | string or null | The name of the CAEN code, in the classification in force. |
| `caen_autorizate` | array or null | The activities the company is authorised for, from the trade register's open data: each with `cod` (code), `versiune` (the version of the CAEN classification it was declared in: 3 is the one of 2025, 2 the one of 2008, 1 the one of 2003, 0 the one of 1998) and `denumire` (name), the newest classification first. The register also keeps what was declared in the old classifications, so the same activity can appear with codes of several versions. `null` when they are not known (the company is not in the register's dataset), which does not mean that it has none. |
| `data_inregistrare` | date or null | The date of fiscal registration (source: ANAF). |
| `data_inmatriculare` | date or null | The date of entry in the trade register (source: ONRC). |
| `web` | string or null | The web address declared to the trade register. |
| `judet` | string or null | The county code of the registered office: `SB`, `B`, ... |
| `localitate` | string or null | The locality of the registered office. |
| `adresa_completa` | string or null | The address of the registered office, as one text. |
| `cod_postal` | string or null | The postal code. |
| `telefon` | string or null | The telephone number declared to ANAF. |
| `scp_tva` | boolean | Whether it is registered for VAT. |
| `data_inceput_tva` | date or null | Since when it has been registered for VAT. |
| `data_sfarsit_tva` | date or null | Until when it was registered, if it no longer is. |
| `tva_incasare` | boolean | Whether it applies VAT on collection. |
| `split_tva` | boolean | Whether it applies split VAT payment. |
| `status_inactiv` | boolean | Whether it is declared fiscally inactive. |
| `data_inactivare` | date or null | Since when it has been inactive. |
| `e_factura` | boolean | Whether it is in the RO e-Factura register. |
| `updated_at` | date-time or null | When something in the company's data last changed. |
| `checked_at` | date-time or null | When ANAF was last asked about the company; `null` if not yet. |
| `sources` | array of Source | Which sources the company's data comes from, and since when. |

### POST /rest/v1/companies/batch

Several companies in one request. Up to 100 CUIs in a single request, answered from what is already in the database. It costs as many requests as the CUIs it answers for: one for each company returned and for each CUI we serve no data about, and one at least. A CUI that is not in the database yet appears in `not_stored`, costs nothing and can be asked for by itself, with `GET /rest/v1/companies/{cui}`, which looks it up at ANAF right away. For the limit per minute, a request for several companies is one request.

The request body is a `BatchRequest` object:

| Field | Type | Description |
|---|---|---|
| `cuis` | array | Between 1 and 100 CUIs, as text or as numbers, with or without the `RO` prefix. |

```
curl -X POST https://api.apifirme.dev/rest/v1/companies/batch \
  -H "Authorization: Bearer af_live_..." \
  -H "Content-Type: application/json" \
  -d '{"cuis": ["13548146", "RO4221306"]}'
```

Responses:

| Code | What it means |
|---|---|
| `200` | The companies found, and what became of the other CUIs. |
| `400` | A parameter is missing or not valid; `detail` says which. |
| `401` | The API key is missing or not valid. |
| `429` | The limit per minute or the month's number of requests has been exceeded. Does not count as a request. |

The `200` response is a `BatchResult` object:

| Field | Type | Description |
|---|---|---|
| `data` | array of Company | The companies found, in the order in which they were asked for. |
| `withheld` | array | The CUIs under which there is an entry we serve no data about: a natural person, or one not classified yet. |
| `not_stored` | array | The valid CUIs that are not in the database yet. They cost nothing; asked for by themselves, they are looked up at ANAF. |
| `invalid` | array | What is not a valid CUI, as it was received. |

### GET /rest/v1/companies/search

Find a company without its CUI. Finds a company by its name or by its trade register number, for whoever does not know its CUI. Exactly one of `denumire` and `nr_reg_com` is given. By name, the companies called exactly that come first, with or without the legal form (“dedeman” finds “DEDEMAN SRL”), then those whose name begins with it, then those that contain all its words, in any order. Diacritics and capital letters do not matter, and companies in operation come before the others. The answer has at most 10 companies, each with a few fields, and no next page: `more` says that others match as well, and then the county or a more exact name helps. A company's full data is requested with its CUI. Natural persons and secondary seats are not among the results. A search costs one request and has a limit per minute of its own, by plan; beyond it the answer is `429` with `searches-limited`.

| Parameter | Where | Description |
|---|---|---|
| `denumire` | in the query | The company's name or a part of it: at least one word of three letters or digits, beside the legal form. |
| `nr_reg_com` | in the query | The trade register number, in the old form (`J32/508/2000`) or the new one (`J2000000508324`), whichever way we store it. Numbers that begin with `F` belong to natural persons, about whom no data is served. |
| `judet` | in the query | With `denumire` only: one county's code, as on number plates (`SB`, `B` for Bucharest). |

```
curl https://api.apifirme.dev/rest/v1/companies/search \
  -H "Authorization: Bearer af_live_..."
```

Responses:

| Code | What it means |
|---|---|
| `200` | The companies found. |
| `400` | A parameter is missing or not valid; `detail` says which. |
| `401` | The API key is missing or not valid. |
| `429` | The limit per minute or the month's number of requests has been exceeded. Does not count as a request. |

The `200` response is a `SearchResult` object:

| Field | Type | Description |
|---|---|---|
| `data` | array of CompanyFound | The companies found, the closest first: at most 10. |
| `more` | boolean | `true` if other companies match beside those returned. |

### GET /rest/v1/companies/{cui}/events

What changed at a company. The changes observed at the company since we have been following it, the most recent first (200 at most): registration for VAT or removal from it, VAT on collection, split VAT payment, being declared inactive and being reactivated, being struck off, a change of status, name, address or CAEN code, RO e-Factura. A change is noted when ANAF's answer about the company differs from its previous answer; what was before our first answer from ANAF does not appear, and the list is empty if nothing has been observed. That a company is new is not an event here: that is what `registrations` is for.

| Parameter | Where | Description |
|---|---|---|
| `cui` | in the path, required | The unique registration code (CUI), with or without the `RO` prefix, for example `13548146`. |

```
curl https://api.apifirme.dev/rest/v1/companies/13548146/events \
  -H "Authorization: Bearer af_live_..."
```

Responses:

| Code | What it means |
|---|---|
| `200` | The changes observed at the company. |
| `400` | A parameter is missing or not valid; `detail` says which. |
| `401` | The API key is missing or not valid. |
| `404` | Does not exist: an unregistered CUI, a natural person, or no data for the day or year requested. |
| `429` | The limit per minute or the month's number of requests has been exceeded. Does not count as a request. |

The `200` response is a `EventsList` object:

| Field | Type | Description |
|---|---|---|
| `cui` | integer | The company's CUI. |
| `events` | array of Event | The changes observed, the most recent first. |

### GET /rest/v1/invoice-check/{cui}

Check before invoicing. Answers, in a single request, what an invoicing program wants to know about a customer: whether it exists, is registered for VAT, applies VAT on collection, is fiscally inactive, is in the RO e-Factura register. If ANAF's last answer about the company is more than 24 hours old, the company is asked about again right away; `checked_at` says how old the data is. A valid CUI that ANAF does not know gets `200` with `exists: false`.

| Parameter | Where | Description |
|---|---|---|
| `cui` | in the path, required | The unique registration code (CUI), with or without the `RO` prefix, for example `13548146`. |

```
curl https://api.apifirme.dev/rest/v1/invoice-check/13548146 \
  -H "Authorization: Bearer af_live_..."
```

Responses:

| Code | What it means |
|---|---|
| `200` | The result of the check. |
| `202` | The company is being looked up at ANAF and did not arrive in time. Repeat the request after the number of seconds in `Retry-After`. Does not count as a request. |
| `400` | A parameter is missing or not valid; `detail` says which. |
| `401` | The API key is missing or not valid. |
| `404` | Does not exist: an unregistered CUI, a natural person, or no data for the day or year requested. |
| `429` | The limit per minute or the month's number of requests has been exceeded. Does not count as a request. |
| `503` | The company is not in the database and ANAF could not be asked right now. Does not count as a request. |

The `200` response is a `InvoiceCheck` object:

| Field | Type | Description |
|---|---|---|
| `cui` | integer | The unique registration code (CUI). |
| `exists` | boolean | Whether ANAF knows a taxpayer with this CUI. |
| `denumire` | string or null | The name. |
| `valid_for_invoicing` | boolean | Whether the company exists, is not struck off and is not fiscally inactive. |
| `vat_registered` | boolean | Whether it is registered for VAT. |
| `vat_number` | string or null | The VAT number (`RO` followed by the CUI), if it is registered for VAT. |
| `vat_on_collection` | boolean | Whether it applies VAT on collection. |
| `split_vat` | boolean | Whether it applies split VAT payment. |
| `inactive` | boolean | Whether it is declared fiscally inactive. |
| `struck_off` | boolean | Whether it is struck off. |
| `efactura_registered` | boolean | Whether it is in the RO e-Factura register. |
| `stare` | string or null | The status, in one word, as in the company's data. |
| `checked_at` | date-time or null | When ANAF last answered about the company. |

## Financial statements

The annual financial statements published by the Ministry of Finance.

### GET /rest/v1/companies/{cui}/financials

A company's financial statements. All annual financial statements of the company, the most recent year first. The list is empty if there are none.

| Parameter | Where | Description |
|---|---|---|
| `cui` | in the path, required | The unique registration code (CUI), with or without the `RO` prefix, for example `13548146`. |

```
curl https://api.apifirme.dev/rest/v1/companies/13548146/financials \
  -H "Authorization: Bearer af_live_..."
```

Responses:

| Code | What it means |
|---|---|
| `200` | The company's financial statements. |
| `400` | A parameter is missing or not valid; `detail` says which. |
| `401` | The API key is missing or not valid. |
| `404` | Does not exist: an unregistered CUI, a natural person, or no data for the day or year requested. |
| `429` | The limit per minute or the month's number of requests has been exceeded. Does not count as a request. |

The `200` response is a `FinancialsList` object:

| Field | Type | Description |
|---|---|---|
| `cui` | integer | The company's CUI. |
| `financials` | array of Statement | The financial statements, the most recent year first. |

### GET /rest/v1/companies/{cui}/financials/{year}

The financial statement of one year.

| Parameter | Where | Description |
|---|---|---|
| `cui` | in the path, required | The unique registration code (CUI), with or without the `RO` prefix, for example `13548146`. |
| `year` | in the path, required | The year of the financial statement, for example `2025`. |

```
curl https://api.apifirme.dev/rest/v1/companies/13548146/financials/2025 \
  -H "Authorization: Bearer af_live_..."
```

Responses:

| Code | What it means |
|---|---|
| `200` | The financial statement of that year. |
| `400` | A parameter is missing or not valid; `detail` says which. |
| `401` | The API key is missing or not valid. |
| `404` | Does not exist: an unregistered CUI, a natural person, or no data for the day or year requested. |
| `429` | The limit per minute or the month's number of requests has been exceeded. Does not count as a request. |

The `200` response is a `Statement` object:

| Field | Type | Description |
|---|---|---|
| `cui` | integer | The company's CUI. |
| `an` | integer | The year of the financial statement. |
| `tip_raportare` | string | The Ministry of Finance's reporting type: `UU` (abbreviated balance sheet), `BL_BS_SL`, `IR` (IFRS), `ONG` (non-profit organisations), ... |
| `tip_raportare_descriere` | string or null | What kind of taxpayers file this type. |
| `caen` | integer or null | The CAEN code from the financial statement. |
| `caen_denumire` | string or null | The name of the code, in the classification in force in that year. |
| `active_imobilizate` | integer or null | Fixed assets, total. In whole lei; `null` if the ministry does not publish the value. |
| `active_circulante` | integer or null | Current assets, total. In whole lei; `null` if the ministry does not publish the value. |
| `stocuri` | integer or null | Inventories. In whole lei; `null` if the ministry does not publish the value. |
| `creante` | integer or null | Receivables. In whole lei; `null` if the ministry does not publish the value. |
| `casa_banca` | integer or null | Cash and bank accounts. In whole lei; `null` if the ministry does not publish the value. |
| `cheltuieli_avans` | integer or null | Prepaid expenses. In whole lei; `null` if the ministry does not publish the value. |
| `datorii` | integer or null | Liabilities. In whole lei; `null` if the ministry does not publish the value. |
| `venituri_avans` | integer or null | Deferred income. In whole lei; `null` if the ministry does not publish the value. |
| `provizioane` | integer or null | Provisions. In whole lei; `null` if the ministry does not publish the value. |
| `capitaluri_total` | integer or null | Equity, total. In whole lei; `null` if the ministry does not publish the value. |
| `capital_social` | integer or null | Subscribed and paid-up share capital. In whole lei; `null` if the ministry does not publish the value. |
| `patrimoniul_regiei` | integer or null | Patrimony of the autonomous state enterprise (regie). In whole lei; `null` if the ministry does not publish the value. |
| `cifra_afaceri` | integer or null | Net turnover. In whole lei; `null` if the ministry does not publish the value. |
| `venituri_totale` | integer or null | Total income. In whole lei; `null` if the ministry does not publish the value. |
| `cheltuieli_totale` | integer or null | Total expenses. In whole lei; `null` if the ministry does not publish the value. |
| `profit_brut` | integer or null | Gross profit. In whole lei; `null` if the ministry does not publish the value. |
| `pierdere_bruta` | integer or null | Gross loss. In whole lei; `null` if the ministry does not publish the value. |
| `profit_net` | integer or null | Net profit. In whole lei; `null` if the ministry does not publish the value. |
| `pierdere_neta` | integer or null | Net loss. In whole lei; `null` if the ministry does not publish the value. |
| `nr_mediu_salariati` | integer or null | Average number of employees. In whole lei; `null` if the ministry does not publish the value. |
| `alti_indicatori` | array | The indicators this reporting type has in addition, each with `cod` (code), `denumire` (name) and `valoare` (value). |
| `updated_at` | date-time | When the statement last changed in our database. |
| `sources` | array of Source | The dataset the statement comes from, when the ministry published it and when we retrieved it. |

## New companies

Newly registered companies, for those who want to learn every day which companies have appeared.

### GET /rest/v1/registrations

Newly registered companies. The legal persons registered in the requested period, in the order in which they were indexed, paginated by cursor. The period is limited by the plan; a longer period is refused with `400`, not silently shortened. Whoever asks daily must continue from `next_cursor` or, after the last page, send as `discovered_after` the `discovered_at` value of the last company received (not the time of their own clock and not the registration date): a company registered on a Monday may be indexed on the Friday, and companies become visible strictly in the order of `discovered_at`, so none is missed this way. A company can appear a second time, with a new `discovered_at`: when its CAEN code, county or legal form becomes known after it was indexed (a quarter of the new companies have no CAEN code yet on their first day). That way those who filter by these receive it too; it is recognised by its `cui`. Secondary seats and natural persons do not appear. Plans that do not include the list get `403`.

| Parameter | Where | Description |
|---|---|---|
| `days` | in the query | The companies registered in the last so many days. Not together with `registered_after`. |
| `registered_after` | in the query | The companies registered on or after this day. |
| `discovered_after` | in the query | Only the companies we indexed after this moment (`2026-10-01T08:00:00Z`) or this day. |
| `caen` | in the query | One or more four-digit CAEN codes, separated by commas. |
| `caen_prefix` | in the query | The first two digits (the division) or three (the group) of the CAEN code. |
| `judet` | in the query | One or more county codes (`SB`, `B`), separated by commas. |
| `forma_juridica` | in the query | One or more legal forms, by the trade register's codes (`SRL`, `SA`), separated by commas. |
| `limit` | in the query | How many companies per page, between 1 and 500. 100 by default. |
| `cursor` | in the query | The `next_cursor` value from the previous answer. |

```
curl https://api.apifirme.dev/rest/v1/registrations?days=7&judet=SB \
  -H "Authorization: Bearer af_live_..."
```

Responses:

| Code | What it means |
|---|---|
| `200` | A page of new companies. |
| `400` | A parameter is missing or not valid; `detail` says which. |
| `401` | The API key is missing or not valid. |
| `403` | The plan does not include this request; `upgrade_url` leads to the plans. |
| `429` | The limit per minute or the month's number of requests has been exceeded. Does not count as a request. |

The `200` response is a `RegistrationsPage` object:

| Field | Type | Description |
|---|---|---|
| `data` | array of Registration | The companies, in the order in which they were indexed. |
| `next_cursor` | string or null | To be sent as `cursor` for the next page; `null` on the last page. |
| `filter` | object | The filter applied, with the resulting period. |

### GET /rest/v1/registrations/count

The number of newly registered companies. How many companies match the filter. Included in every plan, over at most the longest period any plan offers. Without `days` or `registered_after` the last 30 days are counted.

| Parameter | Where | Description |
|---|---|---|
| `days` | in the query | The companies registered in the last so many days. Not together with `registered_after`. |
| `registered_after` | in the query | The companies registered on or after this day. |
| `discovered_after` | in the query | Only the companies we indexed after this moment (`2026-10-01T08:00:00Z`) or this day. |
| `caen` | in the query | One or more four-digit CAEN codes, separated by commas. |
| `caen_prefix` | in the query | The first two digits (the division) or three (the group) of the CAEN code. |
| `judet` | in the query | One or more county codes (`SB`, `B`), separated by commas. |
| `forma_juridica` | in the query | One or more legal forms, by the trade register's codes (`SRL`, `SA`), separated by commas. |

```
curl https://api.apifirme.dev/rest/v1/registrations/count?days=30&caen_prefix=62 \
  -H "Authorization: Bearer af_live_..."
```

Responses:

| Code | What it means |
|---|---|
| `200` | The number of companies. |
| `400` | A parameter is missing or not valid; `detail` says which. |
| `401` | The API key is missing or not valid. |
| `429` | The limit per minute or the month's number of requests has been exceeded. Does not count as a request. |

The `200` response is a `RegistrationsCount` object:

| Field | Type | Description |
|---|---|---|
| `count` | integer | The number of companies that match the filter. |
| `filter` | object | The filter applied, with the resulting period. |
| `upgrade_required_for_records` | boolean | Whether the list of these companies needs a higher plan than the key's. |

## BNR exchange rates

The reference exchange rate of the National Bank of Romania (BNR), since 2005.

### GET /rest/v1/fx/latest

The latest published rate.

```
curl https://api.apifirme.dev/rest/v1/fx/latest \
  -H "Authorization: Bearer af_live_..."
```

Responses:

| Code | What it means |
|---|---|
| `200` | The rates of the last banking day. |
| `401` | The API key is missing or not valid. |
| `404` | Does not exist: an unregistered CUI, a natural person, or no data for the day or year requested. |
| `429` | The limit per minute or the month's number of requests has been exceeded. Does not count as a request. |

The `200` response is a `FxDay` object:

| Field | Type | Description |
|---|---|---|
| `date` | date | The requested day. |
| `effective_date` | date | The banking day for which the rate was published. |
| `base` | string | The currency the rates are expressed in: `RON`. |
| `rates` | object | For each currency: `rate`, in lei for `multiplier` units of the currency. |
| `source` | object | The source of the rate and when it was retrieved. |

### GET /rest/v1/fx/{date}

The rate of one day. For a day on which BNR does not publish (a weekend, a public holiday) the rate of the last banking day before it is returned; `effective_date` says which day that is.

| Parameter | Where | Description |
|---|---|---|
| `date` | in the path, required | The day, for example `2026-10-02`. |

```
curl https://api.apifirme.dev/rest/v1/fx/2026-10-02 \
  -H "Authorization: Bearer af_live_..."
```

Responses:

| Code | What it means |
|---|---|
| `200` | The rates valid on that day. |
| `400` | A parameter is missing or not valid; `detail` says which. |
| `401` | The API key is missing or not valid. |
| `404` | Does not exist: an unregistered CUI, a natural person, or no data for the day or year requested. |
| `429` | The limit per minute or the month's number of requests has been exceeded. Does not count as a request. |

The `200` response is a `FxDay` object:

| Field | Type | Description |
|---|---|---|
| `date` | date | The requested day. |
| `effective_date` | date | The banking day for which the rate was published. |
| `base` | string | The currency the rates are expressed in: `RON`. |
| `rates` | object | For each currency: `rate`, in lei for `multiplier` units of the currency. |
| `source` | object | The source of the rate and when it was retrieved. |

### GET /rest/v1/fx/series

A currency's rate over a period.

| Parameter | Where | Description |
|---|---|---|
| `currency` | in the query, required | The currency code, for example `EUR`. |
| `from` | in the query, required | The first day. |
| `to` | in the query, required | The last day. |

```
curl https://api.apifirme.dev/rest/v1/fx/series?currency=EUR&from=2026-09-01&to=2026-09-30 \
  -H "Authorization: Bearer af_live_..."
```

Responses:

| Code | What it means |
|---|---|
| `200` | The rate of every banking day of the period. |
| `400` | A parameter is missing or not valid; `detail` says which. |
| `401` | The API key is missing or not valid. |
| `429` | The limit per minute or the month's number of requests has been exceeded. Does not count as a request. |

The `200` response is a `FxSeries` object:

| Field | Type | Description |
|---|---|---|
| `currency` | string | The currency. |
| `base` | string | `RON`. |
| `from` | date | The first day of the period. |
| `to` | date | The last day of the period. |
| `series` | array | For each banking day: `date`, `rate` and `multiplier`. |

### GET /rest/v1/fx/convert

Conversion between two currencies.

| Parameter | Where | Description |
|---|---|---|
| `from` | in the query, required | The currency of the amount, for example `EUR`. |
| `to` | in the query, required | The currency of the result, for example `RON`. |
| `amount` | in the query, required | The amount. |
| `date` | in the query | The day of the rate. Today by default. |

```
curl https://api.apifirme.dev/rest/v1/fx/convert?from=EUR&to=RON&amount=100 \
  -H "Authorization: Bearer af_live_..."
```

Responses:

| Code | What it means |
|---|---|
| `200` | The amount converted at BNR's rate. |
| `400` | A parameter is missing or not valid; `detail` says which. |
| `401` | The API key is missing or not valid. |
| `404` | Does not exist: an unregistered CUI, a natural person, or no data for the day or year requested. |
| `429` | The limit per minute or the month's number of requests has been exceeded. Does not count as a request. |

The `200` response is a `FxConversion` object:

| Field | Type | Description |
|---|---|---|
| `from` | string | The currency of the amount. |
| `to` | string | The currency of the result. |
| `amount` | number | The amount. |
| `result` | number | The converted amount. |
| `rate` | number | The rate used, from `from` to `to`. |
| `date` | date | The requested day. |
| `effective_date` | date | The banking day of the rate used. |

## Validation

Checks that need no key.

### GET /rest/v1/validate/cui/{cui}

Check a CUI. Checks the form and the check digit. Does not say whether a company with this CUI exists. No key, free of charge.

Needs no key.

| Parameter | Where | Description |
|---|---|---|
| `cui` | in the path, required | The unique registration code (CUI), with or without the `RO` prefix, for example `13548146`. |

```
curl https://api.apifirme.dev/rest/v1/validate/cui/13548146
```

Responses:

| Code | What it means |
|---|---|
| `200` | The result of the check. |

The `200` response is a `CuiValidation` object:

| Field | Type | Description |
|---|---|---|
| `cui` | integer or string | The CUI as a number, if it is valid; otherwise the text received. |
| `valid` | boolean | Whether the form and the check digit are correct. |

## Webhooks

Plans that include the list of new companies can receive every new company at an address of their own, without asking. The address (`https` only) and the filters are registered in the account, where the secret the messages are signed with is shown, once. The account also shows the latest deliveries, with how each one ended.

### POST `company.registered`

Sent to the address registered in the account, a few minutes after a new company that matches the webhook's filters has entered our database. The `X-Apifirme-Signature` header has the form `t=<Unix time>,v1=<signature>`; the signature is HMAC-SHA256, in hexadecimal, over `<t>.<request body>`, with the webhook's secret as the key. Any `2xx` answer within ten seconds confirms receipt; otherwise the delivery is retried up to eight times, with ever longer pauses, for almost a day. The same company always has the same `id`, so a repeated delivery can be recognised, and each webhook receives a company only once: the first time it matches its filters. After five deliveries in a row have been given up on, the webhook is switched off and can be switched on again in the account. A test message (`test: true`) can be sent from the account at any time.

The body is a [RegistrationEvent](#registrationevent) object. Verifying the signature, for example in a shell:

```
printf '%s.%s' "$t" "$body" | openssl dgst -sha256 -hmac "$secret"
```

The result must be equal to `v1` from the header; a `t` older than a few minutes can be rejected.

## Structures

The objects the responses above refer to.

### Company

| Field | Type | Description |
|---|---|---|
| `cui` | integer | The unique registration code (CUI). |
| `denumire` | string or null | The name. |
| `nr_reg_com` | string or null | The registration number in the trade register. |
| `euid` | string or null | The European unique identifier (source: ONRC, the trade register). |
| `sediu_secundar` | boolean | Whether it is the secondary seat of a company, registered at ANAF with a CUI of its own. |
| `forma_juridica` | string or null | The legal form as ANAF writes it. |
| `forma_juridica_cod` | string or null | The legal form from the trade register: `SRL`, `SA`, ... |
| `stare` | string or null | The status, in one word: `FUNCTIUNE` (in operation), `SUSPENDARE` (suspended), `REORGANIZARE JUDICIARA` (judicial reorganisation), `INSOLVENTA` (insolvency), `DIZOLVARE` (dissolution), `LICHIDARE` (liquidation), `FALIMENT` (bankruptcy), `RADIERE` (struck off). |
| `stari_registru` | array | All statuses and remarks from the trade register, each with `cod` (code) and `denumire` (name). |
| `caen` | integer or null | The CAEN code of the main activity (source: ANAF). CAEN is the Romanian classification of economic activities, aligned with NACE. |
| `caen_denumire` | string or null | The name of the CAEN code, in the classification in force. |
| `caen_autorizate` | array or null | The activities the company is authorised for, from the trade register's open data: each with `cod` (code), `versiune` (the version of the CAEN classification it was declared in: 3 is the one of 2025, 2 the one of 2008, 1 the one of 2003, 0 the one of 1998) and `denumire` (name), the newest classification first. The register also keeps what was declared in the old classifications, so the same activity can appear with codes of several versions. `null` when they are not known (the company is not in the register's dataset), which does not mean that it has none. |
| `data_inregistrare` | date or null | The date of fiscal registration (source: ANAF). |
| `data_inmatriculare` | date or null | The date of entry in the trade register (source: ONRC). |
| `web` | string or null | The web address declared to the trade register. |
| `judet` | string or null | The county code of the registered office: `SB`, `B`, ... |
| `localitate` | string or null | The locality of the registered office. |
| `adresa_completa` | string or null | The address of the registered office, as one text. |
| `cod_postal` | string or null | The postal code. |
| `telefon` | string or null | The telephone number declared to ANAF. |
| `scp_tva` | boolean | Whether it is registered for VAT. |
| `data_inceput_tva` | date or null | Since when it has been registered for VAT. |
| `data_sfarsit_tva` | date or null | Until when it was registered, if it no longer is. |
| `tva_incasare` | boolean | Whether it applies VAT on collection. |
| `split_tva` | boolean | Whether it applies split VAT payment. |
| `status_inactiv` | boolean | Whether it is declared fiscally inactive. |
| `data_inactivare` | date or null | Since when it has been inactive. |
| `e_factura` | boolean | Whether it is in the RO e-Factura register. |
| `updated_at` | date-time or null | When something in the company's data last changed. |
| `checked_at` | date-time or null | When ANAF was last asked about the company; `null` if not yet. |
| `sources` | array of Source | Which sources the company's data comes from, and since when. |

### Source

| Field | Type | Description |
|---|---|---|
| `source` | string | The source's key: `anaf_ws`, `onrc_bulk`, `mf_financials`. |
| `name` | string | The source's name. |
| `publisher` | string | The institution that publishes the data. |
| `url` | string | The source's address. |
| `licence` | string or null | The licence under which the data is published, where one is stated. |
| `dataset` | string or null | The dataset the information was last taken from, for the sources that publish files. |
| `dataset_url` | string or null | The dataset's page (for financial statements). |
| `published_at` | date-time or null | When the institution published the file (for financial statements). |
| `first_retrieved_at` | date-time or null | When the source first supplied the information. |
| `last_retrieved_at` | date-time or null | When it last supplied it. |
| `import_run_id` | integer or null | The import run that brought the information. |

### Event

| Field | Type | Description |
|---|---|---|
| `type` | string | The kind of change: `vat_registered`, `vat_deregistered`, `vat_on_collection_started`, `vat_on_collection_ended`, `split_vat_started`, `split_vat_ended`, `inactivated`, `reactivated`, `efactura_registered`, `efactura_deregistered`, `struck_off`, `status_changed`, `name_changed`, `address_changed`, `caen_changed`. |
| `old` | object or null | The value before, under the name of the field that changed (`denumire`, `adresa_completa`, `stare`, `caen`); `null` when the event is a beginning or an end. |
| `new` | object or null | The value after, likewise. |
| `occurred_on` | date or null | The day the source gives for the change, where it gives one (for example the beginning of the VAT period). |
| `detected_at` | date-time | When we observed the change. |
| `source` | string | The key of the source in whose answer the change appeared: `anaf_ws`. |

### Statement

| Field | Type | Description |
|---|---|---|
| `cui` | integer | The company's CUI. |
| `an` | integer | The year of the financial statement. |
| `tip_raportare` | string | The Ministry of Finance's reporting type: `UU` (abbreviated balance sheet), `BL_BS_SL`, `IR` (IFRS), `ONG` (non-profit organisations), ... |
| `tip_raportare_descriere` | string or null | What kind of taxpayers file this type. |
| `caen` | integer or null | The CAEN code from the financial statement. |
| `caen_denumire` | string or null | The name of the code, in the classification in force in that year. |
| `active_imobilizate` | integer or null | Fixed assets, total. In whole lei; `null` if the ministry does not publish the value. |
| `active_circulante` | integer or null | Current assets, total. In whole lei; `null` if the ministry does not publish the value. |
| `stocuri` | integer or null | Inventories. In whole lei; `null` if the ministry does not publish the value. |
| `creante` | integer or null | Receivables. In whole lei; `null` if the ministry does not publish the value. |
| `casa_banca` | integer or null | Cash and bank accounts. In whole lei; `null` if the ministry does not publish the value. |
| `cheltuieli_avans` | integer or null | Prepaid expenses. In whole lei; `null` if the ministry does not publish the value. |
| `datorii` | integer or null | Liabilities. In whole lei; `null` if the ministry does not publish the value. |
| `venituri_avans` | integer or null | Deferred income. In whole lei; `null` if the ministry does not publish the value. |
| `provizioane` | integer or null | Provisions. In whole lei; `null` if the ministry does not publish the value. |
| `capitaluri_total` | integer or null | Equity, total. In whole lei; `null` if the ministry does not publish the value. |
| `capital_social` | integer or null | Subscribed and paid-up share capital. In whole lei; `null` if the ministry does not publish the value. |
| `patrimoniul_regiei` | integer or null | Patrimony of the autonomous state enterprise (regie). In whole lei; `null` if the ministry does not publish the value. |
| `cifra_afaceri` | integer or null | Net turnover. In whole lei; `null` if the ministry does not publish the value. |
| `venituri_totale` | integer or null | Total income. In whole lei; `null` if the ministry does not publish the value. |
| `cheltuieli_totale` | integer or null | Total expenses. In whole lei; `null` if the ministry does not publish the value. |
| `profit_brut` | integer or null | Gross profit. In whole lei; `null` if the ministry does not publish the value. |
| `pierdere_bruta` | integer or null | Gross loss. In whole lei; `null` if the ministry does not publish the value. |
| `profit_net` | integer or null | Net profit. In whole lei; `null` if the ministry does not publish the value. |
| `pierdere_neta` | integer or null | Net loss. In whole lei; `null` if the ministry does not publish the value. |
| `nr_mediu_salariati` | integer or null | Average number of employees. In whole lei; `null` if the ministry does not publish the value. |
| `alti_indicatori` | array | The indicators this reporting type has in addition, each with `cod` (code), `denumire` (name) and `valoare` (value). |
| `updated_at` | date-time | When the statement last changed in our database. |
| `sources` | array of Source | The dataset the statement comes from, when the ministry published it and when we retrieved it. |

### Registration

| Field | Type | Description |
|---|---|---|
| `cui` | integer | The unique registration code (CUI). |
| `denumire` | string or null | The name. |
| `nr_reg_com` | string or null | The registration number in the trade register. |
| `euid` | string or null | The European unique identifier (source: ONRC, the trade register). |
| `sediu_secundar` | boolean | Whether it is the secondary seat of a company, registered at ANAF with a CUI of its own. |
| `forma_juridica` | string or null | The legal form as ANAF writes it. |
| `forma_juridica_cod` | string or null | The legal form from the trade register: `SRL`, `SA`, ... |
| `stare` | string or null | The status, in one word: `FUNCTIUNE` (in operation), `SUSPENDARE` (suspended), `REORGANIZARE JUDICIARA` (judicial reorganisation), `INSOLVENTA` (insolvency), `DIZOLVARE` (dissolution), `LICHIDARE` (liquidation), `FALIMENT` (bankruptcy), `RADIERE` (struck off). |
| `stari_registru` | array | All statuses and remarks from the trade register, each with `cod` (code) and `denumire` (name). |
| `caen` | integer or null | The CAEN code of the main activity (source: ANAF). CAEN is the Romanian classification of economic activities, aligned with NACE. |
| `caen_denumire` | string or null | The name of the CAEN code, in the classification in force. |
| `caen_autorizate` | array or null | The activities the company is authorised for, from the trade register's open data: each with `cod` (code), `versiune` (the version of the CAEN classification it was declared in: 3 is the one of 2025, 2 the one of 2008, 1 the one of 2003, 0 the one of 1998) and `denumire` (name), the newest classification first. The register also keeps what was declared in the old classifications, so the same activity can appear with codes of several versions. `null` when they are not known (the company is not in the register's dataset), which does not mean that it has none. |
| `data_inregistrare` | date or null | The date of fiscal registration (source: ANAF). |
| `data_inmatriculare` | date or null | The date of entry in the trade register (source: ONRC). |
| `web` | string or null | The web address declared to the trade register. |
| `judet` | string or null | The county code of the registered office: `SB`, `B`, ... |
| `localitate` | string or null | The locality of the registered office. |
| `adresa_completa` | string or null | The address of the registered office, as one text. |
| `cod_postal` | string or null | The postal code. |
| `telefon` | string or null | The telephone number declared to ANAF. |
| `scp_tva` | boolean | Whether it is registered for VAT. |
| `data_inceput_tva` | date or null | Since when it has been registered for VAT. |
| `data_sfarsit_tva` | date or null | Until when it was registered, if it no longer is. |
| `tva_incasare` | boolean | Whether it applies VAT on collection. |
| `split_tva` | boolean | Whether it applies split VAT payment. |
| `status_inactiv` | boolean | Whether it is declared fiscally inactive. |
| `data_inactivare` | date or null | Since when it has been inactive. |
| `e_factura` | boolean | Whether it is in the RO e-Factura register. |
| `updated_at` | date-time or null | When something in the company's data last changed. |
| `checked_at` | date-time or null | When ANAF was last asked about the company; `null` if not yet. |
| `sources` | array of Source | Which sources the company's data comes from, and since when. |
| `registered_on` | date or null | The registration date: the entry in the trade register where it is known, otherwise the fiscal registration at ANAF. |
| `discovered_at` | date-time | The company's place in the order of discovery: the moment it entered our database or, if its CAEN code, county or legal form became known later, that moment. |

### RegistrationEvent

| Field | Type | Description |
|---|---|---|
| `id` | string | Identifies the event: `company.registered:` followed by the CUI. The same on every retry. |
| `type` | string | The kind of event: `company.registered`. |
| `test` | boolean | `true` for a test message requested from the account: it has the form of a real one, with some company, and an `id` that starts with `test:`. |
| `created_at` | date-time | When the event was created. |
| `data` | Registration | The company, exactly as `GET /rest/v1/registrations` returns it. |

### Problem

| Field | Type | Description |
|---|---|---|
| `type` | string | The address that identifies the kind of error. |
| `title` | string | The error, in short. |
| `status` | integer | The HTTP status code. |
| `detail` | string | What exactly went wrong. |
