MOLES v3 API
The CEDA data catalogue (known as MOLES) provides various API end-points as detailed on the MOLES API End Points help article which covers specific API end points that are provided. This article details the v3 API, a full RESTful API to the catalogue which supports both read and write functions using the normal GET, PUT etc functions.
Note - the level of read/write access is determined by the visibility of the model classes per user type. E.g. CEDA catalogue editors will be able to modify and create most types, but classes managed via scripts alone remain generally hidden and uneditable except for super users.
Reading MOLES content via v3 API
Setting up your script to work with the API
import requests # REPLACE WITH TOKEN TOKEN = "" #put your token in here # include token in headers headers = {'Authorization': 'Bearer ' + TOKEN} # set base url to localhost, staging or live BASE_URL = 'https://catalogue.ceda.ac.uk/' API_URL = BASE_URL + 'api/v3/' # example endpoint endpoint = 'observations/' url = API_URL + endpoint
Poll models for content (GET)
# example GET response = requests.get(url=url) print(f"Status code: {response.status_code}") print(response.json()) # filter by uuid and get ob_id filters = '?uuid=7e23b82ec3bdc8e5297c0b623697c559' response = requests.get(url=f'{url}{filters}') if response.status_code == 200: print(f"Success! Status code: {response.status_code}") print(response.json()) ob_id = response.json()['results'][0]['ob_id']
Content filtering
By default all possible Django filters should be present, with the exception of embedded fields where a call to another class would be needed instead.
Response Codes
The responses will be standard HTML response codes. Besides successful codes (200, 201, 204) then you should also get a message with the response that explains the issue you are encountering and how to resolve the error encountered (besides 500 type errors). Within the catalogue you may get codes which indicate issues with your credentials/access permissions or specific issues about the call you've submitted due to content validation errors (400 codes).
Creating or modifying MOLES content via the v3 API (POST, PATCH, DELETE)
Warning: the present roll out (20250909) does not yet have full validation of content in place as seen in the admin form. This will be enacted in the subsequent roll out. Therefore , additional care should be taken for content creation or adaption at present.
A python client will be prepared to aid content management by providing tools for a range of common tasks.
CEDA User account access Tokens and permissions
In order to perform any 'write' functions on MOLES content you will need to ensure you have been granted the correct access level. Contact the CEDA Catalogue service manager to arrange this. Your ability to alter content will depend on which permissions group you are added to:
permission group | permitted access | used for |
---|---|---|
superuser | all model classes and fields | Managing users. Though it has ability to alter all content, othe |
CEDA System Writer | all catalogue classes, but not user accounts | For cronjobs creating regular content, including parameters and access/licence details harvested from other CEDA tools |
Catalogue Editor | all catalogue classes except for those for parameter, access and licence details. can not mint DOIs on records they are 'CEDA Officers' | most content management functions |
DOI Minter | as for catalogue editors, but also able to trigger DOI minting | for creating/updating DOI metadata |
To get an access token you need to:
- log into your CEDA user account: https://services.ceda.ac.uk/services/my_services/
- Select 'access token' from the left hand menu:
- Create new access token - note: these will last only for 3 days. It is possible to also get these tokens programmatically if needed for repeated work.
- Copy the access token to your clipboard. You will need this for subsequent use in your scripts
Creating content (POST)
To create an object a dictionary of content needs to be prepared which is then loaded into the API call in the data attribute.
The following example shows the creation of an outline Observation record which can then be worked up with additional information (see PATCH below)
# create observation data = { 'title': 'title_created1', 'abstract': 'example', 'status': 'ongoing' } response = requests.post(url=url, headers=headers, data=data) if response.status_code == 200: print(f"Success! Status code: {response.status_code}") print(response.json()) obs_ob_id = response.json()['ob_id'] #gives you the object ID for the record
An example of creating a Project recod (finding one)
# create project data = { 'title': 'title_created', 'abstract': 'example', } url = API_URL + 'projects/' response = requests.post(url=url, headers=headers, data=data) if response.status_code == 200: print(f"Success! Status code: {response.status_code}") print(response.json()) proj_ob_id = response.json()['ob_id']
We can then link these together using the next method 'PATCH'
Modify content (PATCH)
This allows you to change the value of a given attribute on an object, e.g. modify a string field, change/add a link from one record to another.
Simple attribute changes
To do this the target object needs to be identified via the correct endpoint and then the attribute's new value is supplied by the data attribute passed to the call to the API:
# update (using the project_id from the previous example) url = API_URL + 'projects/' data = { 'title': 'title_replaced' } response = requests.patch(url=f'{url}{proj_ob_id}/', headers=headers, data=data) if response.status_code == 200: print(f"Success! Status code: {response.status_code}") print(response.json())
Linking objects together
Linking two objects together can, though, be a bit tricker as it will depend on how they are linked in the catalogue and how this is shown in the API. In most cases the linking is done simply via the API by providing the required object ID of the object being linked to (e.g. list of Projects on an Observation record). In other cases this might be via the 'uuid' of the record (this applies where links are to the principle record types (observations, observation collections, projects, instruments, platforms, computations) and are shown in the API view as epxanded elements (e.g. in 'onlineresources' the 'relatedTo' entry shows details of the object it is connected to.
Simple linking by object ID:
# link project to obs using patch data = { 'projects': [proj_ob_id] } url = f'{API_URL}observations/{obs_ob_id}/' response = requests.patch(url=url, headers=headers, data=data) print(f"Status code: {response.status_code}") print(response.json())
Linking using an object's UUID:
Deleting content (DELETE)
# delete observation url = f'{API_URL}observations/{obs_ob_id}/' response = requests.delete(url=url, headers=headers) print(f"Status code: {response.status_code}") print(response.json())