Skip to main content

zotkit

PyPI Python License: MIT

Headless Zotero library management — no desktop app required.

English | 简体中文

"Headless" simply means zotkit never needs the Zotero app (or any window) open: it is a Python library + CLI that talks straight to the Zotero Web API, so you can search, create, tag, and organize items from any terminal — macOS, Windows, or Linux, your laptop or a remote server. If your attachments sync to a personal WebDAV server, zotkit can upload and download the files themselves by speaking Zotero's WebDAV storage format directly — a capability the Web API itself does not provide. The format is documented in docs/webdav-format.md.

Built for servers, scripts, and LLM agents: every write is dry-run by default, batched, and version-checked, and you can define a tag taxonomy that is enforced in code so an agent (or a tired human) can't pollute your library with inconsistent tags.

Why zotkit

Desktop app Other CLI/MCP tools zotkit
Works headless (server, SSH, CI) ✅ read-mostly
Write items/tags/collections ⚠️ usually needs the desktop app running
Attachment files (Zotero Storage) ⚠️ some ✅ upload + download
Attachment files on WebDAV ⚠️ download at best upload + download
Tag conventions enforced in code ✅ optional conventions.toml

The zotkit family

Two independent, complementary projects — use either on its own, or both together:

What it is
zotkit (this repo) Headless Python CLI + library. Runs anywhere, needs no Zotero app.
zotkit-reader A Zotero Reader sidebar that embeds a Codex/Claude agent beside the PDF you're reading. Maintained by @ChanceSiyuan.

They share a name and a philosophy, not a codebase: zotkit-reader can call zotkit over MCP for library-wide operations, but neither requires the other to be installed.

Install

Pure Python (3.11+), no platform-specific bits — the same package works on macOS, Windows, and Linux:

pipx install zotkit        # or: uv tool install zotkit / pip install zotkit
uvx zotkit --help          # …or try it without installing anything

Configure

Copy .env.example to ./.env, ~/.config/zotkit/env, or any path in $ZOTKIT_ENV, and fill in:

  • Zotero Web API: create a key (with write access) at https://www.zotero.org/settings/keys — your numeric ZOTERO_LIBRARY_ID is shown on the same page.
  • WebDAV (only for attach/fetch): copy the exact values from the Zotero desktop app on any of your machines — Settings → Sync → File Syncing — and append /zotero/ to the URL (the desktop does this implicitly).
  • Using Zotero Storage instead of WebDAV? Just leave the WEBDAV_* lines out — attach/fetch automatically use Zotero Storage through the Web API's upload/download endpoints instead. The storage mode is detected from your .env, nothing to configure.

After filling it in, run zotkit doctor — it validates the config file, API access, and attachment storage, and tells you exactly what to fix if anything fails.

Optionally, copy conventions.example.toml to conventions.toml next to your .env to define a namespaced tag taxonomy (field:physics, status:to-read, …). With it in place, zotkit create / zotkit tag reject violations; without it, tags are unrestricted.

Quickstart

zotkit find --title "boson sampling"        # search by title/tag/collection
zotkit find --tag status:to-read

zotkit create --arxiv 2401.12345            # fetch arXiv metadata, dry-run preview
zotkit create --arxiv 2401.12345 --apply --tags field:ai   # create + download & attach the PDF
zotkit create --arxiv 2401.12345 1706.03762 math/0211159 --apply   # batch: one metadata
                                            #   request, PDFs politely spaced ≥3 s apart
zotkit create --doi 10.1038/nature14539     # fetch CrossRef metadata by DOI (no PDF —
                                            #   usually paywalled; attach manually after)

zotkit create --file papers.json            # batch from JSON: dry-run preview
zotkit create --file papers.json --apply    # create (dedups by DOI/title)
zotkit attach --from papers.created.json --all   # upload the PDFs to WebDAV

zotkit attach --key AB12CD34 --pdf paper.pdf     # single attach
zotkit fetch --key AB12CD34 --out downloads      # download attachment from WebDAV

zotkit tag AB12CD34 topic:qaoa prio:high    # validated against conventions.toml
zotkit status AB12CD34 read                 # replaces the status: tag
zotkit move AB12CD34 "Algorithms"           # or "Parent :: Child"; --add keeps old home

zotkit backup                               # full JSON snapshot -> backups/
zotkit lint field:physics topic:new-idea    # offline tag check

--arxiv takes ids or abs/pdf URLs (several, space- or comma-separated) and maps the full record (all authors, abstract, date, DOI, preprint item type); --doi maps CrossRef records (journal articles, conference papers, books, chapters, …) and refuses to guess on CrossRef types it doesn't know. Both accept --collection and --tags, and --no-pdf skips the arXiv PDFs. Rate limiting is built into the request layer — batches use one arXiv metadata request and space PDF downloads per arXiv's terms of use, so callers (humans or agents) never pace themselves. A bad id fails alone, not the batch; the exit code is non-zero only if something failed. Fetching metadata from arbitrary web pages is out of scope (zotkit stays a daemon-free CLI — no translation-server).

