# Extract ZIP Files: Unzip Archives & Email Attachments (`data-done-right/extract-zip-files`) Actor

Extract every file inside ZIP, 7z, TAR/GZ archives, emails (.eml/.msg attachments), PDFs (attachments and images) and Office files (embedded media). Each file gets a direct download URL plus name, path, size, MIME type and SHA-256. Zip-bomb and path-traversal safe.

- **URL**: https://apify.com/data-done-right/extract-zip-files.md
- **Developed by:** [Data Done Right](https://apify.com/data-done-right) (community)
- **Categories:** Automation, Developer tools
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 93.3% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$3.00 / 1,000 extracted files

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

## Extract ZIP Files: Unzip Archives & Email Attachments

This Actor lets you **extract ZIP files** (and 7z, TAR and GZ archives, emails, PDFs and Office files) from **file URLs** and returns **every file inside them**. Each extracted file is saved to the run's key-value store and returned with a **direct download URL**, its name, its path inside the source, size, MIME type and SHA-256.

It is a file extractor utility for automations and AI agents: unzip a vendor's data feed, pull invoice PDFs out of emails, get the XML invoice out of a Factur-X/ZUGFeRD PDF, or collect all the images in a set of PDFs or Word files.

### What can it extract

| Source | What gets extracted |
|---|---|
| **Archives**: `.zip`, `.7z`, `.tar`, `.tar.gz`/`.tgz`, `.tar.bz2`, `.tar.xz`, `.gz`, `.bz2`, `.xz` | Every file, with its folder path |
| **Email**: `.eml` (MIME) | Attachments, inline images, attached emails. Plus one row with **from, to, cc, date, subject and the text/HTML body** |
| **Outlook**: `.msg` | Attachments and embedded messages, plus the same email row |
| **PDF** | Embedded file attachments (portfolios, Factur-X/ZUGFeRD XML, file-attachment annotations) and **embedded images** (PNG/JPG) |
| **Office**: `.docx`, `.xlsx`, `.pptx`, `.odt`, `.ods`, `.odp` | Embedded media (`word/media`, `ppt/media`, `xl/media`, `Pictures/`) and embedded objects (for example an Excel sheet inside a Word file) |

**Nested containers** are opened too, to a depth you set (default 1, max 3): a zip inside a zip, emails inside a zip, an email attached to an email.

### Safe by design

- **Zip-bomb protection:** a compression-ratio limit per entry and per archive, a byte cap on every decompression stream, a per-file size limit, a run-wide total size limit, an entry-count limit and a timeout per source. Limits are enforced on the bytes actually decompressed, not on what the archive's headers claim.
- **Path traversal sanitized:** `../`, absolute paths, drive letters (`C:`) and control characters are removed from every inner path. Rows whose path was changed carry `pathSanitized: true`.
- **Skipped, never followed:** symlinks, hard links, devices and FIFOs. Encrypted entries and password-protected archives or PDFs are skipped with a reason.
- **Nothing is executed.** Files are never written to disk. The actor only reads bytes and stores them.
- **One bad file never stops the run.** Each source gets a summary row that lists what was skipped and why.

### How to unzip files and extract email attachments (input example)

```json
{
  "urls": [
    "https://example.com/feeds/vendor-export.zip",
    "https://example.com/mail/invoice.eml",
    "https://raw.githubusercontent.com/akretion/factur-x/master/tests/fixtures/pdf/invoice_EN16931.pdf"
  ],
  "includeExtensions": ["pdf", "xml", "csv", "png", "jpg"],
  "nestedDepth": 1,
  "maxFiles": 500,
  "emailInlineImages": false
}
```

Main options:

- `includeExtensions` / `excludeExtensions`: filter which files are delivered.
- `maxFiles` and `maxTotalSizeMb`: run-wide caps that also cap the cost.
- `nestedDepth` and `keepNestedArchives`: control nested archives.
- `flattenPaths`: makes `path` and `fileName` unique flat names across the run, so all files can go into one folder. The original folder path stays in `originalPath`.
- `skipDuplicates`: skips files with the same SHA-256 content.
- `skipJunkFiles`: skips `__MACOSX`, `.DS_Store` and similar.
- PDF image minimum size, and toggles for PDF attachments, PDF images and Office embedded files.
- Zip-bomb guards: `maxCompressionRatio`, `maxEntriesPerSource`, `maxFileSizeMb`, `timeoutSecondsPerSource`.
- `keyValueStoreName`: saves files to a named store that you keep.

### What data can you extract (output)

There is one row per extracted file (`rowType: "file"`):

```json
{
  "rowType": "file",
  "sourceUrl": "https://raw.githubusercontent.com/akretion/factur-x/master/tests/fixtures/pdf/invoice_EN16931.pdf",
  "sourceName": "invoice_EN16931.pdf",
  "path": "factur-x.xml",
  "originalPath": "factur-x.xml",
  "fileName": "factur-x.xml",
  "extension": "xml",
  "sizeBytes": 41914,
  "mimeType": "text/xml",
  "sha256": "9f0c...e1",
  "downloadUrl": "https://api.apify.com/v2/key-value-stores/AbC123/records/s001-00002-factur-x.xml",
  "kvKey": "s001-00002-factur-x.xml",
  "container": "pdf-attachment",
  "depth": 0,
  "pathSanitized": false,
  "modifiedAt": null, "page": null, "width": null, "height": null, "contentId": null, "inline": null
}
```

- `container` is one of `zip`, `7z`, `tar`, `tar+gzip`, `gzip`, `email`, `msg`, `pdf-attachment`, `pdf-image` or `office-media`.
- `page`, `width` and `height` are filled in for PDF images.
- `contentId` and `inline` are filled in for email parts.
- `modifiedAt` is filled in for archive entries.

There is one row per email (`rowType: "email"`). It is free and has these fields: `from`, `to`, `cc`, `bcc`, `replyTo`, `date` (ISO), `subject`, `messageId`, `inReplyTo`, `bodyText`, `bodyHtml`, `attachmentCount` and `attachments`.

There is one summary row per source (`rowType: "summary"`). It has these fields: `status` (`ok` / `partial` / `failed`), `sourceType`, `filesExtracted`, `bytesExtracted`, `emailsParsed`, `skippedCount`, `skippedByReason` (for example `{"excluded-by-filter": 12, "symlink": 1}`), `skipped` (the first 100 skipped entries with path and reason), `error` and `notes`. Sources that failed are also listed in the `FAILED` key-value record.

### Limits (honest)

- **Download URLs expire.** By default files go to the run's default key-value store, which Apify deletes after your plan's data retention period. To keep them, set `keyValueStoreName`.
- Each source must be a direct file URL (http/https) of at most `maxDownloadSizeMb` (default 200 MB, max 1000 MB). Pages that need a login are not supported.
- **Not supported:** RAR, ISO, CAB, password-protected archives, PDFs or entries, winmail.dat (TNEF) contents (delivered as the `.dat` file), and inline images drawn directly in PDF content streams (rare; image XObjects are extracted). Office documents and PDFs found *inside* archives are delivered as files, not opened.
- PDF images use their native encoding where possible (JPEG stays JPEG). Unusual encodings are converted to PNG. Images that cannot be decoded (some JBIG2/CCITT) are listed as skipped.
- A solid 7z archive that contains a bomb is refused as a whole, because extracting any one file would mean decompressing the bomb.
- The per-source timeout is checked between reads. A single very large PDF image decode cannot be interrupted midway.

### How much does it cost

Pay per event: **one `extracted-file` event per file delivered**. Email rows, summary rows and skipped files are free. `maxFiles` caps what a run can cost.

### FAQ

#### How do I extract a ZIP file from a URL?

Put the direct link to the .zip file in `urls` and run. Every file inside is saved to the run's key-value store and comes back as a row with a download link, its path, size, MIME type and SHA-256. Use `includeExtensions` to keep only the types you need.

#### Is it safe to extract ZIP files from unknown sources?

The Actor never writes files to disk and never runs them. It limits the compression ratio, the bytes decompressed, the entry count and the time per source, cleans `../` and absolute paths, and skips symlinks. That protects against zip bombs and path traversal.

#### Can it extract attachments from .eml and .msg emails?

Yes. You get the attachments and inline images as files, and one extra free row per email with the sender, recipients, date, subject and body text.

#### Can it extract images or files from a PDF?

Yes. It extracts embedded file attachments (including Factur-X and ZUGFeRD invoice XML) and embedded images. Embedded media in Word, Excel and PowerPoint files can be extracted too.

#### Does it open RAR files or password-protected ZIPs?

No. RAR, ISO, CAB and password-protected archives are not supported, and they are reported as skipped with a reason.

#### How long do the download links last?

Files stay in the run's default key-value store until your Apify data retention deletes them. Set `keyValueStoreName` to keep them in a named store.

### Related tools

- **PDF to Text & Markdown Converter with Automatic OCR** reads the text of the PDFs and Office files you extract.
- **File Converter API: PDF, Office, Images, Audio & Video** converts the extracted files to the format you need.
- **Send Data to Slack, Discord, Email, Notion & Postgres** sends the list of extracted files to your team.

# Actor input Schema

## `urls` (type: `array`):

Links to archives (zip, 7z, tar, tar.gz/tgz, tar.bz2, tar.xz, gz), emails (.eml, .msg), PDFs, or Office files (docx, xlsx, pptx, odt). Everything inside is extracted.

## `includeExtensions` (type: `array`):

Deliver only files with these extensions, e.g. pdf, jpg, png, csv. Empty = everything. Nested archives are still opened.

## `excludeExtensions` (type: `array`):

Never deliver files with these extensions, e.g. exe, dll, js.

## `maxFiles` (type: `integer`):

Stop after this many extracted files. Also caps the cost (you pay per extracted file).

## `maxTotalSizeMb` (type: `integer`):

Files that would push the run past this many extracted megabytes are skipped.

## `nestedDepth` (type: `integer`):

How many levels of archives/emails inside the source to open (a zip inside a zip, an email attached to an email). 0 = deliver inner archives as files.

## `keepNestedArchives` (type: `boolean`):

When an inner archive/email is opened, also deliver the archive file itself.

## `flattenPaths` (type: `boolean`):

Make every fileName unique across the run (report.pdf, report (2).pdf), so all files can be saved into one folder. 'path' becomes the flat name too; the original folder path stays in 'originalPath'.

## `skipDuplicates` (type: `boolean`):

Skip files whose content (SHA-256) was already delivered in this run, e.g. the same logo in every email.

## `skipJunkFiles` (type: `boolean`):

Skip \_*MACOSX folders, .* resource forks, .DS_Store, Thumbs.db and desktop.ini.

## `emailInlineImages` (type: `boolean`):

Deliver inline images (signature logos, embedded pictures). Turn off to get only real attachments.

## `emailMetadataRows` (type: `boolean`):

Add one row per email with from, to, cc, date, subject and the text/HTML body (free, not charged).

## `pdfAttachments` (type: `boolean`):

Extract files attached to PDFs (e.g. ZUGFeRD/Factur-X invoice XML, portfolios).

## `pdfImages` (type: `boolean`):

Extract images embedded in PDF pages (saved as PNG/JPG; identical images are extracted once).

## `pdfMinImageSize` (type: `integer`):

Skip PDF images narrower or shorter than this (removes tiny icons, lines and masks).

## `officeEmbeddedFiles` (type: `boolean`):

Besides media (images, video), also deliver embedded objects from docx/xlsx/pptx (e.g. an Excel sheet inside a Word file).

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

Larger inner files are skipped (listed in the summary).

## `maxDownloadSizeMb` (type: `integer`):

Source files larger than this are not downloaded.

## `maxCompressionRatio` (type: `integer`):

Entries or archives that expand more than this many times their compressed size are refused (only checked above 1 MB uncompressed). Normal files compress 2-20x; zip bombs 1000x+.

## `maxEntriesPerSource` (type: `integer`):

Stop reading a source after this many entries (all nesting levels).

## `timeoutSecondsPerSource` (type: `integer`):

Stop extracting a source after this long; files found so far are kept.

## `keyValueStoreName` (type: `string`):

Optional. Save extracted files into this named store (kept until you delete it) instead of the run's default store (deleted after your plan's data retention period).

