Dataset Filter & Transform avatar

Dataset Filter & Transform

Pricing

from $2.00 / 1,000 row kepts

Go to Apify Store
Dataset Filter & Transform

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

Adam Pearce

Maintained by Community

Actor stats

0

Bookmarked

46

Total users

31

Monthly active users

7 days ago

Last modified

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

  1. Click Try for free (or Start) on this Actor.
  2. 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).
  3. Add Transform steps, a JSON list of operations applied in order (see below).
  4. Add Filter conditions, rows are kept only if they pass, using AND or OR.
  5. Optionally sort, keep one row per distinct value, and set a limit.
  6. 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.

opWhat it doesExample
renameRename a field{"op":"rename","from":"e-mail","to":"email"}
dropRemove fields{"op":"drop","fields":["internal_id"]}
keepKeep only listed fields, drop the rest{"op":"keep","fields":["name","email"]}
copyCopy a value (nested paths allowed) into a new field{"op":"copy","from":"address.city","to":"city"}
setFieldSet a field to a fixed value of any JSON type{"op":"setField","field":"source","value":"apify"}
defaultFill a field only when it's blank{"op":"default","field":"country","value":"US"}
trim / uppercase / lowercaseString whitespace and case{"op":"trim","field":"name"}
castConvert to number, string, or boolean{"op":"cast","field":"price","to":"number"}
addFieldBuild a new field from a template{"op":"addField","field":"fullName","template":"{{first}} {{last}}"}
computeArithmetic over numeric fields{"op":"compute","field":"total","expression":"price * qty","round":2}
roundRound a number{"op":"round","field":"price","decimals":2}
replaceReplace text (all occurrences), plain or regex{"op":"replace","field":"phone","search":"[^0-9+]","replacement":"","regex":true}
regexExtractPull text out with a regex{"op":"regexExtract","field":"sku","pattern":"ITEM-(\\d+)-","into":"itemNumber"}
splitSplit on a separator into an array, or take one part{"op":"split","field":"name","separator":" ","index":-1,"into":"lastName"}
joinArrayJoin an array into one string{"op":"joinArray","field":"tags","separator":", "}
substringSlice a string (negative start counts from the end){"op":"substring","field":"postcode","start":0,"length":4,"into":"area"}
coalesceFirst non-blank of several fields{"op":"coalesce","fields":["mobile","phone","landline"],"into":"bestPhone"}
dateFormatNormalise a date: iso, date (YYYY-MM-DD), unix, unixMs, or a pattern like DD/MM/YYYY{"op":"dateFormat","field":"posted","format":"date"}
dateDiffDays (or hours, minutes, seconds) between two dates, or since a date until now{"op":"dateDiff","from":"posted","into":"daysAgo"}
extractDomainDomain from an email address or URL{"op":"extractDomain","field":"email","into":"company_domain"}
mapValuesLook values up in a table (case-insensitive), with an optional default{"op":"mapValues","field":"country","map":{"US":"United States","GB":"United Kingdom"},"default":"Other"}
hashA stable id from one or more fields (sha256, sha1 or md5), for dedupe and joins{"op":"hash","fields":["email","country"],"into":"rowId"}
parseJsonTurn a JSON string field into a real object{"op":"parseJson","field":"attributes"}
lengthLength 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).

GroupOperators
Textequals, notEquals, contains, notContains, startsWith, endsWith, matchesRegex, in, notIn (value = array)
NumbersgreaterThan, lessThan, greaterOrEqual, lessOrEqual, between (value = [min, max])
PresenceisEmpty, isNotEmpty, isTrue, isFalse (reads yes/no, 1/0, true/false)
ListsarrayContains, lengthGreaterThan, lengthLessThan
DatesdateAfter, 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

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: cast a price string to a number, then filter on the numeric value).
  • Use keep as 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.
  • hash over the fields that identify a record gives you a stable rowId you can dedupe or join on later, even when the source has no id.
  • maxItems caps how many input rows are loaded, useful as a cost guard on very large inputs before you're sure the filter is right; limit caps 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.