Skills › Scientific Agent Skills › pyzotero › write-api.md
write-api.md
145 lines · skills/pyzotero/references/write-api.md at 5f2bc03
Write API Methods
Raw lines 1 to 23## Creating Items
5For the remote API, use `item_template()` to get a valid template. The local API lacks `/items/new`; build data from the type/field endpoints instead (see [authentication.md](authentication.md)). All write examples below are illustrative and must use an authorized target library.
7```python
8# Get a template for a specific item type
9template = zot.item_template('journalArticle')
11# Fill in fields
12template['title'] = 'Example article (illustrative metadata)'
13template['date'] = '2024'
14# Populate DOI, title, journal, and authors together from a verified source;
15# never pair a real DOI with fabricated bibliographic metadata.
16template['creators'] = [
17 {'creatorType': 'author', 'firstName': 'Jane', 'lastName': 'Doe'},
18 {'creatorType': 'author', 'firstName': 'John', 'lastName': 'Smith'},
19]
21# Validate fields before creating (raises InvalidItemFieldsError if invalid)
22zot.check_items([template])
24# Create the item
25resp = zot.create_items([template])
26# Response maps are indexed by each input's string index: '0', '1', ...
27if resp.get('failed'):
28 raise RuntimeError(f"Creation failures: {resp['failed']}")
29new_key = resp['successful']['0']['key']
30# successful holds saved objects; legacy success maps indices to keys.
31# unchanged maps indices to existing keys when no change was needed.
32```
Creating Items
For the remote API, use item_template() to get a valid template. The local API lacks /items/new; build data from the type/field endpoints instead (see authentication.md). All write examples below are illustrative and must use an authorized target library.
# Get a template for a specific item type
template = zot.item_template('journalArticle')
# Fill in fields
template['title'] = 'Example article (illustrative metadata)'
template['date'] = '2024'
# Populate DOI, title, journal, and authors together from a verified source;
# never pair a real DOI with fabricated bibliographic metadata.
template['creators'] = [
{'creatorType': 'author', 'firstName': 'Jane', 'lastName': 'Doe'},
{'creatorType': 'author', 'firstName': 'John', 'lastName': 'Smith'},
]
# Validate fields before creating (raises InvalidItemFieldsError if invalid)
zot.check_items([template])
# Create the item
resp = zot.create_items([template])
# Response maps are indexed by each input's string index: '0', '1', ...
if resp.get('failed'):
raise RuntimeError(f"Creation failures: {resp['failed']}")
new_key = resp['successful']['0']['key']
# successful holds saved objects; legacy success maps indices to keys.
# unchanged maps indices to existing keys when no change was needed.Raw lines 3 to 3334### Create Multiple Items at Once
36```python
37templates = []
38for data in paper_data_list:
39 t = zot.item_template('journalArticle')
40 t['title'] = data['title']
41 t['DOI'] = data['doi']
42 templates.append(t)
44for start in range(0, len(templates), 50):
45 resp = zot.create_items(templates[start:start + 50])
46 if resp.get('failed'):
47 raise RuntimeError(f"Batch starting at {start}: {resp['failed']}")
48```
Create Multiple Items at Once
templates = []
for data in paper_data_list:
t = zot.item_template('journalArticle')
t['title'] = data['title']
t['DOI'] = data['doi']
templates.append(t)
for start in range(0, len(templates), 50):
resp = zot.create_items(templates[start:start + 50])
if resp.get('failed'):
raise RuntimeError(f"Batch starting at {start}: {resp['failed']}")Raw lines 34 to 4950### Create Child Items
52```python
53# Create a note as a child of an existing item
54note_template = zot.item_template('note')
55note_template['note'] = '<p>My annotation here</p>'
56zot.create_items([note_template], parentid='PARENTKEY')
57```
Create Child Items
# Create a note as a child of an existing item
note_template = zot.item_template('note')
note_template['note'] = '<p>My annotation here</p>'
zot.create_items([note_template], parentid='PARENTKEY')Raw lines 50 to 5859## Updating Items
61```python
62# Retrieve, modify, update
63item = zot.item('ITEMKEY')
64item['data']['title'] = 'Updated Title'
65item['data']['abstractNote'] = 'New abstract text.'
66success = zot.update_item(item) # returns True or raises error
68# Update many existing items while retaining per-item status
69items = zot.items(itemType='journalArticle', limit=10)
70for item in items:
71 item['data']['extra'] = item['data'].get('extra', '') + '\nProcessed'
72for start in range(0, len(items), 50):
73 result = zot.create_items([i['data'] for i in items[start:start + 50]])
74 if result.get('failed'):
75 raise RuntimeError(result['failed'])
76```
Updating Items
# Retrieve, modify, update
item = zot.item('ITEMKEY')
item['data']['title'] = 'Updated Title'
item['data']['abstractNote'] = 'New abstract text.'
success = zot.update_item(item) # returns True or raises error
# Update many existing items while retaining per-item status
items = zot.items(itemType='journalArticle', limit=10)
for item in items:
item['data']['extra'] = item['data'].get('extra', '') + '\nProcessed'
for start in range(0, len(items), 50):
result = zot.create_items([i['data'] for i in items[start:start + 50]])
if result.get('failed'):
raise RuntimeError(result['failed'])Raw lines 59 to 7778## Deleting Items
80```python
81# Permanent deletion of one item: use its object version
82item = zot.item('ITEMKEY')
83zot.delete_item(item)
85# Multiple deletion uses a LIBRARY version, not the first item's version.
86# Review the exact selected keys first; at most 50 per request.
87items = zot.items(tag='to-delete', limit=50)
88library_version = int(zot.request.headers['Last-Modified-Version'])
89if items:
90 zot.delete_item(items, last_modified=library_version)
92# To trash instead of permanently delete:
93item = zot.item('ITEMKEY')
94item['data']['deleted'] = 1 # use 0 to restore
95zot.update_item(item['data'])
96```
Deleting Items
# Permanent deletion of one item: use its object version
item = zot.item('ITEMKEY')
zot.delete_item(item)
# Multiple deletion uses a LIBRARY version, not the first item's version.
# Review the exact selected keys first; at most 50 per request.
items = zot.items(tag='to-delete', limit=50)
library_version = int(zot.request.headers['Last-Modified-Version'])
if items:
zot.delete_item(items, last_modified=library_version)
# To trash instead of permanently delete:
item = zot.item('ITEMKEY')
item['data']['deleted'] = 1 # use 0 to restore
zot.update_item(item['data'])Raw lines 78 to 9798## Item Types and Fields
100```python
101# All available item types
102item_types = zot.item_types()
103# [{'itemType': 'artwork', 'localized': 'Artwork'}, ...]
105# All available fields
106fields = zot.item_fields()
108# Valid fields for a specific item type
109journal_fields = zot.item_type_fields('journalArticle')
111# Valid creator types for an item type
112creator_types = zot.item_creator_types('journalArticle')
113# [{'creatorType': 'author', 'localized': 'Author'}, ...]
115# All localised creator field names
116creator_fields = zot.creator_fields()
118# Attachment link modes (needed for attachment templates)
119link_modes = zot.item_attachment_link_modes()
121# Template for an attachment
122attach_template = zot.item_template('attachment', linkmode='imported_file')
123```
Item Types and Fields
# All available item types
item_types = zot.item_types()
# [{'itemType': 'artwork', 'localized': 'Artwork'}, ...]
# All available fields
fields = zot.item_fields()
# Valid fields for a specific item type
journal_fields = zot.item_type_fields('journalArticle')
# Valid creator types for an item type
creator_types = zot.item_creator_types('journalArticle')
# [{'creatorType': 'author', 'localized': 'Author'}, ...]
# All localised creator field names
creator_fields = zot.creator_fields()
# Attachment link modes (needed for attachment templates)
link_modes = zot.item_attachment_link_modes()
# Template for an attachment
attach_template = zot.item_template('attachment', linkmode='imported_file')Raw lines 98 to 124125## Optimistic Locking
127Single-item `last_modified` is the **item version**. The normal read/modify/write example already sends it; an explicit override must come from that same item's read:
129```python
130# Only update if this item's version still matches
131zot.update_item(item, last_modified=item['version'])
132# Raises PreConditionFailedError on a stale version.
133```
Optimistic Locking
Single-item last_modified is the item version. The normal read/modify/write example already sends it; an explicit override must come from that same item's read:
# Only update if this item's version still matches
zot.update_item(item, last_modified=item['version'])
# Raises PreConditionFailedError on a stale version.Raw lines 125 to 134135## Notes
137- `create_items()` accepts up to 50 items per call; batch if needed.
138- `update_items()` auto-chunks at 50 but returns a coarse Boolean without checking each `failed` mapping in 1.15.2. Use `create_items()` with keyed, versioned data in explicit batches when every result must be reconciled.
139- If a dict passed to `create_items()` contains a `key` matching an existing item, it will be updated rather than created.
140- `check_items()` checks field names against the global field set, not all type-specific values or scientific metadata. Server failures remain possible.
141- Item `PATCH` leaves omitted fields unchanged, but supplied arrays replace entire creator/tag/collection lists. Re-fetch after writes before further edits.
142- A timeout does not establish whether a write happened. Pyzotero generates a new write token on another `create_items()` call, so blindly repeating an unkeyed create can duplicate records. Reconcile first; retry only verified failures.
143- Batch POST preconditions use the library version when supplied; per-object `version` values are usually simpler. HTTP 200 can still contain failed entries.
145Sources: [write request/response contracts](https://www.zotero.org/support/dev/web_api/v3/write_requests), [Pyzotero 1.15.2 source](https://github.com/urschrei/pyzotero/blob/v1.15.2/src/pyzotero/_client.py).
Notes
create_items()accepts up to 50 items per call; batch if needed.update_items()auto-chunks at 50 but returns a coarse Boolean without checking eachfailedmapping in 1.15.2. Usecreate_items()with keyed, versioned data in explicit batches when every result must be reconciled.- If a dict passed to
create_items()contains akeymatching an existing item, it will be updated rather than created. check_items()checks field names against the global field set, not all type-specific values or scientific metadata. Server failures remain possible.- Item
PATCHleaves omitted fields unchanged, but supplied arrays replace entire creator/tag/collection lists. Re-fetch after writes before further edits. - A timeout does not establish whether a write happened. Pyzotero generates a new write token on another
create_items()call, so blindly repeating an unkeyed create can duplicate records. Reconcile first; retry only verified failures. - Batch POST preconditions use the library version when supplied; per-object
versionvalues are usually simpler. HTTP 200 can still contain failed entries.
Sources: write request/response contracts, Pyzotero 1.15.2 source.
Raw lines 135 to 145