Skill Tracer
Skills › Scientific Agent Skills › pyzotero › authentication.md

authentication.md

131 lines · skills/pyzotero/references/authentication.md at 5f2bc03

Open on GitHub
1# Authentication & Setup
2
3> **Security:** Never hardcode API keys in source code or commit them to version control. Use environment variables or a `.env` file scoped to `ZOTERO_*` keys only. Placeholder values like `ABC1234XYZ` below are illustrative — substitute your real credentials from env vars.
4

Authentication & Setup

Security: Never hardcode API keys in source code or commit them to version control. Use environment variables or a .env file scoped to ZOTERO_* keys only. Placeholder values like ABC1234XYZ below are illustrative — substitute your real credentials from env vars.
Raw lines 1 to 4
5## Credentials
6
7Obtain from https://www.zotero.org/settings/keys (or create a key at https://www.zotero.org/settings/keys/new):
8
9| Credential | Where to Find |
10|-----------|---------------|
11| **User ID** | "Your userID for use in API calls" section |
12| **API Key** | Create new key at /settings/keys/new, or via Settings → Security → Applications → "Create new key" at https://www.zotero.org/settings/security |
13| **Group Library ID** | Integer after `/groups/` in group URL (e.g. `https://www.zotero.org/groups/169947`) |

Credentials

Obtain from https://www.zotero.org/settings/keys (or create a key at https://www.zotero.org/settings/keys/new):

