Skip to main content

immich-export

Export supported originals and metadata from Immich — albums, people, tags, descriptions, favorites, and coordinates — into a human-readable local folder tree.

The result is useful for inspection, migration, and an additional local copy. It is not a replacement for independent, tested backups of Immich and its database.

immich-export/
  library/2024/03/IMG_1234.jpg          # primary tree (self-contained mode)
  library/2024/03/IMG_1234.jpg.xmp      # sidecar: tags, people, albums, description, geo, favorite
  albums/Japan-2019/IMG_1234.jpg        # → symlink into library/
  people/Anna/IMG_1234.jpg              # → symlink into library/
  manifest.jsonl                        # append-only verified-state history
  manifest-current.jsonl                # authoritative current verified set
  manifest.csv                          # human-readable current projection
  export-report.txt                     # counts, warnings, errors, timing

Install

pipx install immich-export
# or
brew install fileworks/tap/immich-export

Released 0.2.1 on PyPI, GitHub, and fileworks/tap (verified 2026-08-04).

Quick start

export IMMICH_SERVER=https://immich.local:2283
export IMMICH_API_KEY=            # never passed on the command line
immich-export --out ~/immich-export

The first run copies originals and writes sidecars. Every later run rehashes what is already there and transfers only what changed.

Usage

export IMMICH_SERVER=https://immich.local:2283
export IMMICH_API_KEY=...   # Immich → Account Settings → API Keys

# full portable export (copies originals)
immich-export --out ./immich-export

# verified re-run: local originals are rehashed; only missing/changed bytes download
immich-export --out ./immich-export

# sidecar mode: you already have the Storage-Template tree mounted —
# only write .xmp sidecars + album/people views next to it
immich-export --mode sidecar --library-root /volume1/photos --out /volume1/photos

# custom primary tree
immich-export --layout "{year}/{album}" --out ./export

Key flags (see immich-export --help for all):

Flag Default Meaning
--mode self-contained self-contained copies originals; sidecar only writes XMP + views next to an existing tree
--layout {year}/{month} primary tree; tokens {year} {month} {day} {album} {type}; {album} falls back to Unsorted
--album-view / --people-view on build albums/ and people/ symlink views
--sidecars xmp xmp or none
--since only assets taken on/after this date
--resume on use prior state as a resume/migration hint; local bytes are still rehashed
--include-hidden off also export hidden + locked-folder assets
--stale-assets keep keep/report absent outputs or explicitly move owned outputs to quarantine
--concurrency 4 bound concurrent API work, downloads, and local verification
--manifest-batch-size 128 verified history records per durable synchronization
--manifest-flush-interval 0.1 maximum seconds before a partial durable group is synchronized
--history-max-records 100000 rotate the bounded active history at this record count
--history-max-bytes 134217728 rotate the active history at this byte count
--log-file <out>/immich-export.log bounded rotating timestamped diagnostics

Verified behavior

  • Read-only against Immich. Never writes back.
  • Verified originals. Downloads are SHA-1 checked before atomic promotion, and existing local originals are rehashed on every run in both modes.
  • Canonical metadata and XMP. All persisted/path/XMP fields share one typed state. Missing, malformed, or stale required XMP is atomically refreshed.
  • History versus current. manifest.jsonl is bounded active audit history; digest-linked verified archives live under manifest-history/. manifest-current.jsonl, its CSV projection, and generated views contain only assets verified by the latest completed compatible scan.
  • Partial runs are explicit. Asset-specific integrity failures are reported, excluded from current state, and return exit code 1 (PARTIAL); run-level failures do not replace the prior current snapshot.
  • Conservative reconciliation. A compatible full scan removes absent assets from current state and views. Their files remain reported orphans by default. Explicit quarantine moves only manifest-owned outputs; sidecar mode never moves Immich-managed originals.
  • Bounded work and visible progress. Assets are paged, concurrent work is bounded, and terminal/log progress remains available.

These checks cover the API fields and files the exporter supports. They cannot detect metadata Immich does not expose, storage failures that occur after a successful verification, or prove that an export can restore an entire Immich installation. Keep separate backups and test restoration procedures.

Exit codes

immich-export, paperless-export and unpacksort share one exit-code vocabulary, so a script can branch on the code without knowing which tool it ran. The class of outcome is the same everywhere; the specific condition is this tool's, and the table below is what it means here.

Code Name Meaning
0 SUCCESS everything asked for was done
1 PARTIAL the run finished with one or more asset failures; successes are published
2 USAGE bad flags, paths, or credentials the server rejected — nothing was attempted
3 CONFLICT the Immich server is unreachable
4 FATAL unexpected failure, or output that could not be written (re-run with --verbose)
130 INTERRUPTED cancelled by the operator

Sidecar format