## `proxyConfiguration` (type: `object`):

Only needed if the file host blocks datacenter IPs.

## Actor input object example

```json
{
  "urls": [
    "https://raw.githubusercontent.com/akretion/factur-x/master/tests/fixtures/pdf/invoice_EN16931.pdf",
    "https://github.com/octocat/Hello-World/archive/refs/heads/master.zip"
  ],
  "maxFiles": 1000,
  "maxTotalSizeMb": 1024,
  "nestedDepth": 1,
  "keepNestedArchives": false,
  "flattenPaths": false,
  "skipDuplicates": false,
  "skipJunkFiles": true,
  "emailInlineImages": true,
  "emailMetadataRows": true,
  "pdfAttachments": true,
  "pdfImages": true,
  "pdfMinImageSize": 32,
  "officeEmbeddedFiles": true,
  "maxFileSizeMb": 200,
  "maxDownloadSizeMb": 200,
  "maxCompressionRatio": 200,
  "maxEntriesPerSource": 10000,
  "timeoutSecondsPerSource": 300,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

All result items of this run (the default dataset), as JSON, CSV, Excel and more.

## `records` (type: `string`):

Key-value store records: the run summary (OUTPUT), problems (NOTES/FAILED) and any files.

# 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 = {
    "urls": [
        "https://raw.githubusercontent.com/akretion/factur-x/master/tests/fixtures/pdf/invoice_EN16931.pdf",
        "https://github.com/octocat/Hello-World/archive/refs/heads/master.zip"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("data-done-right/extract-zip-files").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 = { "urls": [
        "https://raw.githubusercontent.com/akretion/factur-x/master/tests/fixtures/pdf/invoice_EN16931.pdf",
        "https://github.com/octocat/Hello-World/archive/refs/heads/master.zip",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("data-done-right/extract-zip-files").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 '{
  "urls": [
    "https://raw.githubusercontent.com/akretion/factur-x/master/tests/fixtures/pdf/invoice_EN16931.pdf",
    "https://github.com/octocat/Hello-World/archive/refs/heads/master.zip"
  ]
}' |
apify call data-done-right/extract-zip-files --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,data-done-right/extract-zip-files"
        }
    }
}
```

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/6So8LMAWuhy11hGeG/builds/HxLoSgINDZPegj9w9/openapi.json
