← Back to Skills
Web Search

zotero

terwox By terwox 👁 25 views ▲ 0 votes

Manage Zotero reference libraries via the Web API.

GitHub
---
name: zotero
description: 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 BibTeX/RIS/CSL-JSON, batch-add from files, check PDF attachments, cross-reference citations, find missing DOIs via CrossRef, and fetch open-access PDFs. Supports --json output for scripting. Use when the user asks about academic references, citation management, literature libraries, PDFs for papers, bibliography export, or Zotero specifically.
metadata: {"clawdbot":{"emoji":"📚","requires":{"env":["ZOTERO_API_KEY","ZOTERO_USER_ID"]},"primaryEnv":"ZOTERO_API_KEY"}}
---

# Zotero 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).

```bash
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

```bash
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

```bash
# 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

```bash
python3 zotero.py export --format bibtex --output refs.bib
python3 zotero.py export --format csljson --collection COLLKEY
```

### Update tags/metadata

```bash
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

```bash
python3 zotero.py delete KEY1 KEY2 --yes           # Trash (recoverable, default)
python3 zotero.py delete KEY1 --permanent --yes    # Permanent delete
```

### Cross-reference citations

```bash
python3 zotero.py crossref my-paper.txt
```

Extracts `Author (Year)` patterns from text and matches against library.

### Find missing DOIs

```bash
# 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

```bash
# 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

```bash
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-pdfs` fetches all items; for large libraries (500+), this may be slow
- `fetch-pdfs` also processes all items — use `--collection` to scope for large libraries
- Rate limits are generous; batch-add includes 1s delay between items
- For common errors and troubleshooting, see [references/troubleshooting.md](references/troubleshooting.md)
web search

Comments

Sign in to leave a comment

Loading comments...