Skip to main content

Version Build Status Tests License Python

zotero-cli

Your Zotero library, from the command line.

zotero-cli lets you read, organize and change a Zotero library without opening the Zotero app. Every operation is a plain command, so you can put it in shell scripts, cron jobs and CI, or hand it to an LLM agent as a tool.

It talks to the Zotero Web API (personal and group libraries). It can also read your local zotero.sqlite offline.

Why a CLI for Zotero?

  • Scriptable: items, collections, tags, notes and attachments are all reachable from a terminal, with no GUI steps in between.
  • Machine-readable output: item list prints json, csv or markdown, and --fields picks the columns. Pipe it into jq, a spreadsheet or a prompt.
  • Suited to LLM agents: an agent can run --help on any command to learn its options, and most commands' help includes worked examples. item export --format md extracts the text of an item's PDF into a .md file an LLM can read.
  • Preview first: collection clean, collection delete --recursive, slr prune, slr load, tag purge, item merge, item hydrate, item pdf strip and slr dedupe only show what they would do until you add --execute. A recursive delete also asks for confirmation (--yes in scripts), and a collection name that matches several collections is refused rather than guessed. item purge and collection purge ask for confirmation; item delete deletes the one item you name. system restore has --dry-run, and --offline reads the local database and is read-only apart from trash/restore.
  • Runs anywhere: a single binary for Linux and Windows, no Python needed. Also available as a container or a Python package.

What it can do

Library management

  • Items: list, inspect, add, update, move, merge duplicates, trash/restore, delete, and copy between libraries.
  • Collections: create, rename, nest, empty, delete, and export whole collections (BibTeX, RIS, Markdown).
  • Tags: list, add, and bulk-remove tags across a collection.
  • Search: by DOI or title substring.
  • Attachments: fetch missing PDFs from open-access sources, attach local files, strip attachments, and move stored files out to local or network storage while keeping them linked (storage checkout).

Import and metadata

  • Import from arXiv, DOI, BibTeX/RIS/CSV files, the Brazilian BDTD thesis repository, or manual entry.
  • Metadata lookup from Semantic Scholar, CrossRef, OpenAlex, PubMed, Unpaywall and more when importing by DOI, and item hydrate to fill in empty fields (abstract, venue, date, authors, DOI) for items you already have, including published versions of arXiv preprints.
  • Library health reports: duplicates, missing PDFs, DOIs or abstracts, disk usage, and checking the citations in a LaTeX manuscript against the library.

Operations

  • Backup and restore the whole library, or one collection, as a compressed .zaf archive, attachments included.
  • system check tests the connection to every service you've configured (Zotero and the metadata providers) in one go.
  • Local HTTP API (serve): read-only endpoints for items, collections and background jobs, for local scripts and dashboards.

Optional: literature review toolkit

  • Systematic literature review (slr): screening decisions recorded as auditable Zotero notes, PRISMA statistics, citation snowballing and data extraction. See docs/commands/slr.md.

🍳 Cookbook

Get a collection as JSON

zotero-cli item list --collection "Reading List" --wide --format json | jq '.[] | {title, year, doi}'

Choose exactly the columns you want

zotero-cli item list --collection "Reading List" --fields key,first_author,year,venue,DOI --format csv > reading.csv

Add papers from a script

for doi in 10.1145/3290605.3300233 10.1038/nature14539; do
  zotero-cli import doi "$doi" --collection "Inbox"
done
zotero-cli item pdf fetch --collection "Inbox"

Hand a paper to an LLM

# The text of the item's PDF, saved as .md
zotero-cli item export --key ABCD1234 --format md --output ./context/

Clean up tags, previewing before you change anything

zotero-cli tag purge --collection "Old Project"            # preview only
zotero-cli tag purge --collection "Old Project" --execute  # apply

Query your library without internet access

# Reads the local zotero.sqlite (set database_path in config.toml)
zotero-cli --offline item list --collection "Reading List" --format markdown

Back up everything

zotero-cli system backup --output library_2026-09.zaf
zotero-cli system restore --file library_2026-09.zaf --dry-run

Try it without touching your real library

zotero-cli system check          # is every configured service reachable?
zotero-cli system demo-sandbox   # disposable collection of sample papers
zotero-cli system demo-sandbox --clean

📚 Command Reference

