Nami
Nami is an open-source Python CLI for downloading media from Instagram, TikTok, Facebook, and X. It wraps gallery-dl and yt-dlp with safer target parsing, per-media archives, retry/fallback logic, workspace configuration, diagnostics, and an optional Rich-powered interactive UI.
Use Nami only for content you are allowed to access and download. Platform availability can change when upstream sites or extractors change.
Highlights
- Interactive no-argument menu for guided setup and downloads.
- Noninteractive CLI for scripts, CI, and automation.
- Downloads explicit URLs or profile lists from a workspace.
- Supports photos, videos, stories, and highlights where upstream extractors support them.
- Uses per-target archives to avoid duplicate downloads without deleting archives implicitly.
- Validates target names and proves output paths stay inside the configured workspace.
- Supports Netscape cookie files and TikTok browser-cookie fallback.
- Classifies auth, cookie, rate-limit, network, dependency, not-found, extractor, timeout, and unknown failures.
- Provides JSON output for
download,doctor,setup,config, and archive reset workflows. - Includes a read-only
doctorcommand for local diagnostics.
Platform support
| Platform | Photos/posts | Videos/reels | Stories | Highlights | Authentication |
|---|---|---|---|---|---|
| Yes | Yes | Yes | Yes | Optional Netscape cookies | |
| TikTok | Limited by upstream extractor | Yes | No | No | Netscape cookies or configured browser cookies |
| Limited by upstream extractor | Yes | No | No | Optional Netscape cookies | |
| X / Twitter | Limited by upstream extractor | Yes | No | No | Optional Netscape cookies |
Unsupported combinations are reported as unsupported operations instead of being silently treated as successful downloads.
Installation
python -m pip install nami
Upgrade an existing installation:
python -m pip install --upgrade nami
Nami requires Python 3.10 or newer. Runtime dependencies are installed automatically: rich, gallery-dl, and yt-dlp.
Quick start
Interactive mode
Run Nami without arguments:
nami
The interactive UI guides you through workspace setup, settings, profile-file downloads, and media selection. Nami no longer creates a workspace as a hidden import-time side effect; setup is explicit through the interactive prompt or the setup command.
Noninteractive setup
Create a workspace under a chosen root directory:
nami setup --root .
This creates:
Nami/
├── downloads/
├── cookies/
└── profiles/
├── facebook_profiles.txt
├── instagram_profiles.txt
├── tiktok_profiles.txt
└── x_profiles.txt
Create optional cookie templates too:
nami setup --root . --cookie-templates
Download explicit URLs
nami download https://www.instagram.com/example/ --media photos,videos
Multiple URLs are supported:
nami download URL1 URL2 URL3 --media all
Force platform inference when a URL is ambiguous:
nami download https://x.com/example --platform x --media videos
Download from profile files
Add one profile URL per line in the appropriate file under Nami/profiles/, then run:
nami download --profiles --media photos,videos
Limit profile loading to one platform:
nami download --profiles --platform instagram --media stories,highlights
JSON output
Use --json for machine-readable output:
nami download --profiles --media all --json
nami doctor --json
nami config show --json
CLI reference
nami setup --root PATH [--cookie-templates] [--json]
nami download [URL ...] [--profiles] [--platform instagram|tiktok|facebook|x] [--media KINDS] [--json]
nami doctor [--json]
nami config show|get|set|unset ... [--json]
nami archive reset (--all | selectors...) [--dry-run] [--yes] [--delete] [--json]
Media kinds are photos, videos, stories, highlights, or all. Comma-separated values are accepted, for example photos,videos.
Configuration
Nami loads configuration in this order:
- Environment variables
~/.nami/nami_config.json- Defaults based on the current user's home directory
The config file is written atomically and avoids world-readable permissions where the platform supports it.
Config commands
nami config show
nami config get base_dir
nami config set browser firefox
nami config unset browser
Supported keys:
| Key | Environment variable | Description |
|---|---|---|
base_dir |
NAMI_BASE_DIR |
Download output directory |
cookies_dir |
NAMI_COOKIES_DIR |
Netscape cookie file directory |
profiles_dir |
NAMI_PROFILES_DIR |
Profile text-file directory |
browser |
NAMI_BROWSER |
Browser for TikTok browser-cookie fallback (brave, chrome, edge, firefox) |
user_agent |
NAMI_USER_AGENT |
User-Agent sent to extractors |
timeout_seconds |
NAMI_TIMEOUT_SECONDS or NAMI_TIMEOUT |
Per-attempt subprocess timeout |
Authentication and cookies
Place Netscape-format cookie files in the configured cookie directory. Nami validates that files contain at least one real seven-column Netscape cookie row; placeholder files and headers alone are rejected.
Common cookie candidate names include platform-specific files such as:
instagram_cookies.txt
tiktok_cookies.txt
facebook_cookies.txt
x_cookies.txt
TikTok can fall back to browser cookie extraction when no valid cookie file is available. If the configured browser is running, its database may be locked; nami doctor reports this as a warning.
Archives
Each target/media operation uses an archive file to avoid duplicate downloads. Archives are never deleted implicitly.
Preview archive reset actions:
nami archive reset --all --dry-run
Back up matching archives after confirmation:
nami archive reset --platform instagram --target example --yes
Permanently delete matching archives only when intentional:
nami archive reset --all --delete --yes
Without --yes, archive reset reports a dry run only.
Diagnostics
Run local, read-only diagnostics:
nami doctor
Doctor checks include:
- configuration validity
- Python version
- importability of runtime dependencies
- workspace readability/writability
- configured browser state
- cookie-file validity
- profile-file readability
- potential
urllib3namespace conflicts - stale archive locks
Doctor does not make network requests and does not mutate the workspace.
Exit codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Failure |
| 2 | Invalid input or configuration |
| 3 | Partial result, warning, or unsupported-only result |
| 4 | No results |
| 130 | Cancelled |
Development
Clone the repository, create an environment, and install development dependencies:
python -m pip install --upgrade pip
python -m pip install -e .[dev]
Run tests and quality checks:
PYTHONPATH=src python -m pytest -q
python -m ruff check src tests
python -m ruff format --check src tests
python -m build
python -m twine check dist/*
check-wheel-contents dist/*.whl
The test suite is designed to avoid network calls.
Troubleshooting
- Run
nami doctorfirst; it provides targeted remediation messages. - If
pytestimports an installed old copy of Nami, run withPYTHONPATH=srcor install the package editable withpython -m pip install -e .[dev]. - If cookie authentication fails, export fresh Netscape cookies and ensure the file has real cookie rows, not just template comments.
- If TikTok browser-cookie extraction fails, close the configured browser and retry.
- If downloads repeatedly time out, increase
timeout_secondswithnami config set timeout_seconds 3600. - If you intentionally want to re-download content, use
nami archive resetinstead of deleting files manually.
License
Nami is distributed under the MIT License. See LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file nami-5.0.0.tar.gz.
File metadata
- Download URL: nami-5.0.0.tar.gz
- Upload date:
- Size: 65.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fbaad73b4795d337f7acd3c6eb15aeba421c586e03e4f71abd837a22a003578b
|
|
| MD5 |
1d1d332a201600c4f05c4165813bfcae
|
|
| BLAKE2b-256 |
b6844a9342bd71320babf3c49b58eb4cc18877e193eb57eb195f17416f36e727
|
Provenance
The following attestation bundles were made for nami-5.0.0.tar.gz:
Publisher:
publish.yml on OpenSelena/nami
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
nami-5.0.0.tar.gz -
Subject digest:
fbaad73b4795d337f7acd3c6eb15aeba421c586e03e4f71abd837a22a003578b - Sigstore transparency entry: 2490858022
- Sigstore integration time:
-
Permalink:
OpenSelena/nami@cad064c823c6f3743ebc3f0f14afa589e062a246 -
Branch / Tag:
refs/tags/v5.0.0 - Owner: https://github.com/OpenSelena
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@cad064c823c6f3743ebc3f0f14afa589e062a246 -
Trigger Event:
release
-
Statement type:
File details
Details for the file nami-5.0.0-py3-none-any.whl.
File metadata
- Download URL: nami-5.0.0-py3-none-any.whl
- Upload date:
- Size: 53.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6797ff0c13b68bf255ee2c2f4fa9e6883bd194ec4c2cdb24e8675ee01c7d1819
|
|
| MD5 |
1f1282c2a8b625a7d00fee2e77f1054f
|
|
| BLAKE2b-256 |
f5bf7ab9f3224a46d51fcada17bbdf7be28b40297e46cfa5cb53bd1d8a8fb2b4
|
Provenance
The following attestation bundles were made for nami-5.0.0-py3-none-any.whl:
Publisher:
publish.yml on OpenSelena/nami
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
nami-5.0.0-py3-none-any.whl -
Subject digest:
6797ff0c13b68bf255ee2c2f4fa9e6883bd194ec4c2cdb24e8675ee01c7d1819 - Sigstore transparency entry: 2490858350
- Sigstore integration time:
-
Permalink:
OpenSelena/nami@cad064c823c6f3743ebc3f0f14afa589e062a246 -
Branch / Tag:
refs/tags/v5.0.0 - Owner: https://github.com/OpenSelena
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@cad064c823c6f3743ebc3f0f14afa589e062a246 -
Trigger Event:
release
-
Statement type: