# Backup npm / PyPI / Docker Hub Packages (`onescales/backup-npm-pypi-docker-packages`) Actor

Back up every published version of your npm, PyPI and Docker Hub packages, with metadata and READMEs. Paste package URLs and get a checksum-verified ZIP per version, plus one ZIP of everything.

- **URL**: https://apify.com/onescales/backup-npm-pypi-docker-packages.md
- **Developed by:** [One Scales](https://apify.com/onescales) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $10.00 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## Backup npm / PyPI / Docker Hub Packages

***

### Introduction

**Back up your npm, PyPI and Docker Hub packages in one run: the latest version, the newest few, every version ever published, or exactly the versions you name.** Paste your package pages, and get a ZIP of each version with its files, metadata and README. A single ZIP of the whole batch comes at the end. Download links are private and expire after 24 hours by default, or any time you choose up to about a month.

Each version is downloaded exactly as the registry published it: the npm tarball, every PyPI wheel and source distribution, or the full Docker image for every platform. Each file is checked against the checksum the registry publishes, so you know the backup is byte-for-byte what your users install.

**Back up only packages you own or are authorized to copy.** Being public on a registry does not make a package free to copy. Each package's license and the registry's terms still apply, and you are responsible for how you use what you download.

***

### Features

- **Latest, newest N, every version, or exactly the one you name** — npm, PyPI and Docker Hub in one run. By default you get the latest version of each package; set a number for the newest N, 0 for the full history, or put a version or tag in the URL to back up exactly that one.
- **Checksum-verified** — every file is checked against the registry's own checksum (npm integrity, PyPI SHA-256, Docker digests).
- **Metadata and READMEs included** — each version's ZIP carries its manifest or metadata and its README alongside the files.
- **Restorable Docker images** — each tag is saved as a standard OCI image layout covering every platform, ready for `skopeo` or `docker load`.
- **Expiring download links** — links last 24 hours by default; set anywhere from 1 hour to 730 hours (about one month). The final row downloads everything in one ZIP.

***

### Use Cases

- **Off-site copy of your releases** — keep every version of your packages outside the registry, in case of account lockout, takedown or accidental unpublish.
- **Release archive** — a complete, dated history of everything you have shipped, with publish times.
- **Recover an unpublished version** — keep the exact artifact users installed, even if a version is later removed.
- **Compliance and audit** — prove what you released and when, with checksums that match the registry.
- **Registry migration** — pull down every version before moving packages to a private registry or another account.
- **Organization-wide snapshots** — back up all of your team's packages across npm, PyPI and Docker Hub in one run.
- **Image retention** — keep old Docker tags you have overwritten or that a retention policy might delete.
- **Client handover** — give a client every version of the packages you published for them.

***

### Input

Configure the actor in the UI, or pass the same fields via the API.

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| **Package URLs** (`packageUrls`) | Array of strings | Yes | Package pages, one per line: npm (`https://www.npmjs.com/package/name`), PyPI (`https://pypi.org/project/name/`) or Docker Hub (`https://hub.docker.com/r/owner/image`, or `https://hub.docker.com/_/image` for official images). Shorthand also works: `npm:name`, `pypi:name`, `docker:owner/image`. Add a version or tag to back up exactly that version — see the table below. |
| **Backup Latest Versions** (`latestVersions`) | Integer | No — defaults to `1` | How many of each package's newest versions to back up, by publish date. `1` backs up only the latest version, `0` backs up every published version, and any other number N backs up the newest N. Ignored for lines that name a specific version or tag. |
| **Download Link Expiry (hours)** (`linkExpiryHours`) | Integer | No — defaults to `24` | How long the download links keep working. Minimum `1` hour, maximum `730` hours (about one month). |
| **Proxy Configuration** (`proxyConfiguration`) | Object | No — defaults to residential | Proxy used to download from the registries, which reduces rate-limiting and blocking. The backups themselves are unaffected. |

```json
{
  "packageUrls": [
    "https://www.npmjs.com/package/is-odd",
    "https://pypi.org/project/sampleproject/",
    "https://hub.docker.com/r/tianon/true"
  ],
  "latestVersions": 1,
  "linkExpiryHours": 24,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": ["RESIDENTIAL"]
  }
}
```

#### Backing up a specific version

Put a version or tag in the line to back up exactly that version. **A line that names a version ignores Backup Latest Versions**: it always backs up that one version. Lines without a version still follow the setting, so you can mix both in one run.

| Registry | Whole package | One specific version |
|----------|---------------|----------------------|
| npm | `https://www.npmjs.com/package/is-odd` or `npm:is-odd` | `https://www.npmjs.com/package/is-odd/v/2.0.0`, `npm:is-odd@2.0.0`, or an npm tag such as `npm:is-odd@latest` / `npm:name@beta` (scoped: `npm:@scope/name@1.0.0`) |
| PyPI | `https://pypi.org/project/sampleproject/` or `pypi:sampleproject` | `https://pypi.org/project/sampleproject/1.3.1/` or `pypi:sampleproject==1.3.1` |
| Docker Hub | `https://hub.docker.com/r/tianon/true` or `docker:tianon/true` (official images: `https://hub.docker.com/_/nginx` or `docker:nginx`) | `docker:tianon/true:oci`, a digest `docker:tianon/true@sha256:…`, or a tag page link `https://hub.docker.com/layers/tianon/true/oci/…` |

- **A version that does not exist** gets one error row, for example `error: version 9.9.9 not found on npm`, with the version you asked for in the `version` column.
- **A version requested twice** (for example pinned on one line and also picked as the latest on another) is backed up once, so you only pay for it once.
- **npm tags** (`latest`, `beta`, `next`, …) back up the version the tag points to right now, and the row shows that version number.

For example, this backs up the latest `is-odd`, plus `is-odd` 2.0.0, plus exactly the `oci` tag of `tianon/true`:

```json
{
  "packageUrls": [
    "https://www.npmjs.com/package/is-odd",
    "https://www.npmjs.com/package/is-odd/v/2.0.0",
    "docker:tianon/true:oci"
  ],
  "latestVersions": 1
}
```

***

### Output

One row per version backed up (the latest one per package by default, plus any versions you named), grouped by package in the order you entered them and oldest version first, each with a download link. A package that cannot be found gets a single error row. **The final row is the bulk download**: one link to a ZIP of every version that was backed up successfully. Its `fileCount` is the number of versions it holds, and `sizeBytes` is their combined size.

| Field | Type | Description |
|-------|------|-------------|
| `packageUrl` | string | The package page that was requested, normalized |
| `registry` | string | `npm`, `pypi` or `docker` |
| `packageName` | string | Package name (npm and PyPI) or image name (Docker Hub) |
| `version` | string | The published version (npm, PyPI) or tag (Docker Hub) |
| `publishedAt` | string | When this version was published (ISO 8601, UTC) |
| `fileName` | string | Name of the backup ZIP for this version |
| `fileCount` | integer | Published files archived: the npm tarball, PyPI wheels and sdists, or Docker image blobs (versions on the final row) |
| `sizeBytes` | integer | Size of the backup ZIP in bytes |
| `checksumVerified` | boolean | `true` when every archived file matched the registry's published checksum |
| `downloadUrl` | string | Download link for the backup, valid for `linkExpiryHours` (24 hours by default) |
| `backedUpAt` | string | When the backup was taken (ISO 8601, UTC) |
| `status` | string | `success`, or `error: <reason>` |

```json
[
  {
    "packageUrl": "https://www.npmjs.com/package/is-odd",
    "registry": "npm",
    "packageName": "is-odd",
    "version": "3.0.1",
    "publishedAt": "2018-05-31T20:04:53.306Z",
    "fileName": "npm-is-odd-3.0.1.zip",
    "fileCount": 1,
    "sizeBytes": 10350,
    "checksumVerified": true,
    "downloadUrl": "https://api.apify.com/v2/key-value-stores/.../records?signature=...&prefix=pkg-00000-npm-is-odd-3.0.1.zip",
    "backedUpAt": "2026-10-01T04:44:09.203Z",
    "status": "success"
  },
  {
    "packageUrl": "ALL PACKAGES (ZIP)",
    "registry": "N/A",
    "packageName": "N/A",
    "version": "N/A",
    "publishedAt": "N/A",
    "fileName": "all-packages.zip",
    "fileCount": 3,
    "sizeBytes": 36161,
    "checksumVerified": false,
    "downloadUrl": "https://api.apify.com/v2/key-value-stores/.../records?signature=...&prefix=pkg-",
    "backedUpAt": "2026-10-01T04:44:16.317Z",
    "status": "success"
  }
]
```

**Good to know**

- **Your own packages only.** Use it for packages you own or have permission to copy, and respect each package's license.
- **What is in each version's ZIP:**
  - **npm:** the published `.tgz` tarball, `metadata.json` (that version's manifest) and `README.md`. The tarball also holds the README exactly as published with that version.
  - **PyPI:** every wheel and source distribution for the version, `metadata.json` and that version's own README.
  - **Docker Hub:** an `image/` folder in OCI image layout covering every platform of the tag, plus `metadata.json` and the repository README. Restore with `skopeo copy oci:image docker-daemon:name:tag`, or tar the folder (`tar -cf image.tar -C image .`) and run `docker load -i image.tar`.
- **Each link downloads a ZIP that contains the version's ZIP.** That is how Apify serves expiring links. Unzip once to get the backup, and the bulk link gives you every backup in one ZIP.
- **Download links expire.** Each link works for `linkExpiryHours` hours from when it was created (24 by default, 1 to 730 allowed), then returns an access error. After that, open the run's storage in the Apify Console, for as long as your plan keeps run data. A link set to 730 hours also stops early if the run's storage is deleted first.
- **Links are scoped to the run, not the file.** Anyone who has a link and knows how to edit it can reach the other backups from the same run until it expires. Share links only with people you would share the whole run with.
- **Specific versions:** a line that names a version or tag backs up exactly that version and ignores Backup Latest Versions. See *Backing up a specific version* above.
- **"Latest" means most recently published.** Versions are ranked by publish date on every registry, so on npm a pre-release published after your last stable release counts as the latest. For Docker Hub, versions are tags, ranked by when they were last pushed. Versions with no publish date never count as the latest.
- **Docker tags often share one image.** A release is commonly pushed as `latest`, `1.4` and `1.4.2` at once. With the default of 1 you get one of them, which is the same image either way. With higher numbers, tags that share an image are each backed up in full.
- **Versions with nothing to download** (for example a PyPI release with no files) get an error row so you can see them.
- **Docker Hub tags** are listed up to 10,000 per image. Tags can be moved, so each backup is the tag as it is now. Non-distributable Windows base layers are not hosted on Docker Hub and are skipped. When that happens, `checksumVerified` is `false` and `metadata.json` records it.
- **Size limit:** a single version over 2 GB is skipped with an error status. Files are streamed to disk, never held in memory.
- **Retries:** every download and file write is retried up to 3 times (2s, 8s, 20s), with a fresh proxy IP on each attempt. That includes Docker Hub's anonymous pull limit and any download whose checksum does not match. Permanent failures such as a 404 fail fast.
- **Speed and memory:** versions are backed up one at a time with a 2-second pause between them, to stay gentle on the registries. On the live registries that came to about 3–5 seconds per version: 18 versions across all three registries took under 90 seconds. That is roughly 12–20 versions a minute, so 100 versions take about 5–8 minutes. Large Docker images add their own download time. Memory stays flat at about 200 MB however large the files are, even for a 500 MB Docker image, so 512 MB is plenty.
- **Proxy traffic:** downloads go through a residential proxy by default, and residential traffic is billed per GB on your Apify account. For large Docker images, switch to datacenter proxies or turn the proxy off in the input.
- **Pricing:** pay-per-event, one row per version. You only pay for rows actually produced. Before downloading anything, the actor counts the versions you asked for, reads your spending limit and remaining account balance, backs up exactly as many versions as the budget covers, and never goes over it.

***

### Support

**Need help or want a feature added?**

Contact Support at **<https://docs.google.com/forms/d/e/1FAIpQLSfsKyzZ3nRED7mML47I4LAfNh_mBwkuFMp1FgYYJ4AkDRgaRw/viewform?usp=dialog>** — Fill out this quick form.

We respond quickly and are happy to add new fields or custom integrations.

***

### Tags

npm backup, pypi backup, docker hub backup, package backup, backup npm packages, backup pypi packages, backup docker images, download all versions, latest version download, specific version download, pinned version backup, npm all versions, pypi all versions, docker all tags, npm tarball download, pypi wheel download, sdist download, download docker image, oci image layout, oci archive, docker image backup, container image backup, release archive, package archive, artifact backup, software artifact archive, package version history, release history, npm unpublish recovery, registry migration, private registry migration, package mirror, npm mirror, pypi mirror, docker mirror, checksum verified, sha256 verification, npm integrity, software supply chain, open source maintainer tools, package maintainer backup, compliance archive, audit trail, disaster recovery, devops backup, python packages, javascript packages, node packages, container registry, zip archive, bulk download, expiring download links, apify actor

# Actor input Schema

## `packageUrls` (type: `array`):

Enter one or more packages, one per line. Each line can be a package page or a specific version:<br><br><b>Whole package</b> (how many versions is set by Backup Latest Versions below):<br>• npm: https://www.npmjs.com/package/name or npm:name<br>• PyPI: https://pypi.org/project/name/ or pypi:name<br>• Docker Hub: https://hub.docker.com/r/owner/image (official images: https://hub.docker.com/\_/image) or docker:owner/image<br><br><b>One specific version</b> (backs up exactly that version and ignores Backup Latest Versions for that line):<br>• npm: https://www.npmjs.com/package/name/v/1.2.3, npm:name@1.2.3, or an npm tag such as npm:name@beta<br>• PyPI: https://pypi.org/project/name/1.2.3/, pypi:name==1.2.3<br>• Docker Hub: docker:owner/image:tag, docker:owner/image@sha256:…, or a tag page link (https://hub.docker.com/layers/owner/image/tag/…)<br><br>Public packages only. A version that does not exist gets an error row; a version listed twice is backed up once.

## `latestVersions` (type: `integer`):

How many of each package's newest versions to back up, by publish date. Defaults to 1 (only the latest version). Enter 0 to back up every published version, or any number N for the newest N. For Docker Hub, versions are tags, newest pushed first. Lines in Package URLs that name a specific version or tag ignore this setting and back up exactly that version.

## `linkExpiryHours` (type: `integer`):

How long the download links stay valid, in hours. Defaults to 24 hours. Minimum 1 hour, maximum 730 hours (about one month). After that the links stop working. You can still download the files from the run's storage in the Apify Console for as long as your plan keeps run data.

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

Proxy settings for downloading the packages. Residential proxies reduce the chance of a registry rate-limiting or blocking the run — Docker Hub in particular limits anonymous pulls per IP. Residential proxy traffic is billed per GB, so for large Docker images you can switch to datacenter proxies or turn the proxy off. The backups themselves are unaffected.

## Actor input object example

```json
{
  "packageUrls": [
    "https://www.npmjs.com/package/is-odd",
    "https://pypi.org/project/sampleproject/",
    "https://hub.docker.com/r/tianon/true"
  ],
  "latestVersions": 1,
  "linkExpiryHours": 24,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `packageBackups` (type: `string`):

Every version with its registry, publish date, file count, size, checksum check, and download URL. The last row links to one ZIP of every backup.

# 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 = {
    "packageUrls": [
        "https://www.npmjs.com/package/is-odd",
        "https://pypi.org/project/sampleproject/",
        "https://hub.docker.com/r/tianon/true"
    ],
    "latestVersions": 1,
    "linkExpiryHours": 24,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("onescales/backup-npm-pypi-docker-packages").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 = {
    "packageUrls": [
        "https://www.npmjs.com/package/is-odd",
        "https://pypi.org/project/sampleproject/",
        "https://hub.docker.com/r/tianon/true",
    ],
    "latestVersions": 1,
    "linkExpiryHours": 24,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("onescales/backup-npm-pypi-docker-packages").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 '{
  "packageUrls": [
    "https://www.npmjs.com/package/is-odd",
    "https://pypi.org/project/sampleproject/",
    "https://hub.docker.com/r/tianon/true"
  ],
  "latestVersions": 1,
  "linkExpiryHours": 24,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call onescales/backup-npm-pypi-docker-packages --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,onescales/backup-npm-pypi-docker-packages"
        }
    }
}
```

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/UlFFl0V7NSbNROaX7/builds/7BSWSByqO6QNY5C1z/openapi.json
