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:

  1. log into your CEDA user account: https://services.ceda.ac.uk/services/my_services/
  2. Select 'access token' from the left hand menu:
  3. 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.
  4. 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())
Did this answer your question? Thanks for the feedback There was a problem submitting your feedback. Please try again later.

Still need help? Contact Us Contact Us