Clean, deduplicate and transform data: an Apify dataset, pasted JSON or a CSV, JSON, JSONL or Excel XLSX file URL. Filter rows, remove duplicates by any field, sort, rename fields, flatten JSON. Get a dataset and an optional CSV or JSONL file: JSON to CSV, CSV to JSON, Excel to CSV or Excel to JSON.
Versions follow MAJOR.MINOR.PATCH (src/version.py); Apify shows MAJOR.MINOR from .actor/actor.json.
Every run logs its version and records it in the RUN_STATS key-value record.
1.3.2 (2026-10-16)
A run that returns nothing (every input bad, private, blocked or unreachable, or an empty input) now ends
SUCCEEDED with an empty dataset and the reason as its status message and in the log, instead of FAILED. Only an
unexpected error still fails the run. RUN_STATS keeps the reason as before.
No change to output fields or price.
1.3.1 (2026-10-07)
An uploaded file is read with the run's own Apify token, so it works when your account keeps storage private (the
default; 1.3.0 fetched the upload's link anonymously, which Apify refuses then). A gzip-compressed upload is
decompressed. If Apify still refuses, the run says how to share that key-value store.
1.3.0 (2026-10-07)
Upload a file: the file field (Or: file URL or upload) now has an upload button, so a CSV, JSON or Excel
file on your computer works without hosting it somewhere first. A link still works as before.
Google Sheets links: a sheet's /edit or share link is read through Google's export (the tab in the link, or
the one named in sheetName); a published sheet's link too. A sheet that isn't shared fails with how to share it.
A path on your own computer (file:///C:/..., C:\...) or an Apify dataset id in the file field now fails
straight away with what to do instead, rather than "not a web address" / "no host". (All three came from real runs
on 2026-10-06.) Same price; no change for datasets, pasted rows or direct links.
1.2.0 (2026-10-05)
Paste rows instead of a link: the new Or: paste rows (JSON array) field (items) takes the rows directly,
e.g. [{"name": "Ada"}, {"name": "Bo"}], so an AI agent or a script holding JSON gets it filtered, deduplicated or
converted to CSV without uploading a file first. A picked dataset still wins; pasted rows win over an integration's
dataset and the file URL. An empty list counts as nothing pasted, so a blank box in the form leaves the file URL
working. Same price.
README: the "Run it automatically" steps no longer say an empty Apify dataset cleans the example file or that
the integration's payload is ignored (both untrue since 1.1.3); the first screen points scraper users to the
worked example; where to download OUTPUT.csv from the API.
Store description: names deduplicate, pasted JSON, XLSX, CSV to JSON and Excel to JSON.
1.1.3 (2026-10-04)
Started by an Actor-to-Actor integration or a webhook with Apify dataset left empty, the run now reads the
finished run's dataset (payload.resource.defaultDatasetId, then resource.defaultDatasetId) instead of the
pre-filled example file. A filled-in dataset still wins. No input-schema, output or price change.
README: the price line no longer compares us with other actors.
1.1.2 (2026-10-04)
No change in what it does, its input or its price.
README: a worked example, "Example: scraper results to a clean CSV": a Google Maps-style scraper's 15 rows (two
searches, so places repeat) filtered, deduplicated by placeId, sorted and trimmed to 12 renamed columns, with the
before and after tables, the input and the CSV from a local run.
README: "Run it automatically after your scraper finishes": the same input as an Actor-to-Actor integration
({{resource.defaultDatasetId}}), and as a webhook on ACTOR.RUN.SUCCEEDED created through the API.
1.1.1 (2026-10-03)
No change in what it does. The file and dataset reader, the row operations and the file export now live in a
shared package (mms_rows) that the new Dataset Diff and Join Datasets actors read with too, so a fix
to reading CSV, JSON or Excel reaches all three. The "that is this run's own output dataset" error now says "pick
the dataset with your rows".
README: Related actors links Dataset Diff and Join Datasets.
1.1.0 (2026-10-02)
Reads Excel files: a link to an .xlsx (or .xlsm) workbook in Or: file URL is detected from the file
itself and read like any other source: one row per spreadsheet row, keyed by the first non-empty row. Numbers and
booleans keep their type, dates become ISO 8601 text, formulas give the value Excel last saved, empty rows are
skipped. Excel: sheet name picks a sheet other than the first, and Excel: header row the row with the column
names when a title sits above the table. Excel to JSON, or to CSV with Also save as a file, in one step.
Numbers are written to Excel's own 15 significant digits (15.7, not the 15.700000000000001 some tools save).
Two new ready-to-run examples: Excel to JSON or CSV, and a table that starts below a title.
Old .xls and password-protected workbooks fail with what to do instead (save as .xlsx or CSV). Workbooks that
would unzip past 1 GB, or 150 MB of text, are refused before they're read.
No change to price, or to how datasets, CSV and JSON files are read.
1.0.8 (2026-10-01)
Waits out longer Retry-After answers: when a site or API asks it to wait before retrying, it now waits up
to 90 seconds (was 30), once per site, with at most 3 minutes of such long waits per run; a longer wait, or a
site that asks again after it was waited out, is reported with the reason instead of stalling the run.
No change to input, output or price.
1.0.7 (2026-10-01)
Remove duplicates by accepts comma-separated fields on one line (name, city), as agents often send them;
before, that line was read as one field name nobody had, so no duplicates were removed. A field whose name has a
comma goes in double quotes.
A row with a broken character (half an emoji, left by a scraper that cut text mid-character) no longer fails the
run: the character becomes �, the row is kept, and the log and RUN_STATS (rowsTextRepaired) count it.
Filters and duplicate removal treat text that looks the same as the same ("é" as one character or as "e" plus an
accent). Your values are written unchanged.
Files saved as UTF-16 (Excel "Unicode Text", Windows PowerShell) are read correctly, and a JSON or JSON Lines file
in Windows-1252 is read like a CSV in it, instead of failing or garbling accented letters.
1.0.6 (2026-09-29)
Three more example views (JSON API to CSV, CSV to JSON, top rows) for the new ready-to-run examples. Your own rows are unchanged.
1.0.5 (2026-09-29)
README: step-by-step chaining after any actor through its Integrations tab ({{resource.defaultDatasetId}}).
1.0.4 (2026-09-28)
1 ready-to-run example published on Apify Store and linked from the README.
1.0.3 (2026-09-28)
Dataset schema with an "Example: exchange rates" view for the pre-filled example. Your own rows are unchanged and not validated against it: they keep whatever fields you choose.
1.0.2 (2026-09-28)
Cheaper on paid Apify plans: $0.90 per 1,000 rows on Starter, $0.80 on Scale and $0.70 on Business ($1.00 on the Free plan).
1.0.1 (2026-09-25)
Input field descriptions rewritten for AI agents (Apify's MCP server shows agents only the description, not the
form): each now states its default, its allowed range, the value of each option and how it combines with other
fields. No change to field names, types, defaults or behaviour.
1.0.0 (2026-09-24)
First release.
Reads one source per run: one of the user's own Apify datasets (picked in the input, read page by page with the
run's own token, read-only), or a CSV/TSV, JSON (an array, or a list inside an object via itemsPath) or JSON
Lines file from a URL the user supplies (up to 200 MB, gzip or not; format, delimiter and encoding detected).
Transforms, in this order: flatten nested objects and arrays into dotted fields (optional); filters (==, !=,
>, >=, <, <=, contains, not contains, starts with, ends with, is empty, is not empty; all or
any); remove duplicates by one or more fields (or whole rows with *), keeping the first; sort by one or more
fields, each ascending or descending; keep and rename fields. Dotted paths reach nested values everywhere.
Writes the rows to the default dataset, and optionally also as one CSV or JSONL file in the key-value store
(OUTPUT.csv / OUTPUT.jsonl, up to 100 MB, no extra charge).
Charged per output row through Apify's standard apify-default-dataset-item event ($1.00 per 1,000 rows) plus
the $0.00005 start fee. Rows that are empty after the transformation, or larger than 5 MB, are skipped and never
charged.
Memory-bounded: files stream to disk and are parsed a row at a time; duplicates are remembered as 64-bit
fingerprints (about 110 bytes of memory per distinct row, measured); sorting keeps only the top rows when a row
limit applies, and otherwise holds at most 100 MB of rows (the run says so and writes nothing if the input is
larger). At most 1,000,000 rows are read per run.
Measured locally on a 1,000,000-row, 179 MB CSV: peak memory 96-116 MB when streaming, filtering, deduping and
keeping the top 1,000; 303 MB for a sort that reaches the 100 MB cap. Default memory 1024 MB, minimum 512 MB.
"Max rows per run" input and the maximum cost per run are honoured: without sorting, reading stops as soon as
the limit is covered; with sorting, the limit selects the top rows.
Clear failures: a dataset the run can't read, the run's own output dataset, a missing file, a web page instead
of a file, a JSON object without itemsPath (the error names the lists it found), or an invalid filter all fail
the run with the reason before anything is charged. A source that breaks midway keeps the rows already written
and reports the problem. Fields named in the input that no row has are reported.
Sources (ACTOR-CHECKLIST.md, section 0 and 6):
The actor has no source of its own: it reads what the user names. Apify datasets through Apify's own API
(https://docs.apify.com/api/v2/dataset-items-get ) with the run's token, which reaches only datasets the user can
access; the input field declares READ-only access for limited-permission runs
(https://docs.apify.com/platform/actors/development/actor-definition/input-schema/specification/v1 , read
2026-09-24). Files at user-supplied URLs are fetched through mms_common (robots.txt, private-network guard,
ports 80/443, size cap), the PLAN.md legal-hold "tools that only process URLs or files the user supplies" case.
The default input is the US Treasury Fiscal Data API, v1/accounting/od/rates_of_exchange as CSV
(https://fiscaldata.treasury.gov/api-documentation/ , read 2026-09-24): "Our API is open, meaning that it does not
require a user account or registration for a token", and "The data is offered free, without restriction, and
available to copy, adapt, redistribute, or otherwise use for non-commercial or commercial purposes." Its
robots.txt allows the path. The date is fixed (2025-12-31), so the default output doesn't change.