Skip to content
O2A Documentation

REGISTRY API

The API of REGISTRY provides REST-based endpoints. Have a look at the OpenAPI documentation. For testing the API, please use the REGISTRY sandbox.

Reading information from REGISTRY is generally open. To save or delete entities, an authorization token is required. Please follow the authentication and authorization hints.

REGISTRY has incorporated support for complex querries via RSQL. A complete description is totally out of scope in this place, but we compiled a Jupyter notebook to highlight some of its capabilities: https://github.com/o2a-data/o2a-data-dws/blob/master/registry-api-rsql.ipynb

See the following descriptions for Bash, Python and R.

Data Model

The data model of REGISTRY is pretty straight forward. Technically it is a JSON notated block of information, following a key-value structure, e.g.

json
{
  "key1": "string value",
  "key2": 42,
  "key3": [
    {"a": "lorem"},
    {"a": "ipsum"}
  ]
}

In the example above, key1 refers to a string value, noticeable by the encapsulation in quotes ". key2 has an integer as value, thus no " are required. If a numeric value is pasted with " it is treated and saved as string value. key3 holds a list ([ ... ]), with two distinct entries, also called objects.

In REGISTRY some keys hold nested structures of objects, since some values follow terms and vocabularies. For example the item status "public" contains more than just the name "public", but also a meta description, a timestamp of the last modification (lastModified), an numeric id and a global unique ID (uuid) additionally.

json
"status": {
  "@uuid": "a080ff68-30b2-450a-9a44-06ecd244c0b7",
  "id": 1,
  "lastModified": "2020-11-18T09:14:53.011",
  "name": "public",
  "meta": "Item is completely described and operational."
}

The nested structure also allows deduplication of information in GET requests. Content, e.g. status, is stated once, and every other use of this term is just a reference via a uuid to the first appearance. That procedure saves a lot of data volume.

An example for a more complex output could look like the JSON below. It clearly can be seen that in the parameters section, the first parameter (ID 91804) has a complete entry for unit and type, the second entry (ID 91805) has a complete entry for type since it differs from the first entry, but unit is a reference to the first parameter unit ("ecbb48c4-577e-4c2b-851a-b967133dd5e5"). The third parameter (ID 91816) again is a complete entry without references, while the last parameter (ID 91817) has references for both, unit ("e2a3c08d-5fd1-4df9-8a05-61a753b900e8") and type ("7f41d7aa-6255-47ce-94c6-11eda56fd625").

