Zotero
Manage Zotero reference libraries via the Web API. Search, list, add items by DOI/ISBN/PMID (with duplicate detection), delete/trash items, update metadata and tags, export in BibT…
Terwox
@terwox
What This Skill Does
Command-line tool to manage Zotero reference libraries via the REST API. Search, add items by DOI/ISBN/PMID with duplicate detection, update metadata and tags, export in BibTeX/RIS/CSL-JSON, batch-add from files, check PDF attachments, cross-reference citations, find missing DOIs via CrossRef, and fetch open-access PDFs.
Replaces manual library management in Zotero's GUI by automating bulk operations, duplicate detection, bibliography export, and PDF retrieval through a scriptable CLI.
When to Use It
- Add a research paper to your Zotero library by DOI with automatic duplicate checking
- Export your entire library as BibTeX for use in LaTeX documents
- Batch-import multiple papers from a list of DOIs or ISBNs
- Check which items in your library are missing PDF attachments
- Find and add missing DOIs to items in your Zotero collection
- Cross-reference a bibliography file against your library to identify missing citations
Install
$ openclaw skills install @terwox/zoteroZotero Skill
Interact with Zotero personal or group libraries via the REST API v3.
Setup
Requires two environment variables:
ZOTERO_API_KEY — Create at https://www.zotero.org/settings/keys/new
ZOTERO_USER_ID — Found on the same page (numeric, not username)
For group libraries, set ZOTERO_GROUP_ID instead of ZOTERO_USER_ID.
Optional env var for CrossRef/Unpaywall polite pool (improves DOI lookup success rate):
CROSSREF_EMAIL — Your email (optional; uses fallback if unset)
If credentials are missing, tell the user what's needed and link them to the key creation page.
CLI Script
All operations use scripts/zotero.py (Python 3, zero external dependencies).
python3 scripts/zotero.py <command> [options]
Commands
| Command | Description | Example |
|---|---|---|
items | List top-level items | zotero.py items --limit 50 |
search | Search by query | zotero.py search "cognitive load" |
get | Full item details + attachments | zotero.py get ITEMKEY |
collections | List all collections | zotero.py collections |
tags | List all tags | zotero.py tags |
children | List attachments/notes for item | zotero.py children ITEMKEY |
add-doi | Add item by DOI (dedup enabled) | zotero.py add-doi 10.1234/example |
add-isbn | Add item by ISBN (dedup enabled) | zotero.py add-isbn 978-0-123456-78-9 |
add-pmid | Add item by PubMed ID | zotero.py add-pmid 12345678 |
delete | Move items to trash (recoverable by default) | zotero.py delete KEY1 KEY2 --yes |
update | Modify item metadata/tags | zotero.py update KEY --add-tags "new" |
export | Export as BibTeX/RIS/CSL-JSON | zotero.py export --format bibtex |
batch-add | Add multiple items from file | zotero.py batch-add dois.txt --type doi |
check-pdfs | Report which items have/lack PDFs | zotero.py check-pdfs |
crossref | Match citations vs library | zotero.py crossref bibliography.txt |
find-dois | Find & add missing DOIs via CrossRef | zotero.py find-dois --limit 10 |
fetch-pdfs | Fetch open-access PDFs for items | zotero.py fetch-pdfs --dry-run |
Global Flags
--json— JSON output instead of human-readable (works with items, search, get)
Common Options
--limit N— Max items to return (default 25)--sort FIELD— Sort by dateModified, title, creator, date--direction asc|desc— Sort direction--collection KEY— Filter by or add to collection--type TYPE— Filter by item type (journalArticle, book, conferencePaper, etc.)--tags "tag1,tag2"— Add tags when creating items--force— Skip duplicate detection on add commands
Workflows
Add a paper by DOI
python3 zotero.py add-doi "10.1093/jamia/ocaa037" --tags "review"
# Warns if already in library. Use --force to override.
Duplicate detection: translates DOI to metadata, searches library by first author, compares DOI fields.
Bulk add from a file
# One identifier per line, # for comments
python3 zotero.py batch-add dois.txt --type doi --tags "imported"
Skips duplicates. Reports summary: added/skipped/failed.
Export bibliography
python3 zotero.py export --format bibtex --output refs.bib
python3 zotero.py export --format csljson --collection COLLKEY
Update tags/metadata
python3 zotero.py update ITEMKEY --add-tags "important" --remove-tags "unread"
python3 zotero.py update ITEMKEY --title "Corrected Title" --date "2024"
python3 zotero.py update ITEMKEY --doi "10.1234/example"
python3 zotero.py update ITEMKEY --url "https://example.com/paper"
python3 zotero.py update ITEMKEY --add-collection COLLKEY
Delete items
python3 zotero.py delete KEY1 KEY2 --yes # Trash (recoverable, default)
python3 zotero.py delete KEY1 --permanent --yes # Permanent delete
Cross-reference citations
python3 zotero.py crossref my-paper.txt
Extracts Author (Year) patterns from text and matches against library.
Find missing DOIs
# Dry run (default) — show matches without writing anything
python3 zotero.py find-dois --limit 20
# Actually write DOIs to Zotero
python3 zotero.py find-dois --apply
# Filter by collection
python3 zotero.py find-dois --collection COLLKEY --apply
Scans journalArticle and conferencePaper items missing DOIs, queries CrossRef, and matches
by title similarity (>85%), exact year, and first author last name. Dry run by default — use
--apply to write. Only patches the DOI field; never touches other metadata. 1s delay between
CrossRef requests (polite pool with mailto).
Fetch open-access PDFs
# Dry run — show which PDFs are available and from where
python3 zotero.py fetch-pdfs --dry-run --limit 10
# Fetch and attach as linked URLs (no storage quota used)
python3 zotero.py fetch-pdfs --limit 20
# Also save PDFs locally
python3 zotero.py fetch-pdfs --download-dir ./pdfs
# Upload to Zotero storage instead of linked URL
python3 zotero.py fetch-pdfs --upload --limit 10
# Only try specific sources
python3 zotero.py fetch-pdfs --sources unpaywall,semanticscholar
Tries three legal OA sources in order: Unpaywall → Semantic Scholar → DOI content negotiation.
By default creates linked URL attachments (no Zotero storage quota needed). Use --upload for
full S3 upload to Zotero storage. Use --download-dir to also save PDFs locally.
Sources: unpaywall, semanticscholar, doi (default: all three)
Rate limits: 1s between Unpaywall/Semantic Scholar requests, 2s between DOI requests.
Scripting with JSON
python3 zotero.py --json items --limit 100 | jq '.items[].DOI'
python3 zotero.py --json get ITEMKEY | jq '.title'
Notes
- Zero dependencies — Python 3 stdlib only (urllib, json, argparse)
- Write operations require an API key with write permissions
- If Zotero translation server is down (503), DOI lookups fall back to CrossRef
- Input validation: DOIs must be
10.xxxx/...format. Item keys are 8-char alphanumeric (e.g.,VNPN6FHT). ISBNs must be valid checksums. check-pdfsfetches all items; for large libraries (500+), this may be slowfetch-pdfsalso processes all items — use--collectionto scope for large libraries- Rate limits are generous; batch-add includes 1s delay between items
- For common errors and troubleshooting, see references/troubleshooting.md
Top skills in this category
Agent Browser
@matrixyHeadless browser automation CLI optimized for AI agents with accessibility tree snapshots and ref-based element selection
Browser Use
@shawnpanaAutomates browser interactions for web testing, form filling, screenshots, and data extraction. Use when the user needs to navigate websites, interact with w...
Playwright MCP
@spiceman161Browser automation via Playwright MCP server. Navigate websites, click elements, fill forms, extract data, take screenshots, and perform full browser automation workflows.
Playwright (Automation + MCP + Scraper)
@ivangdavilaAutomates, tests, and debugs browsers with Playwright: locators, auto-waiting, traces, CI runs, and MCP browser control. Use when a test is flaky, times out, or fails only in CI or headless; when a locator matches multiple elements or the wrong one (strict mode violation); when clicks need force, waits become sleeps, or networkidle never settles; for storageState and login setup, request mocking and HAR replay, uploads and downloads, iframes and shadow DOM, popups and dialogs, screenshot diffs that change per machine, trace and report artifacts, sharding a slow suite, device and permission emulation, accessibility checks, driving a real browser through Playwright MCP, extracting data from JS-rendered pages, or porting a Cypress, Puppeteer, or Selenium suite to Playwright. Not for maintaining an existing Cypress or Puppeteer suite (cypress, puppeteer) or for work a plain HTTP request answers (http).
Browser Automation
@peytoncasperAutomate web browser interactions using natural language via CLI commands. Use when the user asks to browse websites, navigate web pages, extract data from websites, take screenshots, fill forms, click buttons, or interact with web applications.