Interface to the 'Zotero' reference manager < https://www.zotero.org> through its public web API and local client database. Provides paginated reads, versioned writes, read-only local queries, batch plans and resumable ledgers, bibliographic comparison, metadata mapping, file attachment, and optional semantic-index queries.
zot provides an R interface to the Zotero Web API, read-only queries over a
local Zotero database, batch-plan execution, bibliographic comparison, and
selected external integrations.
The public documentation is available at https://averriK.github.io/zot/.
Install the released package from CRAN:
install.packages("zot")
zotConfig() accepts a Zotero user identifier and API key explicitly:
library(zot)
Config <- zotConfig(
userID = "42",
key = "example-key"
)
With no arguments, it reads ZOTERO_LIBRARY_ID and ZOTERO_API_KEY from the
process environment:
Config <- zotConfig()
A non-interactive caller reads no shell profile, so an Rscript job started
by cron, make, or an external tool takes both variables from
~/.Renviron, the file R reads at startup in every session.
Printing the object omits the API key. The default library = "user" selects
the configured personal library; numeric group identifiers are passed through
the library argument of the relevant functions.
This explicit configuration interface is suitable for interactive sessions, scripts, automated jobs, and agent-driven workflows. The package does not select an authorization policy for its callers.
The package exposes three read paths:
| Source | Entry points | Characteristics |
|---|---|---|
| Local Zotero database | zotLocalItems(), zotLocalQuery() |
Read-only SQLite queries; may lag remote state |
| Semantic index | ztSemantic() |
Optional query through the companion Python environment |
| Zotero Web API | zotGet(), zotGetAll(), ztCensus() |
Current remote records and response metadata |
For example, a local query does not modify the Zotero database:
Items <- zotLocalItems(library = "user")
Items[, .(key, itemType, title)]
A paginated API request uses the configured user library:
Path <- paste0(zotLibraryPath(Config), "/items/top")
Items <- zotGetAll(Config, path = Path, query = list(format = "json"))
Plan builders return data.table objects without executing the represented
changes. This example normalizes copy suffixes and file extensions in titles:
library(data.table)
Items <- data.table(
itemKey = c("A", "B"),
library = "user",
title = c("Reviewed book (1).pdf", "Existing title")
)
Plan <- ztRetitle(Items, transform = ztCleanTitle)
Preview <- zotPreview(Plan, describe = "Normalize reviewed titles")
Preview
Other functions build collection-membership plans, compare possible duplicate records, map Crossref metadata, and divide cross-library moves into create and Trash phases.
zotPreview() validates a plan, copies it, and records its digest.
zotApprove() creates a record containing that digest and optional notes.
zotExecute() requires matching preview and approval digests, applies rows,
and writes a resumable ledger. zotVerify() samples completed patch rows and
compares their requested fields with current API data.
Approval <- zotApprove(Preview, notes = "Reviewed batch")
Ledger <- zotExecute(
Config,
preview = Preview,
approval = Approval,
ledgerPath = "zot-ledger.json"
)
zotVerify(Config, preview = Preview, ledger = Ledger)
An approval object is an application record, not an authentication mechanism.
Callers should treat a preview as immutable after approval; the current
executor compares stored digests and does not recalculate the digest from a
modified preview$plan.
The package also exports functions with immediate external effects, including
zotPost(), zotPatch(), zotTrash(), ztCollectionTree(), and
ztAttach(). Callers are responsible for deciding when those operations are
appropriate.
ztCrossref() maps DOI metadata into a Zotero item payload. ztAttach()
implements Zotero's file-upload protocol and verifies the stored MD5 after an
upload. ztSemantic() queries an optional Chroma index produced by the
companion zotero-mcp-server installation; it does not create or refresh that
index.
See vignette("zot", package = "zot") for an introductory workflow,
vignette("guarded-workflow", package = "zot") for an offline batch example,
and vignette("external-integrations", package = "zot") for dependency and
effect details.