Standard XMP wherever a standard slot exists — dc:subject (tags), Iptc4xmpExt:PersonInImage (people), dc:description, photoshop:DateCreated, exif:GPSLatitude/Longitude, xmp:Rating (favorite → 5) — so digiKam, Lightroom and exiftool can read them. Album membership and Immich ids live in a custom immich: namespace in the same file.

When XMP is enabled, an asset is current only after its canonical sidecar matches the manifest state. Exact generated-state validation removes metadata that was deleted in Immich rather than retaining stale XMP nodes.

Immich API compatibility

Built against the Immich v3 API (spec version 3.0.1). Instead of a generated client, the exact API slice used is declared in src/immich_export/api_contract.py and checked in CI against a vendored, pruned copy of the official OpenAPI spec. To check a new Immich release:

uv run python scripts/refresh_api_spec.py --ref v3.1.0
uv run pytest tests/test_contract.py

A removed endpoint or field fails the tests before it breaks at runtime.

Configuration

Everything is configured through environment variables and flags; there is no configuration file to keep in sync.

Variable Purpose
IMMICH_SERVER Base URL of your Immich instance
IMMICH_API_KEY API key. Read from the environment only, never from argv
IMMICH_EXPORT_CONCURRENCY Parallel downloads (default: conservative)
IMMICH_EXPORT_MANIFEST_BATCH_SIZE Records per durable history group
IMMICH_EXPORT_MANIFEST_FLUSH_INTERVAL Maximum partial-group delay in seconds
IMMICH_EXPORT_HISTORY_MAX_RECORDS Active-history record rotation threshold
IMMICH_EXPORT_HISTORY_MAX_BYTES Active-history byte rotation threshold
IMMICH_EXPORT_LOG_FILE Rotating logfile path

Flags are documented under Usage; --help is authoritative.

Troubleshooting

The run stops with a network error. Re-run it. The manifest records what was verified, so a repeat run resumes rather than restarting.

A previous run was interrupted and the manifest looks damaged. Damaged lines are skipped and reported; the export continues from the last intact state.

Nothing is downloaded and the count is zero. Check that IMMICH_SERVER points at the API root and that the key has library access — a wrong base URL authenticates fine and returns nothing.

Symlinks fail on the target. Album and people views require symlinks. Disable them with --no-album-view --no-people-view on FAT/exFAT or cloud folders that cannot represent links; the verified primary export remains available.

Development

uv sync --locked --all-extras --all-groups
uv run ruff check . && uv run ruff format --check .   # lint
uv run mypy                                           # strict types
uv run pytest                                         # tests (mock Immich API)
uv build                                              # sdist + wheel

Conventional Commits drive releases (python-semantic-release): merge to main → version bump + changelog + GitHub Release + PyPI publish (OIDC) + Homebrew formula bump.

Each successful publication is also recorded as a GitHub Deployment in its matching protected environment: github-release, then pypi, then homebrew. The release itself remains the user-facing version; deployments provide channel history and policy enforcement.

For per-clone paths, commands, or preferences, create an ignored CLAUDE.local.md at the repository root. Do not put credentials or other secrets in it.

Security

Report vulnerabilities privately as described in SECURITY.md. The API key is read from the environment and never written to disk, a log line, or the manifest.

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

immich_export-1.0.0.tar.gz (116.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

immich_export-1.0.0-py3-none-any.whl (46.2 kB view details)

Uploaded Python 3

File details

Details for the file immich_export-1.0.0.tar.gz.

File metadata

  • Download URL: immich_export-1.0.0.tar.gz
  • Upload date:
  • Size: 116.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for immich_export-1.0.0.tar.gz
Algorithm Hash digest
SHA256 c2572542e3770aedc80f3ba03af13ae16c7d9c6ac0536035cdddb4acb8e701b8
MD5 3e543c80d900833091bb732e64c0aee0
BLAKE2b-256 84492595319357578d1ea60edafef309606d77cb96129ee67dd3e93a3d85105d

See more details on using hashes here.

Provenance

The following attestation bundles were made for immich_export-1.0.0.tar.gz:

Publisher: release.yml on fileworks/immich-export

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file immich_export-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: immich_export-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 46.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for immich_export-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 6bf1fedc9acf008f0fea73b9317c7ebff1b201b62a50fb03bded2b03aa79f4a6
MD5 259cd9895815cc0bc59b5a760170ebf4
BLAKE2b-256 7a1b127d70a8e55583bd9bd82c929fb6503f1687712c2c08a652bf3b12a7b878

See more details on using hashes here.

Provenance

The following attestation bundles were made for immich_export-1.0.0-py3-none-any.whl:

Publisher: release.yml on fileworks/immich-export

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

1.0.1

2 files

This release

1.0.0 This release

2 files

0.2.1

2 files

0.2.0

2 files

0.1.1

2 files

0.1.0

2 files

0.0.4

2 files

0.0.3

2 files

0.0.2

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page