Noun Description Key Verbs
init Config wizard (default)
item Items list, inspect, add, update, move, merge, export, pdf, hydrate, purge, delete, trash, restore, transfer
collection Folders list, create, rename, delete, clean, export, backup, purge
tag Tags list, add, purge
search Finder --doi, --title
import Ingest arxiv, doi, file, bdtd, manual
report Library health duplicates, audit, stats, attachments, verify-latex
storage Attachments checkout
system Operations info, check, selftest, groups, switch, backup, verify, restore, normalize, demo-sandbox, jobs
serve Local HTTP API (default)
slr Literature review source, screen, decide, load, list, reconcile, promote, dedupe, prune, extract, snowball, sdb, report
rag Semantic search (optional rag extra) ingest, query, context, purge, model

Every command has built-in help: zotero-cli <noun> <verb> --help. Every example in these docs is checked against the real command-line parser in CI.


📦 Installation

Supported platforms

Platform Standalone binary Python package (zotero-command-line)
Linux x86_64, glibc 2.35+ (Ubuntu 22.04, Debian 12, Fedora 36, RHEL 10 or newer) ✅ .tar.gz, .deb, .rpm, Docker ✅ Python 3.11–3.14
Linux x86_64 or ARM64, older glibc (2.17+) ❌ ✅ Python 3.11–3.13; 3.14 needs glibc 2.28+
Linux with musl (Alpine) ❌ ✅ Python 3.11–3.14
Windows x64 ✅ .msi, .zip ✅ Python 3.11–3.14
macOS (Apple Silicon and Intel) ❌ ✅ Python 3.11–3.14, via uv tool install or pipx

CI checks that the Python package installs from pre-built wheels on each of these platforms. Other platforms may work but aren't tested.

Download a pre-built binary for your system. You don't need Python.

  • Windows: .msi installer or .zip from Latest Releases.
  • Linux (Ubuntu/Debian): .deb package.
  • Linux (Fedora/RHEL): .rpm package.
  • Any Linux: zotero-cli-linux-amd64.tar.gz.

Or use the one-line installer scripts. They fetch the latest release (or the one named in ZOTERO_CLI_VERSION, e.g. v3.0.0) and check it against the release's SHA256SUMS before installing:

# Linux (x86_64, glibc 2.35+; on other systems it prints the PyPI command instead)
curl -fsSL https://raw.githubusercontent.com/fchicout/zotero-cli/main/install.sh | bash
# Windows (PowerShell)
irm https://raw.githubusercontent.com/fchicout/zotero-cli/main/install.ps1 | iex

Verifying a download yourself: every release from v2.8.12 on includes a SHA256SUMS file and signed build provenance. With the GitHub CLI, gh attestation verify zotero-cli-linux-amd64.tar.gz -R fchicout/zotero-cli confirms the file was built by this repository's release workflow.

The package is called zotero-command-line on PyPI, because the zotero-cli name there belongs to an older, unrelated project. The command it installs is still zotero-cli. This is the way to install on macOS, Alpine, ARM Linux and older Linux systems, where there is no binary. uv can install a suitable Python for you.

uv tool install zotero-command-line   # or: pipx install zotero-command-line
zotero-cli --help

🐳 Option 3: Containers

The repo includes a Dockerfile. It builds the same standalone binary as the releases.

git clone https://github.com/fchicout/zotero-cli.git
cd zotero-cli
docker build -t zotero-cli .

# Configure with an env file (ZOTERO_API_KEY=..., ZOTERO_LIBRARY_ID=..., one per line),
# which keeps keys out of your shell history...
docker run --rm --env-file zotero.env zotero-cli system info

# ...or mount your existing config directory, running as yourself so any
# files zotero-cli writes there (logs, job state) belong to you
docker run --rm --user "$(id -u):$(id -g)" \
  -v ~/.config/zotero-cli:/config/zotero-cli zotero-cli system info

The container runs as an unprivileged user and keeps its config in /config/zotero-cli. On SELinux hosts (Fedora, RHEL), add :z to the mount: -v ~/.config/zotero-cli:/config/zotero-cli:z.

Note: --offline mode (reading a local zotero.sqlite) needs that file mounted into the container too, e.g. -v /path/to/zotero.sqlite:/data/zotero.sqlite.

Note: inside a container, serve has to bind 0.0.0.0 to be reachable, which needs --allow-remote (it prints an access token). Publish the port on the host's loopback only: docker run -p 127.0.0.1:1969:1969 ... zotero-cli serve --host 0.0.0.0 --allow-remote.

