Walking an OPTIMADE API in-process¶
This notebook is the narrated version of examples/query_in_process.py; the
code cells are that script, unchanged. It answers one question: what does an
httk-optimade server actually reply?
The temptation is to answer it by calling the query engine directly. That
answer is incomplete, because the JSON:API envelope — data, meta,
links, included — is built by the ASGI application, not by the engine. So
we speak HTTP instead, but without a port: create_asgi_app returns an
ordinary ASGI application, and starlette’s TestClient drives it in this very
process. Routing, query-string validation, error handling and response
rendering all run; nothing touches a socket, and there is no server to start
or stop.
The cells are not executed when the documentation is built, so no output is
shown below. Run the notebook (or python examples/query_in_process.py) to
see the responses.
Setup¶
Three imports carry the whole example: the two ready-made providers
httk-data registers for the standard files and calculations entry types,
adapter_from_providers/create_asgi_app from httk-optimade, and
starlette’s TestClient. No provider class has to be written to have
something to query — see
Serving entry providers for how to write one.
import json
from typing import Any
from httk.core import RelatedEntry
from httk.data.entry_providers import CalculationEntryProvider, FileEntryProvider
from starlette.testclient import TestClient
from httk.optimade import adapter_from_providers, create_asgi_app
/tmp/ipykernel_2473/1898162355.py:6: StarletteDeprecationWarning: Using `httpx` with `starlette.testclient` is deprecated; install `httpx2` instead.
from starlette.testclient import TestClient
The data¶
Three files and two calculations, as plain mappings. CALCULATION_FILES
declares which files each calculation consumed and produced; those
RelatedEntry objects become the calculation’s OPTIMADE relationships
block, which is what makes the HAS filter and the include=files request
further down meaningful.
FILES = {
"file-1": {
"name": "INCAR",
"url": "https://example.org/files/calc-1/INCAR",
"size": 512,
"media_type": "text/plain",
"last_modified": "2021-03-15T10:20:00Z",
},
"file-2": {
"name": "OUTCAR",
"url": "https://example.org/files/calc-1/OUTCAR",
"size": 204800,
"media_type": "text/plain",
"last_modified": "2021-03-15T10:25:00Z",
},
"file-3": {
"name": "trajectory.xyz",
"url": "https://example.org/files/calc-2/trajectory.xyz",
"size": 1048576,
"media_type": "chemical/x-xyz",
"last_modified": "2021-03-16T08:00:00Z",
},
}
CALCULATIONS = {
"calc-1": {"last_modified": "2021-03-15T10:30:00Z"},
"calc-2": {"last_modified": "2021-03-16T08:30:00Z"},
}
# Which files each calculation consumed and produced. The provider serves these
# as the calculation's OPTIMADE relationships block.
CALCULATION_FILES = {
"calc-1": [RelatedEntry("files", "file-1", role="input"), RelatedEntry("files", "file-2", role="output")],
"calc-2": [RelatedEntry("files", "file-3", role="output")],
}
The application, and a client bound straight to it¶
adapter_from_providers builds the backend adapter (schema, filter handlers
and record store all derived from what the providers declare);
create_asgi_app wraps it in the OPTIMADE HTTP API. sortable= is the one
thing declared by hand: a property may only be used in ?sort= if the server
advertises it in /v1/info/files.
baseurl is the URL the server uses when it builds absolute links (the
links.next we follow later), so it must match the host the client talks to.
def make_client() -> TestClient:
"""An HTTP client bound straight to the OPTIMADE app — no socket, no server process."""
adapter = adapter_from_providers(
[FileEntryProvider(FILES), CalculationEntryProvider(CALCULATIONS, relationships=CALCULATION_FILES)],
# Sortable is a promise the server publishes in /v1/info/files; only
# declared properties may be used in ?sort=.
sortable={"files": ["size", "name"]},
)
# baseurl is what the server uses to build absolute URLs (links.next below),
# so it must match the host the client talks to.
app = create_asgi_app(adapter, baseurl="http://localhost/")
return TestClient(app, base_url="http://localhost")
A one-line helper: issue a GET and print the response exactly as a client would receive it — envelope included, since half the point is to see the envelope.
def show(client: TestClient, url: str, **params: Any) -> dict[str, Any]:
"""GET one OPTIMADE URL and print the response, JSON:API envelope included."""
response = client.get(url, params=params)
query = "?" + "&".join(f"{key}={value}" for key, value in params.items()) if params else ""
print("\n=== GET " + url + query + " -> " + str(response.status_code))
payload: dict[str, Any] = response.json()
print(json.dumps(payload, indent=1, sort_keys=True))
return payload
client = make_client()
1. /v1/info — what is served at all¶
Capability discovery comes first: everything else is only valid if this
endpoint advertises it. The response is a single resource object
(data is an object here, not an array) whose attributes give the
api_version, the available_api_versions with their base URLs, the
available_endpoints, and entry_types_by_format — the entry types this
implementation serves, per response format.
meta is present on every OPTIMADE response: API version, the implementation
and provider identification, a timestamp, and query.representation echoing
the request that produced it.
# 1. What does this implementation serve? Capability discovery comes first,
# because everything below is only valid if /v1/info advertises it.
info = show(client, "/v1/info")
print("\nserved entry types:", info["data"]["attributes"]["entry_types_by_format"]["json"])
=== GET /v1/info -> 200
{
"data": {
"attributes": {
"api_version": "1.3.0",
"available_api_versions": [
{
"url": "http://localhost/v1",
"version": "1.3.0"
},
{
"url": "http://localhost/v1.3",
"version": "1.3.0"
},
{
"url": "http://localhost/v1.3.0",
"version": "1.3.0"
}
],
"available_endpoints": [
"info",
"links",
"files",
"calculations"
],
"entry_types_by_format": {
"json": [
"files",
"calculations"
]
},
"formats": [
"json"
],
"is_index": false
},
"id": "/",
"type": "info"
},
"meta": {
"api_version": "1.3.0",
"implementation": {
"homepage": "https://httk.org/",
"issue_tracker": "https://github.com/httk/httk-optimade/issues",
"name": "httk-optimade",
"source_url": "https://github.com/httk/httk-optimade",
"version": "0.2.0"
},
"more_data_available": false,
"provider": {
"description": "This is a database hosted with the High-Throughput Toolkit (httk), for which the hoster has not specifically configured the provider.",
"name": "httk",
"prefix": "httk"
},
"query": {
"representation": "/v1/info"
},
"time_stamp": "2026-07-29T06:52:25.966095+00:00"
}
}
served entry types: ['files', 'calculations']
2. /v1/files — an entry listing¶
Now a listing: data is an array of resource objects, each with id,
type and an attributes object holding the properties.
response_fields narrows attributes to the properties asked for. It is not
an absolute filter — properties the standard requires a response to carry
(url for files) come back regardless — but it is the way to keep a
response small. Without it, every served property is returned, nulls
included.
The listing’s meta gains the counters a client needs: data_available (how
many entries exist), data_returned (how many match), and
more_data_available. links.next is null because everything fits in one
page.
# 2. An entry listing. response_fields trims 'attributes' to what is asked
# for (plus the required ones, such as 'url' here); without it every
# served property is returned, nulls included.
show(client, "/v1/files", response_fields="name,size,media_type")
=== GET /v1/files?response_fields=name,size,media_type -> 200
{
"data": [
{
"attributes": {
"media_type": "text/plain",
"name": "INCAR",
"size": 512,
"url": "https://example.org/files/calc-1/INCAR"
},
"id": "file-1",
"type": "files"
},
{
"attributes": {
"media_type": "text/plain",
"name": "OUTCAR",
"size": 204800,
"url": "https://example.org/files/calc-1/OUTCAR"
},
"id": "file-2",
"type": "files"
},
{
"attributes": {
"media_type": "chemical/x-xyz",
"name": "trajectory.xyz",
"size": 1048576,
"url": "https://example.org/files/calc-2/trajectory.xyz"
},
"id": "file-3",
"type": "files"
}
],
"links": {
"next": null
},
"meta": {
"api_version": "1.3.0",
"data_available": 3,
"data_returned": 3,
"implementation": {
"homepage": "https://httk.org/",
"issue_tracker": "https://github.com/httk/httk-optimade/issues",
"name": "httk-optimade",
"source_url": "https://github.com/httk/httk-optimade",
"version": "0.2.0"
},
"last_id": "file-3",
"more_data_available": false,
"provider": {
"description": "This is a database hosted with the High-Throughput Toolkit (httk), for which the hoster has not specifically configured the provider.",
"name": "httk",
"prefix": "httk"
},
"query": {
"representation": "/v1/files?response_fields=name%2Csize%2Cmedia_type"
},
"time_stamp": "2026-07-29T06:52:25.973915+00:00"
}
}
{'data': [{'attributes': {'media_type': 'text/plain',
'name': 'INCAR',
'size': 512,
'url': 'https://example.org/files/calc-1/INCAR'},
'id': 'file-1',
'type': 'files'},
{'attributes': {'media_type': 'text/plain',
'name': 'OUTCAR',
'size': 204800,
'url': 'https://example.org/files/calc-1/OUTCAR'},
'id': 'file-2',
'type': 'files'},
{'attributes': {'media_type': 'chemical/x-xyz',
'name': 'trajectory.xyz',
'size': 1048576,
'url': 'https://example.org/files/calc-2/trajectory.xyz'},
'id': 'file-3',
'type': 'files'}],
'links': {'next': None},
'meta': {'api_version': '1.3.0',
'data_available': 3,
'data_returned': 3,
'implementation': {'homepage': 'https://httk.org/',
'issue_tracker': 'https://github.com/httk/httk-optimade/issues',
'name': 'httk-optimade',
'source_url': 'https://github.com/httk/httk-optimade',
'version': '0.2.0'},
'last_id': 'file-3',
'more_data_available': False,
'provider': {'description': 'This is a database hosted with the High-Throughput Toolkit (httk), for which the hoster has not specifically configured the provider.',
'name': 'httk',
'prefix': 'httk'},
'query': {'representation': '/v1/files?response_fields=name%2Csize%2Cmedia_type'},
'time_stamp': '2026-07-29T06:52:25.973915+00:00'}}
3. ?filter= — a numeric comparison¶
size is described in the entry type definition as an integer, and that
description alone is what makes numeric comparison operators available for it.
No handler code was written for this filter; the provider only said what type
the property has.
In the response, data_returned drops to the number of matches while
data_available stays at the full collection size — that pair is how a client
tells “filtered” from “empty database”.
# 3. Filtering. 'size' is described as an integer, so numeric comparisons
# are offered for it; no handler code was written for this.
show(client, "/v1/files", filter="size > 100000", response_fields="name,size")
=== GET /v1/files?filter=size > 100000&response_fields=name,size -> 200
{
"data": [
{
"attributes": {
"name": "OUTCAR",
"size": 204800,
"url": "https://example.org/files/calc-1/OUTCAR"
},
"id": "file-2",
"type": "files"
},
{
"attributes": {
"name": "trajectory.xyz",
"size": 1048576,
"url": "https://example.org/files/calc-2/trajectory.xyz"
},
"id": "file-3",
"type": "files"
}
],
"links": {
"next": null
},
"meta": {
"api_version": "1.3.0",
"data_available": 3,
"data_returned": 2,
"implementation": {
"homepage": "https://httk.org/",
"issue_tracker": "https://github.com/httk/httk-optimade/issues",
"name": "httk-optimade",
"source_url": "https://github.com/httk/httk-optimade",
"version": "0.2.0"
},
"last_id": "file-3",
"more_data_available": false,
"provider": {
"description": "This is a database hosted with the High-Throughput Toolkit (httk), for which the hoster has not specifically configured the provider.",
"name": "httk",
"prefix": "httk"
},
"query": {
"representation": "/v1/files?filter=size+%3E+100000&response_fields=name%2Csize"
},
"time_stamp": "2026-07-29T06:52:26.060687+00:00"
}
}
{'data': [{'attributes': {'name': 'OUTCAR',
'size': 204800,
'url': 'https://example.org/files/calc-1/OUTCAR'},
'id': 'file-2',
'type': 'files'},
{'attributes': {'name': 'trajectory.xyz',
'size': 1048576,
'url': 'https://example.org/files/calc-2/trajectory.xyz'},
'id': 'file-3',
'type': 'files'}],
'links': {'next': None},
'meta': {'api_version': '1.3.0',
'data_available': 3,
'data_returned': 2,
'implementation': {'homepage': 'https://httk.org/',
'issue_tracker': 'https://github.com/httk/httk-optimade/issues',
'name': 'httk-optimade',
'source_url': 'https://github.com/httk/httk-optimade',
'version': '0.2.0'},
'last_id': 'file-3',
'more_data_available': False,
'provider': {'description': 'This is a database hosted with the High-Throughput Toolkit (httk), for which the hoster has not specifically configured the provider.',
'name': 'httk',
'prefix': 'httk'},
'query': {'representation': '/v1/files?filter=size+%3E+100000&response_fields=name%2Csize'},
'time_stamp': '2026-07-29T06:52:26.060687+00:00'}}
4. HAS — list membership¶
HAS is OPTIMADE’s membership test on a list-valued property. Here the list
is the set of file ids related to each calculation, so
files.id HAS "file-2" asks which calculations touched that file — and only
calc-1 did.
The matched calculation’s relationships object shows what the filter
searched: a files entry whose data array names each related resource by
type and id, with the declared role (input/output) carried in the
per-identifier meta.
# 4. List membership. HAS tests a list-valued property; here the list is the
# set of file ids related to each calculation.
show(client, "/v1/calculations", filter='files.id HAS "file-2"')
=== GET /v1/calculations?filter=files.id HAS "file-2" -> 200
{
"data": [
{
"attributes": {
"immutable_id": null,
"last_modified": "2021-03-15T10:30:00Z"
},
"id": "calc-1",
"relationships": {
"files": {
"data": [
{
"id": "file-1",
"meta": {
"role": "input"
},
"type": "files"
},
{
"id": "file-2",
"meta": {
"role": "output"
},
"type": "files"
}
]
}
},
"type": "calculations"
}
],
"links": {
"next": null
},
"meta": {
"api_version": "1.3.0",
"data_available": 2,
"data_returned": 1,
"implementation": {
"homepage": "https://httk.org/",
"issue_tracker": "https://github.com/httk/httk-optimade/issues",
"name": "httk-optimade",
"source_url": "https://github.com/httk/httk-optimade",
"version": "0.2.0"
},
"last_id": "calc-1",
"more_data_available": false,
"provider": {
"description": "This is a database hosted with the High-Throughput Toolkit (httk), for which the hoster has not specifically configured the provider.",
"name": "httk",
"prefix": "httk"
},
"query": {
"representation": "/v1/calculations?filter=files.id+HAS+%22file-2%22"
},
"time_stamp": "2026-07-29T06:52:26.070878+00:00"
}
}
{'data': [{'attributes': {'immutable_id': None,
'last_modified': '2021-03-15T10:30:00Z'},
'id': 'calc-1',
'relationships': {'files': {'data': [{'id': 'file-1',
'meta': {'role': 'input'},
'type': 'files'},
{'id': 'file-2', 'meta': {'role': 'output'}, 'type': 'files'}]}},
'type': 'calculations'}],
'links': {'next': None},
'meta': {'api_version': '1.3.0',
'data_available': 2,
'data_returned': 1,
'implementation': {'homepage': 'https://httk.org/',
'issue_tracker': 'https://github.com/httk/httk-optimade/issues',
'name': 'httk-optimade',
'source_url': 'https://github.com/httk/httk-optimade',
'version': '0.2.0'},
'last_id': 'calc-1',
'more_data_available': False,
'provider': {'description': 'This is a database hosted with the High-Throughput Toolkit (httk), for which the hoster has not specifically configured the provider.',
'name': 'httk',
'prefix': 'httk'},
'query': {'representation': '/v1/calculations?filter=files.id+HAS+%22file-2%22'},
'time_stamp': '2026-07-29T06:52:26.070878+00:00'}}
5. ?sort= — ordering by a declared-sortable property¶
A leading - sorts descending, so the largest file comes first. Sorting is
only offered for properties declared sortable when the adapter was built
(sortable={"files": ["size", "name"]} above, published in
/v1/info/files); asking to sort on anything else is answered with an OPTIMADE
error response, not silently ignored.
# 5. Sorting. A leading '-' sorts descending. Sorting on a property that was
# not declared sortable is an error response, not a silently ignored one.
show(client, "/v1/files", sort="-size", response_fields="name,size")
=== GET /v1/files?sort=-size&response_fields=name,size -> 200
{
"data": [
{
"attributes": {
"name": "trajectory.xyz",
"size": 1048576,
"url": "https://example.org/files/calc-2/trajectory.xyz"
},
"id": "file-3",
"type": "files"
},
{
"attributes": {
"name": "OUTCAR",
"size": 204800,
"url": "https://example.org/files/calc-1/OUTCAR"
},
"id": "file-2",
"type": "files"
},
{
"attributes": {
"name": "INCAR",
"size": 512,
"url": "https://example.org/files/calc-1/INCAR"
},
"id": "file-1",
"type": "files"
}
],
"links": {
"next": null
},
"meta": {
"api_version": "1.3.0",
"data_available": 3,
"data_returned": 3,
"implementation": {
"homepage": "https://httk.org/",
"issue_tracker": "https://github.com/httk/httk-optimade/issues",
"name": "httk-optimade",
"source_url": "https://github.com/httk/httk-optimade",
"version": "0.2.0"
},
"last_id": "file-1",
"more_data_available": false,
"provider": {
"description": "This is a database hosted with the High-Throughput Toolkit (httk), for which the hoster has not specifically configured the provider.",
"name": "httk",
"prefix": "httk"
},
"query": {
"representation": "/v1/files?sort=-size&response_fields=name%2Csize"
},
"time_stamp": "2026-07-29T06:52:26.079954+00:00"
}
}
{'data': [{'attributes': {'name': 'trajectory.xyz',
'size': 1048576,
'url': 'https://example.org/files/calc-2/trajectory.xyz'},
'id': 'file-3',
'type': 'files'},
{'attributes': {'name': 'OUTCAR',
'size': 204800,
'url': 'https://example.org/files/calc-1/OUTCAR'},
'id': 'file-2',
'type': 'files'},
{'attributes': {'name': 'INCAR',
'size': 512,
'url': 'https://example.org/files/calc-1/INCAR'},
'id': 'file-1',
'type': 'files'}],
'links': {'next': None},
'meta': {'api_version': '1.3.0',
'data_available': 3,
'data_returned': 3,
'implementation': {'homepage': 'https://httk.org/',
'issue_tracker': 'https://github.com/httk/httk-optimade/issues',
'name': 'httk-optimade',
'source_url': 'https://github.com/httk/httk-optimade',
'version': '0.2.0'},
'last_id': 'file-1',
'more_data_available': False,
'provider': {'description': 'This is a database hosted with the High-Throughput Toolkit (httk), for which the hoster has not specifically configured the provider.',
'name': 'httk',
'prefix': 'httk'},
'query': {'representation': '/v1/files?sort=-size&response_fields=name%2Csize'},
'time_stamp': '2026-07-29T06:52:26.079954+00:00'}}
6. Pagination — follow links.next¶
With page_limit=2 the server returns the first two files, sets
meta.more_data_available to true, and hands back a complete URL in
links.next. The client’s job is simply to keep following that link until the
server stops providing one — never to construct the next page’s URL itself,
since the server is free to paginate by offset, by cursor, or by anything else
behind that opaque link.
Note the URL that comes back is absolute (built from the baseurl given to
create_asgi_app) and carries the query parameters of the original request,
so filters and response_fields survive across pages.
# 6. Pagination. Never construct the next page's URL yourself: follow
# links.next until the server stops handing one back.
print("\n--- following links.next ---")
page_url = "/v1/files?page_limit=2&response_fields=name,size"
page_number = 0
while page_url is not None:
page_number += 1
page = show(client, page_url)
print(f"page {page_number}: " + ", ".join(entry["id"] for entry in page["data"]))
page_url = page["links"]["next"]
--- following links.next ---
=== GET /v1/files?page_limit=2&response_fields=name,size -> 200
{
"data": [
{
"attributes": {
"atime": null,
"checksums": null,
"ctime": null,
"description": null,
"immutable_id": null,
"last_modified": "2021-03-15T10:20:00Z",
"media_type": "text/plain",
"modification_timestamp": null,
"mtime": null,
"name": "INCAR",
"size": 512,
"url": "https://example.org/files/calc-1/INCAR",
"url_stable_until": null,
"version": null
},
"id": "file-1",
"type": "files"
},
{
"attributes": {
"atime": null,
"checksums": null,
"ctime": null,
"description": null,
"immutable_id": null,
"last_modified": "2021-03-15T10:25:00Z",
"media_type": "text/plain",
"modification_timestamp": null,
"mtime": null,
"name": "OUTCAR",
"size": 204800,
"url": "https://example.org/files/calc-1/OUTCAR",
"url_stable_until": null,
"version": null
},
"id": "file-2",
"type": "files"
},
{
"attributes": {
"atime": null,
"checksums": null,
"ctime": null,
"description": null,
"immutable_id": null,
"last_modified": "2021-03-16T08:00:00Z",
"media_type": "chemical/x-xyz",
"modification_timestamp": null,
"mtime": null,
"name": "trajectory.xyz",
"size": 1048576,
"url": "https://example.org/files/calc-2/trajectory.xyz",
"url_stable_until": null,
"version": null
},
"id": "file-3",
"type": "files"
}
],
"links": {
"next": null
},
"meta": {
"api_version": "1.3.0",
"data_available": 3,
"data_returned": 3,
"implementation": {
"homepage": "https://httk.org/",
"issue_tracker": "https://github.com/httk/httk-optimade/issues",
"name": "httk-optimade",
"source_url": "https://github.com/httk/httk-optimade",
"version": "0.2.0"
},
"last_id": "file-3",
"more_data_available": false,
"provider": {
"description": "This is a database hosted with the High-Throughput Toolkit (httk), for which the hoster has not specifically configured the provider.",
"name": "httk",
"prefix": "httk"
},
"query": {
"representation": "/v1/files"
},
"time_stamp": "2026-07-29T06:52:26.090297+00:00"
}
}
page 1: file-1, file-2, file-3
7. ?include= — a compound document¶
The relationships block only names related resources by type and id. A
client that wants the resources themselves adds include=files, and the
server returns them in full in a top-level included array — a JSON:API
compound document: one request instead of one round trip per relationship.
Each included resource appears once no matter how many entries reference it,
and included is a sibling of data, not nested inside it.
# 7. A compound document. The related resources named in each calculation's
# relationships block are returned in full in the top-level 'included'.
compound = show(client, "/v1/calculations", include="files")
print("\nincluded resources:", [(entry["type"], entry["id"]) for entry in compound["included"]])
=== GET /v1/calculations?include=files -> 200
{
"data": [
{
"attributes": {
"immutable_id": null,
"last_modified": "2021-03-15T10:30:00Z"
},
"id": "calc-1",
"relationships": {
"files": {
"data": [
{
"id": "file-1",
"meta": {
"role": "input"
},
"type": "files"
},
{
"id": "file-2",
"meta": {
"role": "output"
},
"type": "files"
}
]
}
},
"type": "calculations"
},
{
"attributes": {
"immutable_id": null,
"last_modified": "2021-03-16T08:30:00Z"
},
"id": "calc-2",
"relationships": {
"files": {
"data": [
{
"id": "file-3",
"meta": {
"role": "output"
},
"type": "files"
}
]
}
},
"type": "calculations"
}
],
"included": [
{
"attributes": {
"atime": null,
"checksums": null,
"ctime": null,
"description": null,
"immutable_id": null,
"last_modified": "2021-03-15T10:20:00Z",
"media_type": "text/plain",
"modification_timestamp": null,
"mtime": null,
"name": "INCAR",
"size": 512,
"url": "https://example.org/files/calc-1/INCAR",
"url_stable_until": null,
"version": null
},
"id": "file-1",
"type": "files"
},
{
"attributes": {
"atime": null,
"checksums": null,
"ctime": null,
"description": null,
"immutable_id": null,
"last_modified": "2021-03-15T10:25:00Z",
"media_type": "text/plain",
"modification_timestamp": null,
"mtime": null,
"name": "OUTCAR",
"size": 204800,
"url": "https://example.org/files/calc-1/OUTCAR",
"url_stable_until": null,
"version": null
},
"id": "file-2",
"type": "files"
},
{
"attributes": {
"atime": null,
"checksums": null,
"ctime": null,
"description": null,
"immutable_id": null,
"last_modified": "2021-03-16T08:00:00Z",
"media_type": "chemical/x-xyz",
"modification_timestamp": null,
"mtime": null,
"name": "trajectory.xyz",
"size": 1048576,
"url": "https://example.org/files/calc-2/trajectory.xyz",
"url_stable_until": null,
"version": null
},
"id": "file-3",
"type": "files"
}
],
"links": {
"next": null
},
"meta": {
"api_version": "1.3.0",
"data_available": 2,
"data_returned": 2,
"implementation": {
"homepage": "https://httk.org/",
"issue_tracker": "https://github.com/httk/httk-optimade/issues",
"name": "httk-optimade",
"source_url": "https://github.com/httk/httk-optimade",
"version": "0.2.0"
},
"last_id": "calc-2",
"more_data_available": false,
"provider": {
"description": "This is a database hosted with the High-Throughput Toolkit (httk), for which the hoster has not specifically configured the provider.",
"name": "httk",
"prefix": "httk"
},
"query": {
"representation": "/v1/calculations?include=files"
},
"time_stamp": "2026-07-29T06:52:26.098159+00:00"
}
}
included resources: [('files', 'file-1'), ('files', 'file-2'), ('files', 'file-3')]
Where to go next¶
Serving entry providers — writing the
EntryProviderthat supplies entry types, properties and records.examples/in_memory_backend.py— standing up a backend from plain dict rows without writing a provider at all.examples/provider_server/andexamples/demo_server/— the same API served over a real port, the latter also covering partial data and trajectories.