# Dataset Transform: JSON to CSV, Flatten, Filter, Rename (`ucchiii/dataset-transform`) Actor

Turn any Apify dataset (or pasted JSON) into clean tables: flatten nested JSON, pick and rename columns, convert types, filter, sort, then export CSV, JSON or JSONL with a link. Empty input = free demo. $0.50 per 1,000 output rows.

- **URL**: https://apify.com/ucchiii/dataset-transform.md
- **Developed by:** [Actor Factory](https://apify.com/ucchiii) (community)
- **Categories:** Developer tools, Automation, Integrations
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.35 / 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 Transform — JSON to CSV, flatten, filter, rename any Apify dataset

**For anyone whose scraper output is nested, messy JSON and who needs a clean table — a CSV for Excel or Google Sheets, a flat file for a database, or tidy rows for the next step of a workflow.** Point this Actor at an Apify dataset (or paste rows), say which columns you want, and get **clean rows plus a CSV / JSONL / JSON file with a download link**.
**$0.50 per 1,000 output rows.** Reading, flattening and filtering are free; rows removed by your filters are not charged. No scraping, no external requests, no API keys.

### What it does

- **Flatten nested JSON**: `{"address": {"city": "Austin"}}` becomes the column `address.city` (up to 20 levels, your own separator)
- **Arrays your way**: keep as JSON, join values (`a; b; c`), one column per item (`tags.0`, `tags.1`), first item only, or item count
- **Select, order, omit and rename columns** (selecting `address` keeps every column under it)
- **Convert types**: text to number (`$1,234.50`, `(12)`, `1.234,50`), integer, boolean (`yes/no/true/1`), ISO 8601 date (from ISO text or Unix seconds/milliseconds), or to text
- **Filter rows**: equals, contains, greater than, in a list, exists and more, combined with AND or OR
- **Sort** (numbers first, empty values last), **skip** and **limit**
- **Save a file** next to the dataset: CSV (delimiter, UTF-8 BOM for Excel, formula-injection protection), JSONL or JSON, with a link in the run statistics
- Works on any dataset: Google Maps, jobs, SERP, e-commerce, your own runs

### Input example

```json
{
  "datasetIds": ["WkzbQMuFYuamGv3YF"],
  "select": ["title", "address.city", "rating", "reviews"],
  "rename": {"address.city": "city"},
  "coerce": {"rating": "number", "reviews": "integer"},
  "filters": [{"field": "rating", "op": "gte", "value": 4.5}],
  "sortBy": "reviews",
  "sortOrder": "desc",
  "limit": 500,
  "exportFormat": "csv",
  "csvBom": true
}
```

**Empty input runs a free demo** on sample orders (not charged, rows carry `_demo: true`) so you can see flattening, type conversion, filtering, sorting and the CSV link before using your own data.

### Order of processing

1. Flatten (depth, arrays) and optional text trimming
2. Convert types (`coerce`)
3. Filter rows, then sort
4. Keep / omit columns, then rename, then cut over-long cells
5. Skip and limit; write the dataset rows; save the file

Column names in `coerce`, `filters`, `sortBy`, `select` and `omit` are the **flattened names before renaming** (for example `address.city`).

### Output

- **Dataset**: the transformed rows. Use Apify's own export buttons for Excel/XML/RSS, or the API.
- **File**: saved in the run's key-value store as `<fileName>.csv` (or `.jsonl` / `.json`). The link is in the `STATS` record as `fileUrl`. It uses Apify's signed public link, so you can share it without sharing your token; if no link can be created, the file is still in the key-value store under `fileKey`.
- **STATS** (key-value store):

```json
{
  "rowsRead": 12034, "rowsFilteredOut": 9120, "rowsWritten": 2914,
  "coercionFailures": {"price": 3}, "cellsTruncated": 0,
  "columns": ["title", "city", "rating", "reviews"],
  "fileKey": "export.csv", "fileFormat": "csv", "fileRows": 2914, "fileUrl": "https://api.apify.com/v2/key-value-stores/…/records/export.csv?signature=…"
}
```

### Limits (cannot be raised by input)

- 1,000,000 output rows per run, 1,000 columns per row, 50,000 pasted rows (use a dataset for more)
- Sorting holds matching rows in memory: up to 500,000 rows (the run stops with a clear message, before any charge, if there are more)
- Files are capped at 150 MB (the dataset still gets every row; `fileTruncated` tells you)
- Text cells are cut at 32,000 characters by default (Excel's limit is 32,767); set `maxCellChars` to change or `0` to disable
- Empty rows (all values empty after your column choice) are skipped and not charged
- Rows that are not objects (a plain number or text) become `{"value": …}`

### Use cases

- Turn a Google Maps / jobs / e-commerce scraper dataset into a flat CSV for a spreadsheet
- Keep only the 6 columns your CRM import needs and rename them to its field names
- Convert price and date text into real numbers and ISO dates before loading to a database
- Filter and sort a dataset (top-rated, cheapest, newest) and hand the top 100 to the next Actor

### Pricing

Pay per event: **$0.0005 per output row** ($0.50 per 1,000). Set a maximum charge per run to cap spend; the Actor stops cleanly when it is reached.

### FAQ

**Does it change my source dataset?** No. It only reads it and writes a new dataset.
**Why are my numbers still text?** Add the column to `coerce` as `number` or `integer`. Values that cannot be read become empty and are counted in `coercionFailures`.
**Excel shows broken characters.** Turn on `csvBom`.
**Is the file link private?** Anyone holding the signed link can download the file; the link does not give access to anything else in your account.

### More tools from the same developer

- [Google Sheets Export](https://apify.com/ucchiii/google-sheets-export) — export an Apify dataset to Google Sheets
- [Dataset Merge & Dedup](https://apify.com/ucchiii/dataset-dedup-merge) — merge Apify datasets and remove duplicates by the fields you choose
- [Dataset Change Detector](https://apify.com/ucchiii/dataset-change-detector) — compare two runs of any scraper: only added, removed and changed items
- [Job Postings Normalizer](https://apify.com/ucchiii/job-postings-normalizer) — unify fields, salaries and dates from any job scraper and remove duplicates
- [Google Maps Leads Cleaner](https://apify.com/ucchiii/maps-leads-cleaner) — dedupe Google Maps scraper results, format phones (E.164), CRM-ready leads

# Actor input Schema

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

Dataset IDs (the 'defaultDatasetId' of a finished run of any Actor, e.g. 'WkzbQMuFYuamGv3YF'). Rows are read in the order given. You can paste a dataset URL from Apify Console or the API too. Leave this AND 'items' empty to run a free demo on sample data (not charged).

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

Rows to transform, as a JSON array of objects, e.g. \[{"name":"A","price":"$1,200"}]. Used after the datasets (if any). Up to 50,000 rows; use a dataset for more.

## `select` (type: `array`):

Columns to output, in this order. Use the flattened name, e.g. 'address.city'. A parent name such as 'address' keeps all columns under it ('address.city', 'address.zip'). Empty = all columns.

## `omit` (type: `array`):

Columns to drop from the output. A parent name such as 'meta' removes everything under it ('meta.id', 'meta.tags.0'). Applied after 'Keep only these columns'.

## `rename` (type: `object`):

Map of current column name to new name, e.g. {"address.city": "city", "price": "price_usd"}. Use names as they are after flattening. Two columns cannot share a new name.

## `maxDepth` (type: `integer`):

How many levels of nested objects and arrays to flatten into dot-path columns (e.g. address.geo.lat). 0 = do not flatten (nested values stay as JSON). Anything deeper than this limit stays as a JSON value. Default 10, maximum 20.

## `arrayMode` (type: `string`):

How arrays are turned into columns. 'join' only joins numbers/text; arrays of objects stay as a JSON string. 'index' creates columns like 'lines.0.sku'. Default: json.

## `arrayJoinSeparator` (type: `string`):

Text placed between values when Arrays = join. Default '; ' (semicolon and space). Max 20 characters.

## `maxArrayItems` (type: `integer`):

With Arrays = index, only the first N items of each array get columns (protects against thousands of columns). Rows that were cut are counted in the run statistics. Default 100, maximum 1000.

## `flattenSeparator` (type: `string`):

Character(s) that join nested names in flattened columns, e.g. '.' gives 'address.city' and '\_' gives 'address_city'. Column names in other settings must use the same separator. Max 5 characters.

## `coerce` (type: `object`):

Map of column name to type: string, number, integer, boolean or date. Example: {"price": "number", "inStock": "boolean", "createdAt": "date"}. Numbers accept '$1,234.50' and '(12)'; dates become ISO 8601 UTC. Values that cannot be converted become empty and are counted in the run statistics. Runs before filters and sorting.

## `decimalSeparator` (type: `string`):

How to read numbers written as text when converting to number or integer. Default: dot.

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

Remove leading and trailing spaces from all text values.

## `emptyStringsAsNull` (type: `boolean`):

Turn empty or whitespace-only text into null (an empty cell in CSV).

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

Keep only rows that match. A JSON list of conditions: {"field": "price", "op": "gt", "value": 10}. Operators: eq, ne, gt, gte, lt, lte, contains, notContains, startsWith, endsWith, in, notIn (value is a list), exists, notExists. Up to 50 conditions. Field names are the flattened names, before renaming.

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

Whether a row must match all filter conditions or just one.

## `filterIgnoreCase` (type: `boolean`):

Compare text without regard to upper/lower case in eq, ne, in, contains and similar operators.

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

Flattened column name to sort by, e.g. 'price'. Numbers sort before text; empty values always go last. Sorting holds the matching rows in memory (max 500,000 rows). Empty = keep the original order.

## `sortOrder` (type: `string`):

Ascending or descending. Only used when 'Sort by column' is set.

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

Skip this many rows of the final result (after filtering and sorting). Skipped rows are not charged. Default 0.

## `limit` (type: `integer`):

Stop after this many output rows. Leave empty for no limit (hard cap 1,000,000 rows per run). Billing is per output row, so this also caps cost.

## `maxCellChars` (type: `integer`):

Longer text is cut to this length (32,000 fits Excel's cell limit of 32,767). Cut cells are counted in the run statistics. 0 = no limit. Default 32000.

## `exportFormat` (type: `string`):

Besides the output dataset, save all output rows as a file in the run's key-value store and return a link in the run statistics (record STATS, field fileUrl). The file is capped at 150 MB. Default: csv.

## `fileName` (type: `string`):

File name without extension. Only letters, digits, dot, dash and underscore are kept. The extension (.csv, .jsonl or .json) is added automatically.

## `csvDelimiter` (type: `string`):

Column separator for CSV files. Default: comma.

## `csvBom` (type: `boolean`):

Adds a byte-order mark so Excel opens non-English text correctly. Leave off for pandas, databases and most code.

## `csvEscapeFormulas` (type: `boolean`):

Prefix text cells starting with =, +, @ (or - followed by text) with an apostrophe so Excel or Sheets never run them as formulas. Recommended when the CSV will be opened in a spreadsheet and the data comes from untrusted sources.

## Actor input object example

```json
{
  "maxDepth": 10,
  "arrayMode": "json",
  "arrayJoinSeparator": "; ",
  "maxArrayItems": 100,
  "flattenSeparator": ".",
  "decimalSeparator": ".",
  "trimStrings": false,
  "emptyStringsAsNull": false,
  "filterMode": "all",
  "filterIgnoreCase": true,
  "sortOrder": "asc",
  "offset": 0,
  "maxCellChars": 32000,
  "exportFormat": "csv",
  "fileName": "export",
  "csvDelimiter": ",",
  "csvBom": false,
  "csvEscapeFormulas": false
}
```

# Actor output Schema

## `results` (type: `string`):

The transformed rows (flattened, filtered, renamed) in the default dataset. Download as CSV, JSON, Excel and more from here.

## `stats` (type: `string`):

Record STATS: rows read, written, filtered out, conversion failures, column list and the link (fileUrl) to the saved CSV / JSONL / JSON file.

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("ucchiii/dataset-transform").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("ucchiii/dataset-transform").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 '{}' |
apify call ucchiii/dataset-transform --silent --output-dataset

```

## MCP server setup

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

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/zneWC9ASXGjU0Z7aN/builds/iae86baevgl1zTovb/openapi.json
