| name | query-genomic-intelligence |
| description | Predict regulatory features, gene structure, and expression directly from DNA sequence using Genomic Intelligence's hosted transformer DNA models - no local GPU. Use when the user has a gene symbol, genomic region, or DNA/FASTA sequence and wants promoter, splice-site, enhancer, chromatin, expression (log TPM), or de-novo gene annotation predictions. Triggers on "genomic intelligence", "promoter prediction", "splice site", "enhancer activity", "chromatin state", "expression from sequence", "log TPM", "gene annotation", "DNA language model", "genomicintelligence.ai". |
Genomic Intelligence - DNA Sequence Models
Genomic Intelligence (GI) serves transformer DNA language models over six
sequence-analysis tasks on managed GPUs. Give it a gene symbol, a genomic
region, or a DNA/FASTA sequence; it returns structured predictions.
Nothing runs locally - no model weights, no GPU. It is a thin client over a
hosted, versioned inference API.
Docs: https://docs.genomicintelligence.ai | REST contract at
https://api.genomicintelligence.ai/v1/openapi.json | hosted MCP server at
https://mcp.genomicintelligence.ai/mcp.
Research and development use. Not for clinical or diagnostic decisions.
When to Use
Use GI when the user has DNA and wants a model prediction:
- promoter - promoter regions in a genomic region (sliding window)
- splice - donor/acceptor splice sites
- enhancer - developmental & housekeeping enhancer activity (DeepSTARR)
- chromatin - chromatin state across hundreds of tracks (DeepSEA)
- expression - sequence-to-expression, log(TPM+1), with a cell-type context
- annotation - de-novo gene/transcript annotation (async recommended)
- composite - find the genes in a region and predict each one's expression
Not for local alignment, variant calling, or file I/O - use a local tool
(BioPython, bcftools) for those. GI is for model inference from sequence.
Access and Authentication
- The hosted MCP server (
https://mcp.genomicintelligence.ai/mcp, Streamable
HTTP) works keyless against a rate- and concurrency-limited public demo
tier - prefer it on hosts that support MCP. An optional gi_ bearer key
raises those limits.
- The REST
/v1 API requires a GI_API_KEY (a gi_ bearer), sent as
Authorization: Bearer <key>. Request one at contact@genomicintelligence.ai.
- Never hardcode the key. Read it from the
GI_API_KEY environment variable.
export GI_API_KEY="gi_yourkeyhere"
The Six Tasks
Each task is its own published operation - POST /v1/tasks/promoter/predict,
/v1/tasks/splice/predict, /v1/tasks/enhancer/predict,
/v1/tasks/chromatin/predict, /v1/tasks/annotation/predict,
/v1/tasks/expression/predict - with its own request schema, its own minimum
length, and its own closed options object. There is no shared PredictRequest.
The URLs are the same strings clients already POST to, so no URL construction
changes. Body {sequence, sequence_name?, model?, options?}, returning a
{data, meta} envelope. expression is the strictest: alone among the six it
requires options too - see the rules below.
| Task | Recommended mode | Accepted length | context_window_bp | Notes |
|---|
promoter | sync | 300-500,000 bp | 2,000 bp | sliding-window promoter regions |
splice | sync | 100-500,000 bp | 15,000 bp | donor/acceptor sites (BigBird); strand-specific - feed transcript orientation |
enhancer | sync | 50-500,000 bp | 249 bp | dev + housekeeping (DeepSTARR, Drosophila) |
chromatin | sync | 200-500,000 bp | 1,000 bp | hundreds of tracks (DeepSEA) |
expression | sync | 9,198-500,000 bp | n/a (trained_window_bp 9,198) | log(TPM+1); needs tss_index unless exactly 9,198 bp, plus a cell-type description |
annotation | async | 1,000-500,000 bp | n/a | de-novo transcripts; submit + poll |
Recommended mode is latency guidance, not a constraint - every task accepts
both. Omit Prefer for a synchronous 200; send Prefer: respond-async for a
202 plus GET /v1/tasks/jobs/{job_id}. Only the composite workflow enforces a
mode, rejecting sync above 50,000 bp with 413 sync_too_large.
The minimum is admission control, not a scoring regime. A request above
the floor but shorter than the selected model's bio_spec.context_window_bp
is accepted and scored - against a window padded out to the context window. Enhancer is the
sharp case: the floor is 50 bp but the context window is
249 bp, so 50-248 bp is scored mostly on padding. Compare your length against
context_window_bp from GET /v1/tasks/{task}/models to know whether the model
saw real sequence. Longer input is fine - the scanner steps a prediction window
at a time and pads only the final partial window.
Under the floor and over the 500,000 bp cap are both 422 validation_failed
at loc ["body","sequence"] - over-length is not a 413. All lengths are
measured after whitespace is stripped, so a line-wrapped FASTA body pastes
verbatim (a > header line still fails the alphabet check).
options is typed and closed (additionalProperties: false) per task - an
unknown key is a hard 422 (type: "extra_forbidden"), never ignored: promoter
threshold; splice threshold, site_types; enhancer none; chromatin
threshold; annotation batch_size, shift_coordinates,
reverse_complement; expression description (required, and the only key).
Three hard rules the API enforces for expression (every violation is a 422;
nothing is padded, clamped, or truncated, and there is no opt-out flag):
- The model always scores exactly one 9,198 bp TSS-centred window -
sequence[tss_index-4599 : tss_index+4599]. The endpoint accepts
9,198-500,000 bp; below 9,198 bp is a hard 422.
tss_index is required unless the sequence is exactly 9,198 bp. It is the
0-based TSS offset into the whitespace-stripped sequence, and must satisfy
4599 <= tss_index <= len(sequence) - 4599. At exactly 9,198 bp it defaults to
4,599 - the only legal value there. Hand over a whole locus (up to 500 kb) plus
a tss_index and the server cuts the window for you; it does not discover
the TSS itself, and it does not reverse-complement - submit gene-sense.
options.description - a cell-type / assay string (e.g. "K562 cells") -
is required, and is the only key expression accepts inside options.
Unknown top-level body fields are also rejected.
Trap: the legal tss_index range is wide, so a wrong but in-range offset
(e.g. counted over the raw FASTA including newlines, or relative to a locus
start instead of the submitted slice) returns a confident 200 for the wrong
window. Always assert on meta.task_specific_counts.scored_window (and
.tss_index) in the response. Note also that data.input.sequence_length is
the scored length (always 9,198); the length you submitted is
data.input.submitted_sequence_length / meta.sequence_length.
Both tss_index failures - "required unless exactly 9,198 bp" and the range
check - come from a whole-model validator, so they report at loc: ["body"],
never body.tss_index. Match on error.code == "validation_failed"; never
branch on loc.
Splice: submit gene-sense. Strand-specific, and the wrong strand fails
silently. Submit transcript orientation, reverse-complementing minus-strand
genes. A reverse-complemented sequence does not return zeros or an empty
result: it returns plausible sites at different positions, often still at high
confidence, and the site count can hold or collapse depending on the locus.
Neither the score nor the count tells you the orientation was wrong, so
there is no post-hoc check - get the orientation right on input. A gene-symbol
fetch follows the gene's own strand; a coordinate fetch does not, so a
minus-strand gene pulled by coordinates arrives antisense unless you
reverse-complement it yourself. expression never reverse-complements either;
annotation is strand-insensitive.
A splice coordinate is a token span, not a junction. Each site's
start/end bounds one variable-width tokenizer token - 4-10 bp across the
sequences measured so far - reported with a token_index. The exon/intron
junction sits somewhere inside that span, so do not reduce the pair to a single
base position, and do not intersect it against reference annotation as though it
marked a boundary.
Omit model and the API uses the task's default - that is the recommended
call. Default model IDs are intentionally not documented here: defaults change
and retired IDs fail hard, so never hardcode one. To pin a model, or to pick a
non-human one (Drosophila, yeast, and Arabidopsis models exist for several
tasks - match the species), discover IDs at call time with
GET /v1/tasks/{task}/models (REST) or list_models (MCP). Never invent a
model ID.
How to Execute (REST)
Called synchronously - no Prefer header - a prediction is one request:
import os, requests
BASE = os.environ.get("GI_BASE_URL", "https://api.genomicintelligence.ai")
HEADERS = {"Authorization": f"Bearer {os.environ['GI_API_KEY']}"}
def gi_predict(task, sequence, sequence_name, model=None, options=None, tss_index=None):
body = {"sequence": sequence, "sequence_name": sequence_name}
if model: body["model"] = model
if options: body["options"] = options
if tss_index is not None: body["tss_index"] = tss_index
r = requests.post(f"{BASE}/v1/tasks/{task}/predict", headers=HEADERS, json=body)
r.raise_for_status()
return r.json()
out = gi_predict("promoter", seq, "TP53_region")
print(out["data"]["summary"])
out = gi_predict("expression", tss_window_9198bp, "HBB",
options={"description": })
(out[][][])
out = gi_predict(, locus_seq, , options={: },
tss_index=tss_offset_in_locus)
(out[][][])
Prefer: respond-async is a declared header on all six predict operations
and on the composite - a 202 carries the same {data, meta} envelope with
data = {job_id, status, links} (job id also in Content-Location / X-Job-Id).
Async is JSON-only and available on every task; annotation is the one that
usually needs it: send
Prefer: respond-async, get a job_id, then poll GET /v1/tasks/jobs/{job_id}
until it returns 200 (a 202 means still running):
import time
r = requests.post(f"{BASE}/v1/tasks/annotation/predict",
headers={**HEADERS, "Prefer": "respond-async"},
json={"sequence": seq, "sequence_name": "TP53"})
r.raise_for_status()
job_id = r.json()["data"]["job_id"]
while True:
j = requests.get(f"{BASE}/v1/tasks/jobs/{job_id}", headers=HEADERS)
if j.status_code == 200: break
j.raise_for_status(); time.sleep(5)
transcripts = j.json()["data"]["transcripts"]
Sequence Acquisition
You rarely start from a raw sequence. Fetch reference sequence from Ensembl
REST (rest.ensembl.org, public, no key) for a gene symbol or region, then
feed it to GI. For expression, either build the 9,198 bp TSS-centred window
from the gene's canonical transcript (expand=1): 4,599 bp upstream + 4,598 bp
downstream on the gene's strand - or fetch a wider locus and pass the TSS offset
as tss_index and let the server cut it. Default species is human, GRCh38; use the
Ensembl production name for others (mus_musculus, drosophila_melanogaster).
Fetch at least the task floor (promoter 300, splice 100, enhancer 50, chromatin
200, annotation 1,000 bp) - and ideally at least the model's context_window_bp,
or the score reflects padding rather than sequence.
Hosted MCP (keyless, preferred on MCP hosts)
Acquire a sequence handle (sequence_ref), then predict against it, so large
sequences never enter the context:
load_demo_sequence(name="promoter_tp53") # keyless smoke test -> handle; name REQUIRED
fetch_ensembl_sequence(gene="TP53") # gene symbol or Ensembl ID -> handle
fetch_region(region="chr11:5,225,000-5,235,000") # coordinates -> handle
fetch_gene_for_expression(gene="HBB") # TSS-centred 9,198 bp handle
predict_promoter(sequence_ref=<ref>) # + predict_splice/_enhancer/_chromatin
predict_expression(sequence_ref=<ref>, description="K562 cells")
find_genes(sequence_ref=<ref>) # annotation task
find_genes_and_predict_expression(sequence_ref=<ref>, description=...) # composite
Note the hosted server exposes 15 tools and has no predict_annotation: the
annotation task is find_genes, which takes a handle rather than a region and
runs async internally (wait=True by default returns the result; wait=False
returns a job_id to poll with get_job). There is no load_local_fasta on the
hosted server either; use store_inline_sequence.
Reference context lives in the gi://models, gi://docs/tasks, and
gi://account MCP resources.
Key Endpoints
| Method | Path | Purpose |
|---|
| POST | /v1/tasks/<task>/predict | Six literal paths, one per task (add Prefer: respond-async for annotation). An unrecognised task is 404 not_found, not a 422 |
| POST | /v1/workflows/find-genes-and-predict-expression | Composite (see below) |
| GET | /v1/tasks/jobs · /v1/tasks/jobs/{job_id} | List / poll async jobs (202 running -> 200 terminal) |
| GET | /v1/tasks/{task}/models | Model IDs + bio_spec for a task. Needs a key, and returns a flat {task, default_model, models} - not the {data, meta} envelope |
bio_spec carries request_max_bp (the enforced cap, 500,000 everywhere),
context_window_bp (the sliding window; null for annotation/expression) and
trained_window_bp (9,198 for the expression model). The legacy
max_seq_length_bp has been withdrawn and no longer appears in bio_spec;
request_max_bp is the cap. There is no strand_sensitive flag.
Notes
- Errors:
422 validation_failed is the catch-all - sequence under the task
floor or over 500,000 bp, expression below 9,198 bp, missing/out-of-range
tss_index, missing options.description, or any unknown body/options key.
401/403 missing/invalid key (REST). 404 not_found unknown task or job.
413 means only payload_too_large (the 16 MiB raw-body cap) or
sync_too_large (the composite above 50,000 bp synchronous - retry async),
never an over-long sequence. 415 unsupported_format for a bad format query
value - there is no silent fallback to JSON. 429 rate cap (honour
Retry-After; ask GI to raise the tier). 5xx retry.
error.code is a closed 21-value enum; treat an unlisted value as a generic
failure, not a parse error. Branch on code, never on details or loc:
details matches the declared schema - a validation failure carries
{errors: [{loc, msg, type}, ...]} - so read it defensively all the same.
error.request_id mirrors the X-Request-Id header and both are always
populated; success envelopes carry meta.request_id. Every response carries
RateLimit-* headers.
- The API serves the six literal predict operations, typed
options, per-task
floors, the published composite, the Prefer parameter, the code enum and
the bio_spec fields described here. The served schema at
https://api.genomicintelligence.ai/v1/openapi.json is the authority; check it
if a detail here does not match.
- GI is a hosted service; nothing here ships weights or runs local inference.
Follow-up Suggestions
- For "what genes are here and how are they expressed?", use the composite:
POST /v1/workflows/find-genes-and-predict-expression (REST) or
find_genes_and_predict_expression (MCP). sequence 1,000-500,000 bp and
options are required, and options.description (cell type / assay) is
required too - a missing or empty value is a 422. It cuts a TSS-centred
9,198 bp window per discovered gene (padding with N at region edges rather
than dropping a gene) and returns a prediction each;
meta.task_specific_counts = {genes_found, genes_predicted, genes_skipped},
per-gene causes in data.expression_predictions[].skip_reason. Above
50,000 bp a synchronous call is 413 sync_too_large - retry the same body
with Prefer: respond-async.
- To explore available models per task, call
list_models / GET .../models
before predicting.