# Dataset Explode & Unpivot: arrays to rows (`ucchiii/dataset-explode-unpivot`) Actor

Turn array fields into one row per element (explode), wide columns into name/value rows (unpivot) or back (pivot) for Apify datasets and JSON/CSV files. Works from the Console form. Empty input = free demo. $0.20 per 1,000 output rows.

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

## Pricing

from $0.14 / 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

**Arrays to rows, wide to long, long to wide**

**For anyone whose scraper output has arrays inside rows (reviews, offers, tags, variants) or a spreadsheet layout that does not fit the next tool.** This Actor turns **one row with an array into one row per element**, **wide columns into name/value rows (unpivot)** and **name/value rows back into one wide row per id (pivot)**. It reads Apify datasets or public JSON / CSV file URLs and **works from the Console form**: Apify's own `flatten` and `fields` options for datasets are API-only and not in the Console ([apify-docs issue #3036](https://github.com/apify/apify-docs/issues/3036)), so here you just pick the fields and press Start.

**Try it free: leave input empty and press Start** (clear the prefilled file URL and dataset first; the demo is not charged and rows carry `_demo: true`).
**$0.20 per 1,000 output rows**, charged per row ($0.0002 a row): 1,000 rows cost $0.20, 10,000 rows $2, 100,000 rows $20. Failed runs, runs with no output rows and the demo are free. No scraping, no API keys.

### Sample: explode `reviews`

Input rows: 3 products, 3 reviews in total (one product has none). Output with `explodeFields: ["reviews"]`:

| sku | title | price | \_index | reviews.rating | reviews.user.name | reviews.text |
|---|---|---|---|---|---|---|
| KB-01 | Mechanical keyboard | 89 | 0 | 5 | Aiko | Great keys |
| KB-01 | Mechanical keyboard | 89 | 1 | 3 | Ben | A bit loud |
| MS-07 | Wireless mouse | 25.5 | 0 | 4 | Chen | Light and quick |
| HD-22 | USB hub | 18 | | | | |

(The product without reviews is kept as one row with `reviews: null` and an empty `_index`; turn off *keep empty* to drop it.)

### What it does

- **Explode**: one row per element of the array fields you choose. Dot paths work (`reviews`, `offers.items`). The parent fields are copied onto every row (nested objects are flattened with your separator), an **index column** (0-based) says which element it is, and **arrays of objects get their keys flattened with the path as prefix** (`reviews.user.name`). Arrays of plain values land in a column named after the path (`tags`).
- **Several array fields at once**: *zip* pairs elements by position (shorter arrays padded with null), *cross* makes every combination (with one index column per field).
- **Empty arrays**: *keep empty* (default) keeps the parent as one row with null instead of losing it; turn it off to drop such rows. A value that is not an array counts as a single element.
- **Unpivot (wide to long)**: the id columns stay, the value columns (or every other column) become `name` / `value` rows. Rename the two output columns; empty values are dropped by default.
- **Pivot (long to wide)**: id columns + a name column + a value column become one row per id with a column per name. For duplicates choose **first, last, sum, count or list**.
- **Inputs**: Apify datasets (resource picker, read permission only) and public JSON, NDJSON, CSV or TSV file URLs (encoding and delimiter detected, numbers recognized; for a JSON object that wraps the rows give a `recordsPath` such as `products`).
- **Outputs**: the default dataset, plus an optional **CSV file** in the key-value store with a download link (delimiter, UTF-8 BOM for Excel, formula neutralizing).
- **Safe by default**: `maxOutputRows` (default 100,000, maximum 1,000,000) caps run time and cost; when it cuts the output, `truncated` is `true` in the run summary.

### Input example

```json
{
  "fileUrls": ["https://dummyjson.com/products?limit=10&select=title,price,tags,reviews"],
  "recordsPath": "products",
  "operation": "explode",
  "explodeFields": ["reviews"],
  "exportCsv": true
}
```

Unpivot a dataset: `{"datasetIds": ["<id>"], "operation": "unpivot", "idColumns": ["region"], "valueColumns": ["jan","feb","mar"], "unpivotNameColumn": "month", "unpivotValueColumn": "sales"}`.
Pivot it back: `{"datasetIds": ["<id>"], "operation": "pivot", "idColumns": ["region"], "pivotNameColumn": "month", "pivotValueColumn": "sales", "aggregation": "sum"}`.

### Output

