Search Annotrieve annotations by gene name (symbol / synonym), then drill into genes and xrefs inside one annotation.
Base URL: https://genome.crg.es/annotrieve-xrefs/api/v1
This service is a sidecar to Annotrieve. Annotrieve owns the main annotation catalogue; this API owns name → annotation presence search and per-annotation gene/xref drill-in. It is not a replacement for NCBI Gene or Ensembl.
Typical flow (happy path):
- Discover names (optional) —
GET /names?prefix=…(prefix suggestions with categories) - Search by exact gene names —
GET|POST /hits?names=…(symbol∪alias; no wildcards) - Drill in —
GET /annotations/{id}/genes?names=…and…/xrefs - Or browse xrefs —
…/namespaces→…/namespaces/{ns}/accessions?q=…→…/genes?curies=ns:acc
Paged list endpoints use limit, offset, and exact total:
{ "total": 814, "limit": 100, "offset": 0, "results": [ … ] }- Default
limitis 100; maximum is 200. - Maximum
offsetis 10_000.
/hits returns the full hit set (compact fields + rows; no offset paging).
Failed responses: {"detail": {"code": "...", "message": "..."}}. Validation failures are 422 with code=validation_error.
Production rate limits: 3 req/s per IP, ~20 req/s globally. Saturated /hits concurrency returns 503 with Retry-After: 1.
/hits and /annotations/{id}/genes?names=… match symbol ∪ alias (casefolded; TP53 → tp53).
Inside one annotation, browse any harvested namespace (Tier A and chip namespaces) via /namespaces → /namespaces/{ns}/accessions, then resolve with /genes?curies=ns:accession. Per-gene chips remain at …/genes/{local_id}/xrefs.
Paths bow are relative to the base URL.
Prefix discovery for the gene-name sheet. Returns distinct symbol∪alias accessions under a prefix, with annotation counts in the current taxon scope and a server-assigned category.
| Parameter | Notes |
|---|---|
prefix |
Required. Casefolded stem, no *. At least 3 characters (422 prefix_too_short). * → 422 invalid_prefix. |
include_taxids |
Optional CSV; keep annotations whose lineage includes any listed taxid. Cap 50 (422 too_many_taxids). |
exclude_taxids |
Optional; drop annotations whose lineage includes any listed taxid. Cap 50. |
More than 500 distinct names → 422 prefix_too_broad. No pagination (bounded by that expand cap). Results ordered by n_annotations desc, then name. Categories (first-match-wins): pseudogene, numbered, homeolog, predicted, exact, other.
GET /names?prefix=seleno&include_taxids=7742{
"prefix": "seleno",
"include_taxids": [7742],
"exclude_taxids": [],
"total": 37,
"results": [
{"name": "selenop", "category": "exact", "n_annotations": 8123}
]
}Gene-name presence search (primary API). Exact names only against symbol ∪ alias (casefolded). A name containing * → 422 wildcard_not_supported (use GET /names for prefix discovery).
| Parameter | Notes |
|---|---|
names |
Required. CSV (GET) or JSON list (POST). Cap GET 200 / POST 200. Casefolded duplicates count once. |
include_taxids |
Optional CSV/list; keep annotations whose lineage includes any listed taxid. Cap 50 taxids (422 too_many_taxids). |
exclude_taxids |
Optional; drop annotations whose lineage includes any listed taxid. Cap 50. |
GET /hits?names=SELENOP,SEPP1,TP53&include_taxids=7742
POST /hits
{"names": ["SELENOP", "SEPP1", "TP53"], "include_taxids": [9606]}{
"terms": [
{"input": "SELENOP", "name": "selenop", "namespaces": ["symbol"], "n_hits": 1},
{"input": "TP53", "name": "tp53", "namespaces": ["symbol"], "n_hits": 1}
],
"sources": {"1": {"label": "Ensembl"}, "2": {"label": "GenBank"}, "3": {"label": "RefSeq"}},
"include_taxids": [7742],
"exclude_taxids": [],
"total": 2,
"fields": ["annotation_id", "taxid", "source", "matched", "release_date", "assembly_name"],
"rows": [
["ann-human", 9606, 3, [0, 1], "2024-01-02T00:00:00", "GRCh38.p14"]
]
}matched lists term indices into terms. namespaces / n_hits on each term reflect postings after tax filters. The response is the full hit set (no pagination); clients derive taxon sets from row taxids. Prefer GET with a canonical query string so nginx can cache identical sets.
Namespaces present on one annotation’s shard (Tier A and chip namespaces), with distinct accession counts. annotation_id is path-only (not repeated in the JSON).
Browse / prefix-search accessions under one namespace on this shard. Path {namespace} accepts a stored namespace, a public short prefix (swiss-prot → uniprot/swiss-prot), or a slash form (uniprot/swiss-prot or uniprot%2Fswiss-prot). For symbol / alias, q is casefolded to match stored accessions. 404 namespace_not_found when the namespace is absent on the shard.
| Parameter | Notes |
|---|---|
q |
Optional accession prefix. Omit to browse all. Non-empty must be ≥ 3 characters (422). |
limit, offset |
Pagination; response includes exact total |
Each result: {accession, display, gene_count} (display nullable; representative via MAX(display) when several loci share an accession).
GET /annotations/ann-human/namespaces/refseq_transcript/accessions?q=XM_&limit=50{
"total": 1,
"limit": 50,
"offset": 0,
"results": [
{
"accession": "XM_015139305.2",
"display": "XM_015139305.2",
"gene_count": 1
}
]
}Browse / filter genes inside one annotation. Use names (exact symbol∪alias; wildcards rejected) when drilling in from /hits, or curies (ns:accession exact keys for any shard namespace) after picking from /namespaces/{ns}/accessions. names and curies are mutually exclusive (422 names_and_curies). Taxid filters stay on /hits only. annotation_id is path-only (not repeated in the JSON).
| Parameter | Notes |
|---|---|
names |
Optional CSV exact gene names (cap 200). Omit to browse all genes. Mutually exclusive with curies. |
curies |
Optional CSV CURIEs (cap 20). Exact accessions only. Mutually exclusive with names. |
match |
any (default) or all — union vs intersection over input names or CURIEs |
limit, offset |
Pagination; response includes exact total |
When names is set, each gene includes matches: {input, name, ns}. When curies is set: {input, ns, accession}. Omitted when browsing without a filter.
GET /annotations/ann-human/genes?names=SELENOP,TP53&match=any&limit=50
GET /annotations/ann-human/genes?curies=refseq_transcript:XM_015139305.2{
"total": 1,
"limit": 50,
"offset": 0,
"results": [
{
"local_id": 10,
"primary_name": "SELENOP",
"matches": [
{"input": "SELENOP", "name": "selenop", "ns": "symbol"}
]
}
]
}One gene card (coordinates, name, prose). No annotation_id field (path-scoped).
Paginated xref chips for one gene (limit / offset / total). annotation_id and local_id are path-only (not repeated in the JSON).
- Harvest — per-annotation gene / xref shards
- Merge — global
gene_corpus.sqlite(catalogue, meta, optionalgene_hit) - Index —
xref_filepresence for/hits - Labels (optional) —
xref_label/ FTS (corpus tooling; not exposed as HTTP discovery)
PYTHONPATH=. python -m gene_corpus.index --work-dir /path/to/gene_corpus
PYTHONPATH=. python -m gene_corpus.labels --work-dir /path/to/gene_corpusexport GENE_CORPUS_HOST_DIR=/path/to/gene_corpus
docker compose -f docker-compose-dev.yml up --build- API: http://localhost:5003
- Via nginx: http://localhost:95/annotrieve-xrefs/api/v1
pip install -r requirements-dev.txt
PYTHONPATH=api:. pytest gene_corpus/tests api/tests -q