Skill Tracer
Skills › Scientific Agent Skills › pyzotero › write-api.md

write-api.md

145 lines · skills/pyzotero/references/write-api.md at 5f2bc03

Open on GitHub
1# Write API Methods
2

Write API Methods

Raw lines 1 to 2
3## Creating Items
4
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.
6
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'},
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 33
34### 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 49
50### 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 58
59## 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 77
78## 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 97
98## 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 124
125## 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 134
135## 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 each failed mapping in 1.15.2. Use create_items() with keyed, versioned data in explicit batches when every result must be reconciled.
  • If a dict passed to create_items() contains a key matching 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 PATCH leaves 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 version values 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