NAV
r python

Introduction

LAPIS (Lightweight API for Sequences) is an open web application programming interface (API) allowing easy querying of SARS-CoV-2 sequencing data using web links. The core features are:

This instance uses fully public data from NCBI GenBank pre-proceessed and hosted by Nextstrain. We update the data every day. More information about the underlying software and the code can be found in our Github repository.

In following, we demostrate the core features enabled by the API. On the left, we present the basic syntax of the API and on the right, we show how to use it for queries. In the section "Use Cases", we provide examples how to use the API to query public SARS-CoV-2 sequencing data to generate statistics, create plots, or download sequences for further analysis.

Overview

The API has six main endpoints related to samples. These endpoints provide different types of data:

The API returns a response (data) based on a query to one of the endpoints. You can view a responses in your browser, or use the data programatically. We'll provide some examples in r and python.

Query Format

Query example:

Get the total number of available sequences:
/sample/aggregated

To query an endpoint, use the web link with prefix https://cov-spectrum.ethz.ch/public/api/v0 and the suffix for the relevant endpoint. In the examples, we only show the suffixes to keep things simple, but you can click to try the full link in your browser.

Response Format

Response example:

{
  "info":{"apiVersion":1,"deprecationDate":null,"deprecationInfo":null},
  "errors":[],
  "payload":[{"count":913515}]
}

Responses are returned in JSON format with three top level attributes:

Filters

Examples:

Get the number of all samples in Switzerland in 2021:
/sample/aggregated?country=Switzerland&dateFrom=2021-01-01&dateTo=2021-12-31

{
  "info":{"apiVersion":1,"deprecationDate":null,"deprecationInfo":null},
  "errors":[],
  "payload":[{"count":22701}]
}

Get details about samples from lineage AY.1 in Geneva, Switzerland:
/sample/details?country=Switzerland&division=Geneva&pangoLineage=AY.1

{
  "info": {"apiVersion":1,"deprecationDate":null,"deprecationInfo":null},
  "errors": [],
  "payload": [
    {
      "date": "2021-05-26",
      "dateSubmitted": "2021-06-29",
      "region": "Europe",
      "country": "Switzerland",
      "division": "Geneva",
      "location": null,
      "regionExposure": "Europe",
      "countryExposure": "Switzerland",
      "divisionExposure": "Geneva",
      "age": null,
      "sex": null,
      "host": "Homo sapiens",
      "samplingStrategy": null,
      "pangoLineage": "AY.1",
      "nextstrainClade": "21A (Delta)",
      "gisaidCloade": null,
      "submittingLab": null,
      "originatingLab": null,
      "genbankAccession": "OU268406",
      "sraAccession": null,
      "gisaidEpiIsl": "EPI_ISL_2405325"
    },
    ...
  ]
}

Large queries, for example detailed information on all the samples, will take a bit. Instead, we can adapt the query to filter to only samples of interest. The syntax for additing filters is <attribute1>=<valueA>&<attribute2>=<valueB>.

All six sample endpoints can be filtered by the following attributes:

The endpoints details, aa-mutations, nuc-mutations, fasta, and fasta-aligned can additionally be filtered by these attributes:

To determine which values are available for each attribute, see the example in section "Aggregation".

Filter Pango Lineages

Get the total number of samples of the lineage B.1.617.2 without sub-lineages:
/sample/aggregated?pangoLineage=B.1.617.2

Get the total number of samples of the lineage B.1.617.2 including sub-lineages:
/sample/aggregated?pangoLineage=B.1.617.2*

Pango lineage names inherit the hierarchical nature of genetic lineages. For example, B.1.1 is a sub-lineage of B.1. More information about the pango nomenclature can be found on the website of the Pango network.

With the pangoLineage filter, it is possible to not only filter for a very specific lineage but also to include its sub-lineages. To include sub-lineages, add a * at the end. For example, writing B.1.351 will only give samples of B.1.351. Writing B.1.351* or B.1.351.* (there is no difference between these two options) will return B.1.351, B.1.351.1, B.1.351.2, etc.

An official pango lineage name can only have at most three number components. A sub-lineage of a lineage with a maximal-length name (e.g., B.1.617.2) will get an alias. A list of aliases can be found here. B.1.617.2 has the alias AY so that AY.1 would be a sub-lineage of B.1.617.2. LAPIS is aware of aliases. Filtering B.1.617.2* will include every lineage that starts with AY. It is further possible to search for B.1.617.2.1 which will then return the same results as AY.1.

Filter Mutations

Get the total number of samples with the synonymous nucleotide mutations 913T and 5986T and the amino acid mutation S:484K:
/sample/aggregated?nucMutations=913T,5986T&aaMutations=S:484K

Get the total number of samples for which we do not know whether the S:501 position is mutated:
/sample/aggregated?aaMutations=S:501X

