Skip to content

Immich server compatibility

Immich 3.0 reworked several of the API endpoints pymmich relies on. pymmich speaks both dialects: it asks the server for its version and picks the matching API variant automatically, so the same pymmich release works against old and new servers alike.

Supported versions

Immich server API variant Status
2.7 – 2.x v2 Supported
3.0, 3.1 v3 Supported
older than 2.7 v2 Untested; may work

Nothing needs to be configured for this. The same API key permissions apply to both generations — see API key permissions.

How the variant is detected

On the first command that touches a version-dependent endpoint, pymmich issues a single GET /server/version request and derives the variant from the reported major version: 2.xv2, 3.xv3.

  • Lazy — no request is made until a version-dependent call is actually needed.
  • Cached — the result is remembered for the lifetime of the client, i.e. one per command run, so a run costs at most one extra request.
  • UnprivilegedGET /server/version is a public endpoint on both server generations and needs no API-key permission, so detection also works with a minimally scoped key.

Release candidates

Pre-release versions such as 3.0.0-rc.1 are treated like their final release: only the major version decides which variant is used.

What actually differs

Area Immich 2.x Immich 3.x
Owned albums GET /albums GET /albums?isOwned=true
Shared albums GET /albums?shared=true GET /albums?isShared=true
All visible albums GET /albums and GET /albums?shared=true, merged client-side GET /albums?isOwned=true and GET /albums, merged client-side
Album contents GET /timeline/buckets + GET /timeline/bucket, then GET /assets/{id} per asset POST /search/metadata with an albumIds filter
Upload POST /assets including the deviceId / deviceAssetId fields POST /assets without those fields (dropped in 3.0)
Album members album owner is reported separately from albumUsers album owner appears in albumUsers with the role owner
Asset listings metadata search returns timeline assets by default default widened to everything but locked assets, so pymmich asks for timeline assets explicitly
Album contents scope timeline + archived assets additionally hidden assets, which pymmich filters out for parity

Some of these are worth a sentence more:

  • Which endpoint lists an album. On Immich 2.x, POST /search/metadata forcibly scopes results to the caller's own assets, which would hide photos other members contributed to a shared album. pymmich therefore reads album contents through the timeline endpoints there. Immich 3.0 stopped applying that owner scope when an albumIds filter is given (it checks album read access instead), so on 3.x the stable search endpoint is used — fewer requests, and no reliance on internal endpoints.
  • Album members. Because Immich 3.x lists the owner as an albumUsers entry, unshare explicitly ignores that entry: the owner of an album is never treated as a user the album was shared with, and can never be unshared by accident.
  • Asset listings. Immich 2.x restricted metadata searches to timeline assets; Immich 3.0 changed the default to "everything except locked assets", which would have made list and filename lookups start reporting archived and hidden assets on a 3.x server only. pymmich sends the timeline filter explicitly so listings look the same either way.
  • What an album listing contains. It is deliberately not narrowed the same way: an album is listed and downloaded with its archived members included, as on Immich 2.x. Hidden assets are dropped, though — Immich stores the video part of a motion photo as a hidden asset, and the 2.x timeline endpoints never returned those for an album, so keeping them would mean one surprise extra file per motion photo on a 3.x server.

Pinning the variant

If GET /server/version is unreachable — a restrictive reverse proxy that only forwards a subset of the API is the usual culprit — detection fails and pymmich cannot pick a variant. Set PYMMICH_API_VARIANT to skip detection and pin the variant instead:

# Talk to a 2.x server (e.g. 2.7):
export PYMMICH_API_VARIANT=v2

# Talk to a 3.x server (e.g. 3.0 or 3.1):
export PYMMICH_API_VARIANT=v3

Only v2 and v3 are accepted; anything else makes pymmich exit with a configuration error. When the variable is set, no version request is made at all.

Escape hatch, not a default

Leave PYMMICH_API_VARIANT unset unless detection actually fails. A pinned variant does not track server upgrades: after moving from Immich 2.x to 3.x you would have to update the variable by hand, or pymmich keeps sending the old requests.

If detection fails

pymmich reports the failing request, so a broken detection shows up as an error on GET /server/version. Things to check, in order:

  1. Is PYMMICH_URL right? Both https://host and https://host/api are accepted; the /api suffix is appended when missing.
  2. Is the server reachable at all? GET /server/ping is public too, so if that fails the problem is the URL, the network or TLS verification — not the API variant.
  3. Does a proxy block the endpoint? If ping works but the version request returns 403/404, the proxy in front of Immich is filtering paths. Either allow /api/server/version through, or set PYMMICH_API_VARIANT as shown above.
  4. Does the server report an odd version? A response that isn't a major/minor/patch triple is rejected rather than guessed at; pinning the variant works around that as well.