Dataset Filter & Transform
Pricing
from $2.00 / 1,000 row kepts
Dataset Filter & Transform
Returns rows of any Apify dataset, CSV/Excel/JSON file URL or Google Sheet that pass filter rules (25 operators, AND/OR) after transforms (rename, cast, compute, regex, dates, replace, split, hash), then sort, dedupe, limit, export CSV/Excel or webhook. Agent-ready: pay per event (x402, MCP).
Pricing
from $2.00 / 1,000 row kepts
Rating
0.0
(0)
Developer
Adam Pearce
Maintained by CommunityActor stats
0
Bookmarked
46
Total users
31
Monthly active users
7 days ago
Last modified
Categories
Share
Stop writing one-off scripts to clean up scraper output. Dataset Filter & Transform takes any Apify dataset, a CSV, Excel or JSON file by URL, a Google Sheet, or a JSON array you paste in, and lets you filter rows by rule and transform fields: rename, drop, trim, change case, cast types, compute a field from arithmetic, extract text with a regex, replace, split, fix dates, map values, hash a stable id, or template a new field from existing ones. Then sort, dedupe, limit, and get back exactly the rows and fields you wanted, as a dataset, a ready-to-open CSV or Excel file, a named dataset that accumulates across runs, or a POST to your webhook. All from a JSON config, no code, no spreadsheet formulas to babysit.
Why use Dataset Filter & Transform?
Every scraper dumps more than you need: rows you don't want, messy strings, numbers stored as "$1,234.50" text, dates in five formats, fields with the wrong name for your CRM import. The usual fix is a throwaway Python script or a pile of Excel formulas, redone every time. This Actor turns that into a reusable, scheduled, API-callable step:
- Sales and lead gen: keep only leads in your target country with a phone number, trim and lowercase emails, extract the company domain, dedupe by email, compute a lead score.
- E-commerce: keep only in-stock products under a price threshold, cast price strings to real numbers, compute a margin field, sort by price, take the top 100.
- Job boards and listings: keep only rows posted in the last 7 days, normalise the posted date, drop the fields you never use.
- Data pipelines: chain this after any scraper (via Apify's dataset-to-dataset integrations or a schedule) to always hand the next step clean, filtered data, appended into one named dataset.
- CRM prep: rename scraped field names to match your import template, map country codes to full names, export to CSV.
- Files that never touched Apify: point it at a CSV export from your CRM, an Excel report, or a Google Sheet and clean it the same way.
Because it runs on Apify, you get scheduling, API access, dataset integrations (Zapier, Make, Google Sheets, webhooks), and full run history for free.
How to use Dataset Filter & Transform
- Click Try for free (or Start) on this Actor.
- Bring in your rows: pick an existing dataset in Dataset to process, paste a link into File URL (CSV, TSV, Excel, JSON, JSON Lines, or a Google Sheet), or paste rows into Data (inline).
- Add Transform steps, a JSON list of operations applied in order (see below).
- Add Filter conditions, rows are kept only if they pass, using AND or OR.
- Optionally sort, keep one row per distinct value, and set a limit.
- Pick where the result goes: this run's dataset (always), a named dataset that accumulates across runs, a CSV or Excel file, or a webhook. Run.
The default input runs in a couple of seconds against a small built-in example so you can see exactly how it behaves before pointing it at your own data.
Bringing in a file or a Google Sheet
Set File URL to any public link. The format is detected from the extension, the content type and the content itself, or force it with File format:
- CSV / TSV: header row required; quotes, embedded newlines and semicolon or tab delimiters are handled.
- Excel (.xlsx): the first sheet, header row in row 1; dates come out as ISO strings, formulas as their computed values.
- JSON: an array, an object wrapping an array (
{"items": [...]},{"data": [...]}), or one object per line (JSON Lines). - Google Sheets: paste the normal sheet link. Set sharing to "Anyone with the link can view" (or File > Share > Publish to the web); the Actor converts it to the CSV export link for you, including the specific tab if the link carries a
gid.
Up to 100 MB per run. Downloading the file is never charged, only the rows you keep are.
Transform steps
Each step is one JSON object; steps run top to bottom, so a later step can use an earlier step's output (e.g. trim a field, then reference it in a template). Every step writes a top-level field; wherever a field is named you can read a nested value with a dotted path like address.city.
op | What it does | Example |
|---|---|---|
rename | Rename a field | {"op":"rename","from":"e-mail","to":"email"} |
drop | Remove fields | {"op":"drop","fields":["internal_id"]} |
keep | Keep only listed fields, drop the rest | {"op":"keep","fields":["name","email"]} |
copy | Copy a value (nested paths allowed) into a new field | {"op":"copy","from":"address.city","to":"city"} |
setField | Set a field to a fixed value of any JSON type | {"op":"setField","field":"source","value":"apify"} |
default | Fill a field only when it's blank | {"op":"default","field":"country","value":"US"} |
trim / uppercase / lowercase | String whitespace and case | {"op":"trim","field":"name"} |
cast | Convert to number, string, or boolean | {"op":"cast","field":"price","to":"number"} |
addField | Build a new field from a template | {"op":"addField","field":"fullName","template":"{{first}} {{last}}"} |
compute | Arithmetic over numeric fields | {"op":"compute","field":"total","expression":"price * qty","round":2} |
round | Round a number | {"op":"round","field":"price","decimals":2} |
replace | Replace text (all occurrences), plain or regex | {"op":"replace","field":"phone","search":"[^0-9+]","replacement":"","regex":true} |
regexExtract | Pull text out with a regex | {"op":"regexExtract","field":"sku","pattern":"ITEM-(\\d+)-","into":"itemNumber"} |
split | Split on a separator into an array, or take one part | {"op":"split","field":"name","separator":" ","index":-1,"into":"lastName"} |
joinArray | Join an array into one string | {"op":"joinArray","field":"tags","separator":", "} |
substring | Slice a string (negative start counts from the end) | {"op":"substring","field":"postcode","start":0,"length":4,"into":"area"} |
coalesce | First non-blank of several fields | {"op":"coalesce","fields":["mobile","phone","landline"],"into":"bestPhone"} |
dateFormat | Normalise a date: iso, date (YYYY-MM-DD), unix, unixMs, or a pattern like DD/MM/YYYY | {"op":"dateFormat","field":"posted","format":"date"} |
dateDiff | Days (or hours, minutes, seconds) between two dates, or since a date until now | {"op":"dateDiff","from":"posted","into":"daysAgo"} |
extractDomain | Domain from an email address or URL | {"op":"extractDomain","field":"email","into":"company_domain"} |
mapValues | Look values up in a table (case-insensitive), with an optional default | {"op":"mapValues","field":"country","map":{"US":"United States","GB":"United Kingdom"},"default":"Other"} |
hash | A stable id from one or more fields (sha256, sha1 or md5), for dedupe and joins | {"op":"hash","fields":["email","country"],"into":"rowId"} |
parseJson | Turn a JSON string field into a real object | {"op":"parseJson","field":"attributes"} |
length | Length of a string or array | {"op":"length","field":"tags","into":"tagCount"} |
Number parsing is lenient by default: "$1,234.50", "49 USD" and "(300)" (accounting negative) all read as real numbers for cast, compute, round, sorting and numeric filters. Dates are read from ISO strings, Unix timestamps (seconds or milliseconds), spreadsheet-style text like March 14, 2026, and day-first 14/03/2026; ambiguous slash dates like 03/04/2026 are read month-first, the way a US spreadsheet would.
Filter conditions
Each condition is {"field": "...", "operator": "...", "value": ...}. Combine every condition with AND (must match all) or OR (match any).
| Group | Operators |
|---|---|
| Text | equals, notEquals, contains, notContains, startsWith, endsWith, matchesRegex, in, notIn (value = array) |
| Numbers | greaterThan, lessThan, greaterOrEqual, lessOrEqual, between (value = [min, max]) |
| Presence | isEmpty, isNotEmpty, isTrue, isFalse (reads yes/no, 1/0, true/false) |
| Lists | arrayContains, lengthGreaterThan, lengthLessThan |
| Dates | dateAfter, dateBefore (value = a date, "today" or "now"), withinLastDays, olderThanDays (value = number of days) |
String comparisons are case-insensitive by default ("US" matches "us"); turn on Case-sensitive filters by default, or set "caseSensitive": true on one condition, to require an exact match.
Sort, dedupe and limit
Applied after filtering, in this order:
- Sort by:
[{"field": "price", "direction": "desc"}]or the shorthand["-price", "name"]. Numbers sort numerically, text case-insensitively, blanks last. - Keep one row per distinct value of: field names, e.g.
["email"]. Only the first kept row per distinct value survives, so sort by date descending, then distinct by email keeps the newest row per email. - Maximum rows to output and Skip the first N rows:
top 100 by price, or paging.
Rows removed by sorting, deduping or the limit are never charged.
Input
See the Input tab for the full schema. The three ways to bring in data:
datasetId: point at any existing Apify dataset (yours or from another Actor's run).fileUrl: a CSV, TSV, Excel, JSON or JSON Lines file, or a Google Sheet link.data: paste a JSON array directly for quick, ad-hoc jobs.
{"fileUrl": "https://docs.google.com/spreadsheets/d/1AbC.../edit#gid=0","transforms": [{ "op": "trim", "field": "name" },{ "op": "lowercase", "field": "email" },{ "op": "cast", "field": "revenue", "to": "number" },{ "op": "dateFormat", "field": "signupDate", "format": "date" },{ "op": "extractDomain", "field": "email", "into": "domain" }],"filters": [{ "field": "country", "operator": "equals", "value": "US" },{ "field": "revenue", "operator": "greaterOrEqual", "value": 1000 },{ "field": "signupDate", "operator": "withinLastDays", "value": 90 }],"filterCombineMode": "AND","sortBy": ["-revenue"],"distinctBy": ["email"],"limit": 500,"outputDatasetName": "clean-us-leads","exportFormats": ["csv", "xlsx"]}
Output
Every kept, transformed row is pushed to the run's dataset:
{"name": "jane doe","email": "jane@example.com","country": "US","revenue": 12500,"signupDate": "2026-03-14","domain": "example.com"}
You can download the dataset in various formats such as JSON, HTML, CSV, or Excel directly from the Apify Console, or request a ready-made CSV/Excel file via exportFormats. With Also append to a named dataset set, the same rows are appended to a dataset of that name in your account (created on the first run), so a scheduled pipeline accumulates into one place. A run summary (rows in, kept, excluded, duplicates removed, and any field a transform step couldn't honestly apply) is saved to the key-value store as FILTER_TRANSFORM_SUMMARY.
Webhook destination
Set Webhook URL in the input and the kept, transformed rows (plus download links and a summary) are POSTed there as JSON the instant the run finishes, no need to poll the dataset or remember to check back. Works with a Zapier/Make/n8n catch-hook, your own API endpoint, or a Slack incoming webhook, so this Actor can be the last step in someone else's pipeline instead of a tool they have to run manually. A failed or unreachable webhook never breaks the run, it's reported as a warning in the output and costs nothing. Charged only on a confirmed delivery (see Pricing).
Pricing
Pay-per-event, no subscription:
- $0.002 per row kept (a row that passed your filter, survived sort/dedupe/limit, and was written out)
- $0.01 per file export (CSV or Excel)
- $0.02 per confirmed webhook delivery (only when the endpoint responds 2xx; a failed delivery costs nothing), effective 15 September 2026, free before that
- A small per-GB run-start fee (the platform default)
A typical cleanup of a few thousand scraped rows down to the few hundred you actually wanted costs under a dollar. Rows that get filtered out, deduped or cut by the limit are never charged, and neither is downloading a file or appending to a named dataset.
Works with the rest of the Nero Labs dataset toolkit
- Dataset Cleaner & Exporter: dedupe (exact, normalized or fuzzy), flatten nested JSON, clean emails, phones and URLs, then export CSV or Excel.
- Dataset Filter & Transform (this one): keep the rows you want and reshape the fields (dates, replace, split, hash, 25 ops), sort, dedupe, limit.
- Dataset Join & Merge: VLOOKUP-style joins and unions across two datasets, files or Google Sheets on a key field.
- Dataset Aggregate, Group By & Pivot: counts, sums, averages and pivot tables per group.
- Dataset Diff & Change Detector: what was added, removed or changed since last time.
- Dataset AI Enrich: add LLM-generated columns (classify, extract, summarise) to every row, no API key needed.
- Dataset Charts & Report: chart images (PNG, SVG) and a PDF or HTML report from any data.
- Dataset to Postgres, Supabase & MySQL: write the rows straight into a database table, creating it if needed.
- Dataset to REST API: send every row to any API as its own request, with templating and auth presets.
- Actor Pipeline Runner: chain several of these together in one run, each step fed the previous step's dataset.
A common pipeline: a scraper, then Cleaner, then Filter & Transform, then Join to enrich from a sheet, then Aggregate for the weekly summary, with Diff watching what changed and Charts & Report turning the numbers into the Monday PDF. Pipeline Runner runs that whole chain in one call.
Tips
- Filters run after transforms, so you can compute a field and then filter on it in the same run (see the default example:
casta price string to a number, then filter on the numeric value). - Use
keepas a last transform step to guarantee a clean, fixed column set for your CSV/Excel export, regardless of what extra fields the source data carries. hashover the fields that identify a record gives you a stablerowIdyou can dedupe or join on later, even when the source has no id.maxItemscaps how many input rows are loaded, useful as a cost guard on very large inputs before you're sure the filter is right;limitcaps the output.
FAQ
Does this work on any dataset or file? Yes. This Actor processes only the data you already have (your own dataset, a file you link to, or pasted JSON). It doesn't scrape anything, so there's no target-site data-terms question to worry about.
My Google Sheet link gives an HTTP 401 or 403. The sheet isn't public. Set sharing to "Anyone with the link can view", or use File > Share > Publish to the web and paste that CSV link.
What happens if a filter, sort or distinct field doesn't exist in my data? You'll get a clear warning telling you no row had that field, so a typo doesn't just silently exclude everything with no explanation.
What happens if compute, cast, round or dateFormat can't parse a value? The field is set to null and counted in the run summary, never guessed.
Can I run this on a schedule after another Actor? Yes, that's the intended pattern: schedule your scraper, then schedule this Actor pointed at the same dataset (or chain them via an integration), and set a named output dataset so every run lands in one place.
If this saved you a spreadsheet filter-and-formula pass or a one-off cleaning script, a review on this page helps a small tool get found. Found a bug or want a feature? Open an issue on the Issues tab, replies come from a real person, usually within hours.