How to upload to Zenodo¶
Here we describe how to upload files to Zenodo.
Imports¶
import copy
import datetime as dt
import os
import sys
import tempfile
from pathlib import Path
from loguru import logger
from openscm_zenodo import ZenodoDomain, ZenodoInteractor
We enable logging in this notebook so you can see what is going on in more detail.
logger.configure(handlers=[dict(sink=sys.stderr, level="INFO")])
logger.enable("openscm_zenodo")
Files to upload¶
Before you can get started, you will need to have a file to upload.
to_upload = Path(tempfile.mkdtemp()) / "demo.txt"
with open(to_upload, "w") as fh:
fh.write("Your content\n")
fh.write("will be better than this!")
Zenodo token¶
In order to interact with the API, you will need a token for Zenodo. To create the token, go to https://zenodo.org/account/settings/applications/tokens/new/. Make sure that the token has "deposit:actions" and "deposit:write" permissions, otherwise you won't be able to do anything. Put your token somewhere safe, if you leak it then others can do whatever they want with your Zenodo records! (If you do leak your token, just revoke it, then no more damage can happen.)
Zenodo interactor¶
The ZenodoInteractor class is our key class for interacting with Zenodo.
This class allows you to create new versions of deposits,
upload files and manipulate metadata.
zi = ZenodoInteractor(
# When we run this example in our CI and the docs build,
# we set the environment variable before we run.
token=os.environ["ZENODO_TOKEN"],
# You will need a token in order to upload
# In this example we use the sandbox domain.
# You will want to use the production domain
# once you're ready to actually post things.
zenodo_domain=ZenodoDomain.sandbox,
)
zi
ZenodoInteractor(token=***, zenodo_domain=<ZenodoDomain.sandbox: 'https://sandbox.zenodo.org'>, timeout=10, timeout_upload=3600)
Interact with zenodo¶
Make new version¶
The first step is to make a new version i.e. get a new draft deposition ID. To do this, we start with any deposition ID.
# This is the record we use for testing: https://sandbox.zenodo.org/records/166701
any_deposition_id = "166701"
From this we get the latest deposition ID.
latest_deposition_id = zi.get_latest_deposition_id(any_deposition_id)
latest_deposition_id
2025-08-09 21:30:49.027 | INFO | openscm_zenodo.zenodo:get_latest_deposition_id:350 - Retrieving the ID of the latest deposition in the series which includes deposition ID '166701'
2025-08-09 21:30:49.028 | INFO | openscm_zenodo.zenodo:get_record:440 - Retrieving record '166701'
2025-08-09 21:30:50.618 | INFO | openscm_zenodo.zenodo:get_latest_deposition_id:362 - For deposition ID '166701', the ID of the latest deposition in the series is '308906'
'308906'
Now we can get a draft deposition ID. This will either use an existing draft, or create a new draft if no existing draft exists.
draft_deposition_id = zi.get_draft_deposition_id(latest_deposition_id)
draft_deposition_id
2025-08-09 21:30:50.624 | INFO | openscm_zenodo.zenodo:create_new_version_from_latest:124 - Creating a new version from latest_deposition_id='308906'
2025-08-09 21:30:51.383 | INFO | openscm_zenodo.zenodo:create_new_version_from_latest:131 - Successfully created new version. The new version's deposition id is 308908
308908
Upload metadata¶
The next thing to do is to upload/update any metadata we wish to. Here we simply get the old metadata, but you have full flexibility to start fresh if you wish. The docs on what is allowed here are not great, but these docs are a better start than nothing.
metadata_current = zi.get_metadata(latest_deposition_id, user_controlled_only=True)
metadata_current
2025-08-09 21:30:51.388 | INFO | openscm_zenodo.zenodo:get_metadata:401 - Retrieving metadata for deposition_id='308906'
2025-08-09 21:30:51.389 | INFO | openscm_zenodo.zenodo:get_deposition:275 - Retrieving deposition '308906'
{'metadata': {'title': 'OpenSCM-Zenodo docs run 2025-08-09T21:30:36Z',
'description': 'Test upload for OpenSCM-Zenodo, generated from the docs.',
'access_right': 'open',
'creators': [{'name': 'Doc, Doc',
'affiliation': 'how-to-upload-to-zenodo.py'}],
'license': 'cc-by-4.0',
'upload_type': 'dataset'}}
Update the metadata.
timestamp = dt.datetime.now(dt.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
metadata_updated = copy.deepcopy(metadata_current)
metadata_updated["metadata"]["description"] = (
"Test upload for OpenSCM-Zenodo, generated from the docs."
)
metadata_updated["metadata"]["title"] = f"OpenSCM-Zenodo docs run {timestamp}"
metadata_updated["metadata"]["creators"][0]["affiliation"] = (
"how-to-upload-to-zenodo.py"
)
metadata_updated
{'metadata': {'title': 'OpenSCM-Zenodo docs run 2025-08-09T21:30:51Z',
'description': 'Test upload for OpenSCM-Zenodo, generated from the docs.',
'access_right': 'open',
'creators': [{'name': 'Doc, Doc',
'affiliation': 'how-to-upload-to-zenodo.py'}],
'license': 'cc-by-4.0',
'upload_type': 'dataset'}}
Update the metadata on Zenodo.
zi.update_metadata(
deposition_id=draft_deposition_id,
metadata=metadata_updated,
).raise_for_status()
2025-08-09 21:30:51.886 | INFO | openscm_zenodo.zenodo:update_metadata:868 - Updating metadata for deposition_id=308908
Upload the files¶
Now we can also upload our files to the draft.
files_to_upload_l = [to_upload]
zi.upload_files(
deposition_id=draft_deposition_id,
to_upload=files_to_upload_l,
)
2025-08-09 21:30:52.357 | INFO | openscm_zenodo.zenodo:upload_files:811 - Uploading 1 file to deposition_id=308908
2025-08-09 21:30:52.358 | INFO | openscm_zenodo.zenodo:get_bucket_url:225 - Retrieving bucket URL for deposition_id=308908
2025-08-09 21:30:52.766 | INFO | openscm_zenodo.zenodo:get_bucket_url:232 - Successfully retrieved bucket_url='https://sandbox.zenodo.org/api/files/529c968b-ea0f-4a9b-bb31-44afd8d984c5' for deposition_id=308908
Submitting files to queue: 0%| | 0/1 [00:00<?, ?it/s]
2025-08-09 21:30:52.770 | INFO | openscm_zenodo.zenodo:upload_file_to_bucket_url:754 - Uploading /tmp/tmp690jy9vo/demo.txt to upload_url='https://sandbox.zenodo.org/api/files/529c968b-ea0f-4a9b-bb31-44afd8d984c5/demo.txt'
Submitting files to queue: 100%|██████████| 1/1 [00:00<00:00, 1066.17it/s]
Files to upload: 0%| | 0/1 [00:00<?, ?it/s]
0%| | 0.00/38.0 [00:00<?, ?B/s]
100%|██████████| 38.0/38.0 [00:00<00:00, 182B/s]
100%|██████████| 38.0/38.0 [00:00<00:00, 67.9B/s]
2025-08-09 21:30:53.333 | INFO | openscm_zenodo.zenodo:upload_file_to_bucket_url:770 - Successfully uploaded /tmp/tmp690jy9vo/demo.txt
Files to upload: 100%|██████████| 1/1 [00:00<00:00, 1.78it/s]
Files to upload: 100%|██████████| 1/1 [00:00<00:00, 1.78it/s]
(<Response [201]>,)
Publish the version¶
If you want, you can even publish the version.
zi.publish(draft_deposition_id).raise_for_status()
print(
"The published record is available at: "
f"{zi.zenodo_domain.value}/records/{draft_deposition_id}"
)
2025-08-09 21:30:53.341 | INFO | openscm_zenodo.zenodo:publish:544 - Publishing deposition_id=308908
2025-08-09 21:30:54.709 | INFO | openscm_zenodo.zenodo:publish:549 - Successfully published deposition_id=308908
The published record is available at: https://sandbox.zenodo.org/records/308908
Conclusion¶
This gives a basic demonstration of how to use OpenSCM-Zenodo. We hope this helps and can support your use case, whatever it is.