json
{
  "@uuid": "af127496-b8a6-4c4d-9b95-8c42f6a22afe",
  "id": 4044,
  "lastModified": "2024-01-18T07:35:00.055",
  "code": "vessel:polarstern:losgatos_awi_1303",
  "shortName": "losgatos_awi_1303",
  "longName": "LosGatos AWI 3K430000001303",
  "description": "Labversion of Greenhouse Gas Analyzer",
  "model": "GGA-911(911-0011-0000-0000)",
  "manufacturer": "Los Gatos Research",
  "serialNumber": "3K430000001303",
  "inventoryNumber": "",
  "citation": "10013/sensor.9fd3386a-6c1a-4775-bcbb-cc5d50e27494",
  "parentId": 90,
  "status": {
    "@uuid": "a080ff68-30b2-450a-9a44-06ecd244c0b7",
    "id": 1,
    "lastModified": "2020-11-18T09:14:53.011",
    "name": "public",
    "meta": "Item is completely described and operational."
  },
  "type": {
    "@uuid": "e967b68f-5471-4e08-a109-670c676116bb",
    "id": 537,
    "created": "2025-05-19T13:21:16.923",
    "lastModified": "2025-05-19T13:21:16.924",
    "generalName": "cavity enhanced absorption spectrometers",
    "systemName": "cavity_enhanced_absorption_spectrometers",
    "description": "Instruments that illuminate a sample inside an optical cavity, typically using laser light, and measure the concentration or amount of a species in gas phase by absorption spectroscopy. Techniques include cavity ring-down spectroscopy (CRDS) and integrated cavity output spectroscopy (ICOS).",
    "vocabulary": "NERC",
    "vocableValue": "https://vocab.nerc.ac.uk/collection/L05/current/LAB38",
    "vocableGroup": {
      "@uuid": "85694c85-5aee-441f-a88a-a6bb8f1d0885",
      "id": 7,
      "created": "2024-09-04T09:04:10.581",
      "lastModified": "2024-09-06T13:19:29.361",
      "name": "DeviceTypes",
      "systemName": "DeviceTypes"
    }
  },
  "version": 42,
  "parameters": [
    {
      "@uuid": "dfb5aa54-9b54-11ef-9e4a-005056a5aa32",
      "id": 91804,
      "name": "ambient temperature",
      "shortName": "ambt_c",
      "unit": {
        "@uuid": "ecbb48c4-577e-4c2b-851a-b967133dd5e5",
        "id": 14,
        "code": "°C",
        "name": "degree Celsius",
        "typicalUse": "temperature",
        "ucum": "Cel",
        "mathML": "{°C}",
        "visible": true
      },
      "type": {
        "@uuid": "a45c8d6f-f4b3-4df2-8994-393f4331863f",
        "id": 7,
        "created": "2025-05-19T16:26:06.044",
        "lastModified": "2025-06-04T07:38:48.528",
        "generalName": "temperature",
        "systemName": "temperature",
        "description": "The degree of hotness of an object, a measurement matrix, an environment, expressed against a standard scale. ",
        "vocabulary": "NERC",
        "vocableValue": "https://vocab.nerc.ac.uk/collection/S06/current/S0600082",
        "vocableGroup": {
          "@uuid": "b876cf1e-b073-4569-9ab6-8fa33ff3108c",
          "id": 4,
          "name": "SensorOutputTypes",
          "systemName": "SensorOutputTypes"
        }
      },
      "properties": []
    },
    {
      "@uuid": "dfb5aaa2-9b54-11ef-9e4a-005056a5aa32",
      "id": 91805,
      "name": "standard deviation ambient temperature",
      "shortName": "ambt_c_sd",
      "unit": "ecbb48c4-577e-4c2b-851a-b967133dd5e5",
      "type": {
        "@uuid": "7f41d7aa-6255-47ce-94c6-11eda56fd625",
        "id": 531,
        "created": "2025-05-19T13:20:59.57",
        "lastModified": "2025-05-19T13:20:59.571",
        "generalName": "standard deviation",
        "systemName": "standard_deviation",
        "description": "The square root of the average of the squares of deviations about the mean of a set of data.",
        "vocabulary": "NERC",
        "vocableValue": "https://vocab.nerc.ac.uk/collection/S07/current/S0700007",
        "vocableGroup": "b876cf1e-b073-4569-9ab6-8fa33ff3108c"
      },
      "properties": []
    },
    {
      "@uuid": "dfb5af85-9b54-11ef-9e4a-005056a5aa32",
      "id": 91816,
      "name": "analyzer Status",
      "shortName": "analyzer_status_ma",
      "unit": {
        "@uuid": "e2a3c08d-5fd1-4df9-8a05-61a753b900e8",
        "id": 103,
        "code": "mA",
        "name": "milliampere",
        "typicalUse": "current",
        "ucum": "mA",
        "mathML": "{mA}",
        "visible": true
      },
      "type": {
        "@uuid": "0f301272-4269-46d6-aa26-58cacb054a56",
        "id": 281,
        "created": "2025-05-21T20:08:20.4",
        "lastModified": "2025-05-21T20:45:13.422",
        "generalName": "voltage",
        "systemName": "voltage",
        "description": "The difference in electric potential between two points. For the raw signal output as voltage of a measuring device use S0600183 for 'Raw signal (voltage)'",
        "vocabulary": "NERC",
        "vocableValue": "https://vocab.nerc.ac.uk/collection/S06/current/S0600163",
        "vocableGroup": "b876cf1e-b073-4569-9ab6-8fa33ff3108c"
      },
      "properties": []
    },
    {
      "@uuid": "dfb5b002-9b54-11ef-9e4a-005056a5aa32",
      "id": 91817,
      "name": "standard deviation analyzer status",
      "shortName": "analyzer_status_ma_sd",
      "unit": "e2a3c08d-5fd1-4df9-8a05-61a753b900e8",
      "type": "7f41d7aa-6255-47ce-94c6-11eda56fd625",
      "properties": []
    }
  ]
}

