# SPSS, Stata & SAS to CSV, JSON & Parquet (keeps labels) (`nibble/statistical-file-to-csv`) Actor

Convert SPSS .sav/.zsav, Stata .dta and SAS .sas7bdat/.xpt files to CSV, JSON or Parquet — preserving the value labels, variable labels and metadata that naive exports drop.

- **URL**: https://apify.com/nibble/statistical-file-to-csv.md
- **Developed by:** [Simon Fletcher](https://apify.com/nibble) (community)
- **Categories:** Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.09 / 1,000 file 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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

### What does the SPSS / Stata / SAS Converter do?

**Statistical File to CSV** converts proprietary statistical data files — **SPSS** (`.sav`, `.zsav`), **Stata** (`.dta`) and **SAS** (`.sas7bdat`, `.xpt`), plus SPSS portable (`.por`) — into clean **CSV, JSON or Parquet**. Crucially, it **preserves the value labels, variable labels and missing-value metadata that naive exports throw away**. When you open a `.sav` in pandas and dump a CSV, you get bare numeric codes (`1`, `2`, `99`) with no idea that `1 = Male`, `2 = Female`, `99 = "no answer"`, and no column descriptions. This Actor keeps all of it.

Give it a **file URL**, an **uploaded file**, or **inline base64** — it returns one structured record per file: the rows, the code→label dictionaries, the human-readable column labels, the declared missing ranges, and file metadata. It also saves a downloadable CSV/JSON/Parquet file to the run's key-value store.

Running on Apify means you get an HTTP API, scheduling, [integrations](https://apify.com/integrations) (Make, Zapier, n8n, Google Drive), run history, and access from the [Apify MCP server](https://mcp.apify.com/) so AI agents can call it directly.

### Why use the SPSS / Stata / SAS Converter?

- **Stop losing your labels** — survey and research files encode meaning in value labels and variable labels. This Actor carries them into your CSV/JSON/Parquet output instead of dropping them.
- **No SPSS, Stata or SAS license required** — read files from all three ecosystems without owning any of the (expensive) source software.
- **Feed pipelines and notebooks** — get analysis-ready JSON/CSV/Parquet for pandas, R, BigQuery, Snowflake or a data warehouse.
- **Give AI agents decoded data** — the output is compact structured JSON (no binary, no proprietary blobs), ideal for LLM tool use over the Apify MCP. Turn on *Apply value labels* to hand agents `"Male"` instead of `1`.
- **Batch convert** — pass many files in one run; each converted file is one dataset item and one billable event.

### How to use the SPSS / Stata / SAS Converter

1. Open the **Input** tab.
2. Provide your files one of three ways: **upload files** (delivered as key-value-store keys), add **file URLs**, or paste **inline base64** files (great for API / agent callers). A sample SPSS survey is pre-filled so you can just click **Start**.
3. Pick an **output format** (CSV, JSON or Parquet). Optionally turn on **Apply value labels** to replace codes with their label text, or **Keep user-defined missing values** to retain codes like `99`.
4. Click **Start**. Each input file becomes one dataset item (download as JSON, CSV, Excel or HTML), and a converted file is saved to the key-value store.

### Input

| Field | Type | Description |
|-------|------|-------------|
| `fileUrls` | array | Public URLs of statistical files to download and convert. |
| `keyValueStoreKeys` | array | Keys of uploaded files in the run's key-value store. |
| `filesBase64` | array | Inline files as `{ "filename": "survey.sav", "data": "<base64>" }`. |
| `outputFormat` | string | `csv` (default), `json`, or `parquet` — the downloadable file saved to the key-value store. |
| `applyValueLabels` | boolean | Replace coded values with their value labels in the rows (default false). |
| `keepUserMissing` | boolean | Keep user-defined missing values instead of nulling them (default false). |
| `maxRows` | integer | Cap data rows per file (0/blank = no cap). |
| `format` | string | Force the input format when a URL/key has no recognizable extension. |

#### Example input

```json
{
  "filesBase64": [{ "filename": "survey.sav", "data": "JEZMMkAo..." }],
  "outputFormat": "csv",
  "applyValueLabels": false
}
```

### Output

Each input file produces **one dataset item**. You can download the dataset as JSON, CSV, Excel or HTML, and the flattened CSV/Parquet file is also saved to the key-value store.

```json
{
  "source": "survey.sav",
  "filename": "survey.sav",
  "status": "ok",
  "format": "sav",
  "rowCount": 3,
  "columnCount": 3,
  "columns": ["id", "gender", "satisfaction"],
  "variableLabels": {
    "id": "Respondent ID",
    "gender": "Gender",
    "satisfaction": "Overall satisfaction (1-5)"
  },
  "valueLabels": {
    "gender": { "1": "Male", "2": "Female" },
    "satisfaction": { "1": "Very dissatisfied", "3": "Neutral", "5": "Very satisfied" }
  },
  "missingRanges": {},
  "records": [
    { "id": 1, "gender": 1, "satisfaction": 5 },
    { "id": 2, "gender": 2, "satisfaction": 3 },
    { "id": 3, "gender": 2, "satisfaction": 4 }
  ],
  "meta": {
    "outputFormat": "csv",
    "valueLabelsApplied": false,
    "fileLabel": "Customer satisfaction survey",
    "tableName": null,
    "encoding": "UTF-8",
    "columnTypes": { "id": "double", "gender": "double", "satisfaction": "double" },
    "rowsTruncated": false
  }
}
```

### Output data fields

| Field | Description |
|-------|-------------|
| `format` | Detected input format: `sav`, `zsav`, `dta`, `xpt`, `sas7bdat`, `por`. |
| `columns` | Ordered variable (column) names. |
| `variableLabels` | Variable name → descriptive label (the text naive exports drop). |
| `valueLabels` | Variable name → `{ code: label }` dictionaries for categorical variables. |
| `missingRanges` | Variable name → user-defined missing values/ranges declared in the file. |
| `records` | The converted rows: clean integers/floats, ISO-8601 dates, `null` for missing. |
| `meta` | File label, table name, encoding, per-column storage types, and flags. |

### How much does it cost?

The Actor is billed **per converted file** (one pay-per-event per non-empty file). Empty, metadata-only or unreadable files are **never charged**. Compute is tiny — conversion is a few milliseconds per file — so cost is dominated by container startup. Batch many files in one run to amortize it. See `PRICING.md` for the unit-economics breakdown; the exact per-event price is set by the publisher in the Apify Console.

### Tips and advanced options

- **Codes vs. labels:** leave *Apply value labels* off to keep raw codes plus the decode maps (best for data pipelines); turn it on to get human-readable text directly in the rows.
- **Missing values:** turn on *Keep user-defined missing values* when the missing codes (e.g. `99 = refused`) are analytically meaningful; otherwise they collapse to `null` and the ranges are still reported in `missingRanges`.
- **Large files:** use `maxRows` to sample the first N rows for a quick schema check before a full run.
- **Parquet** output is ideal for loading into data warehouses and columnar analytics.

### FAQ, disclaimers, and support

- **What formats are supported?** SPSS `.sav` / `.zsav` / `.por`, Stata `.dta`, and SAS `.sas7bdat` / `.xpt`.
- **Are value labels really preserved?** Yes — that is the whole point. SAS transport (`.xpt`) and `.sas7bdat` carry variable labels but not inline value labels (SAS stores those in a separate catalog), so only variable labels appear for those formats.
- **Is any of my data sent anywhere?** No third-party services — the Actor only reads the files you supply and writes the converted output back to your run's storage.
- Found a problem or need another format? Use the **Issues** tab. Custom conversions are available on request.

# Actor input Schema

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

Public URLs of statistical files to convert (.sav, .zsav, .dta, .xpt, .sas7bdat, .por). Each is downloaded with retries. The format is detected from the file extension.

## `keyValueStoreKeys` (type: `array`):

Keys in this run's default key-value store that hold statistical-file bytes. This is how files uploaded via the Console form are passed to the Actor. Name each key with its file extension (e.g. survey.sav) so the format can be detected, or set the Format override below.

## `filesBase64` (type: `array`):

Files sent inline as {"filename": "survey.sav", "data": "<base64>"} — convenient for API or AI-agent callers. The filename's extension selects the reader.

## `outputFormat` (type: `string`):

Downloadable file format saved to the key-value store for each converted file. CSV and Parquet are the flattened table; JSON keeps the full structured record (rows + labels + metadata). The dataset always contains the structured JSON regardless.

## `applyValueLabels` (type: `boolean`):

Replace coded values with their human-readable value labels in the rows (e.g. 1 -> "Male"). Off keeps the raw codes and returns the code -> label maps separately (best for pipelines). Only affects SPSS/Stata files that carry inline value labels.

## `keepUserMissing` (type: `boolean`):

Keep user-defined missing values (e.g. 99 = 'no answer') as their coded value instead of collapsing them to null. The missing-value ranges are always reported in the output metadata either way.

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

Cap the number of data rows converted per file (0 or blank = no cap).

## `format` (type: `string`):

Force the input format when a URL or key-value-store key has no recognizable extension. Leave blank to auto-detect from the filename.

## Actor input object example

```json
{
  "fileUrls": [
    {
      "url": "https://raw.githubusercontent.com/pandas-dev/pandas/main/pandas/tests/io/sas/data/airline.sas7bdat"
    }
  ],
  "filesBase64": [
    {
      "filename": "customer-survey.sav",
      "data": "JEZMMkAoIykgU1BTUyBEQVRBIEZJTEUgLSBodHRwczovL2dpdGh1Yi5jb20vV2l6YXJkTWFjL1JlYWRTdGF0IAIAAAADAAAAAAAAAAAAAAADAAAAAAAAAAAAWUAxNSBKdWwgMjYxOTo1NjoxMkN1c3RvbWVyIHNhdGlzZmFjdGlvbiBzdXJ2ZXkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAAAAACAAAAAAAAAAEAAAAAAAAAAggFAAIIBQBJRCAgICAgIA0AAABSZXNwb25kZW50IElEAAAAAgAAAAAAAAABAAAAAAAAAAIIBQACCAUAR0VOREVSICAGAAAAR2VuZGVyAAACAAAAAAAAAAEAAAAAAAAAAggFAAIIBQBTQVRJU0ZBQxoAAABPdmVyYWxsIHNhdGlzZmFjdGlvbiAoMS01KQAAAwAAAAIAAAAAAAAAAADwPwRNYWxlICAgAAAAAAAAAEAGRmVtYWxlIAQAAAABAAAAAgAAAAMAAAADAAAAAAAAAAAA8D8RVmVyeSBkaXNzYXRpc2ZpZWQgICAgICAAAAAAAAAIQAdOZXV0cmFsAAAAAAAAFEAOVmVyeSBzYXRpc2ZpZWQgBAAAAAEAAAADAAAABwAAAAMAAAAEAAAACAAAABQAAAAAAAAAAAAAAP////8BAAAAAQAAAAIAAADp/QAABwAAAAQAAAAIAAAAAwAAAP///////+//////////73/+///////v/wcAAAALAAAABAAAAAkAAAAAAAAACAAAAAEAAAAAAAAACAAAAAEAAAAAAAAACAAAAAEAAAAHAAAADQAAAAEAAAApAAAASUQ9aWQJR0VOREVSPWdlbmRlcglTQVRJU0ZBQz1zYXRpc2ZhY3Rpb24HAAAAEAAAAAgAAAACAAAAAQAAAAAAAAADAAAAAAAAAOcDAAAAAAAAAAAAAAAA8D8AAAAAAADwPwAAAAAAABRAAAAAAAAAAEAAAAAAAAAAQAAAAAAAAAhAAAAAAAAACEAAAAAAAAAAQAAAAAAAABBA"
    }
  ],
  "outputFormat": "csv",
  "applyValueLabels": false,
  "keepUserMissing": false
}
```

# 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": [
        {
            "url": "https://raw.githubusercontent.com/pandas-dev/pandas/main/pandas/tests/io/sas/data/airline.sas7bdat"
        }
    ],
    "filesBase64": [
        {
            "filename": "customer-survey.sav",
            "data": "JEZMMkAoIykgU1BTUyBEQVRBIEZJTEUgLSBodHRwczovL2dpdGh1Yi5jb20vV2l6YXJkTWFjL1JlYWRTdGF0IAIAAAADAAAAAAAAAAAAAAADAAAAAAAAAAAAWUAxNSBKdWwgMjYxOTo1NjoxMkN1c3RvbWVyIHNhdGlzZmFjdGlvbiBzdXJ2ZXkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAAAAACAAAAAAAAAAEAAAAAAAAAAggFAAIIBQBJRCAgICAgIA0AAABSZXNwb25kZW50IElEAAAAAgAAAAAAAAABAAAAAAAAAAIIBQACCAUAR0VOREVSICAGAAAAR2VuZGVyAAACAAAAAAAAAAEAAAAAAAAAAggFAAIIBQBTQVRJU0ZBQxoAAABPdmVyYWxsIHNhdGlzZmFjdGlvbiAoMS01KQAAAwAAAAIAAAAAAAAAAADwPwRNYWxlICAgAAAAAAAAAEAGRmVtYWxlIAQAAAABAAAAAgAAAAMAAAADAAAAAAAAAAAA8D8RVmVyeSBkaXNzYXRpc2ZpZWQgICAgICAAAAAAAAAIQAdOZXV0cmFsAAAAAAAAFEAOVmVyeSBzYXRpc2ZpZWQgBAAAAAEAAAADAAAABwAAAAMAAAAEAAAACAAAABQAAAAAAAAAAAAAAP////8BAAAAAQAAAAIAAADp/QAABwAAAAQAAAAIAAAAAwAAAP///////+//////////73/+///////v/wcAAAALAAAABAAAAAkAAAAAAAAACAAAAAEAAAAAAAAACAAAAAEAAAAAAAAACAAAAAEAAAAHAAAADQAAAAEAAAApAAAASUQ9aWQJR0VOREVSPWdlbmRlcglTQVRJU0ZBQz1zYXRpc2ZhY3Rpb24HAAAAEAAAAAgAAAACAAAAAQAAAAAAAAADAAAAAAAAAOcDAAAAAAAAAAAAAAAA8D8AAAAAAADwPwAAAAAAABRAAAAAAAAAAEAAAAAAAAAAQAAAAAAAAAhAAAAAAAAACEAAAAAAAAAAQAAAAAAAABBA"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("nibble/statistical-file-to-csv").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": [{ "url": "https://raw.githubusercontent.com/pandas-dev/pandas/main/pandas/tests/io/sas/data/airline.sas7bdat" }],
    "filesBase64": [{
            "filename": "customer-survey.sav",
            "data": "JEZMMkAoIykgU1BTUyBEQVRBIEZJTEUgLSBodHRwczovL2dpdGh1Yi5jb20vV2l6YXJkTWFjL1JlYWRTdGF0IAIAAAADAAAAAAAAAAAAAAADAAAAAAAAAAAAWUAxNSBKdWwgMjYxOTo1NjoxMkN1c3RvbWVyIHNhdGlzZmFjdGlvbiBzdXJ2ZXkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAAAAACAAAAAAAAAAEAAAAAAAAAAggFAAIIBQBJRCAgICAgIA0AAABSZXNwb25kZW50IElEAAAAAgAAAAAAAAABAAAAAAAAAAIIBQACCAUAR0VOREVSICAGAAAAR2VuZGVyAAACAAAAAAAAAAEAAAAAAAAAAggFAAIIBQBTQVRJU0ZBQxoAAABPdmVyYWxsIHNhdGlzZmFjdGlvbiAoMS01KQAAAwAAAAIAAAAAAAAAAADwPwRNYWxlICAgAAAAAAAAAEAGRmVtYWxlIAQAAAABAAAAAgAAAAMAAAADAAAAAAAAAAAA8D8RVmVyeSBkaXNzYXRpc2ZpZWQgICAgICAAAAAAAAAIQAdOZXV0cmFsAAAAAAAAFEAOVmVyeSBzYXRpc2ZpZWQgBAAAAAEAAAADAAAABwAAAAMAAAAEAAAACAAAABQAAAAAAAAAAAAAAP////8BAAAAAQAAAAIAAADp/QAABwAAAAQAAAAIAAAAAwAAAP///////+//////////73/+///////v/wcAAAALAAAABAAAAAkAAAAAAAAACAAAAAEAAAAAAAAACAAAAAEAAAAAAAAACAAAAAEAAAAHAAAADQAAAAEAAAApAAAASUQ9aWQJR0VOREVSPWdlbmRlcglTQVRJU0ZBQz1zYXRpc2ZhY3Rpb24HAAAAEAAAAAgAAAACAAAAAQAAAAAAAAADAAAAAAAAAOcDAAAAAAAAAAAAAAAA8D8AAAAAAADwPwAAAAAAABRAAAAAAAAAAEAAAAAAAAAAQAAAAAAAAAhAAAAAAAAACEAAAAAAAAAAQAAAAAAAABBA",
        }],
}

# Run the Actor and wait for it to finish
run = client.actor("nibble/statistical-file-to-csv").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": [
    {
      "url": "https://raw.githubusercontent.com/pandas-dev/pandas/main/pandas/tests/io/sas/data/airline.sas7bdat"
    }
  ],
  "filesBase64": [
    {
      "filename": "customer-survey.sav",
      "data": "JEZMMkAoIykgU1BTUyBEQVRBIEZJTEUgLSBodHRwczovL2dpdGh1Yi5jb20vV2l6YXJkTWFjL1JlYWRTdGF0IAIAAAADAAAAAAAAAAAAAAADAAAAAAAAAAAAWUAxNSBKdWwgMjYxOTo1NjoxMkN1c3RvbWVyIHNhdGlzZmFjdGlvbiBzdXJ2ZXkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAAAAACAAAAAAAAAAEAAAAAAAAAAggFAAIIBQBJRCAgICAgIA0AAABSZXNwb25kZW50IElEAAAAAgAAAAAAAAABAAAAAAAAAAIIBQACCAUAR0VOREVSICAGAAAAR2VuZGVyAAACAAAAAAAAAAEAAAAAAAAAAggFAAIIBQBTQVRJU0ZBQxoAAABPdmVyYWxsIHNhdGlzZmFjdGlvbiAoMS01KQAAAwAAAAIAAAAAAAAAAADwPwRNYWxlICAgAAAAAAAAAEAGRmVtYWxlIAQAAAABAAAAAgAAAAMAAAADAAAAAAAAAAAA8D8RVmVyeSBkaXNzYXRpc2ZpZWQgICAgICAAAAAAAAAIQAdOZXV0cmFsAAAAAAAAFEAOVmVyeSBzYXRpc2ZpZWQgBAAAAAEAAAADAAAABwAAAAMAAAAEAAAACAAAABQAAAAAAAAAAAAAAP////8BAAAAAQAAAAIAAADp/QAABwAAAAQAAAAIAAAAAwAAAP///////+//////////73/+///////v/wcAAAALAAAABAAAAAkAAAAAAAAACAAAAAEAAAAAAAAACAAAAAEAAAAAAAAACAAAAAEAAAAHAAAADQAAAAEAAAApAAAASUQ9aWQJR0VOREVSPWdlbmRlcglTQVRJU0ZBQz1zYXRpc2ZhY3Rpb24HAAAAEAAAAAgAAAACAAAAAQAAAAAAAAADAAAAAAAAAOcDAAAAAAAAAAAAAAAA8D8AAAAAAADwPwAAAAAAABRAAAAAAAAAAEAAAAAAAAAAQAAAAAAAAAhAAAAAAAAACEAAAAAAAAAAQAAAAAAAABBA"
    }
  ]
}' |
apify call nibble/statistical-file-to-csv --silent --output-dataset

```

## MCP server setup

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

```

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/KMf91Nqc97fYgE5f5/builds/q3ejHcNL73u3KesVA/openapi.json
