{
 "openapi": "3.1.0",
 "info": {
  "title": "BioQ API",
  "version": "1.0.0",
  "summary": "Search public biological databases with one API.",
  "description": "BioQ answers questions across public biological databases: UniProt, MGnify, Pfam, PDB and AlphaFold, ClinVar, DepMap, ChEMBL, DrugCentral, Guide to Pharmacology, STRING, BioGRID, RefSeq and GRCh38.\n\n**Authentication.** Send `Authorization: Bearer bq_...` with an API key created at https://bioq.bio/app/keys. Requests from a signed-in browser session also work. A missing or invalid key returns 401 `{\"error\": \"sign_in_required\"}`.\n\n**Quotas.** Each plan has a daily request quota; sequence searches (/search) count against a separate, smaller daily quota. When a quota is used up the API returns 429 `{\"error\": \"quota_exceeded\", \"limit\": n, \"resets_at\": \"<ISO 8601>\"}`. Every response carries X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset and X-Request-Id.\n\n**Reading answers.** The BioQ engine never returns an empty list to mean 'this data source is off'. A layer that is not loaded answers 503 with the reason and a null result field; a 404 or an empty list from a loaded layer is a real absence. Many responses carry a discovery_readiness block (verdict testable, weak, no_claim, absent or unknown) saying whether the returned numbers can support a claim; gate on claim_allowed rather than on counts."
 },
 "servers": [
  {
   "url": "https://bioq.bio/api/v1"
  }
 ],
 "security": [
  {
   "bearerAuth": []
  }
 ],
 "tags": [
  {
   "name": "Identity & sequences",
   "description": "Locate identifiers and sequences across every layer, fetch stored sequences, and run homology searches."
  },
  {
   "name": "Genes & disease",
   "description": "Clinical variants, CRISPR dependency and cell-line omics for human genes."
  },
  {
   "name": "Drugs & interactions",
   "description": "Measured drug-target affinities and protein-protein interactions."
  },
  {
   "name": "Proteins & domains",
   "description": "Pfam domains, experimental and predicted structures, and per-organism protein counts."
  },
  {
   "name": "Genomes",
   "description": "Nucleotide sequence by coordinate, and gene neighbourhoods on metagenomic contigs."
  },
  {
   "name": "Metagenomes",
   "description": "Habitat counts, annotation-poor clusters and assembly co-occurrence for MGnify proteins."
  },
  {
   "name": "Agent",
   "description": "A research agent that answers questions by calling the endpoints above."
  },
  {
   "name": "Service",
   "description": "Service health and loaded data layers."
  }
 ],
 "paths": {
  "/whereis": {
   "get": {
    "operationId": "whereis",
    "tags": [
     "Identity & sequences"
    ],
    "summary": "Ask every database layer at once whether it knows an identifier or a protein sequence.",
    "description": "Give either an identifier (MGnify MGYP id, UniProt accession, PDB code, Pfam accession or gene symbol) or a protein sequence, never both. Each layer answers with a status (hit, miss, unavailable, timeout, error, not_applicable) and a claim type: same_bytes means the exact record or sequence is present, similar_sequence means an aligner found a relative (not presence), annotation and coverage are statements about the identifier. A miss is a real absence; unavailable or timeout means nothing was searched, and not_applicable means that layer cannot address this kind of identifier at all. The call returns 200 even when nothing is found; read summary.absent_everywhere_is_proven before concluding absence. A homolog search that misses the default 8 s deadline keeps running and is cached, so retrying (or raising timeout_ms) usually returns it.",
    "parameters": [
     {
      "name": "id",
      "in": "query",
      "required": false,
      "description": "Identifier to locate: MGYP…, UniProt accession, PDB code, Pfam accession (PF00069) or gene symbol. Give id or seq, not both.",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "seq",
      "in": "query",
      "required": false,
      "description": "Protein sequence in one-letter code (whitespace ignored). Give id or seq, not both.",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "gene",
      "in": "query",
      "required": false,
      "description": "Gene symbol used to consult the symbol-keyed layers (affinity, ClinVar). An accession cannot be turned into a symbol automatically.",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "timeout_ms",
      "in": "query",
      "required": false,
      "description": "Per-layer deadline in milliseconds. Values outside 50-180000 are clamped.",
      "schema": {
       "type": "integer",
       "default": 8000,
       "minimum": 50,
       "maximum": 180000
      }
     },
     {
      "name": "max_hits",
      "in": "query",
      "required": false,
      "description": "Sequence mode only: maximum homolog hits. Clamped to 1-200.",
      "schema": {
       "type": "integer",
       "default": 10,
       "minimum": 1,
       "maximum": 200
      }
     },
     {
      "name": "homolog_timeout_ms",
      "in": "query",
      "required": false,
      "description": "Sequence mode only: separate deadline for the homolog layer. Defaults to timeout_ms.",
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK. Example values are real engine responses, trimmed.",
      "headers": {
       "X-RateLimit-Limit": {
        "$ref": "#/components/headers/X-RateLimit-Limit"
       },
       "X-RateLimit-Remaining": {
        "$ref": "#/components/headers/X-RateLimit-Remaining"
       },
       "X-RateLimit-Reset": {
        "$ref": "#/components/headers/X-RateLimit-Reset"
       },
       "X-Request-Id": {
        "$ref": "#/components/headers/X-Request-Id"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "endpoint": {
           "type": "string"
          },
          "mode": {
           "type": "string"
          },
          "query": {
           "type": "object"
          },
          "verdict": {
           "type": "string",
           "description": "id mode: found | known_by_annotation_only | not_found_anywhere_loaded | not_found_in_what_answered; sequence mode: exact_sequence_present | not_found_anywhere_loaded | not_found_in_what_answered"
          },
          "answers": {
           "type": "object"
          },
          "summary": {
           "type": "object"
          },
          "answer": {
           "type": "string",
           "description": "one plain-language sentence summarising what the layers established"
          },
          "elapsed_seconds": {
           "type": "number"
          },
          "timeout_ms": {
           "type": "integer"
          }
         },
         "additionalProperties": true
        },
        "examples": {
         "default": {
          "summary": "/whereis?id=P04637",
          "value": {
           "endpoint": "/whereis",
           "mode": "id",
           "query": {
            "id": "P04637",
            "id_kind": "uniprot",
            "gene": null
           },
           "verdict": "known_by_annotation_only",
           "answers": {
            "trembl": {
             "claim": "same_bytes",
             "claim_meaning": "this UniProt accession is a record in TrEMBL, sequence reconstructed from the indexed byte offsets",
             "status": "miss",
             "count": 0,
             "searched": "TrEMBL accession index",
             "means": "the TrEMBL index was searched and does not carry this accession. TrEMBL is the UNREVIEWED half of UniProt — a reviewed (Swiss-Prot) accession correctly misses here, and that is an answer, not an error",
             "latency_ms": 0.2
            },
            "structure": {
             "claim": "annotation",
             "claim_meaning": "the structure index holds metadata about this accession (an AlphaFold model: pLDDT and residue count, no residues). That is a statement about the identifier, not its bytes",
             "status": "hit",
             "count": 1,
             "structure_id": "P04637",
             "evidence": {
              "kind": "predicted",
              "method": "ALPHAFOLD",
              "model_version": "4",
              "variant": "F1",
              "mean_plddt": 74.33,
              "frac_plddt_gt90": 0.514,
              "frac_plddt_gt70": 0.5776,
              "quality_call": "confident",
              "quality_rule": "confident if mean_plddt >= 70; low_confidence if >= 50; very_low_confidence below 50",
              "usable_for_claims": true,
              "comparable_with": "nothing experimental — pLDDT is a model's self-estimate, not a measurement error"
             },
             "sequence_available": false,
             "latency_ms": 0.7
            },
            "mgnify_c2": {
             "claim": "same_bytes",
             "claim_meaning": "this exact identifier is a record in the MGnify30-C2 representative store, and the sequence was read back from it",
             "status": "not_applicable",
             "count": null,
             "why": "the C2 store is keyed by MGnify protein id (MGYP...). This identifier is not one, so no lookup is possible now or after mounting it",
             "means": "no lookup was possible, so this is neither a hit nor a miss. Mounting the layer would not change that.",
             "latency_ms": 0.01
            }
           },
           "summary": {
            "present_in": [
             "structure"
            ],
            "absent_from": [
             "trembl"
            ],
            "unavailable_or_incomplete": [],
            "not_applicable": [
             "mgnify_c2",
             "mgnify_members",
             "pfam_domains",
             "pfam_reverse",
             "assemblies",
             "affinity",
             "clinvar"
            ],
            "n_layers": 9,
            "n_hit": 1,
            "n_miss": 1,
            "n_incomplete": 0,
            "absent_everywhere_is_proven": false,
            "absent_everywhere": null
           },
           "answer": "no layer holds this with the same bytes; structure know(s) it by annotation. An annotation or coverage answer is a statement ABOUT the identifier, not the presence of its sequence",
           "elapsed_seconds": 0.001,
           "timeout_ms": 8000
          }
         },
         "sequence": {
          "summary": "/whereis?seq=MVLSPADKTNVKAAWGKVGAHAGEYGAEALERMFLSFPTTKTYFPHFDLSH&max_hits=3&timeout_ms=30000",
          "value": {
           "endpoint": "/whereis",
           "mode": "seq",
           "query": {
            "sequence_length": 51,
            "sequence_head": "MVLSPADKTNVKAAWGKVGAHAGEYGAEALERMFLSFPTT",
            "sequence_sha256": "d4a29e904d27ce37e0adf57868ce0f41310b2551e0592a4becffb0ea01c54709",
            "gene": null
           },
           "verdict": "not_found_in_what_answered",
           "answers": {
            "exact_sequence": {
             "claim": "same_bytes",
             "claim_meaning": "the exact amino-acid string is present, established by a hash hit that was RE-READ and byte-compared. This is presence",
             "status": "unavailable",
             "count": null,
             "why": "no exact-sequence hash index is mounted. This engine's ID stores are keyed by an upstream-assigned identifier, so an exact-sequence question cannot be answered from them without one. UNMEASURED, not absent",
             "means": "nothing was searched. This is NOT evidence of absence; ask the operator to mount the layer, or read /health `ready`.",
             "latency_ms": 0.0
            },
            "homologs": {
             "claim": "similar_sequence",
             "claim_meaning": "an alignment search found proteins resembling the query. Similarity is not presence",
             "status": "hit",
             "count": 3,
             "hits": [
              {
               "target": "P69905",
               "evalue": 2.023e-29,
               "pident": 100.0,
               "alnlen": 51,
               "qcov": 1.0
              },
              {
               "target": "P69906",
               "evalue": 2.023e-29,
               "pident": 100.0,
               "alnlen": 51,
               "qcov": 1.0
              },
              {
               "target": "P69907",
               "evalue": 2.023e-29,
               "pident": 100.0,
               "alnlen": 51,
               "qcov": 1.0
              }
             ],
             "corpus": "sprot",
             "parameters": {
              "sensitivity": 5.7,
              "max_seqs": 3,
              "evalue": 0.001,
              "threads": 8,
              "db_load_mode": 2
             },
             "count_meaning": "alignments passing the e-value gate",
             "means": "these are RELATIVES found by alignment. Their existence does not mean the query is in the corpus; read `exact_sequence` for that",
             "latency_ms": 0.87
            }
           },
           "summary": {
            "present_in": [
             "homologs"
            ],
            "absent_from": [],
            "unavailable_or_incomplete": [
             "exact_sequence"
            ],
            "not_applicable": [],
            "n_layers": 2,
            "n_hit": 1,
            "n_miss": 0,
            "n_incomplete": 1,
            "absent_everywhere_is_proven": false,
            "absent_everywhere": null,
            "exact_sequence_absent_is_proven": null,
            "exact_sequence_absent_note": "the exact-bytes layer did not answer, so exact absence is UNMEASURED. A homolog hit is not evidence either way."
           },
           "answer": "no layer holds this with the same bytes; homologs know(s) it by similar_sequence. An annotation or coverage answer is a statement ABOUT the identifier, not the presence of its sequence",
           "elapsed_seconds": 0.001,
           "timeout_ms": 30000
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "Missing both id and seq, both given, a non-integer timeout_ms/max_hits, or a sequence with non-amino-acid letters.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "give ?id= or ?seq=, not both"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "429": {
      "$ref": "#/components/responses/QuotaExceeded"
     },
     "503": {
      "description": "The federated layer is not attached; nothing was searched.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "the federation layer is not attached"
        }
       }
      }
     }
    }
   }
  },
  "/lookup": {
   "get": {
    "operationId": "lookup",
    "tags": [
     "Identity & sequences"
    ],
    "summary": "Fetch the stored sequence for an MGnify protein id or a UniProt TrEMBL accession.",
    "description": "Returns the exact sequence read back from the source record. MGYP ids are looked up in the MGnify representative store first and then in the full 5.74-billion-protein member table; other accessions are looked up in UniProt TrEMBL (the unreviewed half of UniProt). Reviewed Swiss-Prot accessions such as P69905 are not in this lookup layer and answer 404 — use /search or /structure for those. A 404 says whether it is proof of absence (not_found_is_proof_of_absence) or only 'not in what is loaded'. TrEMBL lookups replay a compressed stream, so latency grows with the record's position and can reach minutes; the response carries a cost_model saying so.",
    "parameters": [
     {
      "name": "id",
      "in": "query",
      "required": true,
      "description": "MGnify protein accession (MGYP000000149140) or UniProt TrEMBL accession.",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK. Example values are real engine responses, trimmed.",
      "headers": {
       "X-RateLimit-Limit": {
        "$ref": "#/components/headers/X-RateLimit-Limit"
       },
       "X-RateLimit-Remaining": {
        "$ref": "#/components/headers/X-RateLimit-Remaining"
       },
       "X-RateLimit-Reset": {
        "$ref": "#/components/headers/X-RateLimit-Reset"
       },
       "X-Request-Id": {
        "$ref": "#/components/headers/X-Request-Id"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "hit": {
           "type": "object"
          }
         },
         "additionalProperties": true
        },
        "examples": {
         "default": {
          "summary": "/lookup?id=MGYP000000149140",
          "value": {
           "hit": {
            "accession": "MGYP000000149140",
            "sequence": "MQGLGDWGFPQKFKKSRIVRDDPLTNFDLFKNSIWVPKCYACQKKRGLAPQSRNSRTEVM…",
            "length": 88,
            "source": "mgnify30-c2",
            "provenance": {
             "part": 0,
             "anchor_offset": 0,
             "window_bytes": 63313,
             "anchor_id": 149140
            },
            "c2_missed": false,
            "cached": true,
            "elapsed_seconds": 5e-06
           }
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "Missing id.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "missing ?id="
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "404": {
      "description": "Not found. not_found_is_proof_of_absence says whether the full layer was searched; searched_only and trembl_coverage appear when it was not.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "not found",
         "id": "P69905",
         "ready": {
          "mgnify": true,
          "trembl": true,
          "c2": true,
          "structure": true
         },
         "c2_missed": null,
         "not_found_is_proof_of_absence": true
        }
       }
      }
     },
     "429": {
      "$ref": "#/components/responses/QuotaExceeded"
     }
    }
   }
  },
  "/search": {
   "get": {
    "operationId": "search",
    "tags": [
     "Identity & sequences"
    ],
    "summary": "Find proteins similar to a query sequence with an MMseqs2 homology search.",
    "description": "Aligns your protein sequence against a named corpus (currently sprot, UniProt Swiss-Prot) and returns hits with e-value, bit score, percent identity and coverage. Hits are relatives, not proof that your exact sequence is in the corpus. Identical requests are answered from a cache: served_in_seconds is the lookup cost and computed_in_seconds is what the search actually took. If a corpus cannot be searched inside the request the engine answers 202 with a job_id; poll /search/result until it returns 200. hits is null whenever no answer was produced (queued, failed, layer off), and is never an empty list in those cases. Sequence searches count against a separate, smaller daily quota.",
    "parameters": [
     {
      "name": "seq",
      "in": "query",
      "required": true,
      "description": "Protein sequence in one-letter code (whitespace and case ignored). Letters outside ACDEFGHIKLMNPQRSTVWYXBZUO* are rejected.",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "corpus",
      "in": "query",
      "required": false,
      "description": "Target corpus id. Unknown ids are refused with the list of available corpora, never silently replaced.",
      "schema": {
       "type": "string",
       "default": "sprot"
      }
     },
     {
      "name": "sensitivity",
      "in": "query",
      "required": false,
      "description": "MMseqs2 sensitivity (-s). Higher is slower and finds more distant relatives.",
      "schema": {
       "type": "number",
       "default": 5.7
      }
     },
     {
      "name": "max_seqs",
      "in": "query",
      "required": false,
      "description": "Maximum number of target hits to return.",
      "schema": {
       "type": "integer",
       "default": 50
      }
     },
     {
      "name": "evalue",
      "in": "query",
      "required": false,
      "description": "E-value threshold; hits above it are dropped.",
      "schema": {
       "type": "number",
       "default": 0.001
      }
     },
     {
      "name": "sync",
      "in": "query",
      "required": false,
      "description": "Only matters for corpora that cannot be searched inline: 1 asks the engine to wait (needs max_wait_seconds at least the estimate), 0 never waits.",
      "schema": {
       "type": "integer",
       "enum": [
        0,
        1
       ]
      }
     },
     {
      "name": "max_wait_seconds",
      "in": "query",
      "required": false,
      "description": "Your own deadline for an inline search; honoured over the server default.",
      "schema": {
       "type": "number"
      }
     },
     {
      "name": "refresh",
      "in": "query",
      "required": false,
      "description": "Bypass the cache and recompute. Pass 1 to enable.",
      "schema": {
       "type": "integer",
       "enum": [
        0,
        1
       ],
       "default": 0
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK. Example values are real engine responses, trimmed.",
      "headers": {
       "X-RateLimit-Limit": {
        "$ref": "#/components/headers/X-RateLimit-Limit"
       },
       "X-RateLimit-Remaining": {
        "$ref": "#/components/headers/X-RateLimit-Remaining"
       },
       "X-RateLimit-Reset": {
        "$ref": "#/components/headers/X-RateLimit-Reset"
       },
       "X-Request-Id": {
        "$ref": "#/components/headers/X-Request-Id"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "hits": {
           "type": "array"
          },
          "hit_count": {
           "type": "integer",
           "description": "number of hits returned"
          },
          "query_length": {
           "type": "integer"
          },
          "parameters": {
           "type": "object",
           "description": "the exact search settings that produced the hits"
          },
          "db": {
           "type": "string"
          },
          "search_seconds": {
           "type": "number"
          },
          "cached": {
           "type": "boolean",
           "description": "true when replayed from cache; then see cache_detail.explain"
          },
          "computed_in_seconds": {
           "type": "number"
          },
          "served_in_seconds": {
           "type": "number"
          },
          "corpus": {
           "type": "string",
           "description": "the population searched; a miss says nothing about other corpora"
          },
          "index": {
           "type": "boolean"
          },
          "corpus_detail": {
           "type": "object"
          },
          "cache_detail": {
           "type": "object"
          },
          "status": {
           "type": "string",
           "description": "ok (200), queued (202); with a 202 also job_id, poll and poll_after_seconds"
          },
          "request_id": {
           "type": "string"
          },
          "corpus_id": {
           "type": "string",
           "description": "the population searched; a miss says nothing about other corpora"
          },
          "elapsed_seconds": {
           "type": "number"
          },
          "source": {
           "type": "string"
          }
         },
         "additionalProperties": true
        },
        "examples": {
         "default": {
          "summary": "/search?seq=MVLSPADKTNVKAAWGKVGAHAGEYGAEALERMFLSFPTTKTYFPHFDLSH",
          "value": {
           "hits": [
            {
             "target": "P69905",
             "evalue": 2.023e-29,
             "bits": 111.0,
             "alnlen": 51,
             "pident": 100.0,
             "qcov": 1.0,
             "tcov": 0.359,
             "qstart": 1,
             "qend": 51,
             "tstart": 1,
             "tend": 51
            },
            {
             "target": "P69906",
             "evalue": 2.023e-29,
             "bits": 111.0,
             "alnlen": 51,
             "pident": 100.0,
             "qcov": 1.0,
             "tcov": 0.359,
             "qstart": 1,
             "qend": 51,
             "tstart": 1,
             "tend": 51
            },
            {
             "target": "P69907",
             "evalue": 2.023e-29,
             "bits": 111.0,
             "alnlen": 51,
             "pident": 100.0,
             "qcov": 1.0,
             "tcov": 0.359,
             "qstart": 1,
             "qend": 51,
             "tstart": 1,
             "tend": 51
            }
           ],
           "hit_count": 50,
           "query_length": 51,
           "parameters": {
            "sensitivity": 5.7,
            "max_seqs": 50,
            "evalue": 0.001,
            "threads": 8,
            "db_load_mode": 2
           },
           "db": "sprot",
           "search_seconds": 8.603,
           "cached": true,
           "computed_in_seconds": 8.763,
           "served_in_seconds": 0.0006,
           "corpus": "sprot",
           "index": true,
           "corpus_detail": {
            "id": "sprot",
            "indexed": true,
            "target_db": "sprot",
            "db_bytes": 2931031187,
            "fingerprint": "sha256:5cf05454c9bbe1ec2c49e7b515441898",
            "fingerprint_kind": "stat-change-detector",
            "index_note": "createindex output present; a query reads the k-mer table"
           },
           "cache_detail": {
            "outcome": "hit",
            "age_seconds": 34377.505,
            "computed_at": "2026-10-22T15:47:39+00:00",
            "computed_by": "sync",
            "cached_from": "2026-10-22T15:47:39+00:00",
            "cached_computed_in_seconds": 8.763,
            "explain": "this answer took 8.76 s to compute on 2026-10-22T15:47:39+00:00 (sync) and is replayed from cache. The 0.0006 s above is the cost of the cache lookup, NOT the cost of the search."
           },
           "status": "ok",
           "request_id": "9cea3c8a8db0",
           "corpus_id": "sprot",
           "elapsed_seconds": 0.001,
           "source": "MMseqs2"
          }
         }
        }
       }
      }
     },
     "202": {
      "description": "Queued: the target corpus is not k-mer indexed and the search cannot run inside the request. hits is null (not yet answered). Poll /search/result.",
      "headers": {
       "X-RateLimit-Limit": {
        "$ref": "#/components/headers/X-RateLimit-Limit"
       },
       "X-RateLimit-Remaining": {
        "$ref": "#/components/headers/X-RateLimit-Remaining"
       },
       "X-RateLimit-Reset": {
        "$ref": "#/components/headers/X-RateLimit-Reset"
       },
       "X-Request-Id": {
        "$ref": "#/components/headers/X-Request-Id"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "description": "Queued: the target corpus is not k-mer indexed and the search cannot run inside the request. hits is null (not yet answered). Poll /search/result.",
         "properties": {
          "status": {
           "type": "string",
           "const": "queued"
          },
          "hits": {
           "type": "null"
          },
          "hit_count": {
           "type": "null"
          },
          "job_id": {
           "type": "string"
          },
          "job_state": {
           "type": "string"
          },
          "deduplicated": {
           "type": "boolean"
          },
          "poll": {
           "type": "string",
           "description": "Relative path to poll, e.g. /search/result?id=<job_id>"
          },
          "poll_after_seconds": {
           "type": "number"
          },
          "estimated_seconds": {
           "type": "number"
          },
          "estimated_basis": {
           "type": "string"
          },
          "why_async": {
           "type": "string"
          },
          "corpus": {
           "type": "string"
          },
          "request_id": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "Missing seq, unknown corpus (body lists corpora_available), bad numeric parameter, or not a protein sequence.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "unknown corpus: 'none'",
         "hits": null,
         "corpora_available": [
          "sprot"
         ]
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "429": {
      "$ref": "#/components/responses/QuotaExceeded"
     },
     "503": {
      "description": "Homology search not available or failed; hits is null (no answer was produced).",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "homology search failed",
         "status": "unavailable",
         "hits": null
        }
       }
      }
     }
    }
   }
  },
  "/search/result": {
   "get": {
    "operationId": "search_result",
    "tags": [
     "Identity & sequences"
    ],
    "summary": "Poll a queued sequence search by its job id.",
    "description": "Use the job_id returned by a 202 from /search. While the job is queued or running the answer is 202 with hits null; when done it is 200 with the same body shape as /search plus the job record, cost_of_first_query_seconds and corpus_current. If the corpus changed after the job ran, corpus_warning says the answer describes the build that was searched. A failed job answers 503 with hits null, which is not a negative result. Pass the same corpus you submitted with, because job ids are only unique within a corpus.",
    "parameters": [
     {
      "name": "id",
      "in": "query",
      "required": true,
      "description": "job_id from the 202 response of /search.",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "corpus",
      "in": "query",
      "required": false,
      "description": "Corpus the job was submitted to.",
      "schema": {
       "type": "string",
       "default": "sprot"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Job finished: same body as a 200 from /search plus the job record.",
      "headers": {
       "X-RateLimit-Limit": {
        "$ref": "#/components/headers/X-RateLimit-Limit"
       },
       "X-RateLimit-Remaining": {
        "$ref": "#/components/headers/X-RateLimit-Remaining"
       },
       "X-RateLimit-Reset": {
        "$ref": "#/components/headers/X-RateLimit-Reset"
       },
       "X-Request-Id": {
        "$ref": "#/components/headers/X-Request-Id"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "hits": {
           "type": "array"
          },
          "hit_count": {
           "type": "integer",
           "description": "number of hits returned"
          },
          "query_length": {
           "type": "integer"
          },
          "parameters": {
           "type": "object",
           "description": "the exact search settings that produced the hits"
          },
          "db": {
           "type": "string"
          },
          "search_seconds": {
           "type": "number"
          },
          "cached": {
           "type": "boolean",
           "description": "true when replayed from cache; then see cache_detail.explain"
          },
          "computed_in_seconds": {
           "type": "number"
          },
          "served_in_seconds": {
           "type": "number"
          },
          "corpus": {
           "type": "string",
           "description": "the population searched; a miss says nothing about other corpora"
          },
          "index": {
           "type": "boolean"
          },
          "corpus_detail": {
           "type": "object"
          },
          "cache_detail": {
           "type": "object"
          },
          "status": {
           "type": "string",
           "description": "ok (200), queued (202); with a 202 also job_id, poll and poll_after_seconds"
          },
          "request_id": {
           "type": "string"
          },
          "corpus_id": {
           "type": "string",
           "description": "the population searched; a miss says nothing about other corpora"
          },
          "elapsed_seconds": {
           "type": "number"
          },
          "source": {
           "type": "string"
          },
          "job": {
           "type": "object",
           "description": "The job record (state, timings)."
          },
          "cost_of_first_query_seconds": {
           "type": [
            "number",
            "null"
           ]
          },
          "corpus_current": {
           "type": "boolean"
          },
          "corpus_warning": {
           "type": "string"
          }
         },
         "additionalProperties": true
        }
       }
      }
     },
     "202": {
      "description": "Still queued or running; hits is null.",
      "headers": {
       "X-RateLimit-Limit": {
        "$ref": "#/components/headers/X-RateLimit-Limit"
       },
       "X-RateLimit-Remaining": {
        "$ref": "#/components/headers/X-RateLimit-Remaining"
       },
       "X-RateLimit-Reset": {
        "$ref": "#/components/headers/X-RateLimit-Reset"
       },
       "X-Request-Id": {
        "$ref": "#/components/headers/X-Request-Id"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "description": "Still queued or running; hits is null.",
         "properties": {
          "status": {
           "type": "string",
           "enum": [
            "queued",
            "running"
           ]
          },
          "hits": {
           "type": "null"
          },
          "job_id": {
           "type": "string"
          },
          "waited_seconds": {
           "type": [
            "number",
            "null"
           ]
          },
          "estimated_seconds": {
           "type": [
            "number",
            "null"
           ]
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "Missing id.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "missing ?id=<job_id>"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "404": {
      "description": "Unknown job id.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "unknown job id",
         "id": "0",
         "hits": null
        }
       }
      }
     },
     "429": {
      "$ref": "#/components/responses/QuotaExceeded"
     },
     "503": {
      "description": "Job failed, its answer could not be read back, or the search service is off. hits is null.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "status": "failed",
         "hits": null
        }
       }
      }
     }
    }
   }
  },
  "/gene": {
   "get": {
    "operationId": "gene",
    "tags": [
     "Genes & disease"
    ],
    "summary": "Summarise what the engine holds about a human gene symbol, starting with its ClinVar records.",
    "description": "Returns the ClinVar significance histogram for the gene, a five-row sample of records, and a discovery_readiness verdict. ClinVar counts are submissions, not independent observations, so the verdict says whether a distribution may be described at all. clusters is always null: MGnify proteins carry no gene symbol, so a gene cannot be mapped to metagenomic clusters by name. An unknown symbol still answers 200, with the reason in unresolved and verdict absent.",
    "parameters": [
     {
      "name": "symbol",
      "in": "query",
      "required": true,
      "description": "Gene symbol, e.g. EGFR, BRCA1 (case-insensitive).",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK. Example values are real engine responses, trimmed.",
      "headers": {
       "X-RateLimit-Limit": {
        "$ref": "#/components/headers/X-RateLimit-Limit"
       },
       "X-RateLimit-Remaining": {
        "$ref": "#/components/headers/X-RateLimit-Remaining"
       },
       "X-RateLimit-Reset": {
        "$ref": "#/components/headers/X-RateLimit-Reset"
       },
       "X-Request-Id": {
        "$ref": "#/components/headers/X-Request-Id"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "gene": {
           "type": "string"
          },
          "variants": {
           "type": "object"
          },
          "clusters": {
           "type": [
            "null",
            "object",
            "array",
            "string",
            "number",
            "boolean"
           ],
           "description": "always null; see unresolved"
          },
          "unresolved": {
           "type": "array"
          },
          "discovery_readiness": {
           "type": "object"
          }
         },
         "additionalProperties": true
        },
        "examples": {
         "default": {
          "summary": "/gene?symbol=EGFR",
          "value": {
           "gene": "EGFR",
           "variants": {
            "n_returned": 20,
            "n_matched": 4141,
            "truncated": true,
            "significance_histogram": {
             "Uncertain_significance": 1899,
             "Likely_benign": 1527,
             "Conflicting_classifications_of_pathogenicity": 254,
             "Benign/Likely_benign": 132,
             "Benign": 95,
             "": 72,
             "Pathogenic": 60,
             "drug_response": 48,
             "Likely_pathogenic": 39,
             "not_provided": 7,
             "Likely_pathogenic|drug_response": 6,
             "Uncertain_significance|drug_response": 1,
             "Pathogenic|drug_response": 1
            },
            "sample": [
             {
              "chrom": "7",
              "pos": 55018796,
              "gene": "EGFR",
              "sig": "Likely_benign",
              "review": "criteria_provided,_single_submitter"
             },
             {
              "chrom": "7",
              "pos": 55019062,
              "gene": "EGFR",
              "sig": "Benign/Likely_benign",
              "review": "criteria_provided,_multiple_submitters,_no_conflicts"
             }
            ]
           },
           "clusters": null,
           "unresolved": [
            "MGnify clusters carry no gene symbol, so a gene cannot be mapped to clusters by name; use /lookup with a cluster id, or a homology search"
           ],
           "discovery_readiness": {
            "verdict": "testable",
            "claim_allowed": true,
            "n_records": 4141,
            "n_distinct_values": 12,
            "min_records_required": 3,
            "why": "4141 records with 12 distinct values: describing the distribution is possible, but these are submissions, not independent samples",
            "can_do": [
             "the 4141 records can describe a distribution (e.g. the pathogenic fraction)",
             "these are submission counts, not independent samples; do not use them as n for confidence intervals or significance tests"
            ]
           }
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "Missing symbol.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "missing ?symbol=BRCA1"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "429": {
      "$ref": "#/components/responses/QuotaExceeded"
     },
     "503": {
      "description": "Joined layers not loaded.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "joins not loaded"
        }
       }
      }
     }
    }
   }
  },
  "/variant": {
   "get": {
    "operationId": "variant",
    "tags": [
     "Genes & disease"
    ],
    "summary": "List ClinVar variants for a gene or around a GRCh38 position.",
    "description": "Query by gene symbol, or by chromosome plus position with a window in base pairs. A chromosome without a position (or the reverse) is refused rather than answered with unrelated rows. matched and significance_histogram cover every matching record; results is one page of at most 50 rows sorted by position, and truncated says when more exist. Uncertain significance (VUS) and conflicting classifications are not pathogenic, and review status is shown because evidence tiers differ.",
    "parameters": [
     {
      "name": "gene",
      "in": "query",
      "required": false,
      "description": "Gene symbol (case-insensitive).",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "chrom",
      "in": "query",
      "required": false,
      "description": "Chromosome name as in ClinVar, e.g. 17 or X. Requires pos.",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "pos",
      "in": "query",
      "required": false,
      "description": "1-based GRCh38 position. Requires chrom.",
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "window",
      "in": "query",
      "required": false,
      "description": "Half-width in base pairs around pos.",
      "schema": {
       "type": "integer",
       "default": 100
      }
     },
     {
      "name": "pathogenic_only",
      "in": "query",
      "required": false,
      "description": "Keep only Pathogenic, Likely_pathogenic and Pathogenic/Likely_pathogenic records (1 or true).",
      "schema": {
       "type": "integer",
       "enum": [
        0,
        1
       ],
       "default": 0
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK. Example values are real engine responses, trimmed.",
      "headers": {
       "X-RateLimit-Limit": {
        "$ref": "#/components/headers/X-RateLimit-Limit"
       },
       "X-RateLimit-Remaining": {
        "$ref": "#/components/headers/X-RateLimit-Remaining"
       },
       "X-RateLimit-Reset": {
        "$ref": "#/components/headers/X-RateLimit-Reset"
       },
       "X-Request-Id": {
        "$ref": "#/components/headers/X-Request-Id"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "matched": {
           "type": "integer",
           "description": "number of records that met the filter (not the page size)"
          },
          "n_returned": {
           "type": "integer",
           "description": "rows in this page (at most 50)"
          },
          "truncated": {
           "type": "boolean",
           "description": "true when matched exceeds the page"
          },
          "significance_histogram": {
           "type": "object",
           "description": "counts per significance over all matched records; sums to matched"
          },
          "caveat": {
           "type": "string",
           "description": "reminder that VUS and conflicting records are not pathogenic"
          },
          "results": {
           "type": "array"
          }
         },
         "additionalProperties": true
        },
        "examples": {
         "default": {
          "summary": "/variant?gene=BRCA1&pathogenic_only=1",
          "value": {
           "matched": 4118,
           "n_returned": 50,
           "truncated": true,
           "significance_histogram": {
            "Pathogenic": 3613,
            "Likely_pathogenic": 277,
            "Pathogenic/Likely_pathogenic": 228
           },
           "caveat": "VUS and Conflicting are not pathogenic; review status is shown because evidence tiers differ",
           "results": [
            {
             "chrom": "17",
             "pos": 43041660,
             "gene": "BRCA1",
             "sig": "Pathogenic",
             "review": "no_assertion_criteria_provided"
            },
            {
             "chrom": "17",
             "pos": 43041662,
             "gene": "BRCA1",
             "sig": "Pathogenic",
             "review": "criteria_provided,_single_submitter"
            },
            {
             "chrom": "17",
             "pos": 43044293,
             "gene": "BRCA1",
             "sig": "Pathogenic",
             "review": "criteria_provided,_single_submitter"
            }
           ]
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "chrom without pos, pos without chrom, or a non-integer pos.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "chrom='17' was given without a position. A variant lookup needs chrom AND pos (1-based) plus an optional window; …"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "429": {
      "$ref": "#/components/responses/QuotaExceeded"
     },
     "503": {
      "description": "Variant index not loaded.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "variant index not loaded"
        }
       }
      }
     }
    }
   }
  },
  "/depmap": {
   "get": {
    "operationId": "depmap",
    "tags": [
     "Genes & disease"
    ],
    "summary": "Look up CRISPR dependency (DepMap Chronos gene effect) by gene, by lineage, or both.",
    "description": "With gene only, returns the gene's dependency profile across all cell lines and per lineage (most dependent lineage first). With lineage only, ranks genes by how lineage-specific the dependency is (selectivity), excluding common-essential genes unless asked. With both, lists the individual cell lines and their effect. mutations=1 lists cell lines with a HIGH-impact somatic variant in the gene. More negative gene effect means knocking the gene out hurt viability more; a line counts as dependent at effect <= -0.5, a named threshold rather than a property of the data. Unknown genes or lineages answer 404 with what was searched.",
    "parameters": [
     {
      "name": "gene",
      "in": "query",
      "required": false,
      "description": "Gene symbol (case-insensitive).",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "entrez",
      "in": "query",
      "required": false,
      "description": "Entrez gene id, as an alternative to gene.",
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "lineage",
      "in": "query",
      "required": false,
      "description": "Lineage name; exact, case-insensitive, or a unique substring (e.g. Pancreas, lung).",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "top",
      "in": "query",
      "required": false,
      "description": "Rows to return (lineages, genes or cell lines depending on mode). Clamped to 1-500.",
      "schema": {
       "type": "integer",
       "default": 25,
       "minimum": 1,
       "maximum": 500
      }
     },
     {
      "name": "all",
      "in": "query",
      "required": false,
      "description": "Gene mode: return every lineage instead of the top ones. Pass 1 to enable.",
      "schema": {
       "type": "integer",
       "enum": [
        0,
        1
       ],
       "default": 0
      }
     },
     {
      "name": "include_pan_essential",
      "in": "query",
      "required": false,
      "description": "Lineage mode: keep common-essential genes in the ranking. Pass 1 to enable.",
      "schema": {
       "type": "integer",
       "enum": [
        0,
        1
       ],
       "default": 0
      }
     },
     {
      "name": "mutations",
      "in": "query",
      "required": false,
      "description": "List lines with a HIGH-impact somatic variant in gene (requires gene; lineage optional). Pass 1 to enable.",
      "schema": {
       "type": "integer",
       "enum": [
        0,
        1
       ],
       "default": 0
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK. Example values are real engine responses, trimmed.",
      "headers": {
       "X-RateLimit-Limit": {
        "$ref": "#/components/headers/X-RateLimit-Limit"
       },
       "X-RateLimit-Remaining": {
        "$ref": "#/components/headers/X-RateLimit-Remaining"
       },
       "X-RateLimit-Reset": {
        "$ref": "#/components/headers/X-RateLimit-Reset"
       },
       "X-Request-Id": {
        "$ref": "#/components/headers/X-Request-Id"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "gene": {
           "type": "object"
          },
          "corpus": {
           "type": "object"
          },
          "by_lineage": {
           "type": "array"
          },
          "thresholds": {
           "type": "object"
          },
          "meaning": {
           "type": "string"
          },
          "elapsed_ms": {
           "type": "number"
          },
          "index": {
           "type": "string"
          }
         },
         "additionalProperties": true
        },
        "examples": {
         "default": {
          "summary": "/depmap?gene=KRAS",
          "value": {
           "gene": {
            "symbol": "KRAS",
            "entrez": 3845,
            "gene_idx": 7807,
            "is_common_essential": false,
            "is_nonessential": false
           },
           "corpus": {
            "n_lines": 1178,
            "n_dependent": 399,
            "mean_effect": -0.5572,
            "median_effect": -0.3732,
            "profile": {
             "n": 1178,
             "bins": {
              "[-9,-1.0)": 168,
              "[-1.0,-0.5)": 231,
              "[-0.5,-0.1)": 698,
              "[-0.1,0.1)": 72,
              "[0.1,9)": 9
             },
             "min": -4.143,
             "max": 0.326
            }
           },
           "by_lineage": [
            {
             "lineage": "Pancreas",
             "n": 47,
             "n_dependent": 43,
             "frac_dependent": 0.9149,
             "mean": -1.7898,
             "median": -1.758,
             "sd": 0.8157,
             "selectivity": -1.2326
            },
            {
             "lineage": "Bowel",
             "n": 63,
             "n_dependent": 48,
             "frac_dependent": 0.7619,
             "mean": -1.1779,
             "median": -1.0111,
             "sd": 0.797,
             "selectivity": -0.6206
            },
            {
             "lineage": "Eye",
             "n": 15,
             "n_dependent": 6,
             "frac_dependent": 0.4,
             "mean": -0.7888,
             "median": -0.39,
             "sd": 0.7294,
             "selectivity": -0.2315
            }
           ],
           "thresholds": {
            "dependent_if_effect_le": -0.5,
            "min_lines_for_lineage_row": 5,
            "mutation_impacts_kept": [
             "HIGH"
            ]
           },
           "meaning": "mean is the Chronos gene effect; MORE NEGATIVE means knocking the gene out reduced viability more. selectivity is the lineage mean minus the corpus mean, so a pan-essential gene has a very negative mean but selectivity near zero.",
           "elapsed_ms": 0.21,
           "index": "depmap_24q4 — Chronos gene effect; a dependency is effect <= dependent_if_effect_le, which is a named constant, not a property of the data"
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "Neither gene nor lineage, mutations=1 without gene, or non-integer top.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "missing ?gene= or ?lineage="
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "404": {
      "description": "Gene or lineage not in the index (the body lists what was searched).",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "not in the index",
         "why": "the layer is loaded and was searched — a real absence"
        }
       }
      }
     },
     "429": {
      "$ref": "#/components/responses/QuotaExceeded"
     },
     "503": {
      "description": "Dependency layer not loaded; nothing was searched.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "depmap index not loaded",
         "status": "not_loaded"
        }
       }
      }
     }
    }
   }
  },
  "/omics": {
   "get": {
    "operationId": "omics",
    "tags": [
     "Genes & disease"
    ],
    "summary": "Get a gene's expression or copy number across DepMap cell lines, or correlate two genes.",
    "description": "layer=expr is expression as log2(TPM+1); layer=cn is absolute copy number on a linear scale. They are different quantities and are never pooled. With one gene you get the distribution overall, per lineage, and the lowest and highest lines; with two genes you get Pearson r over the lines where both are measured. Unmeasured lines are NaN, never zero, and are reported as n_unmeasured. Some lines carry an empty lineage string. An unknown gene in single-gene mode answers 404, but in two-gene mode it answers 200 with an error field (unknown gene) — check for it.",
    "parameters": [
     {
      "name": "layer",
      "in": "query",
      "required": false,
      "description": "expr (log2(TPM+1)) or cn (absolute linear copy number).",
      "schema": {
       "type": "string",
       "default": "expr",
       "enum": [
        "expr",
        "cn"
       ]
      }
     },
     {
      "name": "gene",
      "in": "query",
      "required": false,
      "description": "Gene symbol for a single-gene profile (alias: a).",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "b",
      "in": "query",
      "required": false,
      "description": "Second gene symbol; with gene/a, returns a correlation.",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "compare",
      "in": "query",
      "required": false,
      "description": "Alternative spelling of a two-gene request: compare=ERBB2&compare=EGFR.",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "top",
      "in": "query",
      "required": false,
      "description": "Rows in by_lineage, lowest and highest. Clamped to 1-200.",
      "schema": {
       "type": "integer",
       "default": 15,
       "minimum": 1,
       "maximum": 200
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK. Example values are real engine responses, trimmed.",
      "headers": {
       "X-RateLimit-Limit": {
        "$ref": "#/components/headers/X-RateLimit-Limit"
       },
       "X-RateLimit-Remaining": {
        "$ref": "#/components/headers/X-RateLimit-Remaining"
       },
       "X-RateLimit-Reset": {
        "$ref": "#/components/headers/X-RateLimit-Reset"
       },
       "X-Request-Id": {
        "$ref": "#/components/headers/X-Request-Id"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "layer": {
           "type": "string"
          },
          "gene": {
           "type": "string"
          },
          "n_models": {
           "type": "integer"
          },
          "n_measured": {
           "type": "integer",
           "description": "lines with and without a value for this gene"
          },
          "n_unmeasured": {
           "type": "integer",
           "description": "lines with and without a value for this gene"
          },
          "overall": {
           "type": "object",
           "description": "mean, median, sd over measured lines"
          },
          "by_lineage": {
           "type": "array"
          },
          "lowest": {
           "type": "array"
          },
          "highest": {
           "type": "array"
          },
          "value_meaning": {
           "type": "string",
           "description": "the unit of every value in this response"
          },
          "elapsed_ms": {
           "type": "number"
          },
          "index": {
           "type": "string"
          }
         },
         "additionalProperties": true
        },
        "examples": {
         "default": {
          "summary": "/omics?gene=EGFR&top=5",
          "value": {
           "layer": "expr",
           "gene": "EGFR",
           "n_models": 1987,
           "n_measured": 1673,
           "n_unmeasured": 314,
           "overall": {
            "mean": 3.3837,
            "median": 3.9523,
            "sd": 2.3654
           },
           "by_lineage": [
            {
             "lineage": "Head and Neck",
             "n": 61,
             "mean": 5.7235,
             "median": 5.9991,
             "sd": 1.687,
             "min": 0.0144,
             "max": 9.3546
            },
            {
             "lineage": "Bladder/Urinary Tract",
             "n": 34,
             "mean": 5.7135,
             "median": 5.5229,
             "sd": 1.3177,
             "min": 3.4777,
             "max": 10.0761
            }
           ],
           "lowest": [
            {
             "ModelID": "ACH-003463",
             "lineage": "",
             "value": 0.0
            },
            {
             "ModelID": "ACH-003273",
             "lineage": "",
             "value": 0.0
            }
           ],
           "highest": [
            {
             "ModelID": "ACH-001523",
             "lineage": "Skin",
             "value": 10.5319
            },
            {
             "ModelID": "ACH-000741",
             "lineage": "Bladder/Urinary Tract",
             "value": 10.0761
            }
           ],
           "value_meaning": "log2(TPM+1) as shipped by the release",
           "elapsed_ms": 19.19,
           "index": "depmap_24q4 omics side-car — expr is log2(TPM+1), cn is ABSOLUTE linear copy number; a NaN is an unmeasured line, never a zero"
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "layer other than expr/cn, no gene, or non-integer top.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "?layer= must be expr or cn",
         "got": "tpm"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "404": {
      "description": "Single-gene mode: gene not in this layer.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "not in the index"
        }
       }
      }
     },
     "429": {
      "$ref": "#/components/responses/QuotaExceeded"
     },
     "503": {
      "description": "Omics layer not loaded.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "omics index not loaded",
         "status": "not_loaded"
        }
       }
      }
     }
    }
   }
  },
  "/affinity": {
   "get": {
    "operationId": "affinity",
    "tags": [
     "Drugs & interactions"
    ],
    "summary": "List measured drug-target binding and potency values (ChEMBL, DrugCentral, Guide to Pharmacology).",
    "description": "Query by target gene symbol, by drug name, or both. Every measurement keeps its own type (IC50, Ki, Kd, …), unit and unit_class; molar and log values are never averaged together, and p_value (-log10 molar) is the only column comparable across classes. The list is sorted by p_value, highest first, so a limited page is the most potent slice, not a sample; summary is always computed over all matches (see summary_basis). assay_type separates binding (B) from functional (F) and ADME (A) assays; rows from sources without an assay type are excluded when it is set. An empty list with 200 means the layer answered and nothing matched; a 503 with measurements null means the layer is off.",
    "parameters": [
     {
      "name": "gene",
      "in": "query",
      "required": false,
      "description": "Target gene symbol (case-insensitive).",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "drug",
      "in": "query",
      "required": false,
      "description": "Drug name (case-insensitive exact match).",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "type",
      "in": "query",
      "required": false,
      "description": "Measured type, e.g. IC50, Ki, Kd, EC50, pIC50 (case-insensitive).",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "assay_type",
      "in": "query",
      "required": false,
      "description": "ChEMBL assay type letter (case-insensitive), e.g. B binding, F functional, A ADME.",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "limit",
      "in": "query",
      "required": false,
      "description": "Rows in the measurement list; 0 or negative returns all rows. Does not affect summary.",
      "schema": {
       "type": "integer",
       "default": 100
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK. Example values are real engine responses, trimmed.",
      "headers": {
       "X-RateLimit-Limit": {
        "$ref": "#/components/headers/X-RateLimit-Limit"
       },
       "X-RateLimit-Remaining": {
        "$ref": "#/components/headers/X-RateLimit-Remaining"
       },
       "X-RateLimit-Reset": {
        "$ref": "#/components/headers/X-RateLimit-Reset"
       },
       "X-Request-Id": {
        "$ref": "#/components/headers/X-Request-Id"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "query": {
           "type": "object"
          },
          "n": {
           "type": "integer",
           "description": "all matches vs rows in this page"
          },
          "total_matched": {
           "type": "integer",
           "description": "all matches vs rows in this page"
          },
          "truncated": {
           "type": "boolean",
           "description": "all matches vs rows in this page"
          },
          "measurements": {
           "type": "array"
          },
          "summary": {
           "type": "object"
          },
          "summary_basis": {
           "type": "string",
           "description": "states that the summary covers all matches, not the page"
          },
          "layer": {
           "type": "object"
          },
          "elapsed_seconds": {
           "type": "number"
          }
         },
         "additionalProperties": true
        },
        "examples": {
         "default": {
          "summary": "/affinity?gene=EGFR&drug=gefitinib&limit=5",
          "value": {
           "query": {
            "gene": "EGFR",
            "drug": "gefitinib",
            "type": null,
            "assay_type": null,
            "limit": 5
           },
           "n": 5,
           "total_matched": 328,
           "truncated": true,
           "measurements": [
            {
             "target_id": "CHEMBL203",
             "target_name": "Epidermal growth factor receptor",
             "gene": "EGFR",
             "genes_all": [
              "EGFR"
             ],
             "drug_name": "gefitinib",
             "drug_id": "CHEMBL939",
             "measured_type": "IC50",
             "value": 0.1,
             "unit": "nM",
             "unit_class": "molar",
             "unit_inferred": null,
             "unit_basis": "source_stated_molar_unit",
             "relation": "=",
             "assay_type": "B",
             "standard_flag": null,
             "p_value": 10.0,
             "source": "chembl",
             "source_key": "chembl_37.db|activity_id=13297481",
             "row_ref": 13297481,
             "sha256": "4be13df3b68e25dcd0bff44bf094033b5aebe98f415acdc8c1cdf380e0c15142",
             "file": "chembl_37.db"
            },
            {
             "target_id": "CHEMBL203",
             "target_name": "Epidermal growth factor receptor",
             "gene": "EGFR",
             "genes_all": [
              "EGFR"
             ],
             "drug_name": "gefitinib",
             "drug_id": "CHEMBL939",
             "measured_type": "IC50",
             "value": 0.1,
             "unit": "nM",
             "unit_class": "molar",
             "unit_inferred": null,
             "unit_basis": "source_stated_molar_unit",
             "relation": "=",
             "assay_type": "B",
             "standard_flag": null,
             "p_value": 10.0,
             "source": "chembl",
             "source_key": "chembl_37.db|activity_id=13380887",
             "row_ref": 13380887,
             "sha256": "4be13df3b68e25dcd0bff44bf094033b5aebe98f415acdc8c1cdf380e0c15142",
             "file": "chembl_37.db"
            }
           ],
           "summary": {
            "n_measurements": 328,
            "per_unit_class": {
             "log": {
              "n": 7,
              "min": 7.230000019073486,
              "median": 8.300000190734863,
              "max": 9.4,
              "unit_class": "log",
              "span_orders_of_magnitude": 2.17
             },
             "molar": {
              "n": 321,
              "min": 0.1,
              "median": 14.4,
              "max": 22800.0,
              "unit_class": "molar",
              "span_orders_of_magnitude": 5.36
             }
            },
            "per_type_and_class": {
             "IC50/molar": {
              "n": 269,
              "min": 0.1,
              "median": 20.0,
              "max": 22800.0,
              "unit_class": "molar",
              "span_orders_of_magnitude": 5.36
             },
             "KD/molar": {
              "n": 38,
              "min": 0.52,
              "median": 1.1,
              "max": 470.0,
              "unit_class": "molar",
              "span_orders_of_magnitude": 2.96
             },
             "PIC50/log": {
              "n": 2,
              "min": 7.230000019073486,
              "median": 8.149999618530273,
              "max": 8.149999618530273,
              "unit_class": "log",
              "span_orders_of_magnitude": 0.92
             }
            },
            "per_type_and_assay_type": {
             "AC50/assay=F": {
              "n": 1,
              "min": 3.8,
              "median": 3.8,
              "max": 3.8,
              "unit_class": "molar",
              "span_orders_of_magnitude": 0.0
             },
             "EC50/assay=A": {
              "n": 1,
              "min": 2162.0,
              "median": 2162.0,
              "max": 2162.0,
              "unit_class": "molar",
              "span_orders_of_magnitude": 0.0
             }
            },
            "p_value": {
             "n": 328,
             "min": 4.64,
             "median": 7.85,
             "max": 10.0,
             "unit_class": "log",
             "span_orders_of_magnitude": 5.36,
             "note": "p_value is -log10 molar, so its span is max-min and is comparable across sources"
            },
            "warning": "value is NOT comparable across unit_class; never average them. p_value (-log10 molar) is the only cross-class comparable column."
           },
           "summary_basis": "all 328 matches (the list is a page of 5)",
           "layer": {
            "rows": 5153526,
            "genes": 6682,
            "drugs": 29967,
            "built_at": "2026-10-21T08:57:39Z",
            "sources": {
             "chembl": {
              "file": "chembl_37.db",
              "rows_kept": 5113551,
              "rows_read": 5210648,
              "rows_refused": 97097,
              "sha256": "4be13df3b68e25dcd0bff44bf094033b5aebe98f415acdc8c1cdf380e0c15142"
             },
             "drugcentral": {
              "file": "drugcentral.tsv.gz",
              "rows_kept": 20685,
              "rows_read": 22364,
              "rows_refused": 1679,
              "sha256": "1908684983a79a067a44da952e1f6fd26b0f34cc36560cd9be0f0843bbfee1e1"
             },
             "gtopdb": {
              "file": "gtopdb.csv",
              "rows_kept": 19290,
              "rows_read": 24959,
              "rows_refused": 5669,
              "sha256": "c1b81895f520bd2b29b5631224320da4047a7d175dc06ba2adcc60f1a9e1ffa1"
             }
            }
           },
           "elapsed_seconds": 0.008
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "Neither gene nor drug, or a non-integer limit.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "missing ?gene=<symbol> or ?drug=<name>"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "429": {
      "$ref": "#/components/responses/QuotaExceeded"
     },
     "503": {
      "description": "Affinity layer not available or lookup failed; measurements is null.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "affinity index not available",
         "status": "not_loaded",
         "measurements": null,
         "summary": null
        }
       }
      }
     }
    }
   }
  },
  "/interaction": {
   "get": {
    "operationId": "interaction",
    "tags": [
     "Drugs & interactions"
    ],
    "summary": "Get protein-protein interaction partners from STRING v12 or BioGRID, or test a single pair.",
    "description": "By default this reads STRING v12: partners sorted by combined_score, which is a confidence on a 0-1000 scale, not a probability. source=biogrid reads curated BioGRID records instead; each names an experimental system and a publication, and its score is free text ('-' in most rows) that must not be sorted or averaged. The two sources are never merged. A protein that is not in the species dictionary, an ambiguous symbol (candidates are listed) or a pair with no edge answers 404; an absent edge means 'not in this release at this threshold', not evidence of no interaction. STRING interaction is a different claim from metagenomic co-occurrence (/cooccur).",
    "parameters": [
     {
      "name": "protein",
      "in": "query",
      "required": false,
      "description": "Gene symbol, UniProt accession, Ensembl protein id or STRING id. Aliases: id or gene for STRING; gene or symbol for BioGRID (uppercase exact symbol match).",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "a",
      "in": "query",
      "required": false,
      "description": "First protein of a single-pair query (use with b).",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "b",
      "in": "query",
      "required": false,
      "description": "Second protein of a single-pair query.",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "source",
      "in": "query",
      "required": false,
      "description": "string (STRING v12) or biogrid (BioGRID 4.4).",
      "schema": {
       "type": "string",
       "default": "string",
       "enum": [
        "string",
        "biogrid"
       ]
      }
     },
     {
      "name": "species",
      "in": "query",
      "required": false,
      "description": "NCBI taxonomy id (alias: taxid). STRING only; unknown ids answer 404 with the available list.",
      "schema": {
       "type": "string",
       "default": "9606"
      }
     },
     {
      "name": "min_score",
      "in": "query",
      "required": false,
      "description": "STRING only: minimum combined_score on the 0-1000 scale (not clamped).",
      "schema": {
       "type": "integer",
       "default": 0
      }
     },
     {
      "name": "limit",
      "in": "query",
      "required": false,
      "description": "Maximum partners (STRING) or records (BioGRID).",
      "schema": {
       "type": "integer",
       "default": 50
      }
     },
     {
      "name": "channels",
      "in": "query",
      "required": false,
      "description": "STRING only: include per-channel scores (experimental, database, textmining, …) where available. Pass 1 to enable.",
      "schema": {
       "type": "integer",
       "enum": [
        0,
        1
       ],
       "default": 0
      }
     },
     {
      "name": "uniprot",
      "in": "query",
      "required": false,
      "description": "BioGRID only: query by UniProt accession.",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "type",
      "in": "query",
      "required": false,
      "description": "BioGRID symbol queries only: physical or genetic (exact, lowercase; other values return zero records). Ignored with uniprot.",
      "schema": {
       "type": "string",
       "enum": [
        "physical",
        "genetic"
       ]
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK. Example values are real engine responses, trimmed.",
      "headers": {
       "X-RateLimit-Limit": {
        "$ref": "#/components/headers/X-RateLimit-Limit"
       },
       "X-RateLimit-Remaining": {
        "$ref": "#/components/headers/X-RateLimit-Remaining"
       },
       "X-RateLimit-Reset": {
        "$ref": "#/components/headers/X-RateLimit-Reset"
       },
       "X-Request-Id": {
        "$ref": "#/components/headers/X-Request-Id"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "query": {
           "type": "string"
          },
          "found": {
           "type": "boolean"
          },
          "string_id": {
           "type": "string"
          },
          "preferred_name": {
           "type": "string"
          },
          "uniprot": {
           "type": "string"
          },
          "resolution": {
           "type": "string",
           "description": "how the query was matched (symbol, uniprot, ensembl, string_id)"
          },
          "degree_total": {
           "type": "integer",
           "description": "partner counts before the limit"
          },
          "degree_at_or_above_min_score": {
           "type": "integer",
           "description": "partner counts before the limit"
          },
          "min_score_filter": {
           "type": "integer"
          },
          "returned": {
           "type": "integer"
          },
          "score_name": {
           "type": "string"
          },
          "score_scale": {
           "type": "string",
           "description": "combined_score is 0-1000 confidence, not a probability"
          },
          "channels_available": {
           "type": "array"
          },
          "partners": {
           "type": "array"
          },
          "layer": {
           "type": "string"
          },
          "source": {
           "type": "string"
          },
          "taxid": {
           "type": "string"
          },
          "claim": {
           "type": "string"
          },
          "sibling_sources": {
           "type": "object"
          },
          "absence_note": {
           "type": "string",
           "description": "an absent edge is not evidence of no interaction"
          },
          "elapsed_seconds": {
           "type": "number"
          }
         },
         "additionalProperties": true
        },
        "examples": {
         "default": {
          "summary": "/interaction?protein=TP53&species=9606&min_score=900&limit=5",
          "value": {
           "query": "TP53",
           "found": true,
           "string_id": "9606.ENSP00000269305",
           "preferred_name": "TP53",
           "uniprot": "P04637",
           "resolution": "symbol",
           "degree_total": 7443,
           "degree_at_or_above_min_score": 283,
           "min_score_filter": 900,
           "returned": 5,
           "score_name": "combined_score",
           "score_scale": "0-1000 (STRING v12 combined_score; a confidence, not a probability)",
           "channels_available": [
            "neighborhood",
            "fusion",
            "cooccurence",
            "coexpression",
            "experimental",
            "database",
            "textmining"
           ],
           "partners": [
            {
             "partner": "9606.ENSP00000212015",
             "partner_name": "SIRT1",
             "partner_uniprot": "Q96EB6",
             "score_name": "combined_score",
             "combined_score": 999,
             "score_scale": "0-1000 (STRING v12 combined_score; a confidence, not a probability)"
            },
            {
             "partner": "9606.ENSP00000254719",
             "partner_name": "RPA1",
             "partner_uniprot": "P27694",
             "score_name": "combined_score",
             "combined_score": 999,
             "score_scale": "0-1000 (STRING v12 combined_score; a confidence, not a probability)"
            },
            {
             "partner": "9606.ENSP00000258149",
             "partner_name": "MDM2",
             "partner_uniprot": "Q00987",
             "score_name": "combined_score",
             "combined_score": 999,
             "score_scale": "0-1000 (STRING v12 combined_score; a confidence, not a probability)"
            }
           ],
           "layer": "string_ppi",
           "source": "STRING v12",
           "taxid": "9606",
           "claim": "a STRING interaction edge (curated/detected association), NOT an assembly co-occurrence",
           "sibling_sources": {
            "biogrid": "/interaction?source=biogrid&protein=<SYMBOL> - curated experiments with a PubMed id; keyed by gene symbol and UniProt accession, which is the join our layers use",
            "string_node_count": 19699,
            "note": "STRING and BioGRID are different evidence types; neither replaces the other and this endpoint never merges them"
           },
           "absence_note": "an absent edge means 'not in this release at this threshold'; STRING lists positive associations only",
           "elapsed_seconds": 0.0002
          }
         },
         "pair": {
          "summary": "/interaction?a=TP53&b=MDM2",
          "value": {
           "a": "9606.ENSP00000269305",
           "a_name": "TP53",
           "a_uniprot": "P04637",
           "b": "9606.ENSP00000258149",
           "b_name": "MDM2",
           "b_uniprot": "Q00987",
           "query_as_given": {
            "TP53": "symbol",
            "MDM2": "symbol"
           },
           "found": true,
           "score_name": "combined_score",
           "combined_score": 999,
           "score_scale": "0-1000 (STRING v12 combined_score; a confidence, not a probability)",
           "channels": {
            "neighborhood": 0,
            "fusion": 0,
            "cooccurence": 0,
            "coexpression": 116,
            "experimental": 999,
            "database": 900,
            "textmining": 999
           },
           "layer": "string_ppi",
           "source": "STRING v12",
           "taxid": "9606",
           "elapsed_seconds": 0.0001
          }
         },
         "biogrid": {
          "summary": "/interaction?source=biogrid&protein=TP53&limit=3",
          "value": {
           "query": "TP53",
           "found": true,
           "n": 6110,
           "returned": 3,
           "truncated": true,
           "n_distinct_publications": 1737,
           "by_experimental_system_top10": {
            "Affinity Capture-Western": 2009,
            "Affinity Capture-MS": 1618,
            "Reconstituted Complex": 733,
            "Proximity Label-MS": 413,
            "Biochemical Activity": 395,
            "Two-hybrid": 182,
            "Negative Genetic": 155,
            "Co-localization": 118,
            "Synthetic Growth Defect": 104,
            "Protein-peptide": 59
           },
           "score_field": {
            "name": "biogrid_score_text",
            "type": "text",
            "note": "BioGRID's Score column is free text ('-' in most rows). It is NOT numeric and must not be sorted, averaged or compared as a magnitude."
           },
           "interactions": [
            {
             "interaction_id": 16305,
             "entrez_a": 7157,
             "entrez_b": 51741,
             "symbol_a": "TP53",
             "symbol_b": "WWOX",
             "type": "physical",
             "system": "Two-hybrid",
             "author": "Chang NS (2001)",
             "pubmed": "PUBMED:11058590",
             "tax_a": 9606,
             "tax_b": 9606,
             "throughput": "Low Throughput",
             "score_text": "-",
             "uniprot_a": "P04637",
             "uniprot_b": "Q9NZC7",
             "organism_a": "Homo sapiens",
             "organism_b": "Homo sapiens",
             "score_name": "biogrid_score_text",
             "score_scale": "free text; '-' means no numeric score in this record"
            },
            {
             "interaction_id": 16334,
             "entrez_a": 7157,
             "entrez_b": 8556,
             "symbol_a": "TP53",
             "symbol_b": "CDC14A",
             "type": "physical",
             "system": "Two-hybrid",
             "author": "Li L (2000)",
             "pubmed": "PUBMED:10644693",
             "tax_a": 9606,
             "tax_b": 9606,
             "throughput": "Low Throughput",
             "score_text": "-",
             "uniprot_a": "P04637",
             "uniprot_b": "Q9UNH5",
             "organism_a": "Homo sapiens",
             "organism_b": "Homo sapiens",
             "score_name": "biogrid_score_text",
             "score_scale": "free text; '-' means no numeric score in this record"
            }
           ],
           "layer": "biogrid",
           "source": "BioGRID 4.4.248",
           "claim": "a curated BioGRID interaction record naming an experimental system and a publication, NOT a confidence",
           "elapsed_seconds": 0.2567
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "No protein (or a/b, or uniprot), or a non-integer min_score/limit on STRING.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "missing ?protein=<id-or-symbol>"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "404": {
      "description": "Protein not in the dictionary, ambiguous symbol, no edge, or species not in the index.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "species not in this index",
         "species": "0",
         "note": "404 is about the INDEX VOCABULARY, not about biology"
        }
       }
      }
     },
     "429": {
      "$ref": "#/components/responses/QuotaExceeded"
     },
     "503": {
      "description": "Interaction layer not available; partners is null.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "PPI layer not available",
         "status": "not_loaded",
         "partners": null
        }
       }
      }
     }
    }
   }
  },
  "/domains": {
   "get": {
    "operationId": "domains",
    "tags": [
     "Proteins & domains"
    ],
    "summary": "List the Pfam domains annotated on an MGnify protein.",
    "description": "The Pfam table covers about 2.99 billion of the 5.74 billion MGnify proteins. A protein that is not in it returns domains null and no_annotation true, which means unannotated, not 'has no domains'. Domains are listed in the source's row order and are not ranked by e-value: the index keeps only (protein, domain) pairs, so a marginal hit and a certain one look the same. discovery_readiness says how many independent assemblies the protein was observed in.",
    "parameters": [
     {
      "name": "protein",
      "in": "query",
      "required": true,
      "description": "MGnify protein id, as MGYP000000000001 or the bare integer 1 (alias: cluster).",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK. Example values are real engine responses, trimmed.",
      "headers": {
       "X-RateLimit-Limit": {
        "$ref": "#/components/headers/X-RateLimit-Limit"
       },
       "X-RateLimit-Remaining": {
        "$ref": "#/components/headers/X-RateLimit-Remaining"
       },
       "X-RateLimit-Reset": {
        "$ref": "#/components/headers/X-RateLimit-Reset"
       },
       "X-Request-Id": {
        "$ref": "#/components/headers/X-Request-Id"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "cluster": {
           "type": "integer"
          },
          "domains": {
           "type": "array",
           "description": "Pfam accessions, or null when the protein is unannotated"
          },
          "n_domains": {
           "type": "integer"
          },
          "unit": {
           "type": "string",
           "description": "the ids are proteins (MGYP accessions), not cluster representatives"
          },
          "unresolved": {
           "type": "array"
          },
          "no_annotation": {
           "type": "boolean",
           "description": "true when the protein is not in the Pfam table"
          },
          "note": {
           "type": "string",
           "description": "domains are not ranked by e-value"
          },
          "discovery_readiness": {
           "type": "object"
          }
         },
         "additionalProperties": true
        },
        "examples": {
         "default": {
          "summary": "/domains?protein=1",
          "value": {
           "cluster": 1,
           "domains": [
            "PF02566"
           ],
           "n_domains": 1,
           "unit": "protein (MGYP accession); the index is keyed by annotated protein, 2,985,942,516 of 5,739,566,171, not by the 1,661,142,385 cluster representatives",
           "unresolved": [],
           "no_annotation": false,
           "note": "domains are listed in the source's row order, NOT ranked by e-value — the index keeps only (protein, domain) pairs, so a marginal hit and a certain one are indistinguishable",
           "discovery_readiness": {
            "verdict": "testable",
            "claim_allowed": true,
            "n_assemblies": 282,
            "n_independent_runs": 59,
            "min_assemblies_required": 3,
            "why": "282 assemblies across 59 distinct runs: the minimum for a null contrast is met. The enrichment test on this engine is saturated, so only the depletion direction is valid"
           }
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "Missing or non-integer protein id.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "missing ?protein=<id>"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "429": {
      "$ref": "#/components/responses/QuotaExceeded"
     },
     "503": {
      "description": "Joined layers not loaded.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "joins not loaded"
        }
       }
      }
     }
    }
   }
  },
  "/domain": {
   "get": {
    "operationId": "domain",
    "tags": [
     "Proteins & domains"
    ],
    "summary": "List MGnify proteins that carry a given Pfam domain.",
    "description": "Returns MGnify protein ids carrying the domain, with the exact total (n_total). The key is named clusters but the entries are protein (MGYP) ids. Ids come in ascending order: this is neither a ranking by e-value nor a random sample. discovery_readiness is computed only over the returned page, so it is a lower bound (coverage: lower_bound). An accession that is not in Pfam answers 200 with an empty list and the reason in unresolved, not 404.",
    "parameters": [
     {
      "name": "acc",
      "in": "query",
      "required": true,
      "description": "Pfam accession, e.g. PF00069 (bare digits are zero-padded).",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "limit",
      "in": "query",
      "required": false,
      "description": "Maximum ids to return; n_total is always exact.",
      "schema": {
       "type": "integer",
       "default": 20
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK. Example values are real engine responses, trimmed.",
      "headers": {
       "X-RateLimit-Limit": {
        "$ref": "#/components/headers/X-RateLimit-Limit"
       },
       "X-RateLimit-Remaining": {
        "$ref": "#/components/headers/X-RateLimit-Remaining"
       },
       "X-RateLimit-Reset": {
        "$ref": "#/components/headers/X-RateLimit-Reset"
       },
       "X-Request-Id": {
        "$ref": "#/components/headers/X-Request-Id"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "domain": {
           "type": "string"
          },
          "clusters": {
           "type": "array",
           "description": "MGnify protein ids (integers), ascending"
          },
          "unresolved": {
           "type": "array"
          },
          "n_returned": {
           "type": "integer",
           "description": "page size and whether more exist"
          },
          "n_total": {
           "type": "integer",
           "description": "exact number of proteins carrying the domain"
          },
          "truncated": {
           "type": "boolean",
           "description": "page size and whether more exist"
          },
          "unit": {
           "type": "string"
          },
          "note": {
           "type": "string",
           "description": "ids are not ranked by e-value"
          },
          "discovery_readiness": {
           "type": "object",
           "description": "verdict over the returned page only (coverage: lower_bound)"
          }
         },
         "additionalProperties": true
        },
        "examples": {
         "default": {
          "summary": "/domain?acc=PF00069&limit=5",
          "value": {
           "domain": "PF00069",
           "clusters": [
            1344,
            14050,
            21406,
            22244,
            23853
           ],
           "unresolved": [],
           "n_returned": 5,
           "n_total": 6299635,
           "truncated": true,
           "unit": "protein (MGYP accession). The index is keyed by annotated protein, 2,985,942,516 of 5,739,566,171, not by the 1,661,142,385 cluster representatives",
           "note": "ids are in ascending order, NOT ranked by e-value - the index keeps only (protein, domain) pairs, so a marginal hit and a certain one are indistinguishable here",
           "discovery_readiness": {
            "verdict": "testable",
            "claim_allowed": true,
            "n_assemblies": 36,
            "n_independent_runs": 17,
            "min_assemblies_required": 3,
            "why": "36 assemblies across 17 distinct runs: the minimum for a null contrast is met. The enrichment test on this engine is saturated, so only the depletion direction is valid",
            "coverage": "lower_bound",
            "n_total_proteins": 6299635,
            "coverage_note": "The assembly count is a lower bound over only the 5 returned proteins (of 6,299,635). They are the first ids in ascending order, not a sample, so do not estimate prevalence from it for large domains. The lower bound already meets the minimum, so reading more cannot overturn this verdict"
           }
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "Missing acc.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "missing ?acc=PF00078"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "429": {
      "$ref": "#/components/responses/QuotaExceeded"
     },
     "503": {
      "description": "Joined layers not loaded.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "joins not loaded"
        }
       }
      }
     }
    }
   }
  },
  "/structure": {
   "get": {
    "operationId": "structure",
    "tags": [
     "Proteins & domains"
    ],
    "summary": "Look up an experimental PDB structure or an AlphaFold model and how far it can be trusted.",
    "description": "Accepts a PDB code (experimental) or a UniProt accession (AlphaFold model). evidence.kind says which, and the two are never comparable: an experimental entry carries resolution_a, a model carries mean_plddt and a quality_call (confident if mean pLDDT >= 70, low_confidence if >= 50, otherwise very_low_confidence). pLDDT is the model's own estimate, not a measurement error. Model entries usually carry no sequence (sequence_available false). A 404 means the loaded layer was searched and holds nothing for this id; the body gives the table sizes searched.",
    "parameters": [
     {
      "name": "id",
      "in": "query",
      "required": false,
      "description": "PDB code (1AKE) or UniProt accession (P69905).",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "inventory",
      "in": "query",
      "required": false,
      "description": "With no id, return the layer inventory instead. Pass 1 to enable.",
      "schema": {
       "type": "integer",
       "enum": [
        0,
        1
       ]
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK. Example values are real engine responses, trimmed.",
      "headers": {
       "X-RateLimit-Limit": {
        "$ref": "#/components/headers/X-RateLimit-Limit"
       },
       "X-RateLimit-Remaining": {
        "$ref": "#/components/headers/X-RateLimit-Remaining"
       },
       "X-RateLimit-Reset": {
        "$ref": "#/components/headers/X-RateLimit-Reset"
       },
       "X-Request-Id": {
        "$ref": "#/components/headers/X-Request-Id"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "entry": {
           "type": "object"
          },
          "elapsed_ms": {
           "type": "number"
          }
         },
         "additionalProperties": true
        },
        "examples": {
         "default": {
          "summary": "/structure?id=P69905",
          "value": {
           "entry": {
            "id": "P69905",
            "evidence": {
             "kind": "predicted",
             "method": "ALPHAFOLD",
             "model_version": "4",
             "variant": "F1",
             "mean_plddt": 98.05,
             "frac_plddt_gt90": 0.993,
             "frac_plddt_gt70": 0.993,
             "quality_call": "confident",
             "quality_rule": "confident if mean_plddt >= 70; low_confidence if >= 50; very_low_confidence below 50",
             "usable_for_claims": true,
             "comparable_with": "nothing experimental — pLDDT is a model's self-estimate, not a measurement error"
            },
            "title": null,
            "sequence": null,
            "sequence_length": 142,
            "sequence_available": false,
            "fetch": {
             "tar": "swissprot_pdb_v4.tar",
             "member": "AF-P69905-F1-model_v4.pdb.gz"
            }
           },
           "elapsed_ms": 0.58
          }
         },
         "experimental": {
          "summary": "/structure?id=1AKE",
          "value": {
           "entry": {
            "id": "1AKE",
            "evidence": {
             "kind": "experimental",
             "method": "X-RAY DIFFRACTION",
             "resolution_a": 2.0,
             "resolution_rounded_to": 2,
             "comparable_with": "other experimental structures only"
            },
            "title": "STRUCTURE OF THE COMPLEX BETWEEN ADENYLATE KINASE FROM ESCHERICHIA COLI AND THE INHIBITOR AP5A REFINED AT 1.9 ANGSTROMS RESOLUTION: A MODEL FOR A CATALYTIC TRANSITION STATE",
            "sequence": "MRIILLGAPGAGKGTQAQFIMEKYGIPQISTGDMLRAAVKSGSELGKQAKDIMDAGKLVT…",
            "sequence_length": 214,
            "sequence_available": true,
            "entity_count": 1,
            "atom_count": 3317,
            "fetch": {
             "tar": "mmCIF_ak.tar",
             "member": "ak/1ake.cif.gz",
             "note": "two ranged reads recover the original mmCIF"
            }
           },
           "elapsed_ms": 1.0
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "Missing id (and no inventory=1).",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "missing ?id="
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "404": {
      "description": "The layer is loaded and holds no structure for this id.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "no structure for this identifier",
         "id": "XXXX"
        }
       }
      }
     },
     "429": {
      "$ref": "#/components/responses/QuotaExceeded"
     },
     "503": {
      "description": "Structure layer not loaded.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "structure index not loaded",
         "status": "not_loaded"
        }
       }
      }
     }
    }
   }
  },
  "/organism": {
   "get": {
    "operationId": "organism",
    "tags": [
     "Proteins & domains"
    ],
    "summary": "Count UniProt TrEMBL proteins for an NCBI taxonomy id, or list the largest organisms.",
    "description": "count is exact: every record in UniProt TrEMBL (unreviewed entries) was parsed once. accessions is a sample of up to 256, not the full set; accessions_are states how many of the total are shown. Swiss-Prot (reviewed) entries are not part of this count. With top instead of taxid, returns organisms ranked by protein count. A 404 means the taxid had zero records in the full pass — a real absence, not an unloaded layer.",
    "parameters": [
     {
      "name": "taxid",
      "in": "query",
      "required": false,
      "description": "NCBI taxonomy id, e.g. 9606.",
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "top",
      "in": "query",
      "required": false,
      "description": "Without taxid: number of organisms to list, by protein count. Clamped to 1-1000. Ignored when taxid is given.",
      "schema": {
       "type": "integer",
       "default": 20,
       "minimum": 1,
       "maximum": 1000
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK. Example values are real engine responses, trimmed.",
      "headers": {
       "X-RateLimit-Limit": {
        "$ref": "#/components/headers/X-RateLimit-Limit"
       },
       "X-RateLimit-Remaining": {
        "$ref": "#/components/headers/X-RateLimit-Remaining"
       },
       "X-RateLimit-Reset": {
        "$ref": "#/components/headers/X-RateLimit-Reset"
       },
       "X-Request-Id": {
        "$ref": "#/components/headers/X-Request-Id"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "taxid": {
           "type": "integer"
          },
          "organism": {
           "type": "string"
          },
          "count": {
           "type": "integer",
           "description": "exact number of TrEMBL proteins for this taxid"
          },
          "accessions": {
           "type": "array",
           "description": "a sample (sample_size of count), not the complete list"
          },
          "sample_size": {
           "type": "integer"
          },
          "truncated": {
           "type": "boolean",
           "description": "true when count exceeds the sample"
          },
          "count_is": {
           "type": "string",
           "description": "plain statements of what each number means"
          },
          "accessions_are": {
           "type": "string",
           "description": "plain statements of what each number means"
          },
          "source": {
           "type": "object"
          }
         },
         "additionalProperties": true
        },
        "examples": {
         "default": {
          "summary": "/organism?taxid=9606",
          "value": {
           "taxid": 9606,
           "organism": "Homo sapiens",
           "count": 190275,
           "accessions": [
            "Q29VG3",
            "Q6VHB0",
            "Q6ZWB5",
            "U5ZAD9",
            "A0A5C2G0E2"
           ],
           "sample_size": 256,
           "truncated": true,
           "count_is": "exact — every record in the scanned TrEMBL object was parsed",
           "accessions_are": "256 of 190,275 accessions — a sample, not the full set",
           "source": {
            "key": "uniprot_trembl.fasta.gz",
            "bytes_scanned": 40628134612,
            "bytes_total": 40628134612
           }
          }
         },
         "top": {
          "summary": "/organism?top=3",
          "value": {
           "organisms": [
            {
             "taxid": 182803,
             "organism": "Araneus ventricosus",
             "count": 246652
            },
            {
             "taxid": 200361,
             "organism": "Aegilops tauschii subsp. strangulata",
             "count": 214173
            },
            {
             "taxid": 9606,
             "organism": "Homo sapiens",
             "count": 190275
            }
           ],
           "order": "protein count, descending"
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "Missing taxid and top, or a non-integer taxid.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "taxid must be an integer",
         "got": "human"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "404": {
      "description": "Zero TrEMBL records for this taxid.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "no proteins for this taxid",
         "taxid": 0
        }
       }
      }
     },
     "429": {
      "$ref": "#/components/responses/QuotaExceeded"
     },
     "503": {
      "description": "Taxon layer not loaded.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "taxon index not loaded",
         "status": "not_loaded"
        }
       }
      }
     }
    }
   }
  },
  "/dna": {
   "get": {
    "operationId": "dna",
    "tags": [
     "Genomes"
    ],
    "summary": "Fetch nucleotide bases for a region of the human genome (GRCh38) or a RefSeq sequence.",
    "description": "Coordinates are 1-based and inclusive: region=chr11:1000000-1000100 returns 101 bases. Human chromosome names are tried against GRCh38 first; any other name (e.g. NC_045512.2) is served from RefSeq nucleotide FASTA (archaea, fungi, mitochondrion, protozoa, viral, and mouse GRCm39). layer, assembly and division always say which genome the bases came from. Bare names like 1 exist in both human and mouse; ask for mouse as mm39:1. If end runs past the sequence the answer is clamped and marked truncated; a start past the end, or a name neither layer holds, is an error.",
    "parameters": [
     {
      "name": "region",
      "in": "query",
      "required": true,
      "description": "name:start-end, 1-based inclusive (commas allowed in numbers). Examples: chr11:1000000-1000100, NC_045512.2:21563-21600, mm39:1:3000000-3000100.",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK. Example values are real engine responses, trimmed.",
      "headers": {
       "X-RateLimit-Limit": {
        "$ref": "#/components/headers/X-RateLimit-Limit"
       },
       "X-RateLimit-Remaining": {
        "$ref": "#/components/headers/X-RateLimit-Remaining"
       },
       "X-RateLimit-Reset": {
        "$ref": "#/components/headers/X-RateLimit-Reset"
       },
       "X-Request-Id": {
        "$ref": "#/components/headers/X-Request-Id"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "region": {
           "type": "string"
          },
          "chrom": {
           "type": "string"
          },
          "start": {
           "type": "integer",
           "description": "the span actually returned (1-based, inclusive)"
          },
          "end": {
           "type": "integer",
           "description": "the span actually returned (1-based, inclusive)"
          },
          "length": {
           "type": "integer",
           "description": "the span actually returned (1-based, inclusive)"
          },
          "sequence": {
           "type": "string",
           "description": "the bases as stored in the source FASTA"
          },
          "elapsed_seconds": {
           "type": "number"
          },
          "source": {
           "type": "string"
          },
          "layer": {
           "type": "string",
           "description": "human-grch38 or refseq"
          },
          "assembly": {
           "type": "string",
           "description": "which genome the bases are from"
          },
          "division": {
           "type": "string",
           "description": "which genome the bases are from"
          },
          "line_width": {
           "type": "integer"
          },
          "sequence_length": {
           "type": "integer",
           "description": "RefSeq only: full length of the sequence"
          }
         },
         "additionalProperties": true
        },
        "examples": {
         "default": {
          "summary": "/dna?region=NC_045512.2:21563-21600",
          "value": {
           "region": "NC_045512.2:21563-21600",
           "chrom": "NC_045512.2",
           "start": 21563,
           "end": 21600,
           "length": 38,
           "sequence": "ATGTTTGTTTTTCTTGTTTTATTGCCACTAGTCTCTAG",
           "elapsed_seconds": 0.0003,
           "source": "RefSeq viral",
           "layer": "refseq",
           "assembly": "NC_045512.2",
           "division": "viral",
           "line_width": 80,
           "sequence_length": 29903
          }
         },
         "human": {
          "summary": "/dna?region=chr11:1000000-1000100",
          "value": {
           "region": "chr11:1000000-1000100",
           "chrom": "chr11",
           "start": 1000000,
           "end": 1000100,
           "length": 101,
           "sequence": "GTTTCACCGTGTTAGCCAGGGTGGTCTCGATCTCCTGACCTCGTGATCCGCCCACCTCGG…",
           "elapsed_seconds": 0.0041,
           "source": "GRCh38 primary assembly",
           "layer": "human-grch38",
           "assembly": "GRCh38",
           "organism": "Homo sapiens"
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "Missing or unparseable region.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "use ?region=chr11:1000000-1000100"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "404": {
      "description": "The name is in neither nucleotide layer (or a RefSeq coordinate is out of range; see detail).",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "'chrZ' is in neither nucleotide layer",
         "searched_layers": [
          "human-grch38",
          "refseq"
         ]
        }
       }
      }
     },
     "416": {
      "description": "Human layer: start past the end of the chromosome, or end before start.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "start 999999999 is past the last base of chr11 (135,086,622)",
         "region": "chr11:999999999-1000000000"
        }
       }
      }
     },
     "429": {
      "$ref": "#/components/responses/QuotaExceeded"
     },
     "503": {
      "description": "No genome layer loaded.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "genome index not loaded"
        }
       }
      }
     }
    }
   }
  },
  "/synteny": {
   "get": {
    "operationId": "synteny",
    "tags": [
     "Genomes"
    ],
    "summary": "Show where an MGnify protein sits on assembled contigs and which proteins lie within n base pairs.",
    "description": "For each placement of the protein (assembly, contig, start, end, strand) the response lists neighbouring proteins on the same contig within the window, with the gap in base pairs. This is genomic context on one contig, a different claim from assembly-level co-occurrence (/cooccur) or curated interaction (/interaction). An empty neighbour list at the default 5 kb window is mostly a property of short metagenomic contigs (about 91% of placements have none), so re-ask with the suggested window before reading it as 'this protein is alone'. A 404 means the protein has no placement at all.",
    "parameters": [
     {
      "name": "protein",
      "in": "query",
      "required": true,
      "description": "MGnify protein id, MGYP000187092429 or the bare integer (alias: id).",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "n",
      "in": "query",
      "required": false,
      "description": "Window in base pairs on each side. Values outside 0-1000000 are refused.",
      "schema": {
       "type": "integer",
       "default": 5000,
       "minimum": 0,
       "maximum": 1000000
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK. Example values are real engine responses, trimmed.",
      "headers": {
       "X-RateLimit-Limit": {
        "$ref": "#/components/headers/X-RateLimit-Limit"
       },
       "X-RateLimit-Remaining": {
        "$ref": "#/components/headers/X-RateLimit-Remaining"
       },
       "X-RateLimit-Reset": {
        "$ref": "#/components/headers/X-RateLimit-Reset"
       },
       "X-Request-Id": {
        "$ref": "#/components/headers/X-Request-Id"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "protein": {
           "type": "string"
          },
          "n": {
           "type": "integer"
          },
          "placements": {
           "type": "array"
          },
          "placements_total": {
           "type": "integer",
           "description": "at most 64 placements are returned"
          },
          "neighbour_total": {
           "type": "integer",
           "description": "neighbours across returned placements"
          },
          "truncated": {
           "type": "boolean",
           "description": "at most 64 placements are returned"
          },
          "window_interpretation": {
           "type": "object",
           "description": "whether an empty list is mostly contig sparsity, and a suggested window"
          },
          "ms": {
           "type": "number"
          }
         },
         "additionalProperties": true
        },
        "examples": {
         "default": {
          "summary": "/synteny?protein=MGYP000187092429&n=5000",
          "value": {
           "protein": "MGYP000187092429",
           "n": 5000,
           "placements": [
            {
             "erz": "ERZ22157947",
             "contig": "ERZ22157947.1852784-NODE-1852814-length-597-cov-3.712177",
             "start": 2,
             "end": 595,
             "strand": -1,
             "neighbours": []
            },
            {
             "erz": "ERZ22158282",
             "contig": "ERZ22158282.115901-NODE-115905-length-597-cov-13.690037",
             "start": 2,
             "end": 595,
             "strand": -1,
             "neighbours": []
            }
           ],
           "placements_total": 38,
           "neighbour_total": 0,
           "truncated": false,
           "window_interpretation": {
            "empty_is_mostly_sparsity": true,
            "measured": "gap p50 = 16,355 bp; 91.0% of placements have zero neighbours at n=5000 over 1,911,465 contigs",
            "suggested_n_bp": 50000,
            "note": "an empty list here is predominantly a property of short metagenomic contigs. Re-ask with the suggested window before reading it as 'this protein is alone'."
           },
           "ms": 268.337
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "Missing or malformed protein id, or n not an integer in 0-1000000.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "n must be 0..1000000"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "404": {
      "description": "The protein has no placement in the layer.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "protein": "MGYP000000000001",
         "error": "protein not found",
         "placements": []
        }
       }
      }
     },
     "429": {
      "$ref": "#/components/responses/QuotaExceeded"
     },
     "503": {
      "description": "Synteny layer not loaded.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "synteny layer not loaded"
        }
       }
      }
     }
    }
   }
  },
  "/biome": {
   "get": {
    "operationId": "biome",
    "tags": [
     "Metagenomes"
    ],
    "summary": "Count MGnify protein clusters by habitat (biome lineage), or list the biome vocabulary.",
    "description": "Counts are exact over all 1.66 billion MGnify clusters. clusters includes every deeper lineage under the matched one; clusters_exactly_at_this_lineage is the part assigned to that lineage itself. sample is a bounded sample of cluster ids, never the complete set. Biome membership overlaps (a cluster can be in several biomes), so never subtract counts to get 'in A and not in B'. An ambiguous name (soil matches two environments) or an unknown one answers 404 with options or candidates — a statement about the index vocabulary, not an empty biome. Without lineage, returns the vocabulary ranked by size.",
    "parameters": [
     {
      "name": "lineage",
      "in": "query",
      "required": false,
      "description": "Biome lineage, e.g. root:Environmental:Terrestrial:Soil (case-insensitive; a unique suffix also works). Alias: biome.",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "limit",
      "in": "query",
      "required": false,
      "description": "Number of sample cluster ids to return. Clamped to 0-500.",
      "schema": {
       "type": "integer",
       "default": 50,
       "minimum": 0,
       "maximum": 500
      }
     },
     {
      "name": "children",
      "in": "query",
      "required": false,
      "description": "Include child biomes; 0 to omit.",
      "schema": {
       "type": "integer",
       "enum": [
        0,
        1
       ],
       "default": 1
      }
     },
     {
      "name": "top",
      "in": "query",
      "required": false,
      "description": "Vocabulary mode (no lineage): number of biomes. Clamped to 1-500.",
      "schema": {
       "type": "integer",
       "default": 25,
       "minimum": 1,
       "maximum": 500
      }
     },
     {
      "name": "max_depth",
      "in": "query",
      "required": false,
      "description": "Vocabulary mode: deepest lineage level listed. Clamped to 1-12.",
      "schema": {
       "type": "integer",
       "default": 3,
       "minimum": 1,
       "maximum": 12
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK. Example values are real engine responses, trimmed.",
      "headers": {
       "X-RateLimit-Limit": {
        "$ref": "#/components/headers/X-RateLimit-Limit"
       },
       "X-RateLimit-Remaining": {
        "$ref": "#/components/headers/X-RateLimit-Remaining"
       },
       "X-RateLimit-Reset": {
        "$ref": "#/components/headers/X-RateLimit-Reset"
       },
       "X-Request-Id": {
        "$ref": "#/components/headers/X-Request-Id"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "requested": {
           "type": "string"
          },
          "matched": {
           "type": "boolean"
          },
          "how": {
           "type": "string"
          },
          "lineage": {
           "type": "string"
          },
          "lineage_parts": {
           "type": "array"
          },
          "depth": {
           "type": "integer"
          },
          "clusters": {
           "type": "integer",
           "description": "clusters at this lineage and everything under it (exact)"
          },
          "clusters_exactly_at_this_lineage": {
           "type": "integer",
           "description": "the disjoint part assigned to this lineage itself"
          },
          "members": {
           "type": "integer",
           "description": "member proteins in those clusters"
          },
          "first_cluster_id": {
           "type": "string"
          },
          "last_cluster_id": {
           "type": "string"
          },
          "sample": {
           "type": "array",
           "description": "a bounded sample of cluster ids (sample_is_complete is false)"
          },
          "sample_stride": {
           "type": [
            "null",
            "object",
            "array",
            "string",
            "number",
            "boolean"
           ]
          },
          "sample_thinned": {
           "type": "integer"
          },
          "sample_bounded": {
           "type": "boolean"
          },
          "sample_is_a_sample": {
           "type": "boolean"
          },
          "sample_is_complete": {
           "type": "boolean"
          },
          "corpus": {
           "type": "object"
          },
          "children": {
           "type": "array"
          },
          "children_sum_note": {
           "type": "string"
          },
          "prefix_expansion": {
           "type": "string"
          },
          "elapsed_seconds": {
           "type": "number"
          }
         },
         "additionalProperties": true
        },
        "examples": {
         "default": {
          "summary": "/biome?lineage=root:Environmental:Terrestrial:Soil&limit=3",
          "value": {
           "requested": "root:Environmental:Terrestrial:Soil",
           "matched": true,
           "how": "exact",
           "lineage": "root:environmental:terrestrial:soil",
           "lineage_parts": [
            "root",
            "environmental",
            "terrestrial",
            "soil"
           ],
           "depth": 4,
           "clusters": 49877070,
           "clusters_exactly_at_this_lineage": 25293143,
           "members": 122487759,
           "first_cluster_id": "MGYP000000000012",
           "last_cluster_id": "MGYP011140254586",
           "sample": [
            "MGYP000000000012",
            "MGYP011133358521",
            "MGYP011134264257"
           ],
           "sample_stride": null,
           "sample_thinned": 6755,
           "sample_bounded": false,
           "sample_is_a_sample": true,
           "sample_is_complete": false,
           "corpus": {
            "total_clusters": 1661142385,
            "fraction_of_corpus": 0.030025764
           },
           "children": [
            {
             "prefix": "root:environmental:terrestrial:soil:tropical rainforest",
             "name": "tropical rainforest",
             "clusters": 8188087,
             "clusters_exactly_at_this_lineage": 7730222
            },
            {
             "prefix": "root:environmental:terrestrial:soil:forest soil",
             "name": "forest soil",
             "clusters": 7015265,
             "clusters_exactly_at_this_lineage": 5925088
            },
            {
             "prefix": "root:environmental:terrestrial:soil:agricultural",
             "name": "agricultural",
             "clusters": 5272596,
             "clusters_exactly_at_this_lineage": 4538079
            }
           ],
           "children_sum_note": "children are DISJOINT subtrees of each other, but their sum can exceed `clusters` and that is not an error: 5.5% of clusters are in two or more top-level biomes at once (measured on 10,777,396 clusters), so biome membership OVERLAPS. Never derive 'in A and not in B', or 'everything else', by subtracting these numbers.",
           "prefix_expansion": "lineage prefix expansion is APPLIED: `clusters` counts the matched prefix AND every deeper lineage under it, because the builder indexes every ancestor prefix of every lineage. A query for 'soil' therefore reaches 'soil:Permafrost' and 'soil:Sand'. `clusters_exactly_at_this_lineage` is the disjoint part — clusters whose own lineage is this prefix — so `clusters` >= `clusters_exactly_at_this_lineage` always, and the difference is the subtree.",
           "elapsed_seconds": 5.6e-05
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "Non-integer limit, top or max_depth.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "limit must be an integer"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "404": {
      "description": "Lineage ambiguous or not in the vocabulary.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "requested": "soil",
         "matched": false,
         "how": "ambiguous-suffix:root:environmental:terrestrial:soil,root:host-associated:plants:rhizosphere:soil",
         "ambiguous": true,
         "options": [
          {
           "lineage": "root:environmental:terrestrial:soil",
           "clusters": 49877070,
           "query": "/meta/biome?lineage=root:environmental:terrestrial:soil"
          },
          {
           "lineage": "root:host-associated:plants:rhizosphere:soil",
           "clusters": 27501887,
           "query": "/meta/biome?lineage=root:host-associated:plants:rhizosphere:soil"
          }
         ],
         "note": "no prefix in the index matches this lineage; `candidates` are the closest prefixes the index does hold. This is a 404 about the INDEX VOCABULARY, not a claim that the biome is empty — the `index.clusters_total` field says what corpus was counted."
        }
       }
      }
     },
     "429": {
      "$ref": "#/components/responses/QuotaExceeded"
     },
     "503": {
      "description": "Biome count layer not loaded; clusters is null, not 0.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "biome count index not loaded",
         "status": "not_loaded",
         "clusters": null
        }
       }
      }
     }
    }
   }
  },
  "/unexplored": {
   "get": {
    "operationId": "unexplored",
    "tags": [
     "Metagenomes"
    ],
    "summary": "List large MGnify protein clusters with little or no Pfam annotation.",
    "description": "Returns annotation-poor clusters, optionally limited to a habitat, sorted by size (number of member sequences). Size is not a measure of how well a candidate is supported: each row's discovery_readiness counts the independent assemblies it was seen in, and most of these clusters sit in a single assembly. Gate on n_claimable, not on matched. pfam_poor describes annotation coverage, not function. Each row costs an index read, so keep top small.",
    "parameters": [
     {
      "name": "biome",
      "in": "query",
      "required": false,
      "description": "Case-insensitive text matched inside each cluster's biome lineages, e.g. root:Environmental:Terrestrial:Soil or soil.",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "min_size",
      "in": "query",
      "required": false,
      "description": "Minimum member sequences per cluster.",
      "schema": {
       "type": "integer",
       "default": 5
      }
     },
     {
      "name": "top",
      "in": "query",
      "required": false,
      "description": "Rows to return.",
      "schema": {
       "type": "integer",
       "default": 20
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK. Example values are real engine responses, trimmed.",
      "headers": {
       "X-RateLimit-Limit": {
        "$ref": "#/components/headers/X-RateLimit-Limit"
       },
       "X-RateLimit-Remaining": {
        "$ref": "#/components/headers/X-RateLimit-Remaining"
       },
       "X-RateLimit-Reset": {
        "$ref": "#/components/headers/X-RateLimit-Reset"
       },
       "X-Request-Id": {
        "$ref": "#/components/headers/X-Request-Id"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "matched": {
           "type": "integer",
           "description": "clusters passing min_size and biome"
          },
          "n_returned": {
           "type": "integer"
          },
          "truncated": {
           "type": "boolean"
          },
          "meaning": {
           "type": "string",
           "description": "what pfam_poor and the ordering do and do not mean"
          },
          "n_claimable": {
           "type": "integer",
           "description": "returned rows whose readiness allows a claim"
          },
          "readiness_note": {
           "type": "string",
           "description": "what pfam_poor and the ordering do and do not mean"
          },
          "results": {
           "type": "array"
          }
         },
         "additionalProperties": true
        },
        "examples": {
         "default": {
          "summary": "/unexplored?biome=root:Environmental:Terrestrial:Soil&top=5",
          "value": {
           "matched": 49158,
           "n_returned": 5,
           "truncated": true,
           "meaning": "pfam_poor describes annotation coverage, not function",
           "n_claimable": 0,
           "readiness_note": "size is the number of member sequences in the cluster, not the number of assemblies. Each row's discovery_readiness says how many independent assemblies the candidate was actually observed in; the size ordering is not a ranking of claimability",
           "results": [
            {
             "cluster": "MGYP003380827528",
             "size": 2560,
             "pfam_poor": true,
             "n_biomes": 8,
             "biomes": [
              "root:Host-associated:Human:Digestive system:Large intestine:Fecal",
              "root:Engineered:Biogas plant:Wet fermentation",
              "root:Host-associated:Mammals:Respiratory system"
             ],
             "discovery_readiness": {
              "verdict": "no_claim",
              "claim_allowed": false,
              "n_assemblies": 1,
              "n_independent_runs": 1,
              "min_assemblies_required": 3,
              "why": "One assembly: a single independent observation supports no association claim. The partner list is only neighbours inside the same sample"
             }
            },
            {
             "cluster": "MGYP003545442403",
             "size": 989,
             "pfam_poor": true,
             "n_biomes": 8,
             "biomes": [
              "root:Mixed",
              "root:Host-associated:Mammals:Digestive system:Fecal",
              "root:Host-associated:Birds:Digestive system:Ceca"
             ],
             "discovery_readiness": {
              "verdict": "no_claim",
              "claim_allowed": false,
              "n_assemblies": 1,
              "n_independent_runs": 1,
              "min_assemblies_required": 3,
              "why": "One assembly: a single independent observation supports no association claim. The partner list is only neighbours inside the same sample"
             }
            }
           ]
          }
         }
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "429": {
      "$ref": "#/components/responses/QuotaExceeded"
     },
     "503": {
      "description": "Unexplored table not loaded.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "unexplored index not loaded"
        }
       }
      }
     }
    }
   }
  },
  "/unexplored_with_known": {
   "get": {
    "operationId": "unexplored_with_known",
    "tags": [
     "Metagenomes"
    ],
    "summary": "Pair annotation-poor clusters with the clusters they co-occur with across assemblies.",
    "description": "Takes the largest annotation-poor clusters matching the filters, keeps only those seen in at least two assemblies, and lists their co-occurring partners. A partner is a hypothesis about what an unexplored cluster does, not evidence. Each partner carries pair_readiness: pairs whose shared assemblies all come from one collection run are no_claim. The call examines at least 200 candidates and can take tens of seconds; if no candidate survives, meaning explains that this is the shape of the data.",
    "parameters": [
     {
      "name": "biome",
      "in": "query",
      "required": false,
      "description": "Case-insensitive text matched inside biome lineages.",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "min_size",
      "in": "query",
      "required": false,
      "description": "Minimum member sequences per candidate cluster.",
      "schema": {
       "type": "integer",
       "default": 50
      }
     },
     {
      "name": "limit",
      "in": "query",
      "required": false,
      "description": "Maximum pairs to return (the candidate pool is max(40 x limit, 200)).",
      "schema": {
       "type": "integer",
       "default": 10
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK. Example values are real engine responses, trimmed.",
      "headers": {
       "X-RateLimit-Limit": {
        "$ref": "#/components/headers/X-RateLimit-Limit"
       },
       "X-RateLimit-Remaining": {
        "$ref": "#/components/headers/X-RateLimit-Remaining"
       },
       "X-RateLimit-Reset": {
        "$ref": "#/components/headers/X-RateLimit-Reset"
       },
       "X-Request-Id": {
        "$ref": "#/components/headers/X-Request-Id"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "pairs": {
           "type": "array"
          },
          "unresolved": {
           "type": "array"
          },
          "candidates_examined": {
           "type": "integer",
           "description": "how the pool was narrowed"
          },
          "candidates_matched": {
           "type": "integer",
           "description": "how the pool was narrowed"
          },
          "min_assemblies": {
           "type": "integer",
           "description": "assemblies a candidate needs to have partners (2)"
          },
          "survivors": {
           "type": "integer",
           "description": "how the pool was narrowed"
          }
         },
         "additionalProperties": true
        },
        "examples": {
         "default": {
          "summary": "/unexplored_with_known?biome=root:Environmental:Terrestrial:Soil&limit=2",
          "value": {
           "pairs": [
            {
             "unexplored_cluster": 1004231992,
             "size": 304,
             "size_meaning": "member sequences in this cluster (MGnify 'size'), not assembly count",
             "n_assemblies": 9,
             "n_biomes": 8,
             "partners": [
              {
               "cluster": 1004938243,
               "n_both": 9,
               "n_partner": 9,
               "id_distance": 706251,
               "pair_readiness": {
                "n_shared": 9,
                "n_independent_runs": 1,
                "verdict": "no_claim",
                "claim_allowed": false,
                "why": "All 9 assemblies come from the same collection run (ERZ724…): one sampling event, not independent replication"
               }
              },
              {
               "cluster": 1004937807,
               "n_both": 9,
               "n_partner": 32,
               "id_distance": 705815,
               "pair_readiness": {
                "n_shared": 9,
                "n_independent_runs": 1,
                "verdict": "no_claim",
                "claim_allowed": false,
                "why": "All 9 assemblies come from the same collection run (ERZ724…): one sampling event, not independent replication"
               }
              }
             ],
             "biomes": [
              "root:Engineered:Wastewater",
              "root:Environmental:Terrestrial:Soil:Agricultural",
              "root:Host-associated:Fish:Digestive system"
             ]
            }
           ],
           "unresolved": [],
           "candidates_examined": 200,
           "candidates_matched": 1232,
           "min_assemblies": 2,
           "survivors": 2
          }
         }
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "429": {
      "$ref": "#/components/responses/QuotaExceeded"
     },
     "503": {
      "description": "Joined layers not loaded.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "joins not loaded"
        }
       }
      }
     }
    }
   }
  },
  "/neighbourhood": {
   "get": {
    "operationId": "neighbourhood",
    "tags": [
     "Metagenomes"
    ],
    "summary": "Find MGnify proteins that appear in the same assemblies as a given one, with habitat attached.",
    "description": "Partners are scored by shared assemblies (n_both) and come from the queried protein's own index block, i.e. nearby ids. Partners whose shared assemblies span different collection runs (distant) are listed before those explained by one run (adjacent); pairs from one sampling run co-occur because they share genomes, not because they interact. Each partner has its own pair_readiness, and discovery_readiness covers the queried protein. An id absent from the release answers 200 with status other than FOUND, not 404.",
    "parameters": [
     {
      "name": "cluster",
      "in": "query",
      "required": true,
      "description": "MGnify protein id, MGYP… or the bare integer.",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "min_both",
      "in": "query",
      "required": false,
      "description": "Minimum shared assemblies for a partner.",
      "schema": {
       "type": "integer",
       "default": 3
      }
     },
     {
      "name": "top",
      "in": "query",
      "required": false,
      "description": "Maximum partners to return.",
      "schema": {
       "type": "integer",
       "default": 20
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK. Example values are real engine responses, trimmed.",
      "headers": {
       "X-RateLimit-Limit": {
        "$ref": "#/components/headers/X-RateLimit-Limit"
       },
       "X-RateLimit-Remaining": {
        "$ref": "#/components/headers/X-RateLimit-Remaining"
       },
       "X-RateLimit-Reset": {
        "$ref": "#/components/headers/X-RateLimit-Reset"
       },
       "X-Request-Id": {
        "$ref": "#/components/headers/X-Request-Id"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "cluster": {
           "type": "integer"
          },
          "partners": {
           "type": "array"
          },
          "unresolved": {
           "type": "array",
           "description": "habitat of the queried protein, or why it is missing"
          },
          "discovery_readiness": {
           "type": "object"
          },
          "status": {
           "type": "string",
           "description": "FOUND, or an absence status with meaning"
          },
          "n_assemblies": {
           "type": "integer"
          },
          "n_partners_found": {
           "type": "integer",
           "description": "counts before the top limit"
          },
          "n_adjacent": {
           "type": "integer",
           "description": "counts before the top limit"
          },
          "n_distant": {
           "type": "integer",
           "description": "counts before the top limit"
          },
          "n_partners_claimable": {
           "type": "integer",
           "description": "returned partners whose pair_readiness allows a claim"
          },
          "partners_readiness_note": {
           "type": "string"
          },
          "strongest_distant": {
           "type": "object",
           "description": "best partner not explained by one collection run"
          },
          "biomes": {
           "type": "array",
           "description": "habitat of the queried protein, or why it is missing"
          },
          "note": {
           "type": "string"
          }
         },
         "additionalProperties": true
        },
        "examples": {
         "default": {
          "summary": "/neighbourhood?cluster=1&top=5",
          "value": {
           "cluster": 1,
           "partners": [
            {
             "cluster": 80,
             "n_both": 234,
             "n_partner": 1606,
             "id_distance": 79,
             "pair_readiness": {
              "n_shared": 234,
              "n_independent_runs": 55,
              "verdict": "testable",
              "claim_allowed": true,
              "why": "234 assemblies across 55 distinct runs: the minimum for a null contrast is met. The enrichment test on this engine is saturated, so only the depletion direction is valid"
             }
            },
            {
             "cluster": 64,
             "n_both": 230,
             "n_partner": 1679,
             "id_distance": 63,
             "pair_readiness": {
              "n_shared": 230,
              "n_independent_runs": 56,
              "verdict": "testable",
              "claim_allowed": true,
              "why": "230 assemblies across 56 distinct runs: the minimum for a null contrast is met. The enrichment test on this engine is saturated, so only the depletion direction is valid"
             }
            }
           ],
           "unresolved": [
            "no biome annotation for this cluster"
           ],
           "discovery_readiness": {
            "verdict": "testable",
            "claim_allowed": true,
            "n_assemblies": 282,
            "n_independent_runs": 59,
            "min_assemblies_required": 3,
            "why": "282 assemblies across 59 distinct runs: the minimum for a null contrast is met. The enrichment test on this engine is saturated, so only the depletion direction is valid",
            "detail": "The assembly list is the representative protein's, so it is a lower bound for multi-member clusters"
           },
           "status": "FOUND",
           "n_assemblies": 282,
           "n_partners_found": 3399,
           "n_adjacent": 764,
           "n_distant": 2635,
           "n_partners_claimable": 5,
           "partners_readiness_note": "Each partner's pair_readiness counts the independent runs among the assemblies the pair shares. However large n_both is, a pair that comes from one run was observed once",
           "strongest_distant": {
            "cluster": 80,
            "n_both": 234,
            "id_distance": 79,
            "why_it_matters": "no single accession prefix accounts for half the shared assemblies, so one collection run does not explain this pair"
           },
           "biomes": [],
           "note": "id_distance under 1,000,000 usually means the same sampling run; those pairs co-occur because they share genomes, not because they interact"
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "Missing or non-integer cluster.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "missing ?cluster=<id>"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "429": {
      "$ref": "#/components/responses/QuotaExceeded"
     },
     "503": {
      "description": "Joined layers not loaded.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "joins not loaded"
        }
       }
      }
     }
    }
   }
  },
  "/cooccur": {
   "get": {
    "operationId": "cooccur",
    "tags": [
     "Metagenomes"
    ],
    "summary": "Count the assemblies an MGnify protein appears in, and its overlap with a second protein.",
    "description": "With only with, returns how many assemblies the protein was observed in and a sample of them. With without as well, returns the overlap (n_both), the assemblies unique to the first (n_only_with), and readiness for each side and for the pair; the weaker side bounds any claim. An observed/expected ratio is given only when you pass universe, the number of assemblies (universe_assemblies in the response), never the cluster count. Only the depletion direction is meaningful on this corpus; enrichment.quotable says whether the ratio may be quoted. Rows are representative proteins, so for a multi-member cluster the assembly list is a lower bound.",
    "parameters": [
     {
      "name": "with",
      "in": "query",
      "required": true,
      "description": "MGnify protein id (MGYP… or bare integer).",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "without",
      "in": "query",
      "required": false,
      "description": "Second protein id for an overlap query.",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "universe",
      "in": "query",
      "required": false,
      "description": "Assembly count to use as the denominator of an observed/expected ratio (e.g. the universe_assemblies value). Non-integer values are ignored.",
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK. Example values are real engine responses, trimmed.",
      "headers": {
       "X-RateLimit-Limit": {
        "$ref": "#/components/headers/X-RateLimit-Limit"
       },
       "X-RateLimit-Remaining": {
        "$ref": "#/components/headers/X-RateLimit-Remaining"
       },
       "X-RateLimit-Reset": {
        "$ref": "#/components/headers/X-RateLimit-Reset"
       },
       "X-Request-Id": {
        "$ref": "#/components/headers/X-Request-Id"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "with": {
           "type": "integer"
          },
          "n_with": {
           "type": "integer",
           "description": "assemblies each protein was observed in"
          },
          "unit": {
           "type": "string",
           "description": "rows are proteins; assembly lists are lower bounds for clusters"
          },
          "universe_assemblies": {
           "type": "integer",
           "description": "the assembly count of the corpus"
          },
          "discovery_readiness": {
           "type": "object",
           "description": "verdicts for each side and the pair"
          },
          "without": {
           "type": "integer"
          },
          "n_without": {
           "type": "integer",
           "description": "assemblies each protein was observed in"
          },
          "n_only_with": {
           "type": "integer",
           "description": "shared and first-only assemblies"
          },
          "n_both": {
           "type": "integer",
           "description": "shared and first-only assemblies"
          },
          "sample_only_with": {
           "type": "array",
           "description": "first 20 assembly accessions"
          },
          "pair_readiness": {
           "type": "object",
           "description": "verdicts for each side and the pair"
          },
          "weakest_side": {
           "type": "object",
           "description": "verdicts for each side and the pair"
          },
          "enrichment": {
           "type": [
            "null",
            "object",
            "array",
            "string",
            "number",
            "boolean"
           ],
           "description": "null unless universe is given; then observed, expected, ratio, quotable"
          },
          "enrichment_note": {
           "type": "string"
          },
          "caveat": {
           "type": "string"
          },
          "elapsed_ms": {
           "type": "number"
          }
         },
         "additionalProperties": true
        },
        "examples": {
         "default": {
          "summary": "/cooccur?with=1&without=80",
          "value": {
           "with": 1,
           "n_with": 282,
           "unit": "protein (MGYP accession); for a multi-member cluster this is the representative's assembly list, a lower bound on the cluster's",
           "universe_assemblies": 51274,
           "discovery_readiness": {
            "verdict": "testable",
            "claim_allowed": true,
            "n_assemblies": 282,
            "n_independent_runs": 59,
            "min_assemblies_required": 3,
            "why": "282 assemblies across 59 distinct runs: the minimum for a null contrast is met. The enrichment test on this engine is saturated, so only the depletion direction is valid"
           },
           "without": 80,
           "n_without": 1606,
           "n_only_with": 48,
           "n_both": 234,
           "sample_only_with": [
            "ERZ1026854",
            "ERZ1026878",
            "ERZ1030449"
           ],
           "pair_readiness": {
            "verdict": "testable",
            "claim_allowed": true,
            "n_shared": 234,
            "n_independent_runs": 55,
            "bounded_by": "testable",
            "why": "234 assemblies across 55 distinct runs: the minimum for a null contrast is met. The enrichment test on this engine is saturated, so only the depletion direction is valid"
           },
           "weakest_side": {
            "which": "with",
            "verdict": "testable",
            "claim_allowed": true,
            "n_assemblies": 282,
            "n_independent_runs": 59
           },
           "enrichment": null,
           "enrichment_note": "pass &universe=<assembly count> for an observed/expected ratio; it is omitted rather than guessed because a wrong denominator makes the number meaningless",
           "caveat": "a pair present in few assemblies is not evidence whatever the ratio",
           "elapsed_ms": 3.6
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "Missing with, a non-integer id, or both a and with given.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "missing ?with=<cluster id>",
         "example": "/cooccur?with=12345&without=67890"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "404": {
      "description": "The with id is absent from this MGnify release.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "with": 0,
         "status": "NOT_IN_RELEASE"
        }
       }
      }
     },
     "429": {
      "$ref": "#/components/responses/QuotaExceeded"
     },
     "503": {
      "description": "Co-occurrence index not loaded.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "co-occurrence index not loaded"
        }
       }
      }
     }
    }
   }
  },
  "/agent": {
   "post": {
    "operationId": "agent",
    "tags": [
     "Agent"
    ],
    "summary": "Ask a research question; streamed, cited answer (beta)",
    "description": "Beta. Send a JSON body; the response is a server-sent event stream. The agent plans, calls BioQ endpoints (in parallel where it can), and writes a markdown answer whose citations [n] refer to step n. Protein sequences in the question are replaced with handles such as [SEQ1] before the language model sees the text, and swapped back only when a step sends them to the BioQ engine. One request counts as one agent question; the queries the agent makes are not counted again. Up to 10 steps per question. Event payloads may change while in beta. Events: start, step, delta, turn_end, done, error.",
    "x-beta": true,
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "question": {
          "type": "string",
          "description": "The question, in plain language."
         },
         "lang": {
          "type": "string",
          "description": "Preferred answer language, e.g. en or ko."
         }
        },
        "required": [
         "question"
        ]
       },
       "example": {
        "question": "Which lineages depend most on KRAS?",
        "lang": "en"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Server-sent event stream: step, answer, citations, done, error.",
      "headers": {
       "X-RateLimit-Limit": {
        "$ref": "#/components/headers/X-RateLimit-Limit"
       },
       "X-RateLimit-Remaining": {
        "$ref": "#/components/headers/X-RateLimit-Remaining"
       },
       "X-RateLimit-Reset": {
        "$ref": "#/components/headers/X-RateLimit-Reset"
       },
       "X-Request-Id": {
        "$ref": "#/components/headers/X-Request-Id"
       }
      },
      "content": {
       "text/event-stream": {
        "schema": {
         "type": "string"
        },
        "example": "event: step\ndata: {\"name\": \"gene\", \"args\": {\"symbol\": \"EGFR\"}, \"status\": \"ok\", \"ms\": 41}\n\nevent: answer\ndata: {\"text\": \"EGFR has 4,141 ClinVar records, of which 60 are Pathogenic…\"}\n\nevent: citations\ndata: {\"items\": [{\"endpoint\": \"/gene?symbol=EGFR\"}]}\n\nevent: done\ndata: {}\n\n"
       }
      }
     },
     "400": {
      "description": "Body is not JSON or question is missing.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/EngineError"
        },
        "example": {
         "error": "question_required"
        }
       }
      }
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "429": {
      "$ref": "#/components/responses/QuotaExceeded"
     }
    }
   }
  },
  "/status": {
   "get": {
    "operationId": "status",
    "tags": [
     "Service"
    ],
    "summary": "Check that the service is up and which data layers are loaded (no authentication).",
    "description": "Public health check. layers maps each data layer to true when it is loaded; an endpoint whose layer is false answers 503 with the reason rather than an empty result. counts gives headline sizes of the loaded datasets. Does not count against any quota.",
    "security": [],
    "parameters": [],
    "responses": {
     "200": {
      "description": "OK. Example values are real engine responses, trimmed.",
      "headers": {
       "X-RateLimit-Limit": {
        "$ref": "#/components/headers/X-RateLimit-Limit"
       },
       "X-RateLimit-Remaining": {
        "$ref": "#/components/headers/X-RateLimit-Remaining"
       },
       "X-RateLimit-Reset": {
        "$ref": "#/components/headers/X-RateLimit-Reset"
       },
       "X-Request-Id": {
        "$ref": "#/components/headers/X-Request-Id"
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "status": {
           "type": "string",
           "description": "ok when the gateway and engine are reachable"
          },
          "layers": {
           "type": "object",
           "description": "map of layer name to loaded (true/false)"
          },
          "counts": {
           "type": "object",
           "description": "headline record counts for loaded datasets"
          }
         },
         "additionalProperties": true
        },
        "examples": {
         "default": {
          "summary": "/status",
          "value": {
           "status": "ok",
           "layers": {
            "mgnify": true,
            "trembl": true,
            "structure": true,
            "taxon": true,
            "c2": true,
            "dna": true,
            "refseq": true,
            "variants": true,
            "unexplored": true,
            "cooccur": true,
            "pfam": true,
            "pfam_reverse": true,
            "homology": true,
            "affinity": true,
            "gdsc": false,
            "string": true,
            "biogrid": true,
            "biome": true,
            "seqhash": false,
            "synteny": true,
            "depmap": true,
            "depmap_omics": true
           },
           "counts": {
            "mgnify_clusters": 1661142385,
            "pfam_annotated_proteins": 2985942516,
            "trembl_accessions": 149430635,
            "structures_experimental": 260621,
            "structures_predicted": 542378,
            "affinity_measurements": 5153526,
            "string_edges": 162725618,
            "biogrid_records": 2804534,
            "synteny_placements": 177896636
           }
          }
         }
        }
       }
      }
     }
    }
   }
  }
 },
 "components": {
  "securitySchemes": {
   "bearerAuth": {
    "type": "http",
    "scheme": "bearer",
    "bearerFormat": "bq_ API key",
    "description": "API key from https://bioq.bio/app/keys, sent as `Authorization: Bearer bq_...`."
   }
  },
  "headers": {
   "X-RateLimit-Limit": {
    "description": "Requests allowed in the current daily quota window.",
    "schema": {
     "type": "integer"
    }
   },
   "X-RateLimit-Remaining": {
    "description": "Requests left in the current window.",
    "schema": {
     "type": "integer"
    }
   },
   "X-RateLimit-Reset": {
    "description": "When the current quota window resets.",
    "schema": {
     "type": "string"
    }
   },
   "X-Request-Id": {
    "description": "Identifier of this request; quote it when reporting a problem.",
    "schema": {
     "type": "string"
    }
   }
  },
  "schemas": {
   "EngineError": {
    "type": "object",
    "description": "Error body from the BioQ engine. Always has `error`; most also explain `why` or give `examples`. Result fields are null (not empty) when no answer was produced.",
    "properties": {
     "error": {
      "type": "string"
     },
     "why": {
      "type": "string"
     },
     "status": {
      "type": "string"
     },
     "note": {
      "type": "string"
     }
    },
    "additionalProperties": true
   },
   "SignInRequired": {
    "type": "object",
    "required": [
     "error"
    ],
    "properties": {
     "error": {
      "type": "string",
      "const": "sign_in_required"
     }
    }
   },
   "QuotaExceeded": {
    "type": "object",
    "required": [
     "error",
     "limit",
     "resets_at"
    ],
    "properties": {
     "error": {
      "type": "string",
      "const": "quota_exceeded"
     },
     "limit": {
      "type": "integer"
     },
     "resets_at": {
      "type": "string",
      "format": "date-time"
     }
    }
   }
  },
  "responses": {
   "Unauthorized": {
    "description": "Missing or invalid API key or session.",
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/SignInRequired"
      },
      "example": {
       "error": "sign_in_required"
      }
     }
    }
   },
   "QuotaExceeded": {
    "description": "Daily plan quota used up (sequence searches have their own, smaller quota).",
    "headers": {
     "X-RateLimit-Limit": {
      "$ref": "#/components/headers/X-RateLimit-Limit"
     },
     "X-RateLimit-Remaining": {
      "$ref": "#/components/headers/X-RateLimit-Remaining"
     },
     "X-RateLimit-Reset": {
      "$ref": "#/components/headers/X-RateLimit-Reset"
     },
     "X-Request-Id": {
      "$ref": "#/components/headers/X-Request-Id"
     }
    },
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/QuotaExceeded"
      },
      "example": {
       "error": "quota_exceeded",
       "limit": 1000,
       "resets_at": "2026-10-24T00:00:00Z"
      }
     }
    }
   }
  }
 }
}