Bash

Using the command line requires curl or similar to submit HTTP methods to the API.

Necessary (++) and useful (+) programs:

Commonly used flags for curl in this use case are:

  • -c <filename>: Write cookies to <filename> after operation, this includes the necessary auth token
  • -b <data|filename>: send cookies from string/file
  • -d <data>: HTTP POST data
  • -H <header/@file>: pass custom header(s) to server
  • -o: write to file instead of stdout
  • -s: silent mode
  • -X: the type of request (see HTTP methods)

Basic Login

The following request carries two data fields (username and password), each contains a key-value pair. Together they deliver the authentification credentials. Please be aware that you cannot authenticate via your password when using the API. Instead, please generate a auth token for such tasks.

bash
curl -X POST \
  -c cookiefile
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -o /dev/null \
  -H 'Accept: application/json' \
  -d 'username=<your username>&password=<o2a-token>' \
  'https://registry.o2a-data.de/rest/v2/auth/login'

The cookiefile looks like this:

bash
# Netscape HTTP Cookie File
# https://curl.se/docs/http-cookies.html
# This file was generated by libcurl! Edit at your own risk.

.o2a-data.de  TRUE  /  FALSE  1708695265  x-auth-token7e8e2beb9933f7bc0961bcf4fca8e949

and can be removed after successful operation by

bash
rm -v cookiefile

HTTP Requests

GET

This example gets basic information about an item.

bash
curl -X GET 'https://registry.o2a-data.de/rest/v2/items/456'

or nicely parsed (because piped into jq):

bash
curl -X GET 'https://registry.o2a-data.de/rest/v2/items/456' | jq .

Some requests allow to filter a priori. Basically these filters are only some additional statements amended to the request URL. hits restricts the output to the integer number that is provided along the request. Correspondingly offset skips the first n numbers of hits. But the most powerful statement is where, which gives the opportunity to filter, and sorts, which allows to sort the output with regard to certain keys in the output.

Hence,

bash
url="https://registry.o2a-data.de/rest/v2/contacts?"
where="where=firstName=LIKE=Peter&"
offset="offset=1&"
hits="hits=2&"
sorts="sorts=lastName"

curl -X GET $url$where$offset$hits$sorts

selects all contacts with the first name "Peter", skips (offset) the first entry, restricts to two results and sorts it according the lastName (ascendingly).

Remark: The request link was split for better readability.

POST

In this example, we create a new item utilizing a POST request. Be aware that you need proper privileges to modify things. For existing items, you must be a contact of this item, see Item Contacts. Via -d flag in this case literally a string formatted in JSON format is submitted as data payload.

Remember the cookiefile? Now it comes into action:

bash
curl -X POST \
  -b cookiefile \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json'
  -d '{
        "shortName": "string",
        "longName": "super heavy thingy",
        "description": "lorem",
        "inventoryNumber": "string",
        "statusId": 2,
        "typeId": 110,
      }' \
  'https://registry.o2a-data.de/rest/v2/items'

Some JSON keys are necessary, some are optional. Please have a look at the respective JSON schemes at the bottom of the OpenAPI documenation.

PUT

This example uses PUT to modify an existing custom field with ID 17 of item with ID 5755. Be aware that i) you need proper privileges to modify items and ii) the field needs to exists in REGISTRY.

bash
curl -X PUT \
  -b cookiefile \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -d '{
    "name": "myCustomField",
    "value": "23",
    "description": "no it's different"
      }'
  'https://registry.o2a-data.de/rest/v2/items/5755/fields/17'

