[toc]
This guide describes the **read-only Open API** endpoints exposed by the SciLifeLab Serve system. The audience is expected to be comfortable with HTTP requests, query parameters, and JSON responses, but not necessarily familiar with Django/DRF internals.
## Base path and versioning
The URLs shown in the docstrings use a versioned prefix (example: `/openapi/v1/...`). Versioning is read from `request.version` and is used by the **API Info** endpoint.
In examples below, the base is written as:
- `/<BASE>/<VERSION>/...` (example: `/openapi/v1/...`)
## Common response shape
Most endpoints return JSON shaped like:
- `{"data": ...}` (typical for list/lookup/stats)
- `{"app": ...}` (single public app retrieve)
- `{"status": true}` (healthcheck)
- or error payloads like `{"error": "..."}`
Timestamps and fields come directly from the system objects and may include date-time values.
---
# Generic API endpoints
## Health check: `are-you-there`
**GET** `/<BASE>/<VERSION>/are-you-there`
Docstring wording:
> Most simple API endpoint useful for testing
> and verifications.
> :returns bool: true
---
## System version: `system-version`
**GET** `/<BASE>/<VERSION>/system-version`
Docstring wording:
> Gets the version of the deployed application.
> :returns dict: A dictionary of system version information.
Returned fields include:
- `system-version`
- `build-date`
- `image-tag`
---
## API information: `api-info`
**GET** `/<BASE>/<VERSION>/api-info`
Docstring wording:
> The Open API basic API information endpoints, in accordance to the standard
> https://dev.dataportal.se/rest-api-profil/versionhantering
> :returns dict: A dictionary of app-info information.
This endpoint returns different payloads depending on the requested API version. It explicitly handles:
- `beta`
- `v1`
### Version: `beta`
Returned fields:
- `apiName`
- `apiVersion`
- `apiReleased`
- `apiDocumentation`
- `apiStatus`
- `latest-api-version`
### Version: `v1`
Returned fields:
- `apiName`
- `apiVersion`
- `apiReleased`
- `apiDocumentation`
- `apiStatus`
- `latest-api-version`
Notes:
- `apiReleased` and `apiDocumentation` are currently `"TODO"` in code.
- `latest-api-version` is `"v1"` in both cases.
---
# Public Apps resource API
## List public apps: `public-apps`
**GET** `/<BASE>/<VERSION>/public-apps`
Docstring wording:
> This endpoint gets a list of public apps.
> :returns list: A list of app information, ordered by date created from the most recent to the oldest.
>
> Available GET query parameters:
> - limit: integer. Optional. Determines the maximum number of records to return.
>
> Example requests:
> /openapi/v1/public-apps
> /openapi/v1/public-apps?limit=5
### Query parameters
- `limit` (optional, integer): maximum number of records to return.
- If provided but cannot be parsed as an integer, the API returns HTTP 403 with an error message.
### What is included
- Only app instances with `access="public"` are included.
- Deleted apps are excluded based on derived `app_status`.
### Returned fields (per app)
Each list item includes, at minimum:
- `id`
- `name`
- `url`
- `description`
- `created_on`
- `updated_on`
- `access`
- `k8s_user_app_status`
- `latest_user_action`
- `app_status` (computed)
- `app_type`
- `table_field.url` (for compatibility with previous schema)
The following field is intentionally omitted:
- `app_id` (removed because it only refers to the app type)
### Ordering and limiting
- Items are sorted by `created_on` (most recent first).
- `limit` truncation is applied after sorting.
### Example output
```json
{
"data": [
{
"id": 123,
"name": "My Public App",
"url": "https://…",
"description": "…",
"created_on": "2025-12-01T10:00:00Z",
"updated_on": "2025-12-10T12:00:00Z",
"access": "public",
"latest_user_action": "…",
"k8s_user_app_status": "Running",
"app_status": "Running",
"app_type": "Streamlit",
"table_field": {"url": "https://…"}
}
]
}
```
### Errors
- HTTP 403 if `limit` is invalid.
- HTTP 500 if the system is unable to collect the list of public apps.
---
## Retrieve a single public app: `public-apps/<id>`
**GET** `/<BASE>/<VERSION>/public-apps/<int:pk_in>`
Docstring wording:
> This endpoint retrieves a single public app instance.
> :pk_in: The primary key of the public app to return.
> :returns dict: A dict of app information.
### Path parameter
- `pk_in` (required, integer): the primary key of the app instance.
If `pk_in` is missing or less than 1, the API returns HTTP 403.
### Returned fields
The response contains an `app` object with:
- `id`
- `name`
- `url`
- `description`
- `created_on`
- `updated_on`
- `access`
- `k8s_user_app_status`
- `app_status`
- `app_type`
The following field is intentionally omitted:
- `app_id`
### Not-found and validation behavior
The endpoint returns HTTP 404 in the following cases:
- No app exists with the given id.
- The app exists but is of an incorrect type.
- The app is not public.
- The app has been deleted.
### Server errors
Unexpected failures retrieving the app return HTTP 500.
---
# Content statistics resource API
## Content statistics: `content-stats`
**GET** `/<BASE>/<VERSION>/content-stats`
Docstring wording:
> The Content Stats API with read-only methods to get aggregated statistics
> about the content in the system.
>
> The top-level elements nested under data include:
> - stats_date_utz
> - stats_success
> - stats_message
> - stats_notes
> - n_projects
> - n_users
> - n_apps
> - n_apps_public
> - apps_by_type
> - new_users_by_year
> - users_by_university
> - apps_by_image_registry
### Interpretation notes
- `stats_date_utz` is set to the current time in UTC.
- `stats_success` indicates whether all statistic sub-queries succeeded.
- If parts fail, `stats_success` is set to false and `stats_message` accumulates one or more messages.
- Numeric counts default to `-1` if the related computation fails.
- `apps_by_type` starts from a predefined set (`customapp`, `dashapp`, `gradio`, `shinyapp`, `streamlit`, `tissuumaps`) and may grow dynamically.
- All shiny-based apps are grouped under `shinyapp`.
- Image registry classification includes `dockerhub`, `ghcr`, and `noimage`.
---
# Supplementary lookups API
## Universities lookup: `lookups/universities`
**GET** `/<BASE>/<VERSION>/lookups/universities`
Docstring wording:
> This method handles the /universities endpoint.
> With a query string get parameter "code" it returns a single university.
> Without a query string parameter it returns the entire list.
>
> :returns list of dict or dict: The dict contains attributes code and name.
### Query parameters
- `code` (optional): university code.
- Converted to lowercase.
- Must be alphabetic and no longer than 10 characters.
### Behavior
- With no query parameters, the endpoint returns the full list of universities.
- With `code=<value>`, the endpoint returns a single university entry.
### Errors
- Validation error if `code` is invalid.
- Not found error if no university matches the provided code.
- Method not allowed if unsupported query parameters are provided.
- Server error if the universities JSON file cannot be read.
---
## Departments lookup: `lookups/departments`
**GET** `/<BASE>/<VERSION>/lookups/departments`
Docstring wording:
> Gets a list of departments.
### Behavior
- Returns the list of departments as defined in the system JSON file.
### Errors
- Server error if the departments JSON file cannot be read.
---
# Error handling expectations
Depending on endpoint and failure mode, responses may include:
- HTTP 403 for invalid user input (for example, invalid `limit` or app id).
- HTTP 404 for non-existent, deleted, or non-public resources.
- HTTP 500 for unexpected server-side failures.
- Standard Django REST Framework exceptions such as `ValidationError`, `NotFound`, `MethodNotAllowed`, and `APIException`.
The SciLifeLab Serve user guide is powered by django-wiki, an open source application under the GPLv3 license. Let knowledge be the cure.