- **Dataset**: the reshaped rows. Column names follow the flatten separator (`.` by default). When you use several sources (except pivot, which combines them) each row has a `_source` column.
- **STATS** record (key-value store): `inputRows`, `outputRows`, `columns`, `truncated`, `chargedUnits`, per-source `errors`, counts of skipped rows (`skippedRows` for pivot rows without a name, `rowsWithoutValues` for unpivot, `nonNumericSkipped` for pivot sums) and, with the CSV option, `csvKey` and `csvUrl` (Apify's signed public link).

### Rules worth knowing

- Other nested objects are flattened to dotted columns; other arrays stay arrays in the dataset and become JSON text in the CSV.
- With an empty array the exploded column is the plain path (`reviews`) holding null, because there are no element keys to name.
- Pivot: rows whose name is empty are skipped (counted). `first` and `last` ignore empty values. `sum` adds numbers and numeric text and skips other values (counted). `count` counts rows. A name equal to an id column gets a `_2` suffix. At most 1,000 distinct names.
- Unpivot and pivot use column names as they are after flattening; an id or value column given as a parent name (`customer`) covers every column under it when unpivoting.

### Limits (cannot be raised by input)

- 20 datasets or files per run; 50 MB per file; 1,000,000 input rows and 1,000,000 output rows per run; 50 field names per setting
- Source URLs must be public `http` or `https` links. Addresses on internal networks (localhost, private ranges, cloud metadata) are blocked, also after redirects. Excel files are not read (convert them first)
- The CSV file is capped at 200 MB; the dataset always has every row up to your row limit

### Pricing

Pay per event: **$0.20 per 1,000 output rows** (event `row`, $0.0002 per output row): 2,500 output rows cost $0.50. Input rows are free. Nothing is charged when the run fails or produces no rows. Set a maximum charge per run: the Actor then stops producing rows at that budget (never past it) and says so in `truncated`.
On the Apify Free plan, the $5 of monthly platform credit covers about **25,000 rows** a month — enough to try it on real data.

### Use cases

- One row per review, offer, variant or tag from a scraper dataset, ready for a pivot table or a database
- Wide monthly or per-country columns to a tidy long table for charts and BI tools
- Long exports (id, metric, value) to one wide row per id for a spreadsheet
- Doing from the Console form what the dataset API's `flatten` / `fields` options only do through the API

### FAQ

**Does it change my source dataset?** No. It only reads it and writes a new dataset.
**Why `_index` is empty on some rows?** Those parents had an empty or missing array and were kept because *keep empty* is on.
**My JSON file is an object, not an array.** Give `recordsPath` (for example `data.items`). Without it, the only array of objects in the file is used.
**Can I explode an array inside an array?** Run the Actor twice: explode `orders` first, then explode `orders.items` on the resulting dataset (use the same separator).

If this Actor saved you time, a rating on the Store helps others find it.

### More tools from the same developer

- [Dataset Transform](https://apify.com/ucchiii/dataset-transform) — flatten, filter, rename and convert any Apify dataset, export CSV / JSON / JSONL with a link
- [CSV / XLSX Converter](https://apify.com/ucchiii/csv-xlsx-converter) — convert CSV, TSV, Excel XLSX, JSON and NDJSON files or Apify datasets into each other
- [Remove Duplicates](https://apify.com/ucchiii/dataset-dedup-merge) — merge Apify datasets and remove duplicates by the fields you choose
- [Google Sheets Export](https://apify.com/ucchiii/google-sheets-export) — export an Apify dataset to Google Sheets
- [Fuzzy Dedupe](https://apify.com/ucchiii/dataset-fuzzy-dedupe) — find near-duplicate names, addresses, phones and URLs that exact dedup misses

# Actor input Schema

## `operation` (type: `string`):

What to do with the rows. Explode copies the parent fields onto one row per element of an array field. Unpivot (wide to long) keeps id columns and turns value columns into name/value rows. Pivot (long to wide) does the reverse. Default: explode.

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

Datasets to reshape (the 'defaultDatasetId' of a finished run). You can paste a dataset URL from Apify Console too. Up to 20 datasets and files together; several sources are processed one after the other (pivot combines them). Leave this AND 'File URLs' empty to run a free demo on a built-in sample (not charged).

## `fileUrls` (type: `array`):

Public http(s) links to JSON, NDJSON, CSV or TSV files (up to 50 MB each). Links to internal networks are blocked. Clear this list and 'Apify datasets' to run the free demo.

## `inputFormat` (type: `string`):

Format of the files in 'File URLs'. 'auto' uses the file extension, content type and content. Ignored for datasets. Default: auto.

## `recordsPath` (type: `string`):

For a JSON file whose rows sit inside an object, the dot path to the array of rows, e.g. 'products' or 'data.items'. Empty = the file is an array, or the only array of objects in it is used.

## `explodeFields` (type: `array`):

Explode only. Dot paths of the array fields to turn into rows, such as 'reviews' or 'offers.items' (up to 50). Each element becomes a row with the parent fields copied; object elements get their keys flattened with the path as prefix (reviews.rating). A value that is not an array counts as one element.

## `multiFieldMode` (type: `string`):

Explode only, when you list more than one array field. Zip pairs the elements by position (shorter arrays are padded with null). Cross makes every combination, which can multiply the rows quickly. Default: zip.

## `indexColumn` (type: `string`):

Name of the column that holds the 0-based position of the element in its array. With 'cross' and several fields each field gets its own column, '*index*<field>'. Default: \_index.

## `keepEmpty` (type: `boolean`):

When an array is empty, null or missing, keep the parent as one row with null in the exploded column instead of dropping it. Default: on.

## `idColumns` (type: `array`):

Unpivot: columns that stay on every output row. Pivot: columns that identify one output row (the same combination of values becomes one row). Nested names use the flatten separator (customer.id); a parent name keeps every column under it when unpivoting.

## `valueColumns` (type: `array`):

Unpivot only. Columns to turn into name/value rows, e.g. jan, feb, mar. Empty = every column that is not an id column.

## `unpivotNameColumn` (type: `string`):

Unpivot only. Output column that holds the old column name (for example 'month'). Default: name.

## `unpivotValueColumn` (type: `string`):

Unpivot only. Output column that holds the value (for example 'sales'). Default: value.

## `unpivotDropEmpty` (type: `boolean`):

Skip name/value rows whose value is null or an empty string. Turn off to keep them. Default: on.

## `pivotNameColumn` (type: `string`):

Pivot only. The column whose values become the new column names (for example 'month'). Rows where it is empty are skipped and counted in the run summary.

## `pivotValueColumn` (type: `string`):

Pivot only. The column whose values fill the new columns (for example 'sales').

## `aggregation` (type: `string`):

Pivot only. What to put in a cell when several rows share the same id and name. 'sum' adds numbers and numeric text and skips other values; 'list' keeps every value as an array. Default: first.

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

Text that joins nested key names, e.g. '.' gives shop.city and '\_' gives shop_city. Max 5 characters. Field paths you type are always split on dots. Default: '.'.

## `maxOutputRows` (type: `integer`):

Stop after this many output rows. Exploding can multiply rows, so this caps both the run time and the cost ($0.20 per 1,000 rows). When it stops early, 'truncated' is true in the run summary. Maximum 1,000,000. Default: 100,000.

## `exportCsv` (type: `boolean`):

Besides the dataset, save all output rows as one CSV file in the key-value store (a public download link is in the run summary). Nested values are written as JSON text. Default: off.

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

File name without extension for the CSV (only letters, digits, dot, dash and underscore are kept). Empty = 'reshaped'.

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

Column separator of the CSV file. Default: comma.

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

Adds a byte-order mark so Excel opens non-English text (for example Japanese) correctly. Leave off for pandas and databases.

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

Prefix text cells that start with =, +, @ (or - followed by text) with an apostrophe so Excel never runs them as formulas.

## Actor input object example

```json
{
  "operation": "explode",
  "fileUrls": [
    "https://dummyjson.com/products?limit=10&select=title,price,tags,reviews"
  ],
  "inputFormat": "auto",
  "recordsPath": "products",
  "explodeFields": [
    "reviews"
  ],
  "multiFieldMode": "zip",
  "indexColumn": "_index",
  "keepEmpty": true,
  "unpivotNameColumn": "name",
  "unpivotValueColumn": "value",
  "unpivotDropEmpty": true,
  "aggregation": "first",
  "flattenSeparator": ".",
  "maxOutputRows": 100000,
  "exportCsv": false,
  "csvDelimiter": ",",
  "csvBom": false,
  "csvEscapeFormulas": false
}
```

# Actor output Schema

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

The output rows (exploded, unpivoted or pivoted). Download as CSV, JSON, Excel and more.

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

Record STATS: input and output row counts, the columns, whether the output was cut at the row limit, units charged, per-source errors and the CSV link.

## `files` (type: `string`):

The key-value store that holds the optional CSV 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 = {
    "fileUrls": [
        "https://dummyjson.com/products?limit=10&select=title,price,tags,reviews"
    ],
    "recordsPath": "products",
    "explodeFields": [
        "reviews"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("ucchiii/dataset-explode-unpivot").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 = {
    "fileUrls": ["https://dummyjson.com/products?limit=10&select=title,price,tags,reviews"],
    "recordsPath": "products",
    "explodeFields": ["reviews"],
}

# Run the Actor and wait for it to finish
run = client.actor("ucchiii/dataset-explode-unpivot").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 '{
  "fileUrls": [
    "https://dummyjson.com/products?limit=10&select=title,price,tags,reviews"
  ],
  "recordsPath": "products",
  "explodeFields": [
    "reviews"
  ]
}' |
apify call ucchiii/dataset-explode-unpivot --silent --output-dataset

```

## MCP server setup

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

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/zPqRXSXk7HzjeK2L7/builds/KmoLZLdmqbgQoHAGd/openapi.json