The parts of the json:

  • "uuid" string -->
  • "name" string --> the key, in terms of json language
  • "value" string --> the adapted value
  • "description" string -->
  • "id" numeric --> the item ID the altered information belongs to

DELETE

The delete methods delete entities if not related to other existing entities. For items, the status of an item will be set to "decommissioned". You need proper privileges on the item to do this.

This example sets the status of the item with the ID 11487 to "decomissioned".

bash
curl -X DELETE \
  -b cookiefile \
  -H 'Accept: application/json' \
  'https://registry.o2a-data.de/rest/v2/items/11487'

Python

API Wrapper

The o2a.registry.python.lib package provides a basic wrapper around the REGISTRY API. It can be installed using:

bash
pip install o2a.registry.python.lib

It simplifies the access to the API, by providing a well documented and typed interface to the REGISTRY data model:

python
from o2a_registry.api import ProductionAPI  # registry.o2a-data.de
from o2a_registry.api import SandboxAPI     # registry.sandbox.o2a-data.de

POLARSTERN_ITEM_ID = 90
polarstern = await SandboxAPI.get_item(POLARSTERN_ITEM_ID)
print("Polarstern description:", polarstern.description)

await ProductionAPI.close()

See the documentation on how to use the library.

Do it yourself

Necessary (++) and useful (+) packages

You can create and source a new virtual environment like this

bash
python3 -m venv .env
source .env/bin/activate
pip install requests

json, re, itertools, and datetime are built-in modules and do not need to be installed separately.

Basic Login

The carried body contains the authentification credentials as a dict element. The auth cookie is then extracted (theToken) from the response of the POST request (see HTTP requests). Please be aware that you cannot authenticate via your password when using the API. Please generate a token (description here) for such tasks.

python
import requests
import json

auth = requests.post('https://registry.o2a-data.de/rest/v2/auth/login',
  data = {
    'username': '<your username>',
    'password': '<o2a-token>'
  }
)
theToken = auth.cookies['x-auth-token']

HTTP requests

GET

This example gets basic information about an item. The result is a dictionary object.

python
a = requests.get('https://registry.o2a-data.de/rest/v2/items/456')
theItem = json.loads(a.content)

Some requests allow to filter a priori. Basically these filters are only some additional statements amended to the request URL. hits restricts the output to the integer number that is provided along the request. Correspondingly offset skips the first n numbers of hits. But the most powerful statement is where, which gives the opportunity to filter, and sorts, which allows to sort the output with regard to certain keys in the output.

Hence,

python
a = requests.get(
  'https://registry.o2a-data.de/rest/v2/contacts?'
  + 'where=firstName=LIKE=Peter&'
  + 'offset=1&'
  + 'hits=2&'
  + 'sorts=lastName'
)
theFilteredContent = json.loads(a.content)

selects all contacts with the first Name "Peter", skips (offset) the first entry, restricts to two results and sorts it according the lastName (ascendingly).

POST

In this example, we create a new item utilizing a POST request. Be aware that you need proper privileges to modify things. For existing items, you must be a contact of this item, see Item Contacts.

python
requests.post(
  "https://registry.o2a-data.de/rest/v2/items",
  headers={"content-type": "application/json"},
  cookies={"x-auth-token": theToken},
  data=json.dumps(
    {
      "shortName": "string",
      "longName": "super heavy thingy",
      "description": "lorem",
      "inventoryNumber": "string",
      "statusId": 2,
      "typeId": 110,
    }
  )
)

Some JSON keys are necessary, some are optional. Please have a look at the respective JSON schemes at the bottom of the OpenAPI documenation.

PUT

This uses PUT to modify custom fields in REGISTRY. Be aware that i) you need proper privileges to modify items and ii) the field needs to exist in REGISTRY.

