Use whenever the user mentions Zenodo, depositing or publishing research artifacts (datasets, software, papers, posters) to Zenodo, minting a DOI for a dataset/code release, uploading files to a Zenodo record, creating a new version of a Zenodo deposit, or searching Zenodo records. Covers the full Zenodo REST API workflow — create deposition, upload files via the bucket API, set metadata, publish, version, and search — for both production (zenodo.org) and sandbox (sandbox.zenodo.org).
Interact with the Zenodo REST API to deposit, publish, version, and search research artifacts. Zenodo issues a citable DOI for every published record.
Two environments — pick one and stick with it during a session:
| Env | Base URL | Token page |
|---|---|---|
| Production | https://zenodo.org/api | https://zenodo.org/account/settings/applications/tokens/new/ |
| Sandbox (testing) | https://sandbox.zenodo.org/api | https://sandbox.zenodo.org/account/settings/applications/tokens/new/ |
Sandbox accounts/tokens are separate from production. Always test new workflows in sandbox first — published production records cannot be deleted.
Required token scopes: deposit:write and deposit:actions.
Export the token before running commands:
export ZENODO_TOKEN=... # never inline the token in commands you show the user
export ZENODO_BASE=https://sandbox.zenodo.org/api # or https://zenodo.org/api
If ZENODO_TOKEN is unset, ask the user for it (and which environment) before proceeding.
The deposit lifecycle is create → upload files → set metadata → publish. Each step is a separate API call; do them in order.
curl -sS -X POST "$ZENODO_BASE/deposit/depositions" \
-H "Authorization: Bearer $ZENODO_TOKEN" \
-H "Content-Type: application/json" \
-d '{}'
The response JSON contains two things you need to remember:
id — the deposition id, used for metadata/publish/version actionslinks.bucket — the bucket URL for file uploads (new files API)Capture them, e.g. with jq:
RESP=$(curl -sS -X POST "$ZENODO_BASE/deposit/depositions" \
-H "Authorization: Bearer $ZENODO_TOKEN" -H "Content-Type: application/json" -d '{}')
DEPOSIT_ID=$(echo "$RESP" | jq -r .id)
BUCKET=$(echo "$RESP" | jq -r .links.bucket)
The bucket API supports up to 50 GB total / 100 files per record. Use --upload-file (HTTP PUT) — not multipart form upload. The filename in the URL is what shows up on the record.
curl -sS --upload-file ./data.csv \
-H "Authorization: Bearer $ZENODO_TOKEN" \
"$BUCKET/data.csv"
Repeat per file. For many files, loop in shell. The legacy /files multipart endpoint is capped at 100 MB/file — avoid it.
Metadata goes via PUT on the deposition. Required fields: upload_type, title, creators, description. See references/metadata.md for the full schema, allowed upload_type values, license codes, and conditional fields (e.g. publication_type, embargo_date).
Minimal example:
curl -sS -X PUT "$ZENODO_BASE/deposit/depositions/$DEPOSIT_ID" \
-H "Authorization: Bearer $ZENODO_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"metadata": {
"title": "My dataset",
"upload_type": "dataset",
"description": "<p>A short description (HTML allowed).</p>",
"creators": [{"name": "Doe, Jane", "affiliation": "Example Univ.", "orcid": "0000-0002-1825-0097"}]
}
}'
Read the response — Zenodo validates here and returns 400 with field-level errors if anything is missing or malformed. Fix and retry before publishing.
Publishing is irreversible on production (you can edit metadata later but cannot remove files or delete the record). Confirm with the user before this step unless they're on sandbox.
curl -sS -X POST "$ZENODO_BASE/deposit/depositions/$DEPOSIT_ID/actions/publish" \
-H "Authorization: Bearer $ZENODO_TOKEN"
The response contains doi and links.record_html — show both to the user.
Use this when the user has previously published and wants to release updated data/code under the same concept-DOI.
# 1. Create new version draft (returns links.latest_draft)
curl -sS -X POST "$ZENODO_BASE/deposit/depositions/$DEPOSIT_ID/actions/newversion" \
-H "Authorization: Bearer $ZENODO_TOKEN"
Then follow the bucket of the new draft (from links.latest_draft → GET it → use its links.bucket) to upload changed files, update metadata, and publish as in steps 2–4 above. The new version inherits files from the previous version by default — delete any you want to replace via DELETE $BUCKET/<filename>.
curl -sS -X POST "$ZENODO_BASE/deposit/depositions/$DEPOSIT_ID/actions/discard" \
-H "Authorization: Bearer $ZENODO_TOKEN"
No token needed for public search.
curl -sS "$ZENODO_BASE/records?q=climate+model&size=10&sort=mostrecent"
Query syntax is Elasticsearch — fielded queries like creators.name:"Doe, Jane", communities:zenodo, resource_type.type:dataset all work. See references/search.md for query patterns.
X-RateLimit-Remaining.$ZENODO_TOKEN. Don't write the token to files.actions/publish on production.--progress-bar and warn the user about time/bandwidth.references/metadata.md — full metadata schema, upload_type values, license codes, conditional fieldsreferences/search.md — search query syntax and useful filtersreferences/examples.md — end-to-end shell scripts for common scenarios (dataset upload, software release, new version)