# ipull

> The only file downloader you'll ever need. For node.js and the browser, CLI and library for fast and reliable file downloads.

Latest version **4.0.3** (published 2025-05-28) · MIT license · 0 weekly downloads

## Install

```sh
npm install ipull
pnpm add ipull
yarn add ipull
bun add ipull
```

Provides the command `ipull`.

## Health

**Score 50/100 (C)** — status: stable.

Positive: has types; esm support; no vulnerabilities; high quality score.

Warnings: low downloads.

Negative: stale.

## Facts

| | |
|---|---|
| Version | 4.0.3 |
| Published | 2025-05-28 |
| First published | 2023-08-26 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Node | >=18.0.0 |
| Dependencies | 20 |
| Unpacked size | 381.4 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| GitHub stars | 57 |
| Author | Ido S. |
| Maintainers | ido.pluto |
| Keywords | fast-download, partial-download, parallel-connections, browser-download, node-download, copy-file, stream-download, io, retry-download, resume-download, file-downloader, transfer-speed, transfer-statistics, cli-progress, cli, file-copy, downloader, download, file, pull, xhr, fetch, idm, download-engine |

## Links

- npm: https://www.npmjs.com/package/ipull
- Repository: https://github.com/ido-pluto/ipull
- Homepage: https://github.com/ido-pluto/ipull#readme
- Issues: https://github.com/ido-pluto/ipull/issues
- Funding: https://github.com/ido-pluto/ipull?sponsor=1
- npm.io page: https://npm.io/package/ipull

## Dependencies (20)

