Skip to content

Unzip archive

Extracts every file in a .zip and returns one link per entry, keeping the entry's path. The archive can be a public http(s):// URL or an s3:// object.

Slugunzip-files
SyncPOST /v1/utilities/unzip-filesrunUnzipFiles
AsyncPOST /v1/utilities/unzip-files/jobscreateUnzipFilesJob
Max duration900 s (15 min)
Outputone file per archive entry, plus fileCount

Quick example

bash
curl -X POST https://api.servicelabs.dev/v1/utilities/unzip-files/jobs \
  -H "Authorization: Bearer $DU_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: bundle-7" \
  -d '{ "input": { "url": "s3://finnoto-data/uploads/bundle.zip" } }'
# → 202 { "id": "job_01K7Z3Q4N5P6R7S8T9V0W1X2Y3", "status": "queued", … }

curl "https://api.servicelabs.dev/v1/jobs/job_01K7Z3Q4N5P6R7S8T9V0W1X2Y3?wait=30" \
  -H "Authorization: Bearer $DU_API_KEY"
js
const job = await fetch('https://api.servicelabs.dev/v1/utilities/unzip-files', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.DU_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ input: { url: 'https://example.com/bundle.zip' } }),
}).then((r) => r.json())

for (const f of job.result?.files ?? []) console.log(f.path, f.size, f.url)
json
// → 200 OK (abridged)
{
  "id": "job_01K7Z3Q4N5P6R7S8T9V0W1X2Y3",
  "utility": "unzip-files",
  "status": "succeeded",
  "result": {
    "fileCount": 3,
    "files": [
      {
        "url": "https://files.servicelabs.dev/unzip-files/2026-09-10/job_01K7Z3Q4N5P6R7S8T9V0W1X2Y3/readme.txt",
        "key": "unzip-files/2026-09-10/job_01K7Z3Q4N5P6R7S8T9V0W1X2Y3/readme.txt",
        "name": "readme.txt",
        "path": "readme.txt",
        "size": 1204,
        "contentType": "text/plain"
      },
      {
        "url": "https://files.servicelabs.dev/unzip-files/2026-09-10/job_01K7Z3Q4N5P6R7S8T9V0W1X2Y3/docs/invoice.pdf",
        "key": "unzip-files/2026-09-10/job_01K7Z3Q4N5P6R7S8T9V0W1X2Y3/docs/invoice.pdf",
        "name": "invoice.pdf",
        "path": "docs/invoice.pdf",
        "size": 88410,
        "contentType": "application/pdf"
      },
      { "name": "summary.csv", "path": "docs/summary.csv", "…": "…" }
    ]
  }
}

Input

FieldTypeRequiredDescription
urlstringyesThe .zip to extract. http://, https:// or s3://, ≤ 4096 chars.

Result

FieldDescription
result.fileCountNumber of files extracted (= files.length).
files[].pathThe entry's path inside the archive, e.g. docs/invoice.pdf.
files[].nameThe last path segment, e.g. invoice.pdf.
files[].keyWhere it lives: unzip-files/<date>/<job id>/<path>. The archive's folder structure is preserved under the job's folder.
files[].contentTypeSet from the entry's file extension (PDF, images, Office documents, CSV, JSON, text, …); application/octet-stream when the extension is unknown.
files[].sizeThe uncompressed size in bytes.

Limits & behaviour

  • Directory entries are skipped — only files are returned.
  • Paths can't escape. Entry names containing .., leading slashes or backslashes are normalised when stored, so every file stays inside the job's folder even if the archive itself was crafted maliciously.
  • No file-count limit, and entries are extracted one at a time. A zip with thousands of small files is slow — use the async route for anything big (the sync route answers 202 with the job id after 90 s anyway).
  • The whole archive is read into memory, so extremely large archives can exceed the function's memory (502 upstream_error).
  • An empty archive succeeds with fileCount: 0 and files: [].

Errors specific to this utility

SituationOutcome
url missing or not http(s):// / s3://400 invalid_request — no job
Archive can't be downloaded, or isn't a valid zipfailed job, 502 upstream_error (message from the extractor)
Out of memory on a huge archivefailed job, 502 upstream_error
Exceeded 900 sfailed job, 504 upstream_timeout

ServiceLabs · a Finnoto company