# Excel to JSON Converter - XLSX, XLS, ODS & CSV to JSON (`gazidev/spreadsheet-to-json`) Actor

Convert Excel (XLSX, XLSM, XLS, XLSB), ODS, CSV and Google Sheets files to clean JSON: all or selected sheets, header-row auto-detection, merged-cell fill, ISO dates and typed numbers. Reverse mode exports any dataset, JSON or CSV to a styled XLSX. Pay per file.

- **URL**: https://apify.com/gazidev/spreadsheet-to-json.md
- **Developed by:** [Cemal Atakli](https://apify.com/gazidev) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $6.00 / 1,000 spreadsheet converteds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## Excel to JSON Converter: XLSX, XLS, ODS & CSV to JSON (and JSON to Excel)

Convert **Excel spreadsheets to JSON** in seconds. Give it a URL of an **XLSX, XLSM, XLS, XLSB, ODS, CSV or TSV**
file, or a Google Sheets link, and get clean, typed JSON back. You can choose one JSON object per row in an Apify dataset, or one JSON
file per sheet. Messy real-world sheets are handled for you:

- Title rows and offset tables are skipped, and the **header row is detected automatically**.
- **Merged cells** are filled in.
- Dates come out as **ISO 8601**, and numbers and booleans come out as real JSON types.

The converter also works the other way. **JSON to Excel** turns any Apify dataset, JSON URL or CSV URL into a
**styled XLSX** with a bold frozen header, filters, auto-sized columns and, optionally, one sheet per category.

No API key, no browser, no code. **$0.006 per file.**

### What it does

- **Formats:** XLSX, XLSM (macros are ignored), XLS (Excel 97-2003), XLSB, ODS (LibreOffice / OpenOffice), CSV and TSV.
  The format is detected from the file content, so a CSV saved as `.xls` still works.
- **Links:** direct URLs, **Google Sheets** (shared as "Anyone with the link", all tabs), Google Drive, Dropbox,
  OneDrive and GitHub links. You can also upload a file in the input form.
- **All sheets or selected ones**, by name or number. Hidden sheets are skipped unless you ask for them.
- **Header auto-detection:**
  - Skips report titles, blank rows and tables that don't start in A1.
  - Combines **two-row grouped headers** (`Q1` above `Sales` / `Units` → `Q1 Sales`, `Q1 Units`).
  - Can also be told exactly which row holds the header, or that there is no header row.
- **Merged cells** are filled into every row and column they span (e.g. a `Region` merged over 5 rows).
- **Clean types:**
  - Dates become `2024-05-31` or `2024-05-31T14:30:00`.
  - `5.0` becomes `5`, and floating-point noise is removed.
  - Booleans stay booleans.
  - In CSV, numbers and `true`/`false` are detected, while IDs with leading zeros (`00123`) stay text. European `1.234,56` is supported too.
- **Skips empty rows and empty columns**, and drops repeated header rows found in multi-page exports.
- **Column names:** as in the file, or normalised to `camelCase` / `snake_case`. Duplicate names get `_2`, `_3`.
- **Traceability:** each row gets `_file`, `_sheet` and `_row` (the original row number in the sheet).
- **Big files:** a fast Rust engine (calamine) handles typical files. Sheets too large for memory are **streamed** with
  openpyxl read-only mode. 200,000 rows × 10 columns took about 6 s in a local test.
- **Encoding and delimiter** detection for CSV: UTF-8, UTF-8 BOM, UTF-16 and Windows-1252; `,` `;` tab `|`.
- **Per-file errors never stop the run.** A broken link or a PDF gives a free error row and the other files continue.

### Use cases

- Feed Excel data into an API, database, n8n/Make/Zapier flow or LLM/RAG pipeline as JSON.
- Import supplier price lists, product catalogs or inventory sheets that start with logos, titles and merged headers.
- Read every tab of a Google Sheet as JSON without the Google Sheets API.
- Convert legacy `.xls` / `.ods` archives to modern JSON in bulk.
- **JSON to Excel:** send any scraper's dataset to a client as a nicely formatted Excel file, with one sheet per country or category.

### Input example

```json
{
  "fileUrls": [
    "https://go.microsoft.com/fwlink/?LinkID=521962",
    "https://docs.google.com/spreadsheets/d/<id>/edit#gid=0"
  ],
  "sheets": [],
  "headerCase": "snake_case",
  "outputMode": "rows"
}
```

Reverse mode (JSON → Excel):

```json
{ "mode": "toExcel", "datasetIds": ["<datasetId>"], "groupByField": "country", "outputFileName": "leads" }
```

### Output example

One dataset item per spreadsheet row:

```json
{
  "Segment": "Government", "Country": "Canada", "Product": "Carretera", "Units Sold": 1618.5,
  "Sale Price": 20, "Sales": 32370, "Profit": 16185, "Date": "2014-01-01",
  "_file": "Financial Sample.xlsx", "_sheet": "Sheet1", "_row": 2
}
```

A messy sheet with a title row, a grouped two-row header and a vertically merged `Region`:

```json
{"Region": "North", "Q1 Sales": 1200.5, "Q1 Units": 10, "Notes": "ok", "_row": 6}
{"Region": "North", "Q1 Sales": 300,    "Q1 Units": 3,  "Notes": null, "_row": 7}
```

With `outputMode: "jsonFiles"` each sheet becomes one JSON array in the key-value store (for example
`Financial_Sample-Sheet1.json`), and the dataset gets one summary row per sheet with a `jsonUrl` download link.
The `OUTPUT` record summarises every file: sheets, header rows, column names, row counts and what was charged.

### Pricing (pay per event)

| Event | Price |
|---|---|
| Spreadsheet converted (all sheets, first 10,000 rows) | **$0.006** |
| Each further 10,000 rows | $0.002 |
| XLSX workbook created (JSON → Excel) | $0.006 |
| Dataset row (dataset output mode only; **free in JSON-file mode**) | $0.02 per 1,000 rows |
| Actor start | $0.00005 |

Failed, empty or unsupported files are **free**. Set a *maximum cost per run* and the Actor stops cleanly at that limit.

| Actor | Price per small file |
|---|---|
| **This Actor** | **$0.006** + $0.02 per 1,000 dataset rows (a 500-row file ≈ $0.016) |
| eliai/excel-to-json | $0.03 |
| glistening_film (Excel to JSON) | $0.01 |

Competitor prices are from the Apify Store on 2026-10-16.

### FAQ

**Are formulas evaluated?** You get the value Excel saved with the file, which is what you see in Excel. A formula in a
file that was generated by a script and never opened in Excel has no saved value, so it comes back as `null`.

**How are dates returned?** As ISO 8601 strings: `2024-05-31` when the cell holds a date only, and `2024-05-31T14:30:00` when it also has a time.
Times are returned as `14:30:00`.

**The header was detected wrongly.** Set `headerRow` to the row number of the header, and `headerRowCount` if the
header spans several rows. Use `headerRow: 0` for sheets without a header (columns are named `column_A`, `column_B`, ...).

**Password-protected files?** These are not supported. They come back as a free error row.

**Google Sheets?** Share the sheet as "Anyone with the link can view" and paste the normal URL. All tabs are converted.

**Is there a size limit?** 200 MB per file by default (`maxFileSizeMb`, up to 2 GB). Very large sheets are streamed, so memory use stays flat.

**How do I get Excel back out?** Use mode `JSON → Excel` with the dataset ID of any run, or with a JSON or CSV URL.

### Use with AI agents (Apify MCP)

This Actor works as a tool in the [Apify MCP server](https://mcp.apify.com), so Claude, ChatGPT, Cursor and
other agents can call it to **read spreadsheets as JSON**. For example: "Convert this XLSX to JSON and tell me the top 5
products by profit". Agents can also call it to **produce Excel files**, for example "Export these results to an Excel sheet per country".
Inputs are plain URLs and the output is typed JSON with column names, so agents can use it without extra parsing.
Call it from code with the Apify API or client (`apify-client` for JS/Python) using the input shown above.

# Actor input Schema

## `mode` (type: `string`):

'Spreadsheet → JSON' converts XLSX / XLSM / XLS / XLSB / ODS / CSV / TSV files into JSON rows. 'JSON → Excel' turns an Apify dataset, a JSON URL or a CSV URL into a styled XLSX workbook.

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

Public URLs of XLSX, XLSM, XLS, XLSB, ODS, CSV or TSV files. Google Sheets links ('Anyone with the link' sharing), Google Drive, Dropbox, OneDrive and GitHub links are converted to direct downloads automatically. The real format is detected from the file content, not the extension.

## `uploadedFile` (type: `string`):

Upload a file from your computer instead of giving a URL.

## `sheets` (type: `array`):

Sheet names or 1-based sheet numbers (e.g. 'Sales' or '2'). Leave empty to convert every visible sheet.

## `includeHiddenSheets` (type: `boolean`):

Also convert sheets that are hidden in Excel. Hidden sheets you name in 'Sheets to convert' are always included.

## `headerRow` (type: `integer`):

Leave empty to auto-detect the header row (title rows, blank rows and offset tables are skipped automatically). 1 = first row. 0 = the sheet has no header row (columns are named column_A, column_B, ...).

## `headerRowCount` (type: `integer`):

Number of stacked header rows to combine (e.g. 'Q1' above 'Sales' becomes 'Q1 Sales'). Leave empty to auto-detect (1, or 2 when the header contains merged group cells).

## `headerCase` (type: `string`):

Keep the header text as it is, or normalise it to camelCase / snake_case keys (handy for databases and code).

## `fillMergedCells` (type: `boolean`):

Copy the value of a merged cell into every cell it spans (e.g. a 'Region' merged over 5 rows appears on all 5 rows).

## `inferTypes` (type: `boolean`):

Convert CSV text to numbers, booleans (true/false) and ISO dates. IDs with leading zeros (e.g. ZIP '02134') and huge integers stay text. Excel/ODS cells keep their native types; dates are always output as ISO 8601.

## `decimalComma` (type: `boolean`):

Read CSV numbers like '1.234,56' as 1234.56. Only needed for CSV/TSV exported with a European locale.

## `skipEmptyRows` (type: `boolean`):

Drop rows where every cell is empty. Empty columns without a header are always dropped.

## `trimWhitespace` (type: `boolean`):

Remove leading/trailing spaces from text cells.

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

Auto-detects comma, semicolon, tab and pipe. Encoding (UTF-8, UTF-8 BOM, UTF-16, Windows-1252) is detected automatically.

## `outputMode` (type: `string`):

'Dataset rows': one dataset item per spreadsheet row (download as JSON, CSV, Excel or via API). 'JSON file per sheet': one JSON array file per sheet in the key-value store (+ one summary row per sheet). 'Both' does both (same price).

## `includeRowMetadata` (type: `boolean`):

Add the source file name, sheet name and original row number to every dataset row.

## `maxRowsPerSheet` (type: `integer`):

Stop after this many data rows per sheet (0 = no limit). Useful for previews.

## `maxFileSizeMb` (type: `integer`):

Files larger than this are skipped (not charged).

## `maxConcurrency` (type: `integer`):

How many files are downloaded and converted at the same time.

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

Apify dataset IDs to export as XLSX, e.g. the output of another Actor run. In an integration use {{resource.defaultDatasetId}}.

## `sourceUrls` (type: `array`):

URLs returning a JSON array, JSON Lines, an object with an 'items'/'data'/'results' array, or CSV.

## `groupByField` (type: `string`):

Optional field name (dot notation for nested fields, e.g. 'address.country'). Each distinct value gets its own sheet (max 200 sheets).

## `combineSources` (type: `boolean`):

Put every dataset / URL into one XLSX (one sheet per source) instead of one XLSX per source.

## `outputFileName` (type: `string`):

Name of the created workbook in the key-value store (default: the dataset ID or URL file name).

## `convertIsoDates` (type: `boolean`):

Values like '2024-05-31' or '2024-05-31T10:00:00' become real Excel date cells (sortable, filterable).

## `maxRows` (type: `integer`):

Only export the first N items of each source (0 = all).

## Actor input object example

```json
{
  "mode": "toJson",
  "fileUrls": [
    "https://go.microsoft.com/fwlink/?LinkID=521962",
    "https://raw.githubusercontent.com/mwaskom/seaborn-data/master/tips.csv"
  ],
  "includeHiddenSheets": false,
  "headerCase": "original",
  "fillMergedCells": true,
  "inferTypes": true,
  "decimalComma": false,
  "skipEmptyRows": true,
  "trimWhitespace": true,
  "csvDelimiter": "auto",
  "outputMode": "rows",
  "includeRowMetadata": true,
  "maxRowsPerSheet": 0,
  "maxFileSizeMb": 200,
  "maxConcurrency": 3,
  "combineSources": false,
  "convertIsoDates": true,
  "maxRows": 0
}
```

# Actor output Schema

## `rows` (type: `string`):

No description

## `files` (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 = {
    "fileUrls": [
        "https://go.microsoft.com/fwlink/?LinkID=521962",
        "https://raw.githubusercontent.com/mwaskom/seaborn-data/master/tips.csv"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("gazidev/spreadsheet-to-json").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://go.microsoft.com/fwlink/?LinkID=521962",
        "https://raw.githubusercontent.com/mwaskom/seaborn-data/master/tips.csv",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("gazidev/spreadsheet-to-json").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://go.microsoft.com/fwlink/?LinkID=521962",
    "https://raw.githubusercontent.com/mwaskom/seaborn-data/master/tips.csv"
  ]
}' |
apify call gazidev/spreadsheet-to-json --silent --output-dataset

```

## MCP server setup

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

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/eyuCgijT7LpNTWbag/builds/JVfgcKeZVFTgCBsIt/openapi.json
