Archive tools
Three in-process archive operations on fflate (MIT, pure-JS) — the same open-source library you could run yourself. Every op is a POST https://api.relaystation.ai/v1/archive/<op>. Everything happens in memory: cputools never writes an entry to a filesystem, so there is no path-traversal (“zip-slip”) surface — and the extraction is bomb-guarded (below).
Inputs and outputs
Same uniform shape as the PDF tools: a file input source is { "inline": "<base64>" } (≤ 4 MB) or { "inputKey": "..." } (from POST /v1/cputools/upload-url, ≤ 50 MB). zip takes a files array. unzip returns a manifest — { entries: [{ name, output }] } — where each name is a sanitized basename and each output is the uniform envelope (inline ≤ 4 MB, else a presigned outputUrl). zip/gzip return a single output envelope.
Billing
Per MB of input (compressed) bytes, minimum 1 MiB, at cputools.price.archive.<op>.per_mb_micros (launch default $0.0002 / MB). Billing is on the input you send — so unzip bills the compressed archive, not its (larger) expansion. The live 402 challenge is authoritative.
| Op | Billed on | Rate |
|---|---|---|
zip | total input MB | $0.0002 / MB |
unzip | input MB | $0.0002 / MB |
gzip | input MB | $0.0002 / MB |
zip
Zip one or more files into a single archive. files is an array of input sources (≥ 1, ≤ cputools.archive.max_files_per_zip, default 1000); optional filenames must match files.length. Filenames are reduced to a sanitized basename (path separators, .., drive prefixes, and null bytes stripped); collisions get a -N suffix.
POST /v1/archive/zip
{ "files": [{"inline":"<b64>"}, {"inline":"<b64>"}], "filenames": ["a.txt", "b.txt"] }
unzip
Extract a zip to a manifest of entries. Zip-bomb-guarded in two layers (see below). A nested .zip entry is returned as raw bytes — never recursively extracted (one level only).
POST /v1/archive/unzip
{ "file": {"inline":"<b64 zip>"} }
gzip
Compress or decompress a single file. mode is compress (→ application/gzip) or decompress (→ application/octet-stream). Decompress is guarded by the same uncompressed-size cap (gzip bombs too).
POST /v1/archive/gzip
{ "file": {"inline":"<b64>"}, "mode": "compress" }
The zip-bomb defense (documented limits)
A decompression bomb — a tiny archive that expands to gigabytes — is stopped by layered, operator-tunable caps, and the rejection is a feature:
- Pre-charge directory check. Before any charge, cputools reads the archive’s central directory and rejects — with a
422, no charge — an archive whose declared uncompressed total exceedscputools.archive.max_uncompressed_bytes(default 128 MiB), whose entry count exceedscputools.archive.max_entries(default 10000), or whose per-entry declared compression ratio exceedscputools.archive.max_compression_ratio(default 1000:1). - Authoritative running-total during extraction. Declared sizes can lie, so extraction streams and sums the actual decompressed bytes, aborting the instant it crosses the cap — memory never balloons (and the charge is refunded).
- Encrypted/password-protected zips are rejected (
422 UNSUPPORTED_ENCRYPTED_ZIP) — cputools doesn’t decrypt.
Sample
curl -X POST https://api.relaystation.ai/v1/archive/unzip \
-H 'X-Payment: <base64 EIP-3009 auth>' \
-H 'Idempotency-Key: unzip-bundle-20260609' \
-H 'Content-Type: application/json' \
-d '{"file":{"inline":"<b64 zip>"}}'
Errors
402 PAYMENT_REQUIRED— no valid payment.422 ARCHIVE_TOO_LARGE— declared (pre-charge) or actual (in-flight) uncompressed output over the cap.422 ARCHIVE_TOO_MANY_ENTRIES/422 ARCHIVE_RATIO_EXCEEDED— over the entry-count or compression-ratio cap (pre-charge).422 UNSUPPORTED_ENCRYPTED_ZIP— an encrypted/password-protected entry.422 ARCHIVE_INVALID— not a valid zip/gzip (or a ZIP64 archive, which is unsupported).422 ARCHIVE_TOO_MANY_FILES/422 FILENAME_COUNT_MISMATCH— zip input over the file cap, or afilenameslength that doesn’t matchfiles.
Next
PDF tools · CSV tools · Image tools · Utils tools · Generate tools · Pricing · API reference