Docs / Genome Source Releases API / Endpoints / List source releases
List source releases
One row per upstream source, newest probe first. `release_version` is that source's current release as a string — `"237"`, `"18.37.0"`, `"20130502"` — and `status` says how the latest probe went, which is the half a version number alone cannot tell you: a release that has not moved in two years and a probe that has been failing for two days both look like an unchanged string. A failed probe leaves the last good version standing rather than nulling it, so read `retrieved_at` (the last successful read) against `ingested_at` (the last probe) to see how stale an answer is. `order` takes `ingested_at` and nothing else, and it decides very little: one probe pass writes every row in a single batch, so they all share an `ingested_at` and the whole collection fits in one page at any `limit` this API offers. For the upstream registry's own ordering, read `sort_order` off the rows — that is the field's whole job.
Query parameters
| Parameter | Type | Description |
|---|---|---|
status |
string | How the latest probe went: `ok`, `degraded`, `unreachable` or `error`. `?status=ok` is how you ask for the rows to trust today — a failing source still carries its last good `release_version`. |
authority |
string | The publishing body, exactly as the row carries it — `NCBI`, `EMBL-EBI`. Case-sensitive, and an unknown value answers an empty page rather than a 400, because this vocabulary is the upstream registry's and grows when a source from a new body is added. |
category |
string | The registry's own kind label — `reference_sequence`, `genome_metadata`, `variation`, `population_reference`. Open for the same reason `authority` is. |
limit |
integer | Items per page, 1–100. Defaults to 25. |
cursor |
string | Opaque cursor from a previous response's meta.next. Do not construct one. |
order |
string | Sort field: `ingested_at`, each optionally prefixed with `-` to reverse. Default is `-ingested_at`. Rows with no value for the sort column always sort LAST in either direction, so paging never leads with undated rows. |
source |
string | Restrict to ONE scraper source, by its stem — `abb`, not `ABB` or `abb-bank.az`. Not a list: `?source=a,b` and a repeated `?source=` do not select two sources. On `/v1/jobs` an unknown stem is a `400` naming it, and `source_type` is the way to select several sources at once. `GET /v1/sources` lists every stem. |
Request
curl -G https://api.softon.dev/v1/genome/releases \
-H "Authorization: Bearer $SOFTON_KEY" \
-d status=ok -d authority=NCBI -d category=variation
req, _ := http.NewRequestWithContext(ctx, "GET", "https://api.softon.dev/v1/genome/releases", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("SOFTON_KEY")) q := req.URL.Query() q.Set("status", "ok") q.Set("authority", "NCBI") q.Set("category", "variation") req.URL.RawQuery = q.Encode() res, err := http.DefaultClient.Do(req) if err != nil { log.Fatal(err) } defer res.Body.Close() var page struct { Data []Release `json:"data"` Meta struct{ Next *string `json:"next"` } `json:"meta"` } if err := json.NewDecoder(res.Body).Decode(&page); err != nil { log.Fatal(err) } // page.Meta.Next → send it back as ?cursor= for the following page
import json, os, urllib.parse, urllib.request url = "https://api.softon.dev/v1/genome/releases" + "?" + urllib.parse.urlencode({ "status": "ok", "authority": "NCBI", "category": "variation", }) req = urllib.request.Request(url, headers={ "Authorization": "Bearer " + os.environ["SOFTON_KEY"], }) page = json.load(urllib.request.urlopen(req)) # page["meta"]["next"] → send it back as ?cursor= for the following page
const url = new URL("https://api.softon.dev/v1/genome/releases"); url.searchParams.set("status", "ok"); url.searchParams.set("authority", "NCBI"); url.searchParams.set("category", "variation"); const res = await fetch(url, { headers: { Authorization: `Bearer ${process.env.SOFTON_KEY}` }, }); if (!res.ok) throw new Error(`${res.status} ${await res.text()}`); const { data, meta } = await res.json(); // meta.next → send it back as ?cursor= for the following page
Response
The envelope is identical on every softon.dev API: data, meta, error. Only the shape inside data changes per dataset — see the response envelope.
{
"data": [
{
"id": "refseq",
"source": "ncbi_ebi",
"source_id": "refseq",
"source_name": "RefSeq",
"authority": "NCBI",
"category": "reference_sequence",
"landing_url": "https://www.ncbi.nlm.nih.gov/refseq/",
"api_url": null,
"bulk_url": "https://ftp.ncbi.nlm.nih.gov/refseq/release/",
"update_frequency": "periodic_release",
"release_version": "237",
"release_date": null,
"checksum": null,
"format": "flatfile",
"retrieved_at": "2026-09-15T05:48:10.412Z",
"status": "ok",
"extra": {
"probe_status": 200,
"probe_url": "https://ftp.ncbi.nlm.nih.gov/refseq/release/RELEASE_NUMBER"
},
"sort_order": 10,
"ingested_at": "2026-09-15T05:48:11.204817Z"
}
],
"meta": { "count": 1, "next": "eyJvIjoyfQ", "request_id": "req_9Fv3" },
"error": null
}
Fields of each item in data[]
| Field | Type | Description |
|---|---|---|
id |
string | The upstream registry's own key — `refseq`, `dbsnp`, `igsr_1000g`, `ncbi_datasets`. **The one id on this API with no `<source>:` prefix**, because the row's identity IS an upstream source; it is the same string as `source_id` and it is what `/v1/genome/releases/{id}` takes. |
source |
string | Which softon scraper delivered this row. One probe pass reads every source here, so it is the same value on every row — `source_id` is the field that varies, and the one you almost certainly mean. |
source_id |
string | The upstream registry's key, identical to `id`. Published under this name because it is the name the registry this replaces uses, so a consumer writing softon's answer back into that registry keys on it without a mapper. |
source_name |
string | What the source calls itself: `RefSeq`, `dbSNP`, `1000 Genomes / IGSR`, `NCBI Datasets`. |
authority |
string | The body that publishes it — `NCBI` or `EMBL-EBI`. |
category |
string | The registry's own label for what kind of source this is: `reference_sequence`, `genome_metadata`, `variation`, `population_reference`. A shared free-form vocabulary rather than a closed set, so `?category=` accepts anything and answers an empty page for a label no source carries. |
landing_url |
string · nullable | The human-facing page for this source. Never the FTP index — this is the one to link a reader to. |
api_url |
string · nullable | The source's machine-facing API root, where it has one. Null for RefSeq and IGSR, which have none. |
bulk_url |
string · nullable | The source's bulk archive root, where it has one. Null for NCBI Datasets. **Recorded, never mirrored**: nothing on this platform downloads it, and this API serves no data from behind it. |
update_frequency |
string | How often the SOURCE cuts a release — `periodic_release`, `release_based`, `continuous`. Not how often softon probes it: the probe runs daily whatever this says. |
release_version |
string · nullable | The release the source is currently on, **as a string and never a number**: `"237"`, `"18.37.0"`, `"157"`, `"20130502"`. Release names are not all numeric and a JSON number would also defeat a `?? "—"` null check the day one of them is `0`. **This is a release MARKER and nothing else.** It pins no download, selects no file and carries no sequence, assembly, annotation or variant data — for RefSeq, for instance, assembly facts come from the separate ASSEMBLY_REPORTS bulk tables and not from anything here. Null until a probe has succeeded once. After that it is **sticky**: a probe that fails leaves the last good value standing and moves `status` instead, so read the two together rather than treating a non-null version as fresh. |
release_date |
date · nullable | The release's own date where the source publishes one. Today only `igsr_1000g` has it, and there it is the **freeze name** sliced out of a directory name (`20130502` → `2013-05-02`) — an identity, not a publication date. Deliberately not filled from an upstream `Last-Modified`, which is a different fact. |
checksum |
string · nullable | The version signal for sources identified by a digest rather than a number. Null on every source served today. |
format |
string · nullable | How the UPSTREAM distributes its data — `flatfile`, `rest_v2`, `ftp_release_dirs`, `text_release_notes`. Not the format of this response, which is JSON for all of them. |
retrieved_at |
timestamp · nullable | When softon last **successfully** read this upstream. Not when your request arrived, and never advanced by a probe that failed — it is a provenance claim, and a failed refresh that moved it would assert a read that did not happen. Compare it with `ingested_at`, which is the last probe of any kind, to see how stale the answer is. Null only for a source that has never answered. |
status |
string | How the LATEST probe went. Four values, and they are four because the distinctions matter to different people: `ok` — a release was read. `degraded` — the host answered 2xx and nothing parseable came back, so the upstream's format moved and a person has to fix the probe. `unreachable` — the host never answered: DNS, TLS, connection or timeout. Nothing is wrong with softon's parser and nothing is wrong with the data. `error` — the host answered with a non-2xx. The feed this replaces collapsed `unreachable` into `degraded` and then counted the source as reachable anyway, so an outage read as a parser bug. |
extra |
object · nullable | Per-source probe metadata: which URL was read, what it answered, and the handful of fields the upstream published beside the version. **An allow-listed projection, not the upstream body** — one of these endpoints echoes a submitted API key back inside its own error body, so a verbatim pass-through would publish a credential. Null where a source records none. |
sort_order |
integer | This source's position in the upstream registry's declaration order, 1–10. Sort on it to reproduce that order: this collection pages on `ingested_at` like every other one here, and one probe pass stores every row at the same instant, so the order rows come back in is the id's and not the registry's. |
ingested_at |
timestamp | When the latest probe ran and this version was stored — successful or not. See `retrieved_at` for the last successful read. |
Try it
Send the request to see a response.