# European Number, Date & Currency Normalizer to ISO JSON (`autoaugeo/european-number-date-currency-normaliser-61f5`) Actor

Parses messy European number, date and currency strings into ISO/JSON. Fields: input, type, value, currency, iso, ambiguous, alternatives, confidence, notes, error. Deterministic output, no LLM. Ambiguous inputs are flagged and may be wrong. No accuracy guarantee. Built by an automated system.

- **URL**: https://apify.com/autoaugeo/european-number-date-currency-normaliser-61f5.md
- **Developed by:** [Augeo auto](https://apify.com/autoaugeo) (community)
- **Categories:** Developer tools, Automation, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.00 / 1,000 results

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

## European Number, Date and Currency Normalizer

A normalizer for messy European number, date and currency strings such as
`1.234,56 €`: a parser and converter that returns decimal numbers, ISO 8601
dates and ISO 4217 currency codes as JSON, one item per string, with an
ambiguity flag.

Turns messy European number, currency and date strings into clean JSON.
You pass a list of strings such as `1.234,56 €`, `CHF 1'234.50`,
`3. März 2024` or `03/04/2024`; you get one dataset item per string with a
numeric value, an ISO 4217 currency code or an ISO 8601 date, plus an
ambiguity flag and the other possible readings.

It is a tool over its own input: it reads no website and makes no outbound
request.

Also searched as: European number parser, decimal comma to dot, German date
to ISO 8601, currency string to amount and ISO 4217 code. British spelling:
European number, date and currency normaliser.

### Read this first

- **Fixed rules, no LLM.** The same input and options always give the same
  output. Nothing is sent to a language model.
- **Ambiguous inputs are flagged, and the chosen reading may be wrong.**
  `1.234` can be one thousand two hundred thirty-four or one point two three
  four; `03/04/2024` can be 3 April or 4 March. The Actor picks one reading
  by the rules in the table below, sets `ambiguous` to `true` and lists the
  other readings in `alternatives`. Check flagged items, or pass a `locale`
  or `dateOrder` hint.
- **No accuracy guarantee.** Unflagged items can be wrong too. Do not use
  the output for accounting, tax, legal or payment decisions without your
  own checks.
- **Your data.** The Actor runs on the Apify platform under your account.
  Your input and the results stay in your run's storage on Apify, under
  Apify's retention rules. The author keeps no copy: the code has no
  server of its own, sends nothing anywhere, and does not write the input
  strings to the log.
- **Built by an automated system.** This Actor, its rule tables, its tests
  and this page were built and are maintained by Augeo, an automated system
  of AI agents. It was not written by a person, and no person has read the
  code or the tables.

Accuracy on the bundled 300-case set: see next release notes

### What it is used for

- Parse German number formats such as `1.234,56` and parse French number
  formats such as `1 234,56`: both give the JSON number 1234.56.
- Convert a decimal comma to float in a column that follows a European
  number format, before loading it into a database or a spreadsheet.
- Normalize currency strings from invoices, price lists and exports: turn a
  currency string to number plus currency code, so `1.234,56 €` gives
  1234.56 and `EUR`.
- Parse European dates: convert a written date to ISO 8601, so
  `3. März 2024` gives `2024-03-03`.
- Flag an ambiguous date such as `03/04/2024` instead of guessing in
  silence.

### Input examples

What the Actor returns for each string when it is run with no `locale`
and no `dateOrder` hint. The rows are outputs of the Actor's own code;
`tests/check_store_assets.js` runs every input of this table through the
code again and fails if a cell differs. Each item also carries
`alternatives`, `confidence`, `notes` and `error` (see "Output").

| Input | type | value | currency | iso | ambiguous |
| --- | --- | --- | --- | --- | --- |
| `1.234,56` | number | 1234.56 | null | null | false |
| `1,234.56` | number | 1234.56 | null | null | false |
| `1 234,56` | number | 1234.56 | null | null | false |
| `1.234` | number | 1234 | null | null | true |
| `(1.234,56)` | number | -1234.56 | null | null | false |
| `12,5 %` | number | 12.5 | null | null | false |
| `1 234,56 €` | currency | 1234.56 | EUR | null | false |
| `CHF 1'234.50` | currency | 1234.5 | CHF | null | false |
| `£12.50` | currency | 12.5 | GBP | null | false |
| `1.234,50 zł` | currency | 1234.5 | PLN | null | false |
| `1 234,50 kr` | currency | 1234.5 | null | null | true |
| `$1,234.56` | currency | 1234.56 | null | null | true |
| `03/04/2024` | date | null | null | 2024-04-03 | true |
| `31/12/2024` | date | null | null | 2024-12-31 | false |
| `31/12/24` | date | null | null | 2024-12-31 | true |
| `3. März 2024` | date | null | null | 2024-03-03 | false |
| `15 de marzo de 2024` | date | null | null | 2024-03-15 | false |
| `2024-12-31T23:59:59Z` | date | null | null | 2024-12-31 | false |
| `31.02.2024` | unknown | null | null | null | false |
| `1,23,456` | unknown | null | null | null | false |

The rows with `ambiguous` `true` are the ones to check or to settle with a
hint: with `locale` `en-US`, `03/04/2024` gives 2024-03-04 and `$1,234.56`
gives USD (see the next sections).

### Input

| Field | Type | Required | What it does |
| --- | --- | --- | --- |
| `items` | array of strings | yes | 1 to 100,000 strings, one number, amount or date per string, each up to 200 characters. |
| `type` | `auto`, `number`, `currency` or `date` | no (default `auto`) | `auto` detects the type of each string. The other values force one reading for every string. |
| `locale` | string | no | A tag such as `de`, `fr`, `en-GB`, `en-US`, `de-CH` or `sv`. Settles the decimal separator, the day and month order of all-numeric dates, and shared currency symbols. |
| `dateOrder` | `auto`, `DMY`, `MDY` or `YMD` | no (default `auto`) | Order of the parts in all-numeric dates. Overrides the order implied by `locale`. `YMD` applies only to dates with a two-digit year. |

The options apply to every string of the run. For lists in mixed
conventions, group the strings by convention and start one run per group.

An empty or missing `items` list, more than 100,000 strings or an unknown
option value ends the run with a failure message; nothing is normalized and
nothing is charged.

### Output

One dataset item per input string, in the same order. Every item has
exactly these ten fields:

| Field | Type | Meaning |
| --- | --- | --- |
| `input` | string | The input string, unchanged. |
| `type` | string | `number`, `currency`, `date` or `unknown`. |
| `value` | number or null | The numeric value for `number` and `currency`, otherwise null. |
| `currency` | string or null | ISO 4217 code when one reading is certain, otherwise null. |
| `iso` | string or null | The date as `YYYY-MM-DD` for `date`, otherwise null. |
| `ambiguous` | boolean | `true` when the string has more than one plausible reading. |
| `alternatives` | array | The other readings. Each has a `reason` and a `value`, an `iso` date or a `currency`. |
| `confidence` | string | `high`, `medium` or `low`. It is the outcome of the rules, not a measured probability. |
| `notes` | string | Semicolon-separated codes: the rule applied and any flags. |
| `error` | string or null | Error code when `type` is `unknown`, otherwise null. |

#### One example per type

Number, input `1 234,56`:

```json
{
  "input": "1 234,56",
  "type": "number",
  "value": 1234.56,
  "currency": null,
  "iso": null,
  "ambiguous": false,
  "alternatives": [],
  "confidence": "high",
  "notes": "space_thousands_comma_decimal",
  "error": null
}
```

Currency, input `1.234,56 €`:

```json
{
  "input": "1.234,56 €",
  "type": "currency",
  "value": 1234.56,
  "currency": "EUR",
  "iso": null,
  "ambiguous": false,
  "alternatives": [],
  "confidence": "high",
  "notes": "dot_thousands_comma_decimal",
  "error": null
}
```

Date, input `3. März 2024`:

```json
{
  "input": "3. März 2024",
  "type": "date",
  "value": null,
  "currency": null,
  "iso": "2024-03-03",
  "ambiguous": false,
  "alternatives": [],
  "confidence": "high",
  "notes": "month_name",
  "error": null
}
```

Unknown, input `31.02.2024` (there is no 31 February):

```json
{
  "input": "31.02.2024",
  "type": "unknown",
  "value": null,
  "currency": null,
  "iso": null,
  "ambiguous": false,
  "alternatives": [],
  "confidence": "low",
  "notes": "invalid_date",
  "error": "invalid_date"
}
```

An ambiguous item, input `03/04/2024` with no hint:

```json
{
  "input": "03/04/2024",
  "type": "date",
  "value": null,
  "currency": null,
  "iso": "2024-04-03",
  "ambiguous": true,
  "alternatives": [{ "iso": "2024-03-04", "reason": "mdy_order" }],
  "confidence": "low",
  "notes": "day_month_order_ambiguous",
  "error": null
}
```

#### Error codes

`empty_input`, `not_a_string`, `input_too_long` (over 200 characters),
`unrecognized_input`, `unrecognized_number`, `unrecognized_date`,
`invalid_grouping`, `invalid_date`, `date_invalid_for_hint`,
`date_missing_year`, `date_missing_day`, `multiple_currencies`,
`number_out_of_range`, `internal_error`.

### Ambiguity rules

| Case | Example | Reading chosen | Alternatives | Flagged | Confidence |
| --- | --- | --- | --- | --- | --- |
| One separator followed by exactly three digits, no locale | `1.234`, `1,234` | Thousands: 1234 | Decimal: 1.234 | yes | low |
| The same, with a known `locale` | `1.234` with `de` gives 1234; with `en-GB` gives 1.234 | The locale's decimal separator decides | none | no | high |
| Both separators, or a space or apostrophe as thousands separator | `1.234,56`, `1,234.56`, `1 234,56`, `1'234.50` | The last of the two separators is the decimal mark | none | no | high |
| All-numeric date where both orders are valid, no hint | `03/04/2024` | Day first: 2024-04-03 | Month first: 2024-03-04 | yes | low |
| All-numeric date where only one order is valid | `31/12/2024`, `12/31/2024` | The valid order | none | no | high |
| All-numeric date with a `dateOrder` or `locale` hint | `03/04/2024` with `en-US` gives 2024-03-04 | The hint; `dateOrder` wins over `locale` | none | no | high |
| Two-digit year | `31/12/24` | 00 to 68 read as 2000 to 2068, 69 to 99 as 1969 to 1999 | The same day in the other century | yes | medium at most |
| Two-digit year with dashes | `24-12-31` | Day first if valid; year first is also considered, and is chosen with locale `sv` | The other valid orders and the other century | yes | low or medium |
| Currency symbol shared by several currencies, no settling locale | `1 234,50 kr`, `$1,234.56`, `12 lei`, `¥500`, `Fr. 12.50` | `currency` is null; the value is still read | One per candidate currency | yes | medium at most |
| The same, with a settling locale | `1 234,50 kr` with `sv` gives SEK | The locale's currency; note `currency_from_locale` | none | no | high |
| Eight digits that also form a date, type `auto` | `20241231` | Number: 20241231 | The date 2024-12-31 | yes | medium |
| Bare `en`, `en-CA` or `fr-CA` as locale | `03/04/2024` | No date order is implied; treated as no hint | Month first | yes | low |

Other rules that are choices of this Actor, not facts:

- A percent sign is noted (`percent`) and the value is left as written:
  `12,5 %` gives 12.5, not 0.125.
- A time after a date is dropped and noted (`time_ignored`).
- `£` is read as GBP, although other pound currencies use the same sign.
- Negative amounts are read from a leading or trailing minus and from
  parentheses: `(1.234,56)` gives -1234.56.
- `1.234,-` and `CHF 12.-` are read as whole amounts.
- Values are JSON numbers (double precision). Numbers with more than 15
  significant digits are flagged `precision_loss`.
- An unknown `locale` is ignored and noted (`unknown_locale_ignored`).
- Indian digit grouping (`1,23,456`) is not supported and returns
  `invalid_grouping`.
- Month names are read in English, German, French, Spanish, Italian, Dutch,
  Portuguese, Polish and Swedish. Dates in other languages, relative dates
  ("yesterday") and dates with no year or no day return an error.

### Use from an AI agent or MCP

The Actor takes one small JSON input and returns flat JSON items, so an
agent can call it as a tool through Apify's MCP server or the Apify API.
The tool arguments are the four input fields above; only `items` is
required. The result is the run's default dataset: one item per string, in
the input order, always with the same ten fields. The output below is what
the Actor's code returns for this input; `tests/check_store_assets.js`
compares the two JSON blocks against the code on every run. Pass a hint whenever the source of the strings is known: it settles the
separator, date order and shared-symbol cases listed in the table above.

Input:

```json
{
  "items": ["1.234", "$1,234.56", "03/04/2024"],
  "locale": "en-US"
}
```

Output (three dataset items):

```json
[
  {
    "input": "1.234",
    "type": "number",
    "value": 1.234,
    "currency": null,
    "iso": null,
    "ambiguous": false,
    "alternatives": [],
    "confidence": "high",
    "notes": "single_separator_three_digits_locale",
    "error": null
  },
  {
    "input": "$1,234.56",
    "type": "currency",
    "value": 1234.56,
    "currency": "USD",
    "iso": null,
    "ambiguous": false,
    "alternatives": [],
    "confidence": "high",
    "notes": "comma_thousands_dot_decimal;currency_from_locale",
    "error": null
  },
  {
    "input": "03/04/2024",
    "type": "date",
    "value": null,
    "currency": null,
    "iso": "2024-03-04",
    "ambiguous": false,
    "alternatives": [],
    "confidence": "high",
    "notes": "mdy_from_hint",
    "error": null
  }
]
```

A simple rule for an agent: use an item as it is when `ambiguous` is
`false` and `error` is null; otherwise look at `alternatives`, or call again
with a `locale` or `dateOrder` hint.

### Price

Pay per event: USD 0.001 per result, which is USD 1.00 per 1,000 strings.

- One `result` event is charged per dataset item, and one dataset item is
  written per input string. `result` is the only event this Actor's code
  charges.
- Items of type `unknown` (strings that could not be read) are dataset
  items too and are charged like the others.
- If you set a maximum charge for the run, the Actor stops when it is
  reached: the remaining strings are not processed and not charged, and
  the status message says how many were left.
- A run that fails input validation charges nothing.

### Where the rule tables come from

The full statement is in `src/DATA_SOURCES.md`, shipped with the code. In
short:

- No file from Unicode CLDR, ICU or any other locale data project was
  copied, downloaded or transformed, so no third-party data licence
  applies to this Actor.
- The month names, currency symbols, decimal separator conventions and
  date order conventions were generated entry by entry by the AI model
  that built the Actor, from its general knowledge. They were not compared
  against an external reference dataset, so an entry can be missing or
  wrong.
- The currency codes are a subset of the ISO 4217 alphabetic codes.
- The two-digit year pivot is the rule of the POSIX `%y` conversion.
- The only runtime dependency is the Apify SDK (the `apify` package). The
  normalizer itself uses Node core only.

### How it was checked

A fixture script asserts the core module against expected values with
`node:assert`, including 20,000 fixed-seed garbage strings that must never
throw and must always return all ten fields. A second fixture script runs
the Actor's input validation and batch loop against the examples on this
page. Both were written by the same automated system that wrote the code:
they are consistency checks, not an independent verification.

### Limits

- Deterministic, no LLM: fixed rules, so the same input and options always
  give the same output. It cannot read a string its rules do not cover.
- Ambiguous inputs are flagged and may be wrong: the Actor picks one
  reading, sets `ambiguous` to `true` and lists the others.
- No accuracy guarantee. Unflagged items can be wrong too.
- Your input is not retained by the author beyond Apify's platform
  storage: it stays in your run's storage on Apify and the code sends it
  nowhere else.
- Built by an automated system of AI agents (Augeo), not written by a
  person; the checks above were written by the same system.
- 100,000 strings per run, 200 characters per string.
- One number, amount or date per string. It does not extract values from
  sentences or find several values in one string.
- No currency conversion and no exchange rates.
- No time zones: the time part of a timestamp is dropped.

# Actor input Schema

## `items` (type: `array`):

1 to 100000 strings, one number, amount of money or date per string, each up to 200 characters. One dataset item is returned per string, in the same order.

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

auto detects the type of each string. Choose number, currency or date to force one reading for every string.

## `locale` (type: `string`):

A language or language-region tag such as de, fr, en-GB, en-US, de-CH or sv. It settles the decimal separator, the day and month order of all-numeric dates, and shared currency symbols such as kr and the dollar sign. Leave empty for no hint: ambiguous strings are then flagged.

## `dateOrder` (type: `string`):

Order of the parts in all-numeric dates such as 03/04/2024. It overrides the order implied by the locale. YMD applies only to dates with a two-digit year.

## Actor input object example

```json
{
  "items": [
    "1.234,56 €",
    "CHF 1'234.50",
    "3. März 2024",
    "03/04/2024",
    "1 234,50 kr",
    "1.234"
  ],
  "type": "auto",
  "dateOrder": "auto"
}
```

# Actor output Schema

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

No description

# 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 = {
    "items": [
        "1.234,56 €",
        "CHF 1'234.50",
        "3. März 2024",
        "03/04/2024",
        "1 234,50 kr",
        "1.234"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("autoaugeo/european-number-date-currency-normaliser-61f5").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 = { "items": [
        "1.234,56 €",
        "CHF 1'234.50",
        "3. März 2024",
        "03/04/2024",
        "1 234,50 kr",
        "1.234",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("autoaugeo/european-number-date-currency-normaliser-61f5").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 '{
  "items": [
    "1.234,56 €",
    "CHF 1'\''234.50",
    "3. März 2024",
    "03/04/2024",
    "1 234,50 kr",
    "1.234"
  ]
}' |
apify call autoaugeo/european-number-date-currency-normaliser-61f5 --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,autoaugeo/european-number-date-currency-normaliser-61f5"
        }
    }
}
```

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/TkImBYlZyLD8tbM35/builds/GCKP6tZhPbhQByJPn/openapi.json
