Data Cleaner: Normalize Phone, URL, Price & Date Fields
Pricing
from $0.50 / 1,000 cleaned rows
Data Cleaner: Normalize Phone, URL, Price & Date Fields
Data cleaner from $0.50 per 1,000 rows ($0.80 on the Free plan): normalizes 6 field types in datasets, CSV, JSON or Excel: phone to E.164, email, URL, price and currency, date to ISO, company names. Works on nested fields and flags new, changed and removed rows since the last run.
Pricing
from $0.50 / 1,000 cleaned rows
Rating
0.0
(0)
Developer
Otama Labs
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
Data cleaner for scraped and exported data: give it a JSON array, an Apify dataset or a CSV / JSON / Excel file, and it normalizes 6 field types in every row: phone numbers to E.164, emails, URLs (canonical form, no tracking parameters), prices (amount + ISO currency), dates (ISO 8601) and company names (legal suffixes removed, near-duplicates grouped). It works on nested fields (contact.phone) and can compare with the last run to flag new, changed and removed rows.
From $0.50 per 1,000 rows ($0.80 on the Free plan): 10,000 rows cost $5 to $8. Rows that are left out (unchanged rows with Only new and changed rows) and removed-row markers are free.
What you can use it for
- Clean scraper output before it goes into a CRM, Google Sheets or a database: one phone format, one date format, one URL per site.
- Merge lists from several sources:
company_key/company_groupmatch "Acme Corp." with "ACME Corporation";contact_website_domainmatcheswww.acme.com/?utm_source=xwithhttps://acme.com. - Price monitoring:
$1,299.99,1.299,99 €,¥12,000and2.5k USDbecome numbers with a currency code you can sort and convert. - Track a feed or catalog over time: set a key field (
id,sku,url) and get_change: new / changed / unchanged / removedfor every row. - AI agents: hand messy rows to the Actor and get typed values back, with a reason for every value it could not read.
Output example
Your row stays as it is; the cleaned values are added next to it (dots in nested names become _). Real output of the default input:
{"id": "A-1","company": "Acme Corp.","contact": { "phone": "(415) 555-0132", "website": "www.acme.com/?utm_source=list" },"price": "$1,299.99","updatedAt": "03/04/2026","company_clean": "Acme","company_key": "acme","company_group": "acme","contact_phone_e164": "+14155550132","contact_phone_country": "US","contact_phone_national": "(415) 555-0132","contact_phone_extension": null,"contact_website_canonical": "https://acme.com/","contact_website_domain": "acme.com","price_amount": 1299.99,"price_currency": "USD","updatedAt_iso": "2026-03-04","_issues": [{ "field": "updatedAt", "value": "03/04/2026", "issue": "day and month could be swapped; read as MDY" }]}
Values that cannot be read become null and are listed in _issues with the reason, so nothing is silently guessed.
Field types
| Type | Added columns | Examples |
|---|---|---|
phone | _e164, _country, _national, _extension | (415) 555-0132 → +14155550132; 03-1234-5678 with country JP → +81312345678 |
email | _normalized, _domain | mailto:Ann@Example.COM → Ann@example.com |
url | _canonical, _domain | HTTPS://Shop.Acme.co.uk/products/?utm_source=x → https://shop.acme.co.uk/products, domain acme.co.uk |
price | _amount, _currency | 1.299,00 € → 1299.0 EUR; R$ 10,00 → 10.0 BRL; 2.5k USD → 2500.0 USD |
date | _iso | Oct 5, 2026 → 2026-10-05; 2026年10月1日 → 2026-10-01; unix timestamps; yesterday |
company | _clean, _key, _group | Globex, Inc → Globex, key globex; 株式会社サンプル → サンプル |
How to use
- Paste your rows into Rows (JSON array), or enter an Apify dataset ID (for example the output of a scraper run), or a File URL of a CSV, JSON, JSON Lines or XLSX file.
- Leave Fields and their types empty to detect fields by name (
phone,tel,email,website,url,price,cost,salary,createdAt,date,company,employer...), or name them yourself:{"contact.tel": "phone", "cost": "price", "vendor": "company"}. - Set the country for phone numbers without a
+, the order of numeric dates (03/04/2026is March 4 in MDY, 3 April in DMY) and the currency for$/¥/ plain numbers. - Click Start. The default input cleans 5 sample rows in a few seconds. Download the result as JSON, CSV or Excel, or read it through the API.
To compare with the last run, set Key field (e.g. id) and keep the same Compare name across runs. Turn on Output only new and changed rows to get just the changes (unchanged rows are not output and not charged).
Input
| Field | What it does |
|---|---|
| Rows (JSON array) | Your rows. Nested objects are fine. |
| Apify dataset ID | Clean the items of a dataset instead. |
| File URL | CSV (comma, semicolon, tab or pipe), JSON, JSON Lines or XLSX (first sheet), up to 50 MB. |
| Fields and their types | {"field": "type"}; types phone, email, url, price, date, company. Dots for nested fields. |
| Detect fields by name | On by default. Fields you name win. |
| Country for phone numbers | Two-letter code, default US. |
| Order of numeric dates | MDY (default), DMY or YMD. Day/month values that could be swapped get a note in _issues. |
| Currency for $, ¥, kr | Default USD. $ follows it when it is a dollar currency (CAD, AUD...), ¥ when it is JPY or CNY. |
| Decimal separator | Auto, dot or comma. |
| URL options | Remove tracking parameters (utm_*, gclid, fbclid...) and www. (both on by default). |
| Company name match | 0-100, default 90: how similar cleaned names must be to share a _group. |
| Key field / Compare name / Only new and changed rows | Compare with the last run. |
| Max rows | 0 = all, up to 100,000 rows per run. |
Compare with the last run
With a key field, each row gets _key and _change:
new: the key was not in the last run with this compare name.changed: the original row differs from the last run (the comparison uses your original values, so a relative date likeyesterdaydoes not mark every row as changed).unchanged: same as last time.removed: a key of the last run that is not in this run. These rows contain only_keyand_changeand are free.
The state is saved in your account only when every row was delivered, so a run that hit your cost limit does not hide changes next time.
Pricing
Pay per event:
| Event | Price |
|---|---|
| Cleaned row | $0.80 per 1,000 on the Free plan, $0.70 Bronze, $0.60 Silver, $0.50 Gold and up |
| Actor start | $0.00005 per GB of memory per run (default memory 512 MB = one start) |
Examples: the default input (5 rows) costs under $0.01; 10,000 rows cost $5 to $8. Platform usage is included. Set a maximum cost per run in the run options and the Actor stops at that limit.
Limits
- It checks formats, not existence: a phone number with a valid area code and length is accepted even if nobody uses it.
- A date without a year (
Oct 5) gets the current year and a note in_issues. Text without any digit is only read as a date when it is a relative word (yesterday,today,3 hours ago). - Company names: legal forms (Inc, Ltd, LLC, GmbH, 株式会社...) are removed; brand words are kept. Fuzzy grouping is used for the first 5,000 different names in a run, exact keys after that.
- Files: UTF-8 is best; Shift_JIS and Windows-1252 CSV files are also read. Old
.xlsfiles are not supported (save as.xlsxor CSV). Only the first sheet of an XLSX file is read. - Up to 100,000 rows per run. If a run is cut by Max rows or your cost limit, the comparison state is not saved.
- Running it again on its own output does not clean the added columns again (
_e164,_key... are skipped by the field detection).
For AI Agents
Call the Actor through the Apify API or the Apify MCP server (https://mcp.apify.com/?tools=tamatama/data-normalizer). Example input:
{"data": [{ "name": "Acme Corp.", "tel": "+1 (415) 555-0132", "web": "acme.com", "cost": "€19,90", "date": "23.10.2026" }],"fields": { "name": "company", "tel": "phone", "web": "url", "cost": "price", "date": "date" },"dateOrder": "DMY"}
Read the cleaned values from the dataset and the reasons for unread values from _issues.
FAQ
Does it change my original values? No. The original row is kept and the cleaned values are added as new columns.
Does it guess when a value is ambiguous? It follows your settings (date order, country, currency) and writes a note in _issues for values that could be read two ways, such as 03/04/2026.
Does it check that a phone number or email exists? No. It checks the format and the numbering plan (a valid area code and length), not whether the line or mailbox is in use. It does not contact anyone.
Does it call other services? No. All parsing runs inside the Actor. Only the file URL you give is downloaded.
Can I use it from Make, n8n, Zapier or LangChain? Yes: use the Apify app or integration of the tool and pick this Actor, or call the REST API (POST https://api.apify.com/v2/acts/tamatama~data-normalizer/run-sync-get-dataset-items). To clean a scraper's output automatically, add this Actor as an integration of the scraper's task and pass the dataset ID.
Which countries and currencies? Phone numbers of all countries (Google's libphonenumber data). Currency codes in ISO form and the common symbols (€ £ ₹ ₩ R$ zł and others).
Related Actors
| Actor | What it does |
|---|---|
| PDF to Markdown | PDF, Word, Excel and PowerPoint to Markdown and RAG chunks. |
| PDF OCR | Scanned PDFs and images to text. |
| Website Change Monitor | Text diff of web pages between runs. |
| ATS Jobs | Company job boards (Greenhouse, Lever, Ashby and more) in one format. |
| Remote Jobs | Remote OK and Jobicy remote jobs. |
Libraries
phonenumbers (Apache-2.0, a port of Google's libphonenumber), dateparser (BSD-3-Clause), price-parser (BSD-3-Clause), rapidfuzz (MIT), tldextract (BSD-3-Clause, with its bundled public suffix list), openpyxl (MIT).
Data sources
The Actor reads only the data you give it (rows, a dataset in your account, or a file URL). Use it on data you are allowed to process.