- [uid](https://npm.io/package/uid.md) ^2.0.2
- [chalk](https://npm.io/package/chalk.md) ^5.3.0
- [lowdb](https://npm.io/package/lowdb.md) ^7.0.1
- [ci-info](https://npm.io/package/ci-info.md) ^4.0.0
- [fs-extra](https://npm.io/package/fs-extra.md) ^11.1.1
- [commander](https://npm.io/package/commander.md) ^10.0.0
- [pretty-ms](https://npm.io/package/pretty-ms.md) ^8.0.0
- [filenamify](https://npm.io/package/filenamify.md) ^6.0.0
- [slice-ansi](https://npm.io/package/slice-ansi.md) ^7.1.0
- [strip-ansi](https://npm.io/package/strip-ansi.md) ^7.1.0
- [async-retry](https://npm.io/package/async-retry.md) ^1.3.3
- [cli-spinners](https://npm.io/package/cli-spinners.md) ^2.9.2
- [pretty-bytes](https://npm.io/package/pretty-bytes.md) ^6.1.0
- [eventemitter3](https://npm.io/package/eventemitter3.md) ^5.0.1
- [sleep-promise](https://npm.io/package/sleep-promise.md) ^9.1.0
- [stdout-update](https://npm.io/package/stdout-update.md) ^4.0.1
- [lifecycle-utils](https://npm.io/package/lifecycle-utils.md) ^1.3.1
- [lodash.debounce](https://npm.io/package/lodash.debounce.md) ^4.0.8
- [is-unicode-supported](https://npm.io/package/is-unicode-supported.md) ^2.0.0
- [@tinyhttp/content-disposition](https://npm.io/package/@tinyhttp/content-disposition.md) ^2.2.0

## Alternatives

- [launchdarkly-js-client-sdk](https://npm.io/package/launchdarkly-js-client-sdk.md) — 2.5M weekly downloads
- [@elastic/elasticsearch](https://npm.io/package/@elastic/elasticsearch.md) — 2.1M weekly downloads
- [@c8y/client](https://npm.io/package/@c8y/client.md) — 15.3K weekly downloads
- [@signaldb/maverickjs](https://npm.io/package/@signaldb/maverickjs.md) — 1.7K weekly downloads
- [@bbc/http-transport-cache](https://npm.io/package/@bbc/http-transport-cache.md) — 1.2K weekly downloads

## Recent versions

- 4.0.3 (latest) — 2025-05-28
- 5.0.0-beta.1 (beta) — 2026-04-22
- 3.9.5 (maintenance/v3) — 2026-02-20
- 3.9.4 — 2026-02-20
- 3.9.3 — 2025-07-08
- 3.10.0-beta.6 — 2025-07-08
- 4.0.2 — 2025-05-25
- 4.0.1 — 2025-05-25
- 4.0.0 — 2025-05-22
- 3.10.0-beta.5 — 2025-03-16
- 3.10.0-beta.4 — 2025-03-12
- 3.10.0-beta.3 — 2025-03-05
- 3.10.0-beta.2 — 2025-03-02
- 3.10.0-beta.1 — 2025-02-26
- 3.9.2 — 2024-11-10
- … 49 more at https://npm.io/package/ipull/versions

## README

<div align="center">
    <h1>iPull</h1>
    <img src="./assets/ipull-logo-rounded.png" height="200px" />
</div>

<div align="center">

[![Build](https://github.com/ido-pluto/ipull/actions/workflows/build.yml/badge.svg)](https://github.com/ido-pluto/ipull/actions/workflows/build.yml)
[![License](https://badgen.net/badge/color/MIT/green?label=license)](https://www.npmjs.com/package/ipull)
[![Types](https://badgen.net/badge/color/TypeScript/blue?label=types)](https://www.npmjs.com/package/ipull)
[![npm downloads](https://img.shields.io/npm/dt/ipull.svg)](https://www.npmjs.com/package/ipull)
[![Version](https://badgen.net/npm/v/ipull)](https://www.npmjs.com/package/ipull)

</div>
<br />

> Super fast file downloader with multiple connections

```bash
npx ipull http://example.com/file.large
```

![pull-example](./assets/pull-file.gif)

## Features

- Download using parallels connections
- Maximize download speed (automatic parallelization, 3+)
- Pausing and resuming downloads
- Node.js and browser support
- Smart retry on fail
- CLI Progress bar
- Download statistics (speed, time left, etc.)

### NodeJS API

```ts
import {downloadFile} from 'ipull';

const downloader = await downloadFile({
    url: 'https://example.com/file.large',
    directory: './this/path', // or 'savePath' for full path
    cliProgress: true, // Show progress bar in the CLI (default: false)
    parallelStreams: 3 // Number of parallel connections (default: 3)
});

await downloader.download();
```

## Browser support

Download a file in the browser using multiple connections

```ts
import {downloadFileBrowser} from "ipull/dist/browser.js";

const downloader = await downloadFileBrowser({
    url: 'https://example.com/file.large',
    acceptRangeIsKnown: true, // overcome CORS, force multi-connection download (use only if you know the server supports range requests)
    // defaultFetchDownloadInfo: { // set download info manually to overcome CORS issues && prevent multiple requests
    //     acceptRange: true,
    //     length: 40789822,
    // }
});

await downloader.download();
image.src = downloader.writeStream.resultAsBlobURL();

console.log(downloader.writeStream.result); // Uint8Array
```

### Custom stream

You can use a custom stream

```ts
import {downloadFileBrowser} from "ipull/dist/browser.js";

const downloader = await downloadFileBrowser({
    url: 'https://example.com/file.large',
    onWrite: (cursor: number, buffers: Uint8Array[], options) => {
        const totalLength = buffers.reduce((acc, buffer) => acc + buffer.length, 0);
        console.log(`Writing ${totalLength} bytes at cursor ${cursor}, with options: ${JSON.stringify(options)}`);
    }
});

await downloader.download();
console.log(downloader.writeStream.result.length === 0); // true, because we write to a custom stream
```

## CLI

```
Usage: ipull [options] [files...]

Pull/copy files from a remote server/local directory

Arguments:
  files                         Files to pull/copy

Options:
  -s --save [path]              Save location (directory/file)
  -c --connections [number]     Number of parallel connections (default: "4")
  -p --program [type]           The download strategy (choices: "stream", "chunks")
  -t --truncate-name            Truncate file names in the CLI status to make them appear shorter
  -V, --version                 output the version number
  -h, --help                    display help for command

Commands:
  set [options] [path] <value>  Set download locations

```

### Set custom save directory

You can set a custom save directory by using the `set` command.

```bash
ipull set .zip ~/Downloads/zips
```

(use `default` to set the default save directory)

## Advanced usage

### Skip existing files

Skip downloading files that already exist in the save location and have the same size.

```ts
import {downloadFile} from 'ipull';

const downloader = await downloadFile({
    url: 'https://example.com/file.large',
    directory: './this/path',
    skipExisting: true
});
```

### Download file from parts

Consolidate multiple files parts into one file.
Beneficial for downloading large files from servers that limit file size. (e.g. HuggingFace models)

```ts
import {downloadFile} from 'ipull';

const downloadParts = [
    "https://example.com/file.large-part-1",
    "https://example.com/file.large-part-2",
    "https://example.com/file.large-part-3",
];

const downloader = await downloadFile({
    partURLs: downloadParts,
    directory: './this/path',
    filename: 'file.large'
});

await downloader.download();
```

** The split must be binary and not a zip-split

### Custom headers

You can set custom headers for the download request

```ts
import {downloadFile} from 'ipull';

const downloader = await downloadFile({
    url: 'https://example.com/file.large',
    savePath: './this/path/file.large',
    headers: {
        'Authorization': 'Bearer token 1'
    },
    // You can also add alternative headers in case of an 400-499 error
    tryHeaders: [
        {
            Authorization: 'Bearer token 2'
        }
    ]
});

await downloader.download();
```

### Abort download

You can cancel the download by calling the `close` method (it will not delete the file).

If you want to also delete the file, you can call the `closeAndDeleteFile` method.

```ts
import {downloadFile} from 'ipull';

const downloader = await downloadFile({
    url: 'https://example.com/file.large',
    directory: './this/path'
});

setTimeout(() => {
    downloader.close();
}, 5_000);

await downloader.download();
```

### Pause & Resume download

```ts
import {downloadFile} from 'ipull';

const downloader = await downloadFile({
    url: 'https://example.com/file.large',
    directory: './this/path'
});

setInterval(() => {
    downloader.pause();
    setTimeout(() => {
        downloader.resume();
    }, 5_000);
}, 10_000);

await downloader.download();
```

** The pause may take a few seconds to actually pause the download, because it waits for the current connections to
finish

### Error handling

If a network/file-system error occurs, the download will automatically retry
with [async-retry](https://www.npmjs.com/package/async-retry)

If the maximum reties was reached the download will fail and an error will be thrown from the `download()` call:

```ts
import {downloadFile} from 'ipull';

try {
    const downloader = await downloadFile({
        url: 'https://example.com/file.large',
        directory: './this/path'
    });

    await downloader.download();
} catch (error) {
    console.error(`Download failed: ${error.message}`);
}
```

### Download Stuck

In some edge cases, the re-try mechanism may give the illusion that the download is stuck.

(You can see this in the progress object that "retrying" is true)

To debug this, disable the re-try mechanism:

```js
const downloader = await downloadFile({
    url: 'https://example.com/file.large',
    directory: './this/path',
    retry: {
        retries: 0
    },
    retryFetchDownloadInfo: {
        retries: 0
    }
});
```
### Listening to events

Events are emitted using the `EventEmitter` pattern and can be listened to using the `on` method

```ts
interface DownloadEngineEvents {
    start: [];
    paused: [];
    resumed: [];
    progress: [FormattedStatus];
    save: [DownloadProgressInfo];
    finished: [];
    closed: [];
}

const downloader = await downloadFile({
    url: 'https://example.com/file.large',
    directory: './this/path'
});

downloader.on("progress", (progress) => {
    console.log(`Downloaded ${progress.transferred} bytes`);
});
```

### Remote Download Listing

If you want to show in the CLI the progress of a file downloading in remote.

```ts
const originaldownloader = await downloadFile({
    url: 'https://example.com/file.large',
    directory: './this/path'
});

const remoteDownloader = downloadFileRemote({
    cliProgress: true
});

originaldownloader.on("progress", (progress) => {
    remoteDownloader.emitRemoteProgress(progress);
});

await originaldownloader.download();
```

### Download multiple files

If you want to download multiple files, you can use the `downloadSequence` function.

By default, it will download files one by one, but you can set the `parallel` option to download them in parallel.
It is better to download one file at a time if you are downloading from the same server (as it may limit the number of
connections).

```ts
import {downloadFile, downloadSequence} from "ipull";

const downloader = await downloadSequence(
    {
        cliProgress: true,
        // parallelDownloads: 2, download 2 files in parallel, default is 1
    },
    downloadFile({
        url: "https://example.com/file1.large",
        directory: "."
    }),
    downloadFile({
        url: "https://example.com/file2.large",
        directory: "."
    }),
);

console.log(`Downloading ${downloader.downloads.length} files...`);
await downloader.download();
```

### Custom progress bar

```ts
import {downloadFile, FormattedStatus} from "ipull";

function createProgressBar({fileName, ...data}: FormattedStatus) {
    return `${fileName} ${JSON.stringify(data)}`;
}

const downloader = await downloadFile({
    url: "https://example.com/file.large",
    directory: "./this/path",
    cliStyle: createProgressBar
});

await downloader.download();
```

<br />

<div align="center" width="360">
    <img alt="Star please" src="./assets/star-please.png" width="360" margin="auto" />
    <br/>
    <p align="right">
        <i>If you like this repo, star it ✨</i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    </p>
</div>

---
_Source: https://npm.io/package/ipull · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
