Skip to content

Expression search

With the Expression search API, you can submit a search, poll its progress and download the results and cell data, without touching the browser.

A search always follows the same pattern:

  1. POST /api/expression/submit returns a search_job_id.
  2. Poll GET /api/expression/status/<search_job_id> until the status is completed.
  3. GET /api/expression/results/<search_job_id> returns the expression data.

Endpoints

Method Path Auth Purpose
POST /api/expression/submit Required Submit a search query
GET /api/expression/status/<search_job_id> Required Poll search progress
GET /api/expression/results/<search_job_id> Required Expression results
GET /api/expression/filter-options/<search_job_id> Required Cell types present in a result
GET /api/expression/results/<search_job_id>/cells/info Required Cells summary, datasets, whitelists
GET /api/expression/results/<search_job_id>/cells Required Cell IDs and barcodes per sample
POST /api/expression/results/<search_job_id>/cells/download Required ZIP archive of cell data
POST /api/expression/all-cells-download Required Bulk cells table as gzip TSV
POST /api/expression/cell-expression-export Required Queue a per-cell matrix export
GET /api/expression/cell-expression-export/<export_id> Required Export status
GET /api/expression/cell-expression-export/<export_id>/download Required Export ZIP
POST /api/expression/interpret Required Ask the LLM to interpret a prompt
GET /api/expression/interpret/<task_id> Required Poll the interpretation task
POST /api/expression/sample-metadata Required Metadata for a set of sample IDs
GET /api/expression/quota Required Remaining daily quota
GET /api/expression/datasets Public List datasets
POST /api/expression/search-context-rows Public Server-side result table rows
POST /api/expression/suggestions Public Typo-tolerant query suggestions
curl -X POST https://malva.mdc-berlin.de/api/expression/submit \
  -H "Authorization: Bearer malva_..." \
  -H "Content-Type: application/json" \
  -d '{"query": "CD3E MS4A1"}'

Request body:

Field Type Description
query string Required. Gene names, a FASTA sequence, or a natural-language query such as T cells in lung
dataset_ids array of int Optional. Restrict the search to specific datasets
count_at_least int Optional. Lower bound on cell count
count_at_most int Optional. Upper bound on cell count
unstranded bool Optional. Treat sequences as unstranded

Response:

{
  "job_id": "local-job-uuid",
  "search_job_id": "search-job-uuid",
  "status": "running",
  "searches_remaining": 48
}

The query can contain up to 10 FASTA sequences of up to 10,000 base pairs each. Longer entries are truncated. Each submission consumes one unit of your daily quota. When the quota is exhausted the endpoint returns 429 with quota_exceeded.

Poll the status

curl https://malva.mdc-berlin.de/api/expression/status/<search_job_id> \
  -H "Authorization: Bearer malva_..."
{
  "status": "running",
  "progress": 0.6,
  "stage": "searching",
  "sequence_ids": ["..."]
}

Poll every few seconds until status is completed. If the search service rejects the query, the status becomes error.

Read the results

curl https://malva.mdc-berlin.de/api/expression/results/<search_job_id> \
  -H "Authorization: Bearer malva_..."
{
  "status": "completed",
  "genes": {
    "CD3E": {
      "ncells": 15342,
      "expression_data": {"samples": [...], "cell_types": [...], "data": [...]},
      "sequence": "ACGT...",
      "sequence_length": 300
    }
  }
}

genes maps each searched sequence to its expression data. The expression_data object contains the samples and cell types that matched, and the compact expression table that the web interface renders. This is the same data that drives the results table, so the same display modes apply: relative expression, fraction expressing, raw kmer hits and cell count.

Cell data

Cells summary

curl https://malva.mdc-berlin.de/api/expression/results/<search_job_id>/cells/info \
  -H "Authorization: Bearer malva_..."

Returns {total_cells, dataset_ids, has_whitelists, samples} where samples maps each sample ID to {n_cells, dataset_id, protocol, whitelist_id, whitelist_type}.

Cell IDs and barcodes

curl "https://malva.mdc-berlin.de/api/expression/results/<search_job_id>/cells?sample_ids=1,2&barcodes=1" \
  -H "Authorization: Bearer malva_..."

Returns {has_whitelists, samples} where each sample maps to {cell_ids, barcodes}. This is the endpoint the malva_client package uses to fetch the exact cell set of a search.

Cell data archive

curl -X POST https://malva.mdc-berlin.de/api/expression/results/<search_job_id>/cells/download \
  -H "Authorization: Bearer malva_..." \
  -H "Content-Type: application/json" \
  -d '{"sample_ids": [1, 2], "include_barcodes": true}'

Returns a ZIP archive (malva_cells_<id>_<ts>.zip) with a MANIFEST.txt and gzipped TSV files for cell IDs, barcodes, normalization factors, sample and cell type summaries, and sample metadata.

Bulk download

curl -X POST https://malva.mdc-berlin.de/api/expression/all-cells-download \
  -H "Authorization: Bearer malva_..." \
  -H "Content-Type: application/json" \
  -d '{"sample_ids": [1, 2, 3]}'

Returns a gzip-compressed TSV (malva_all_cells_<ts>.tsv.gz) with the columns sample_id, cell_id, cell_type, total_counts. Accepts at most 50 sample IDs.

Per-cell expression matrix

curl -X POST https://malva.mdc-berlin.de/api/expression/cell-expression-export \
  -H "Authorization: Bearer malva_..." \
  -H "Content-Type: application/json" \
  -d '{"items": [{"job_id": "search-job-uuid", "feature": "CD3E"}], "sample_ids": [1, 2]}'

The request queues an export on the search service and returns an export_id. Poll GET /api/expression/cell-expression-export/<export_id> until the export is ready, then stream the ZIP with GET /api/expression/cell-expression-export/<export_id>/download.

Query interpretation

POST /api/expression/interpret sends a natural-language prompt to the LLM service and returns a task_id. Poll GET /api/expression/interpret/<task_id> until the status is completed; the response contains the reformatted query, the extracted entities and the query type. This is the same engine that powers query interpretation in the web interface.

Sample metadata

curl -X POST https://malva.mdc-berlin.de/api/expression/sample-metadata \
  -H "Authorization: Bearer malva_..." \
  -H "Content-Type: application/json" \
  -d '{"ids": [1, 2, 3]}'
{
  "metadata": {
    "1": {"organ": "lung", "disease": "COVID-19"},
    "2": {"organ": "lung", "disease": "control"}
  }
}

Quota

GET /api/expression/quota returns {can_search, searches_remaining, account_type}. See Quotas and limits for what the numbers mean.