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 listprintsjson,csvormarkdown, and--fieldspicks the columns. Data goes to stdout and warnings to stderr, so you can pipe it intojq, a spreadsheet or a prompt. - Suited to LLM agents: an agent can run
--helpon any command to learn how to use it, since each one includes worked examples.item export --format mdconverts an item's PDF into Markdown an LLM can read. - Safe by default: bulk or destructive commands (
tag purge,item merge,item pdf strip,slr dedupe, ...) only show a preview until you add--execute.item hydrateandsystem restorehave--dry-run, andcollection purgeasks for confirmation.--offlinereads 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 hydrateto add the DOI and journal to arXiv preprints once they're published. - 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
.zafarchive, attachments included. system checktests 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 item's PDF, converted to Markdown
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 |
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, groups, switch, backup, restore, jobs |
serve |
Local HTTP API | (default) |
slr |
Literature review | screen, decide, load, extract, snowball, sdb, report |
Every command has built-in help with worked examples: zotero-cli <noun> <verb> --help.
📦 Installation
Option 1: Standalone binaries (recommended)
Download a pre-built binary for your system. You don't need Python.
- Windows:
.msiinstaller or.zipfrom Latest Releases. - Linux (Ubuntu/Debian):
.debpackage. - Linux (Fedora/RHEL):
.rpmpackage. - 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. v2.8.12) and check it against the release's SHA256SUMS before installing:
# Linux (amd64)
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.
🐍 Option 2: From PyPI (Python 3.11+)
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.
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:
--offlinemode (reading a localzotero.sqlite) needs that file mounted into the container too, e.g.-v /path/to/zotero.sqlite:/data/zotero.sqlite.
Note: inside a container,
servehas to bind0.0.0.0to 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.
Security
To report a vulnerability, see SECURITY.md. Please don't use public issues for security problems.
License
MIT License. See LICENSE for details.
Release files for zotero-command-line 2.8.12
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| zotero_command_line-2.8.12.tar.gz | 308.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| zotero_command_line-2.8.12-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 639.3 kB
Release files / zotero_command_line-2.8.12.tar.gz
| Download URL | zotero_command_line-2.8.12.tar.gz |
|---|---|
| Size | 308.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8306b1314be2d3b690ab0a3d4ac1d7974c55a68183e910eeffc8dcf684974228
|
|
BLAKE2b-256 checksum How to use checksums |
45e93b0a2dfaaad89af76b937e82f0d5a73583ad0b2e5ef7456f8aa02a7ee6dc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.18 {"installer":{"name":"uv","version":"0.12.18","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-2.8.12-py3-none-any.whl
| Download URL | zotero_command_line-2.8.12-py3-none-any.whl |
|---|---|
| Size | 330.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4ba46419c3979bcf97feece7972dab658b0a38c773bff065e5caf4b75f2477c6
|
|
BLAKE2b-256 checksum How to use checksums |
9c587a3baa4a2b5020e3a230ab6e91531c96ade0125179020ceaf9b1428e61e9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.18 {"installer":{"name":"uv","version":"0.12.18","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}
|