CredentialWhere to Find
User ID"Your userID for use in API calls" section
API KeyCreate new key at /settings/keys/new, or via Settings → Security → Applications → "Create new key" at https://www.zotero.org/settings/security
Group Library IDInteger after /groups/ in group URL (e.g. https://www.zotero.org/groups/169947)
Raw lines 5 to 14
15## Environment Variables (recommended)
17Store in `.env` or export in shell:
19```
20ZOTERO_LIBRARY_ID=436
21ZOTERO_API_KEY=your_api_key_here
22ZOTERO_LIBRARY_TYPE=user
23```
25For `.env` loading, install `python-dotenv` (`uv add python-dotenv`). It does not come with Pyzotero. Load in Python:
27```python
28import os
29from dotenv import load_dotenv
30from pyzotero import Zotero
32load_dotenv()
34zot = Zotero(
35 library_id=os.environ['ZOTERO_LIBRARY_ID'],
36 library_type=os.environ.get('ZOTERO_LIBRARY_TYPE', 'user'),
37 api_key=os.environ['ZOTERO_API_KEY'],
39```

Environment Variables (recommended)

Store in .env or export in shell:

ZOTERO_LIBRARY_ID=436
ZOTERO_API_KEY=your_api_key_here
ZOTERO_LIBRARY_TYPE=user

For .env loading, install python-dotenv (uv add python-dotenv). It does not come with Pyzotero. Load in Python:

import os
from dotenv import load_dotenv
from pyzotero import Zotero

load_dotenv()

zot = Zotero(
    library_id=os.environ['ZOTERO_LIBRARY_ID'],
    library_type=os.environ.get('ZOTERO_LIBRARY_TYPE', 'user'),
    api_key=os.environ['ZOTERO_API_KEY'],
)
Raw lines 15 to 40
41## Library Types
43```python
44import os
46# Personal library
47zot = Zotero(
48 os.environ['ZOTERO_LIBRARY_ID'],
49 'user',
50 os.environ['ZOTERO_API_KEY'],
53# Group library — use the group ID as library_id
54zot = Zotero('169947', 'group', os.environ['ZOTERO_API_KEY'])
55```
57**Important**: A `Zotero` instance is bound to a single library. To access multiple libraries, create multiple instances.

Library Types

import os

# Personal library
zot = Zotero(
    os.environ['ZOTERO_LIBRARY_ID'],
    'user',
    os.environ['ZOTERO_API_KEY'],
)

# Group library — use the group ID as library_id
zot = Zotero('169947', 'group', os.environ['ZOTERO_API_KEY'])

Important: A Zotero instance is bound to a single library. To access multiple libraries, create multiple instances.

Raw lines 41 to 58
59## Local Mode
61Zotero 7+ supports unauthenticated local reads at `http://127.0.0.1:23119/api`. User ID `0` selects the local personal library, including an unsynced library. Group libraries still use their numeric group ID.
63```python
64zot = Zotero(library_id='0', library_type='user', local=True)
65items = zot.items(limit=10) # reads from local Zotero
66```
68Enable local API access: Settings → Advanced → "Allow other applications on this computer to communicate with Zotero". See [cli.md](cli.md) and [mcp.md](mcp.md) for richer local access.

Local Mode

Zotero 7+ supports unauthenticated local reads at http://127.0.0.1:23119/api. User ID 0 selects the local personal library, including an unsynced library. Group libraries still use their numeric group ID.

zot = Zotero(library_id='0', library_type='user', local=True)
items = zot.items(limit=10)  # reads from local Zotero

Enable local API access: Settings → Advanced → "Allow other applications on this computer to communicate with Zotero". See cli.md and mcp.md for richer local access.

Raw lines 59 to 69
70## Optional Parameters
72```python
73zot = Zotero(
74 library_id=os.environ['ZOTERO_LIBRARY_ID'],
75 library_type='user',
76 api_key=os.environ['ZOTERO_API_KEY'],
77 locale='en-US', # localise field names (e.g. 'fr-FR' for French)
79```

Optional Parameters

zot = Zotero(
    library_id=os.environ['ZOTERO_LIBRARY_ID'],
    library_type='user',
    api_key=os.environ['ZOTERO_API_KEY'],
    locale='en-US',             # localise field names (e.g. 'fr-FR' for French)
)
Raw lines 70 to 80
81## Key Permissions
83Check what the current API key can access:
85```python
86info = zot.key_info()
87# Returns dict with user info and group access permissions
88```
90Check accessible groups:
92```python
93# Call on a USER client: groups() builds /users/{library_id}/groups.
94groups = zot.everything(zot.groups())
95# Includes accessible groups and public groups the key owner belongs to.
96```

Key Permissions

Check what the current API key can access:

info = zot.key_info()
# Returns dict with user info and group access permissions

Check accessible groups:

# Call on a USER client: groups() builds /users/{library_id}/groups.
groups = zot.everything(zot.groups())
# Includes accessible groups and public groups the key owner belongs to.
Raw lines 81 to 97
98## API Key Scopes
100When creating an API key at https://www.zotero.org/settings/keys/new, choose appropriate permissions:
102- **Read Only**: For retrieving items and collections
103- **Write Access**: For creating, updating, and deleting items
104- **Notes Access**: To include notes in read/write operations
105- **Files Access**: Required for private stored-file access; uploads also need write access.
107Public library reads can omit `api_key`. Pyzotero sends `Zotero-API-Version: 3` and uses bearer authentication for a supplied Web API key. `key_info()` uses `/keys/{key}` internally in 1.15.2, so avoid logging request URLs or exception bodies from that call. The API also supports `/keys/current` with header authentication.
109`preserve_json_order` is deprecated; ordinary Python dictionaries retain insertion order.

API Key Scopes

When creating an API key at https://www.zotero.org/settings/keys/new, choose appropriate permissions:

  • Read Only: For retrieving items and collections
  • Write Access: For creating, updating, and deleting items
  • Notes Access: To include notes in read/write operations
  • Files Access: Required for private stored-file access; uploads also need write access.

Public library reads can omit api_key. Pyzotero sends Zotero-API-Version: 3 and uses bearer authentication for a supplied Web API key. key_info() uses /keys/{key} internally in 1.15.2, so avoid logging request URLs or exception bodies from that call. The API also supports /keys/current with header authentication.

preserve_json_order is deprecated; ordinary Python dictionaries retain insertion order.

Raw lines 98 to 110
111## Local Writes (Zotero 10+)
113Local keys are distinct from zotero.org keys. For an authorized write workflow:
115```python
116zot = Zotero('0', 'user', local=True)
117auth = zot.authorize_local('Research library tool') # Zotero displays its permission dialog
118item = {
119 'itemType': 'journalArticle', 'title': 'Example reference',
120 'creators': [], 'tags': [], 'collections': [], 'relations': {},
122result = zot.create_items([item])
123if result.get('failed'):
124 raise RuntimeError(result['failed'])
125```
127“Allow” yields a single-use key; “Always Allow” yields a reusable key (`auth['remember']`). Persist a reusable key securely together with `zot.server_id`, then pass `local_api_key` and `server_id` on the next run. Pyzotero discovers and sends `Zotero-Server-ID` before writes. A mismatch requires discarding that server's cached objects and versions. Local versions must never be reused for Web API writes or another local database. Do not loop on authorization dialogs.
129The local API lacks `/items/new`: `item_template()`, `attachment_simple()`, and `attachment_both()` are unavailable. Build data using `item_types()` / `item_type_fields()`; for files use `upload_attachments()`. The local API also lacks Atom, so use `format='json'` with `include`, not Pyzotero's `content` exports.
131Sources: [local API](https://www.zotero.org/support/dev/web_api/v3/local_api), [Pyzotero local guidance](https://pyzotero.readthedocs.io/en/latest/#the-local-zotero-api).

Local Writes (Zotero 10+)

Local keys are distinct from zotero.org keys. For an authorized write workflow:

zot = Zotero('0', 'user', local=True)
auth = zot.authorize_local('Research library tool')  # Zotero displays its permission dialog
item = {
    'itemType': 'journalArticle', 'title': 'Example reference',
    'creators': [], 'tags': [], 'collections': [], 'relations': {},
}
result = zot.create_items([item])
if result.get('failed'):
    raise RuntimeError(result['failed'])

“Allow” yields a single-use key; “Always Allow” yields a reusable key (auth['remember']). Persist a reusable key securely together with zot.server_id, then pass local_api_key and server_id on the next run. Pyzotero discovers and sends Zotero-Server-ID before writes. A mismatch requires discarding that server's cached objects and versions. Local versions must never be reused for Web API writes or another local database. Do not loop on authorization dialogs.

The local API lacks /items/new: item_template(), attachment_simple(), and attachment_both() are unavailable. Build data using item_types() / item_type_fields(); for files use upload_attachments(). The local API also lacks Atom, so use format='json' with include, not Pyzotero's content exports.

Sources: local API, Pyzotero local guidance.

Raw lines 111 to 131