Item JSON for zotkit create --file (a list, one object per reference):

[{"itemType": "journalArticle", "title": "…",
  "creators": [{"creatorType": "author", "firstName": "A", "lastName": "B"}],
  "date": "2024", "publicationTitle": "…", "DOI": "10.x/y",
  "tags": ["field:physics", "status:to-read"],
  "collection": "Algorithms", "file_path": "/abs/path/paper.pdf"}]

From Python

from zotkit import Zot

z = Zot()                                   # reads .env automatically
z.find(tag="status:to-read")
z.create_items([...])                       # dedup + convention checks
z.attach("AB12CD34", "paper.pdf")           # PDF -> WebDAV
z.fetch("AB12CD34", "downloads")
z.set_status("AB12CD34", "read")
z.backup()

z.z is the underlying pyzotero client for anything not wrapped.

Using zotkit with AI agents

zotkit is designed to be driven by coding agents (Claude Code and similar): dry-run defaults, code-enforced tag conventions, and a ready-made Claude Code skill in skills/zotkit/ — copy it to ~/.claude/skills/zotkit/ and any Claude session can search, file, and attach papers for you while respecting your taxonomy. (An MCP server is planned.)

Want to clean up a messy library, not just maintain one? The battle-tested method — taxonomy design, parallel read-only analysis, serial reviewed writes — is written up in docs/organizing-with-agents.md.

mkdir -p ~/.claude/skills && cp -r skills/zotkit ~/.claude/skills/

Safety model

  • create is dry-run by default; --apply to execute.
  • Writes go through fetch→modify→update (carries the item version, so concurrent edits fail loudly with 412 instead of clobbering), in batches of ≤ 50.
  • zotkit backup snapshots every item, collection, tag, and membership to one JSON file — run it before bulk operations.
  • Remember: writes propagate to zotero.org and all your synced devices.

How WebDAV attachments work

(With Zotero Storage, zotkit simply uses the Web API's official file endpoints — this section is about the WebDAV mode.) Zotero's WebDAV storage format is undocumented but simple: each attachment item K is stored as K.zip (the file, zipped) plus K.prop (its md5 + mtime). zotkit creates the attachment item via the Web API and PUTs both objects directly — after which every desktop client syncs the file down normally. Details in docs/webdav-format.md.

The format was determined by interoperability inspection of the author's own library. This project is not affiliated with or endorsed by Zotero.

Limits & roadmap

  • find currently lists the library client-side — instant for hundreds of items, sluggish for many thousands. Server-side search is planned.
  • Group libraries should work for item operations (untested); WebDAV file sync is personal-libraries-only (a Zotero limitation).
  • --doi/--arxiv import covers arXiv + CrossRef; DataCite-only DOIs and arbitrary-URL scraping (translation-server territory) are out of scope.
  • Planned: an MCP server wrapper, server-side search.

License

MIT. If you build on the WebDAV implementation, a link back is appreciated.

Download files

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

Source Distribution

zotkit-0.4.1.tar.gz (24.0 kB view details)

Uploaded Source

Built Distribution

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

zotkit-0.4.1-py3-none-any.whl (21.7 kB view details)

Uploaded Python 3

File details

Details for the file zotkit-0.4.1.tar.gz.

File metadata

  • Download URL: zotkit-0.4.1.tar.gz
  • Upload date:
  • Size: 24.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for zotkit-0.4.1.tar.gz
Algorithm Hash digest
SHA256 4e63585f84c999ea2933f665fc29ea22dc7bab30d4ed1d4baea9bd0c7657ce65
MD5 e746fc0628a91e17113d6cbff0fbbaff
BLAKE2b-256 ef646a9c5ca880031ad2b123e5afc38f20447abacebd2517832b6de28a9c5c82

See more details on using hashes here.

Provenance

The following attestation bundles were made for zotkit-0.4.1.tar.gz:

Publisher: publish.yml on oldantique/zotkit

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

File details

Details for the file zotkit-0.4.1-py3-none-any.whl.

File metadata

  • Download URL: zotkit-0.4.1-py3-none-any.whl
  • Upload date:
  • Size: 21.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for zotkit-0.4.1-py3-none-any.whl
Algorithm Hash digest
SHA256 330202a63dbcbca7f3098b11d4380e58eda75fce0f9f1d273ac64fdb2f54a275
MD5 a436cd05e0d2a64b920a82d61b39218b
BLAKE2b-256 1a9ab9be0be52d184df5e41188e5c3c9c4cb508c01b993ed73c09e093c40a47e

See more details on using hashes here.

Provenance

The following attestation bundles were made for zotkit-0.4.1-py3-none-any.whl:

Publisher: publish.yml on oldantique/zotkit

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

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page