It is possible to filter for amino acid and nucleotide bases/mutations. Multiple mutations can be provided by specifying a comma-separated list.

A nucleotide mutation has the format <position><base>. A "base" can be one of the four nucleotides A, T, C, and G. It can also be - for deletion and N for unknown.

An amino acid mutation has the format <gene>:<position><base>. The following genes are available: E, M, N, ORF1a, ORF1b, ORF3a, ORF6, ORF7a, ORF7b, ORF8, ORF9b, S. A "base" can be one of the 20 amino acid codes. It can also be - for deletion and X for unknown.

Additional features are coming soon. For example, it will be possible to filter for any mutations at a certain position.

Aggregation

Examples:

Get the number of B.1.1.7 samples per country:
/sample/aggregated?fields=country&pangoLineage=B.1.1.7

{
  "info": {"apiVersion":1,"deprecationDate":null,"deprecationInfo":null},
  "errors": [],
  "payload": [
    {"country": "Austria", "count": 82},
    {"country": "Bahrain", "count": 48},
    ...
  ]
}

Get the number of samples per Nextstrain clade and country:
/sample/aggregated?fields=nextstrainClade,country

{
  "info": {"apiVersion":1,"deprecationDate":null,"deprecationInfo":null},
  "errors": [],
  "payload": [
    {"nextstrainClade": "19A", "country": "Australia", "count": 317},
    {"nextstrainClade": "19A", "country": "Bahrain", "count": 2},
    ...
  ]
}

Get all the possible values for attribute "division" in Swtizerland:
/sample/aggregated?division,country=Switzerland

{
  "info": {"apiVersion":1,"deprecationDate":null,"deprecationInfo":null},
  "errors": [],
  "payload": [
    {"division": "Basel-Land", "count": 4658},
    {"division": "Aargau", "count": 2964},
    ...
  ]
}

Above, we used the /sample/aggregated endpoint to get the total counts of sequences with or without filters. Using the query parameter fields, we can group the samples and get the counts per group. For example, we can use it to get the number of samples per country. We can also use it to list the available values for each attribute.

fields accepts a comma-separated list. The following values are available:

Use Cases

We demonstrate two use cases for this API. You can switch between examples in Python and R in the top right.

Plot the global distribution of all sequences

library(jsonlite)
library(ggplot2)

# Query the API
response <- fromJSON("https://cov-spectrum.ethz.ch/public/api/v0/sample/aggregated?fields=region")

# Check for errors
errors <- response$errors
if (length(errors) > 0) {
  stop("Errors")
}

# Check for deprecation
deprecationDate <- response$info$deprecationDate
if (!is.null(deprecationDate)) {
  warning(paste0("This version of the API will be deprecated on ", deprecationDate,
                 ". Message: ", response$info$deprecationInfo))
}

# The data is good to be used!
data <- response$payload

# Make a plot
ggplot(
  data,
  aes(x = "", y = count, fill = region)) + 
  geom_bar(width = 1, stat = "identity") + 
  coord_polar("y", start = 0) + 
  theme_minimal() + 
  theme(
    panel.grid=element_blank(),
    panel.border = element_blank(),
    axis.ticks = element_blank(),
    axis.title.x = element_blank(), 
    axis.title.y = element_blank(),
    axis.text.x = element_blank())
Not implemented yet. Please take a look at the R example.

Steps:

  1. Query data from the API
  2. Check whether there are errors. If yes, abort!
  3. Check whether a deprecation date is given. If yes, write a warning.
  4. Parse data from JSON as a data frame.
  5. Use the data frame to create a plot.

Plot the count of delta samples in a country in the past 100 days

library(jsonlite)
library(ggplot2)

# Query the API
date_from <- format(Sys.Date() - as.difftime(100, unit = "days"), "%Y-%m-%d")
query <- paste0(
  "https://cov-spectrum.ethz.ch/public/api/v0/sample/aggregated?",
  "fields=date",
  "&country=Switzerland",
  "&dateFrom=", date_from,
  "&pangoLineage=B.1.617.2*"
)
response <- fromJSON(query)

# Check for errors
errors <- response$errors
if (length(errors) > 0) {
  stop("Errors")
}

# Check for deprecation
deprecationDate <- response$info$deprecationDate
if (!is.null(deprecationDate)) {
  warning(paste0("This version of the API will be deprecated on ", deprecationDate,
                 ". Message: ", response$info$deprecationInfo))
}

# The data is good to be used!
data <- response$payload

# Make a plot
ggplot(
  data,
  aes(x = as.Date(date), y = count)) + 
  geom_col() + 
  theme_bw() + 
  labs(x = element_blank(), y = "Count") + 
  scale_x_date(date_breaks = "1 month", date_labels = "%B %Y") + 
  ggtitle("Count of delta samples in Switzerland in the past 100 days")
Not implemented yet. Please take a look at the R example.