python
requests.put('https://registry.o2a-data.de/rest/v2/items/5755/fields/17',
  headers={"content-type": "application/json"},
  cookies={"x-auth-token": theToken},
  data=json.dumps(
    {"uuid": "literally any string",
    "name": "myCustomField",
    "value": "23.4321",
    "description": "it's different again",
    "id": 5755}
  )
)

DELETE

The delete methods delete entities if not related to other existing entities. For items, the status of an item will be set to "decommissioned". You need proper privileges on the item to do this.

This example sets the status of the item with ID 11473 to "decomissioned".

python
requests.delete(
  "https://registry.o2a-data.de/rest/v2/items/11473",
  cookies={"x-auth-token": theToken},
  headers={"content-type": "application/json"},
)

R

Necessary (++) and useful (+) packages

R
install.packages(c('httr'), dep = TRUE)

Basic Login

The carried body contains the authentification credentials as a list element. The auth cookie is then extracted (theToken) from the response of the POST request (see HTTP requests). Please be aware that you cannot authenticate via your password when using the API. Please generate a token (description here) for such tasks.

R
library('httr')

x <- POST(
  url = 'https://registry.o2a-data.de/rest/v2/auth/login',
  body = list(
    "username" = "<your username>",
    "password" = "<o2a-token>"
  ),
  encode = "form"
)
theToken <- x$cookies$value[1]

HTTP requests

GET

This example gets basic information about an item. The result is a list object.

R
URL <- 'https://registry.o2a-data.de/rest/v2/items/456'
a <- GET(url = URL)
theItem <- content(a)

Some requests allow to filter a priori. Basically these filters are only some additional statements amended to the request URL. hits restricts the output to the integer number that is provided along the request. Correspondingly offset skips the first n numbers of hits. But the most powerful statement is where, which gives the opportunity to filter, and sorts, which allows to sort the output with regard to certain keys in the output.

Hence,

R
URL <- paste0("https://registry.o2a-data.de/rest/v2/contacts?",
  "where=firstName=LIKE=Peter&",
  "offset=1&",
  "hits=2&",
  "sorts=lastName"
)
a <- GET(url = URL)
theFilteredContent <- content(a)

selects all contacts with the first Name "Peter", skips (offset) the first entry, restricts to two results and sorts it according the lastName (ascendingly).

POST

In this example, we create a new item utilizing a POST request. Be aware that you need proper privileges to modify things. For existing items, you must be a contact of this item, see Item Contacts.

Following parameters are relevant:

  • url: the API endpoint to call
  • add_headers: contains the wrapped auth cookie
  • encode: indicates how the submitted values are encoded, in this case we preferred json
  • body: a list (that is then translated to json) that holds the info about the item to be created
R
a <- POST(
  "https://registry.o2a-data.de/rest/v2/items",
  add_headers("x-auth-token" = theToken),
  encode = 'json',
  body = list(
    "shortName": "string",
    "longName": "super heavy thingy",
    "description": "lorem",
    "inventoryNumber": "string",
    "statusId": 2,
    "typeId": 110,
  )
)

## check for success
print(a$status_code)

Some JSON keys are necessary, some are optional. Please have a look at the respective JSON schemes at the bottom of the OpenAPI documenation.

PUT

This example uses PUT to modify an existing custom field with ID 8 of item with ID 3560. Be aware that i) you need proper privileges to modify items and ii) the field needs to exists in REGISTRY.

R
PUT(
  url = "https://registry.o2a-data.de/rest/v2/items/3560/fields/8",
  encode = 'json',
  add_headers("x-auth-token" = theToken),
  body = list(
    "name" = "myField",
    "customFieldValue" = "42",
    "description" = "This is an empiric value, we hope."
  )
)

DELETE

The delete methods delete entities if not related to other existing entities. For items, the status of an item will be set to "decommissioned". You need proper privileges on the item to do this.

This example deletes event with ID 59794 from item with ID 11469.

DELETE(
  url = "https://registry.o2a-data.de/rest/v2/items/11469/events/59794",
  add_headers("x-auth-token" = theToken)
)