A .devcontainer/ configuration is also included for GitHub Codespaces and VS Code Dev Containers. It sets up the full development environment for contributing, not the lightweight image above.

Option 4: From source (Python 3.11+)

Using uv:

git clone https://github.com/fchicout/zotero-cli.git
cd zotero-cli
uv tool install .

📦 Using zotero-cli as a Python library

pip install zotero-command-line

See the "Distribution" section of docs/ARCHITECTURE.md for details, including installing an unreleased commit from git.

⚙️ Configuration

zotero-cli init         # interactive wizard: API key, library, optional metadata providers
zotero-cli system info  # show which config is in use

The config file lives at ~/.config/zotero-cli/config.toml (Linux/macOS) or %APPDATA%\zotero-cli\config.toml (Windows). See docs/SETUP_GUIDE.md and config.toml.example.

Development & Contribution

git clone https://github.com/fchicout/zotero-cli.git
cd zotero-cli
uv sync --extra dev
uv run pre-commit install --hook-type pre-commit --hook-type pre-push
uv run pytest tests/unit

uv sync creates .venv/, using the Python version pinned in .python-version, and installs the project in editable mode from uv.lock. Commit uv.lock alongside any dependency change so everyone, CI included, gets exactly the same versions. pre-commit install sets up the checks from docs/PROCESS.md: ruff, mypy and bandit run on every git commit, and pytest tests/unit runs on git push. See .pre-commit-config.yaml.

Data Sources

Besides your Zotero library, zotero-cli queries public scholarly services (Crossref, Semantic Scholar, OpenAlex, Unpaywall, arXiv, PubMed and others) and identifies itself honestly when it does. docs/DATA_SOURCES.md lists them, links their terms, and explains attribution: if you publish results from slr snowball, credit Semantic Scholar. zotero-cli is an independent project, not affiliated with or endorsed by Zotero, the Corporation for Digital Scholarship, arXiv or any of these providers.

Versioning

zotero-cli follows Semantic Versioning from 3.0.0 on. docs/COMPATIBILITY.md lists what counts as the public interface (commands, flags, safety defaults, JSON/CSV output, config keys, stored formats) and how deprecations work. Upgrading from 2.x: see "Upgrading to 3.0" in the CHANGELOG.

Security

To report a vulnerability, see SECURITY.md. Please don't use public issues for security problems.

License

MIT License. See LICENSE for details.

Metadata

Release files for zotero-command-line 3.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for zotero-command-line 3.0.0
File Size Uploaded
zotero_command_line-3.0.0.tar.gz 329.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for zotero-command-line 3.0.0
File Interpreter ABI Platform
zotero_command_line-3.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 675.8 kB

Release files / zotero_command_line-3.0.0.tar.gz

Download URL zotero_command_line-3.0.0.tar.gz
Size 329.9 kB
Tags Source
SHA-256 checksum
How to use checksums
9250c62d12cd66af7e0941003eb239e38f6f150b0350a0b7788761376c073d2b
BLAKE2b-256 checksum
How to use checksums
5b1676024e0fd1281d3493085b6b24dbb47a4c04a7f35ab95554369092b82f79
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.19 {"installer":{"name":"uv","version":"0.12.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / zotero_command_line-3.0.0-py3-none-any.whl

Download URL zotero_command_line-3.0.0-py3-none-any.whl
Size 345.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
83655c70373b2d27d3b4ef4e650fe4c107d3a0c59ea2362d4c2ac43095556843
BLAKE2b-256 checksum
How to use checksums
4a03829f440feb7e20a9b2dc1b57ea2e1ac854834a528d7a7e8a1a3a371c485d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.19 {"installer":{"name":"uv","version":"0.12.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

4.0.2

2 release files

4.0.1

2 release files

4.0.0

2 release files

3.6.0

2 release files

3.5.0

2 release files

3.4.1

2 release files

3.4.0

2 release files

3.3.1

2 release files

3.3.0

2 release files

3.2.0

2 release files

3.1.1

2 release files

3.1.0

2 release files

3.0.6

2 release files

3.0.5

2 release files

3.0.4

2 release files

3.0.3

2 release files

3.0.2

2 release files

3.0.1

2 release files

This release

3.0.0 This release

2 release files

2.8.12

2 release 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