# Dataset Filter & Transform: Filter, Pick, Rename, Flatten (`charlesmarkette/dataset-filter-transform-rename-flatten-fields`) Actor

Clean any Apify dataset without code: filter rows (equals, contains, regex, greater than, empty…), keep or drop columns, rename fields, flatten nested JSON, split list fields into rows, add tags. Chain it between a scraper and Google Sheets, CSV export or alerts.

- **URL**: https://apify.com/charlesmarkette/dataset-filter-transform-rename-flatten-fields.md
- **Developed by:** [Charles Markette](https://apify.com/charlesmarkette) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.55 / 1,000 output rows

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

**Dataset Filter & Transform** cleans up any Apify dataset without writing code. Keep only the rows that match your conditions, choose which columns to keep, rename them, flatten nested fields into plain columns, split a list field into one row per element, tag every row with a source, trim spaces, sort and limit. It sits between a scraper and wherever the data goes next (Google Sheets, a CRM import, a file, a Slack alert) and works on one dataset or several, read in order.

- **Filter rows** with conditions such as `equals`, `contains`, `regex`, `greaterThan`, `in` or `isNotEmpty`. Combine them with AND ("all") or OR ("any").
- **Nested fields and lists:** dots reach into objects (`address.city`), and a condition on a list matches when any element matches (`categories contains "bakery"`).
- **Shape the columns:** keep only some fields (in your order), remove others, rename them, flatten nested objects into `a.b.c` columns, and join lists into text.
- **Split a list into rows:** one row per email, phone number or product variant.
- **Tag and clean:** add fixed fields such as `source`, trim spaces, turn empty values into null, lowercase emails.
- **Sort and limit:** sort by any output field, skip the first rows and cap the output.

### Why use Dataset Filter & Transform?

- **No code, and no code execution.** Conditions are plain data (`{"field", "op", "value"}`) checked by fixed rules. The Actor never runs JavaScript or Python that you supply, so a saved task or an integration can't be turned into a script runner.
- **Rows you filter out are free.** You pay only for rows written to the output.
- **Streams large datasets.** Items are read and written in batches of 500, so memory stays flat however big the dataset is (sorting is the one exception, see the limits below).
- **Resumes cleanly.** If the platform migrates or restarts the run, it continues where it stopped instead of writing duplicates.
- **Limited permissions.** It can only read the datasets you select and write its own output.

### Use cases

- **Keep only leads with a website:** after a Google Maps or directory scraper, keep the places where `website` is not empty and `rating` is at least 4.
- **Drop columns before a Google Sheets export:** keep only `title`, `phone`, `website` and `address.city`, in that order, so the sheet has exactly those columns.
- **Rename columns to match a CRM import:** `title` → `Company Name`, `phone` → `Phone Number`, `address.city` → `City`, so the CSV maps without manual work.
- **Split a list field into rows:** a scraper returns `emails: ["a@x.com", "b@x.com"]`; split it so each email gets its own row, ready for an email tool.
- **Tag rows with a source:** add `{"source": "google-maps", "campaign": "october"}` to every row before merging results from several scrapers.

### How to use Dataset Filter & Transform

1. Pick one or more **Datasets** (they are read in the order given). You can also type dataset IDs in **Dataset IDs (typed)**, or, for a quick test, paste a JSON list of objects into **Items** (used only when no dataset is given).
2. Add **Filters** (optional). Leave them empty to keep every row.
3. Choose how to **shape the rows**: split a list into rows, keep or remove fields, rename, flatten, join lists, add fields.
4. Optionally **clean up values**, **sort** and set **Max results**.
5. Click **Start**. The rows are in the run's dataset, and the counts are in the key-value store `OUTPUT`.

#### After every scraper run (integration)

1. Open your scraper's saved task, go to the **Integrations** tab and click **Add integration**.
2. Search for **Dataset Filter & Transform**, choose to start it when the run **succeeded**, and set the input. Apify fills in `{{resource.defaultDatasetId}}` with that run's dataset:

```json
{
  "datasetIds": ["{{resource.defaultDatasetId}}"],
  "filters": [{ "field": "website", "op": "isNotEmpty" }],
  "pickFields": ["title", "website", "phone", "address.city"],
  "renameFields": { "title": "Company Name", "address.city": "City" }
}
```

3. Save, and test the integration on a past run.

To send the cleaned rows on, add a second integration on **this** Actor's runs, for example [Google Sheets Sync](https://apify.com/charlesmarkette/google-sheets-sync), with `{{resource.defaultDatasetId}}`.

### Filters

Each condition is an object with a `field`, an `op` and (for most ops) a `value`:

```json
[
  { "field": "website", "op": "isNotEmpty" },
  { "field": "rating", "op": "greaterOrEqual", "value": 4 },
  { "field": "address.country", "op": "in", "value": "US, CA" },
  { "field": "title", "op": "regex", "value": "^(cafe|coffee)\\b" }
]
```

| Op | Keeps a row when the field… |
|---|---|
| `equals` / `notEquals` | equals the value (or doesn't). `"value": null` matches a missing or null field. |
| `contains` / `notContains` | contains the text (or doesn't). |
| `startsWith` / `endsWith` | starts or ends with the text. |
| `regex` | matches the regular expression anywhere in the text (Python syntax). |
| `greaterThan`, `lessThan`, `greaterOrEqual`, `lessOrEqual` | compares to the value. |
| `in` / `notIn` | equals one of the values (a list, or text like `"US, CA, MX"`). |
| `exists` / `notExists` | is present and not null (or isn't). No value needed. |
| `isEmpty` / `isNotEmpty` | is missing, null, blank text, an empty list or an empty object (or isn't). No value needed. |

How values are compared:

- **Text** ignores letter case unless you turn on **Case-sensitive text matching** (or add `"caseSensitive": true` to one condition).
- **Numbers** compare as numbers when both sides are numbers, including numeric text such as `"4.5"`. So `9 lessThan 10` is true, even if the 9 was scraped as text. When the value is a number, a field that isn't one (like `"N/A"`) never matches a comparison. Whole numbers are compared exactly, so long IDs never match a neighbouring ID.
- **Equals with two texts** compares the text, so a zip code `"02134"` doesn't equal `"2134"`. Write the value as a number (`"value": 10`) to match `10`, `"10"` and `"10.0"` alike.
- **Dates** compare as dates when both sides are ISO 8601 (`2026-03-01` or `2026-03-01T10:00:00Z`). A date without a time means midnight UTC, and a time without an offset is read as UTC.
- **Lists:** a condition matches if any element matches (`emails endsWith "@gmail.com"`). Dots cross lists too: `offers.price lessThan 20` checks every offer's price. The negative ops are the opposite of the positive ones: `tags notContains "x"` keeps rows where no tag contains "x". A missing field counts as "not equal" and "not contains".
- **Filter mode** "all" keeps rows that match every condition (AND); "any" keeps rows that match at least one (OR).
- Shorthands work too: `==`, `!=`, `>`, `<`, `>=`, `<=`, and `operator` instead of `op`.

An invalid regular expression or an unknown op stops the run before anything is written, with a message saying which condition to fix.

### Order of steps

Every item goes through the same steps, in this order:

1. **Filter.** Conditions are checked on the original item, as the scraper wrote it.
2. **Split a list field into rows** (unwind).
3. **Keep only these fields**, then **Remove these fields**.
4. **Rename fields** (in the order you list them).
5. **Flatten** nested objects (and **join lists into text**).
6. **Add fields.**
7. **Clean up:** trim spaces, then empty values to null, then lowercase.

Then the output rows are **sorted**, the first **Skip first rows** are skipped, and **Max results** caps the rest.

Because of this order, later steps use the names produced by earlier ones: rename a picked nested field by its dotted name (`"address.city": "City"`), and give **Lowercase these fields** and **Sort by field** the output name (after renaming, and `contact.email` after flattening).

### Input example

```json
{
  "datasetIds": ["aBcDeFgHiJkLmNoPq"],
  "filters": [
    { "field": "website", "op": "isNotEmpty" },
    { "field": "rating", "op": "greaterOrEqual", "value": 4 }
  ],
  "filterMode": "all",
  "unwindField": "emails",
  "pickFields": ["title", "emails", "website", "address.city", "categories"],
  "renameFields": { "title": "Company Name", "emails": "Email", "address.city": "City" },
  "joinArrays": true,
  "addFields": { "source": "google-maps" },
  "trimStrings": true,
  "lowercaseFields": ["Email"],
  "sortBy": "Company Name",
  "maxResults": 1000
}
```

| Field | Notes |
|---|---|
| Datasets / Dataset IDs (typed) | One or more datasets, read in order (picked ones first). A dataset listed twice is read once. |
| Items | A JSON list of objects, used only when no dataset is given (for quick tests or calls from code). |
| Filters | A JSON list of conditions (see above). Empty = keep every row. |
| Filter mode | `all` (AND) or `any` (OR). |
| Case-sensitive text matching | Off by default. |
| Split a list field into rows | One row per element; other fields are copied. A missing or non-list field leaves the row as it is; an empty list keeps one row with the field set to null. |
| Keep only these fields | In this order. A missing field becomes `null`, so every row has the same columns. Dotted names stay as the column name (`address.city`). |
| Remove these fields | Dots reach nested fields; `images.thumbnail` removes it from every image. |
| Rename fields | `{"old": "New"}`, applied in order. Renamed top-level columns keep their position; a renamed nested field becomes a new column at the end. Missing fields are skipped. |
| Flatten nested fields | `{"address": {"city": "Oslo"}}` → `{"address.city": "Oslo"}`. Lists stay lists. |
| Join lists into text / List separator | `["a", "b"]` → `"a, b"`. Lists of objects are kept. Works with or without flattening. |
| Add fields | Fixed values added to every row; they overwrite a field with the same name. |
| Trim spaces / Empty values to null / Lowercase these fields | Value cleanups. |
| Sort by field / Sort descending | Numbers, dates and text sort naturally; empty values go last in both directions; ties keep their input order. |
| Max results / Skip first rows | Applied to output rows, after sorting. |

### Output example

With the input above, a scraped place like

```json
{ "title": " Blue Door Cafe ", "website": "https://bluedoor.example", "rating": 4.6,
  "emails": ["Hello@BlueDoor.example", "jobs@bluedoor.example"],
  "address": { "street": "1 Main St", "city": "Oslo" }, "categories": ["Cafe", "Bakery"] }
```

becomes two rows:

```json
[
  { "Company Name": "Blue Door Cafe", "Email": "hello@bluedoor.example", "website": "https://bluedoor.example",
    "City": "Oslo", "categories": "Cafe, Bakery", "source": "google-maps" },
  { "Company Name": "Blue Door Cafe", "Email": "jobs@bluedoor.example", "website": "https://bluedoor.example",
    "City": "Oslo", "categories": "Cafe, Bakery", "source": "google-maps" }
]
```

The key-value store **`OUTPUT`** holds the counts:

```json
{
  "itemsRead": 1250,
  "itemsMatched": 410,
  "itemsOutput": 655,
  "rowsOutputThisRun": 655,
  "resumedFrom": 0,
  "itemsWithErrors": 0,
  "errors": [],
  "perDataset": [
    { "datasetId": "aBcDeFgHiJkLmNoPq", "itemsRead": 1250, "itemsMatched": 410, "itemsOutput": 655 }
  ],
  "sortedBy": "Company Name",
  "stoppedBy": null
}
```

`itemsMatched` counts input items that passed the filters; `itemsOutput` counts rows written (more than matched when a list is split into rows). `stoppedBy` is `"max results"` or `"max cost"` when the run stopped early.

### How much does it cost to filter and transform a dataset?

Dataset Filter & Transform uses pay-per-event pricing: **$0.001 per output row, plus $0.005 per run start**. Apify platform usage is included. Items that are read but filtered out, and rows skipped with **Skip first rows**, are free.

**Apify Store discounts:** the price above is for Apify's Free plan. Paid plans pay less per 1,000 output rows: $0.87 on Starter, $0.70 on Scale, $0.55 on Business. The "from" price at the top of the Store page is the Business price.

**Worked examples:**

*Examples use the Free-plan rate. Where an example doesn't add it, each run also has its run-start charge, counted per GB of Actor memory (minimum one; one at the default memory); Apify Proxy (where used) is billed separately.*

- A Google Maps scrape of 10,000 places, of which 3,200 have a website: 3,200 × $0.001 + $0.005 = **$3.205**.
- A daily integration that keeps 150 new leads per run: **$0.155** per run, about **$4.65 per month**.

If you set a maximum cost per run, the Actor stops cleanly at that limit and writes only the rows you paid for.

### FAQ

#### My filter matches nothing. Why?

Check the field name against a row of your dataset (names are case-sensitive, values are not by default). For nested fields use dots: `address.city`, not `city`. If the value is a number, text like `"N/A"` or `"$12"` in the field never matches a comparison.

#### Can I write my own JavaScript or Python condition?

No, on purpose. The Actor doesn't run user code. Most needs are covered by combining conditions, `regex` and **Filter mode**.

#### "Couldn't open dataset …"

Pick the dataset with the dataset picker, or pass a valid dataset ID. In an integration, use `["{{resource.defaultDatasetId}}"]` (a list). The placeholder is filled in only when an integration starts the run; started by hand, the Actor asks you to pick a dataset.

#### Why is a column `null` in some rows?

**Keep only these fields** adds every listed field to every row, as `null` when the item doesn't have it, so spreadsheet columns line up.

#### I need to remove duplicates too.

Run [Merge & Deduplicate Datasets](https://apify.com/charlesmarkette/merge-deduplicate-datasets-only-new-items) first (it can also keep only items not seen in earlier runs), then this Actor on its output.

### Limitations

- **Sorting holds rows in memory**, up to 200,000 output rows. With more, the run stops before writing anything and says so: add filters or set **Max results** (with a limit, any number of rows can be read, because only the best "skip + limit" rows are kept in memory; skip + limit can be at most 200,000). Sorting many large rows may need 1 GB of memory instead of the default 256 MB.
- Regular expressions use Python syntax and are limited to 1,000 characters. A search that takes over 1 second on one value (usually a pattern with nested repeats such as `(a+)+`) stops the run with a message saying which condition to simplify; rows written before that are kept.
- **Split a list field into rows** works on one field per run.
- Flattening leaves lists of objects as lists (they appear as JSON in a CSV). Join lists of plain values with **Join lists into text**.
- Items that aren't JSON objects are skipped. If an item can't be processed, it's skipped and counted in `itemsWithErrors`, with the first few reasons in `OUTPUT`.

### Related Actors

Dataset Filter & Transform is part of a small suite of Actors that work together after any scraper:

| Actor | Use it to |
|---|---|
| [HTTP Request & API Calls](https://apify.com/charlesmarkette/http-request-api-calls-bulk-pagination) | Pull data from any REST API into a dataset (with pagination), then reshape it here. |
| [Dataset Join & VLOOKUP](https://apify.com/charlesmarkette/dataset-join-vlookup-merge-by-key) | Add columns from another dataset or file by a shared key (email, SKU, URL), like VLOOKUP. |
| [CSV & Excel to Dataset](https://apify.com/charlesmarkette/csv-excel-to-dataset-xlsx-json-converter) | Turn CSV, Excel or Google Sheets files into a dataset you can filter and reshape here. |
| [Merge & Deduplicate Datasets](https://apify.com/charlesmarkette/merge-deduplicate-datasets-only-new-items) | Merge datasets, remove duplicates, or keep only items not seen in earlier runs. |
| [Google Sheets Sync](https://apify.com/charlesmarkette/google-sheets-sync) | Append or upsert the cleaned rows into a Google Sheet. |
| [Dataset Delivery](https://apify.com/charlesmarkette/dataset-delivery-export-to-sftp-ftp-dropbox-google-drive) | Deliver the rows as a CSV, JSON or Excel file to SFTP, FTP, Dropbox or Google Drive. |
| [Send Email Report & Slack, Discord, Teams, Telegram Alerts](https://apify.com/charlesmarkette/send-email-report-slack-discord-teams-telegram-alerts) | Get told about the rows by email, Slack, Discord, Teams or Telegram. |
| [Phone Number Validator & Formatter](https://apify.com/charlesmarkette/phone-number-validator-formatter-bulk-e164) | Check and format the phone numbers in your filtered leads (E.164, mobile or landline). |
| [Domain WHOIS & DNS Lookup](https://apify.com/charlesmarkette/domain-whois-dns-lookup-bulk-rdap) | Add domain age, email provider, SPF and DMARC for each lead's website. |
| [RSS & Atom Feed Reader](https://apify.com/charlesmarkette/rss-atom-feed-reader-only-new-items) | Turn RSS, Atom and JSON feeds into a dataset to filter. |
| [Bulk URL Status Checker](https://apify.com/charlesmarkette/bulk-url-status-checker-redirects-ssl) | Check the links in a dataset for 404s, redirects and SSL problems. |
| [PDF & Image to Text](https://apify.com/charlesmarkette/pdf-image-to-text-markdown-ocr) | Turn PDFs and scans into text, Markdown or tables, then filter and reshape the rows here. |
| [Broken Link Checker](https://apify.com/charlesmarkette/broken-link-checker-crawl-website-404) | Crawl a website for broken links, then filter the results down to the ones you care about. |

# Actor input Schema

## `datasetIds` (type: `array`):

One or more datasets to clean up, read in order (usually a scraper run's output). In an integration, use \["{{resource.defaultDatasetId}}"]. Leave empty to use 'Items' below instead.

## `datasetIdList` (type: `array`):

Optional. Dataset IDs (or username~dataset-name) typed by hand, one per line. Read after the picked datasets.

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

Optional. A JSON list of objects to filter and transform, used only when no dataset is given above. Handy for quick tests and for calling this Actor from code.

## `filters` (type: `array`):

Keep only rows that match these conditions. Each condition is {"field", "op", "value"}. Ops: equals, notEquals, contains, notContains, startsWith, endsWith, regex, greaterThan, lessThan, greaterOrEqual, lessOrEqual, in, notIn (value: a list or "a, b, c"), and exists, notExists, isEmpty, isNotEmpty (no value). Dots reach nested fields (address.city); on a list, a condition matches if any element matches. Numbers and ISO dates compare as numbers and dates. Leave empty to keep every row.

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

How the filter conditions combine: all of them must match (AND), or at least one (OR).

## `caseSensitive` (type: `boolean`):

Off: 'ACME' equals 'acme'. A single condition can override this with "caseSensitive": true.

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

Optional. A field holding a list (e.g. emails). Each element becomes its own row, with the other fields copied. Dots for nested fields.

## `pickFields` (type: `array`):

Optional. Output only these fields, in this order (handy for spreadsheet columns). Dots for nested fields; the column is then named 'address.city'. Empty = keep all.

## `omitFields` (type: `array`):

Optional. Fields to drop. Dots for nested fields (images.thumbnail removes it from every image).

## `renameFields` (type: `object`):

Optional. {"old name": "New Name"}, applied in order. Renamed columns keep their position. Example: {"title": "Company Name", "phone": "Phone Number"}.

## `flatten` (type: `boolean`):

Nested objects become columns like 'address.city'. Lists stay lists unless you join them below.

## `joinArrays` (type: `boolean`):

Lists of plain values (text, numbers) become one text value, e.g. \["a", "b"] → "a, b". Lists of objects are kept.

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

Text placed between joined list elements.

## `addFields` (type: `object`):

Optional. Fixed values added to every row (overwriting a field with the same name), e.g. {"source": "google-maps", "campaign": "october"}.

## `trimStrings` (type: `boolean`):

Remove leading and trailing whitespace from every text value.

## `emptyToNull` (type: `boolean`):

Empty text, empty lists and empty objects become null.

## `lowercaseFields` (type: `array`):

Optional. Output fields whose text is lowercased, e.g. email. Use the output name (after renaming).

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

Optional. An output field name (after renaming). Numbers, dates and text sort naturally; empty values go last. Without 'Max results', sorting is limited to 200,000 rows.

## `sortDesc` (type: `boolean`):

Largest, latest or Z first.

## `maxResults` (type: `integer`):

Optional cap on output rows.

## `offset` (type: `integer`):

Optional. Skip this many output rows (after filtering and sorting) before writing. Skipped rows are free.

## Actor input object example

```json
{
  "items": [
    {
      "name": "Blue Door Cafe",
      "website": "https://bluedoor.example",
      "rating": 4.6,
      "emails": [
        "hello@bluedoor.example"
      ],
      "address": {
        "city": "Oslo"
      }
    },
    {
      "name": "Corner Bar",
      "website": "",
      "rating": 4.2,
      "emails": [],
      "address": {
        "city": "Bergen"
      }
    },
    {
      "name": "Harbor Hotel",
      "website": "https://harbor.example",
      "rating": 3.8,
      "emails": [
        "info@harbor.example"
      ],
      "address": {
        "city": "Bergen"
      }
    },
    {
      "name": "Fjord Bakery",
      "website": "https://fjordbakery.example",
      "rating": "4.9",
      "emails": [
        "post@fjordbakery.example"
      ],
      "address": {
        "city": "Oslo"
      }
    }
  ],
  "filters": [
    {
      "field": "website",
      "op": "isNotEmpty"
    },
    {
      "field": "rating",
      "op": "greaterOrEqual",
      "value": 4
    }
  ],
  "filterMode": "all",
  "caseSensitive": false,
  "flatten": false,
  "joinArrays": false,
  "arrayJoiner": ", ",
  "trimStrings": false,
  "emptyToNull": false,
  "sortDesc": false
}
```

# Actor output Schema

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

No description

## `summary` (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": [
        {
            "name": "Blue Door Cafe",
            "website": "https://bluedoor.example",
            "rating": 4.6,
            "emails": [
                "hello@bluedoor.example"
            ],
            "address": {
                "city": "Oslo"
            }
        },
        {
            "name": "Corner Bar",
            "website": "",
            "rating": 4.2,
            "emails": [],
            "address": {
                "city": "Bergen"
            }
        },
        {
            "name": "Harbor Hotel",
            "website": "https://harbor.example",
            "rating": 3.8,
            "emails": [
                "info@harbor.example"
            ],
            "address": {
                "city": "Bergen"
            }
        },
        {
            "name": "Fjord Bakery",
            "website": "https://fjordbakery.example",
            "rating": "4.9",
            "emails": [
                "post@fjordbakery.example"
            ],
            "address": {
                "city": "Oslo"
            }
        }
    ],
    "filters": [
        {
            "field": "website",
            "op": "isNotEmpty"
        },
        {
            "field": "rating",
            "op": "greaterOrEqual",
            "value": 4
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("charlesmarkette/dataset-filter-transform-rename-flatten-fields").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": [
        {
            "name": "Blue Door Cafe",
            "website": "https://bluedoor.example",
            "rating": 4.6,
            "emails": ["hello@bluedoor.example"],
            "address": { "city": "Oslo" },
        },
        {
            "name": "Corner Bar",
            "website": "",
            "rating": 4.2,
            "emails": [],
            "address": { "city": "Bergen" },
        },
        {
            "name": "Harbor Hotel",
            "website": "https://harbor.example",
            "rating": 3.8,
            "emails": ["info@harbor.example"],
            "address": { "city": "Bergen" },
        },
        {
            "name": "Fjord Bakery",
            "website": "https://fjordbakery.example",
            "rating": "4.9",
            "emails": ["post@fjordbakery.example"],
            "address": { "city": "Oslo" },
        },
    ],
    "filters": [
        {
            "field": "website",
            "op": "isNotEmpty",
        },
        {
            "field": "rating",
            "op": "greaterOrEqual",
            "value": 4,
        },
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("charlesmarkette/dataset-filter-transform-rename-flatten-fields").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": [
    {
      "name": "Blue Door Cafe",
      "website": "https://bluedoor.example",
      "rating": 4.6,
      "emails": [
        "hello@bluedoor.example"
      ],
      "address": {
        "city": "Oslo"
      }
    },
    {
      "name": "Corner Bar",
      "website": "",
      "rating": 4.2,
      "emails": [],
      "address": {
        "city": "Bergen"
      }
    },
    {
      "name": "Harbor Hotel",
      "website": "https://harbor.example",
      "rating": 3.8,
      "emails": [
        "info@harbor.example"
      ],
      "address": {
        "city": "Bergen"
      }
    },
    {
      "name": "Fjord Bakery",
      "website": "https://fjordbakery.example",
      "rating": "4.9",
      "emails": [
        "post@fjordbakery.example"
      ],
      "address": {
        "city": "Oslo"
      }
    }
  ],
  "filters": [
    {
      "field": "website",
      "op": "isNotEmpty"
    },
    {
      "field": "rating",
      "op": "greaterOrEqual",
      "value": 4
    }
  ]
}' |
apify call charlesmarkette/dataset-filter-transform-rename-flatten-fields --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,charlesmarkette/dataset-filter-transform-rename-flatten-fields"
        }
    }
}
```

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/w3WgUzg0tW6q6aIDM/builds/hxJj94y0O4CTmaFa4/openapi.json
