---
title: "Scan a sequence for promoter regions"
method: POST
path: "/v1/tasks/promoter/predict"
tags: ["Tasks"]
---

# Scan a sequence for promoter regions

`POST /v1/tasks/promoter/predict`

## Request

- Base URL: `https://api.genomicintelligence.ai`
- URL: `https://api.genomicintelligence.ai/v1/tasks/promoter/predict`
- Auth: HTTP bearer

Slide the selected promoter classifier across the sequence and return the windows and merged regions scoring above ``options.threshold``.

Minimum input is 300 bp — the smallest ``context_window_bp`` among the promoter models, so it is the shortest sequence that fills some model's window with no padding at all.

That floor is admission control, not a quality guarantee. The default ``g0-promoter-2000bp`` has a 2000 bp window, so a 300 bp request to it is scored against a heavily padded window — accepted and meaningful, but not the same regime as a full-window call. Read ``bio_spec.context_window_bp`` from ``GET /v1/tasks/promoter/models`` and compare it against your sequence length; pick ``g0-promoter-large-300bp`` or ``g0-promoter-base-300bp`` if your input really is ~300 bp. Longer input is never a problem — the scanner steps one prediction window at a time and pads only the final partial window.

## Query parameters

- `format` string, nullable: Output format: ``json`` (default) or one of ``bed``, ``bedgraph``. Text formats are synchronous-only — combining one with ``Prefer: respond-async`` is a ``400``. An unsupported value is a ``415 unsupported_format``, never a silent fallback to JSON.

## Headers

- `accept` string, nullable
- `Prefer` string, nullable: Set to ``respond-async`` to dispatch asynchronously: the server returns ``202`` with ``{data: {job_id, status, links}, meta}`` immediately and the result is polled from ``GET /v1/tasks/jobs/{job_id}``. Anything else (or omitting the header) delivers the result synchronously with ``200``. Async is JSON-only — combining it with a text ``format`` is a ``400``. Some endpoints *require* it above a size threshold and reject sync delivery with ``413 sync_too_large``.

## Request body

- PromoterPredictRequest: Body for ``POST /v1/tasks/promoter/predict``.
  - `sequence` string, required: DNA sequence (A/C/G/T/N, case-insensitive). Whitespace (newlines, spaces, tabs) is stripped first, so a line-wrapped FASTA sequence body may be pasted verbatim; **length is measured against the whitespace-stripped string**, not the raw character count — so a line-wrapped body can satisfy the published ``minLength`` as a raw string and still be rejected once stripped. Count bases, not characters. 300–500,000 bp. The 300 bp floor applies to every promoter model — it is the smallest ``context_window_bp`` among them, i.e. the shortest input that fills some promoter model's window exactly, with no padding. Requests outside the range are rejected with ``422 validation_failed`` before any model is loaded. Note that ``minLength`` is admission control, not a statement about regime: a sequence at least this long but shorter than the selected model's ``context_window_bp`` (published by ``GET /v1/tasks/{task}/models``) is accepted and scored, but against a window padded out to that size. Compare your length against that field to know whether the model saw real sequence or padding. Longer input is never a problem — the scanner steps one prediction window at a time and pads only the final partial window.
  - `sequence_name` string, nullable: Display name for the sequence (e.g., 'chr1:1000-5000').
  - `model` string, nullable: Model ID. If omitted, the task's default model is used. The valid ids and the default for this operation are published structurally as ``x-models`` on the operation itself — deliberately **not** as an ``enum`` here, so that adding a model cannot make a previously-generated client reject a valid id. ``GET /v1/tasks/{task}/models`` carries the same roster with full per-model specs.
  - `options` PromoterOptions
    - `threshold` number

## Response `200`

Successful inference. ``data`` is a discriminated union keyed on ``task`` — narrow on ``data.task`` (or ``meta.task``) to reach task-specific fields; here it is always ``"promoter"``. ``meta.task_specific_counts`` is the matching counts variant.

- PredictResponse: Success envelope for ``POST /v1/tasks/{task}/predict``.
  - `data` union, required
    - PromoterData: ``data`` shape for ``POST /v1/tasks/promoter/predict``.
      - `model` string, required: Model id that produced this result.
      - `input` PromoterInput, required: ``data.input`` for ``POST /v1/tasks/promoter/predict``.
        - `sequence_name` string, required: The caller's label for the submitted sequence, echoed verbatim. Defaults to ``"sequence"`` when the request omitted it.
      - `summary` object, required: Task-specific summary statistics. **Its keys are deliberately not part of this contract**: they are per-task statistics that may be added, renamed or removed without a contract revision, so consumers must not depend on them — branch on the typed fields (``meta`` and this payload's own declared keys) instead.
      - `formats` object, nullable: Optional per-format pre-rendered text payloads (BED/GFF3/…). In every GFF3 this service generates, column 2 (``source``) is the id of the model that produced the feature — the same value as ``model`` above — so a saved track records its own provenance. The one exception is a structure-aware model whose upstream pipeline emits its own canonical GFF3 (annotation's ``g0-annotation``): that document is passed through verbatim and keeps its producer's source value, which names a multi-stage pipeline rather than one model.
      - `task` 'promoter', required
      - `regions` object[], required: Detected promoter regions (start/end/score).
      - `window_details` object[], required: Per-window scoring detail (one entry per scanned window).
    - SpliceData: ``data`` shape for ``POST /v1/tasks/splice/predict``.
      - `model` string, required: Model id that produced this result.
      - `input` SpliceInput, required: ``data.input`` for ``POST /v1/tasks/splice/predict``.
        - `sequence_name` string, required: The caller's label for the submitted sequence, echoed verbatim. Defaults to ``"sequence"`` when the request omitted it.
      - `summary` object, required: Task-specific summary statistics. **Its keys are deliberately not part of this contract**: they are per-task statistics that may be added, renamed or removed without a contract revision, so consumers must not depend on them — branch on the typed fields (``meta`` and this payload's own declared keys) instead.
      - `formats` object, nullable: Optional per-format pre-rendered text payloads (BED/GFF3/…). In every GFF3 this service generates, column 2 (``source``) is the id of the model that produced the feature — the same value as ``model`` above — so a saved track records its own provenance. The one exception is a structure-aware model whose upstream pipeline emits its own canonical GFF3 (annotation's ``g0-annotation``): that document is passed through verbatim and keeps its producer's source value, which names a multi-stage pipeline rather than one model.
      - `task` 'splice', required
      - `sites` object[], required: Detected splice sites (donor / acceptor). **``start``/``end`` bound the scored BPE token, not the junction.** The model reads the sequence as byte-pair-encoded tokens and scores whole tokens, so each site's span is that token's character range — typically 4–8 bp, and varying site to site because token lengths do — while a donor or acceptor junction is a single base. ``token_index`` names the token. Treating the span as the feature's extent, or intersecting it with a base-resolution reference annotation, will be quietly wrong at that scale. The same spans are what ``?format=gff3`` writes into columns 4–5, where they outlive the response that produced them. No base-resolution position is published: deriving one from a token score would be a modelling claim the service cannot currently defend, and an invented precision in a saved file is worse than an honest span (GH#58).
      - `tracks` object, required: Per-position score tracks for visualization.
      - `window_details` object[], required: Per-window scoring detail.
    - EnhancerData: ``data`` shape for ``POST /v1/tasks/enhancer/predict``.
      - `model` string, required: Model id that produced this result.
      - `input` EnhancerInput, required: ``data.input`` for ``POST /v1/tasks/enhancer/predict``.
        - `sequence_name` string, required: The caller's label for the submitted sequence, echoed verbatim. Defaults to ``"sequence"`` when the request omitted it.
      - `summary` object, required: Task-specific summary statistics. **Its keys are deliberately not part of this contract**: they are per-task statistics that may be added, renamed or removed without a contract revision, so consumers must not depend on them — branch on the typed fields (``meta`` and this payload's own declared keys) instead.
      - `formats` object, nullable: Optional per-format pre-rendered text payloads (BED/GFF3/…). In every GFF3 this service generates, column 2 (``source``) is the id of the model that produced the feature — the same value as ``model`` above — so a saved track records its own provenance. The one exception is a structure-aware model whose upstream pipeline emits its own canonical GFF3 (annotation's ``g0-annotation``): that document is passed through verbatim and keeps its producer's source value, which names a multi-stage pipeline rather than one model.
      - `task` 'enhancer', required
      - `windows` object[], required: Per-window enhancer scores.
      - `tracks` object, required: Per-position score tracks for visualization.
    - ChromatinData: ``data`` shape for ``POST /v1/tasks/chromatin/predict``.
      - `model` string, required: Model id that produced this result.
      - `input` ChromatinInput, required: ``data.input`` for ``POST /v1/tasks/chromatin/predict``.
        - `sequence_name` string, required: The caller's label for the submitted sequence, echoed verbatim. Defaults to ``"sequence"`` when the request omitted it.
      - `summary` object, required: Task-specific summary statistics. **Its keys are deliberately not part of this contract**: they are per-task statistics that may be added, renamed or removed without a contract revision, so consumers must not depend on them — branch on the typed fields (``meta`` and this payload's own declared keys) instead.
      - `formats` object, nullable: Optional per-format pre-rendered text payloads (BED/GFF3/…). In every GFF3 this service generates, column 2 (``source``) is the id of the model that produced the feature — the same value as ``model`` above — so a saved track records its own provenance. The one exception is a structure-aware model whose upstream pipeline emits its own canonical GFF3 (annotation's ``g0-annotation``): that document is passed through verbatim and keeps its producer's source value, which names a multi-stage pipeline rather than one model.
      - `task` 'chromatin', required
      - `windows` object[], required: Per-window chromatin annotations.
      - `tracks` object, required: Per-position score tracks for visualization.
    - AnnotationData: ``data`` shape for ``POST /v1/tasks/annotation/predict``.
      - `model` string, required: Model id that produced this result.
      - `input` AnnotationInput, required: ``data.input`` for ``POST /v1/tasks/annotation/predict``.
        - `sequence_name` string, required: The caller's label for the submitted sequence, echoed verbatim. Defaults to ``"sequence"`` when the request omitted it.
      - `summary` object, required: Task-specific summary statistics. **Its keys are deliberately not part of this contract**: they are per-task statistics that may be added, renamed or removed without a contract revision, so consumers must not depend on them — branch on the typed fields (``meta`` and this payload's own declared keys) instead.
      - `formats` object, nullable: Optional per-format pre-rendered text payloads (BED/GFF3/…). In every GFF3 this service generates, column 2 (``source``) is the id of the model that produced the feature — the same value as ``model`` above — so a saved track records its own provenance. The one exception is a structure-aware model whose upstream pipeline emits its own canonical GFF3 (annotation's ``g0-annotation``): that document is passed through verbatim and keeps its producer's source value, which names a multi-stage pipeline rather than one model.
      - `task` 'annotation', required
      - `transcripts` object[], required: Detected transcripts. Every transcript carries gene boundaries (``start``/``end``/``strand``/``score``) plus ``tss_position`` and ``polya_position``. Structure-aware models (g0-annotation) additionally populate ``transcript_type`` (mRNA/lnc_RNA), ``transcript_type_score`` and 0-based half-open ``exons``/``introns``/``cds`` arrays. The ``formats`` object exposes ``bed`` and, for structure-aware models, a full ``gff3`` track (also available via ``Accept: text/x-gff3``).
    - ExpressionData: ``data`` shape for ``POST /v1/tasks/expression/predict``. ``input`` echoes the request only: ``sequence_name``, ``description`` and the ``tss_index`` the server applied. The windowing provenance the server derived from that offset is in ``meta`` — which part of the submission the model used is ``meta.task_specific_counts.scored_window`` (see that field), and the submitted length is ``meta.sequence_length``.
      - `model` string, required: Model id that produced this result.
      - `input` ExpressionInput, required: ``data.input`` for ``POST /v1/tasks/expression/predict``. Three echoes, all of them things the caller sent or the server chose on the caller's behalf. ``tss_index`` is the second kind: it is optional on the request and defaults to 4599 when the submission is exactly 9,198 bp, so the echo is the only statement of which offset was actually applied. What is **not** here, deliberately: ``scored_window`` (read ``meta.task_specific_counts.scored_window``) and ``submitted_sequence_length`` (read ``meta.sequence_length``). Both were derived twins of a ``meta`` field.
        - `sequence_name` string, required: The caller's label for the submitted sequence, echoed verbatim. Defaults to ``"sequence"`` when the request omitted it.
        - `description` string, required: The experimental context the prediction was conditioned on (cell type / assay), echoed from ``options.description``.
        - `tss_index` integer, required: The 0-based TSS offset the server applied, into the whitespace-stripped ``sequence`` that was submitted. Echoes the request's ``tss_index``, or the 4599 implied by a submission of exactly 9198 bp. The window it produced is ``meta.task_specific_counts.scored_window``.
      - `summary` object, required: Task-specific summary statistics. **Its keys are deliberately not part of this contract**: they are per-task statistics that may be added, renamed or removed without a contract revision, so consumers must not depend on them — branch on the typed fields (``meta`` and this payload's own declared keys) instead.
      - `formats` object, nullable: Optional per-format pre-rendered text payloads (BED/GFF3/…). In every GFF3 this service generates, column 2 (``source``) is the id of the model that produced the feature — the same value as ``model`` above — so a saved track records its own provenance. The one exception is a structure-aware model whose upstream pipeline emits its own canonical GFF3 (annotation's ``g0-annotation``): that document is passed through verbatim and keeps its producer's source value, which names a multi-stage pipeline rather than one model.
      - `task` 'expression', required
      - `prediction` object, required: Expression prediction record: ``{expression, expression_log_tpm, expression_tpm, unit}``.
  - `meta` Meta, required: Metadata envelope shared by every successful predict response.
    - `job_id` string, required: Server-generated UUIDv4. Mirrored on the X-Job-Id response header.
    - `request_id` string, required: Correlation id for this request, mirrored on the X-Request-Id response header and identical to ``error.request_id`` on the failure envelope. Quote it in support requests: it is the one handle that identifies a call whether it succeeded or failed.
    - `task` string, required: Task name (matches the {task} URL segment). Use this — or ``data.task`` — to narrow the typed unions.
    - `model` string, required: Model id that ran this inference.
    - `cold_start` boolean, required: True if the model had to be loaded into GPU memory for this request. Useful for diagnosing first-request latency.
    - `model_load_time_ms` integer, required: Milliseconds spent loading the model (0 when cold_start is False).
    - `inference_time_ms` integer, required: Milliseconds spent running inference.
    - `sequence_length` integer, required: Length of the submitted sequence (bp).
    - `task_specific_counts` union, required: Discriminated counts payload (see per-task ``*Counts`` models).
      - PromoterCounts
        - `task` 'promoter', required
        - `windows_processed` integer, required: Number of sliding windows scored.
        - `regions_found` integer, required: Detected promoter regions in this call.
      - SpliceCounts
        - `task` 'splice', required
        - `windows_processed` integer, required
        - `sites_found` integer, required: Detected splice sites (donor + acceptor).
      - EnhancerCounts
        - `task` 'enhancer', required
        - `windows_processed` integer, required
      - ChromatinCounts
        - `task` 'chromatin', required
        - `windows_processed` integer, required
        - `total_annotations` integer, required: Sum of per-track annotation counts across all windows.
      - AnnotationCounts
        - `task` 'annotation', required
        - `transcripts_found` integer, required
      - ExpressionCounts: Expression has no per-call counts — the prediction is a single value. What it carries instead is *windowing provenance*: an expression model reads a stretch of sequence around the TSS (how much depends on the model and on the sequence), while a caller may submit up to 500,000 bp. These fields report which part of the submission the model used (see ``scored_window``), so the contract is verifiable from the response alone rather than taken on trust. Always populated by ``POST /v1/tasks/expression/predict``.
        - `task` 'expression', required
        - `tss_index` integer, nullable: 0-based TSS offset the server applied, into the whitespace-stripped ``sequence`` that was submitted. Echoes the request's ``tss_index``, or the implied 4599 when the submission was exactly 9198 bp.
        - `scored_window` integer[], nullable: ``[start, end]`` — the half-open slice of the submitted, whitespace-stripped sequence the server cut for the model. For ``g0-expression`` it is the 9198 bp slice ``[tss_index-4599, tss_index+4599]``, of which the model reads the central part (roughly 3 kb on each side of the TSS, depending on sequence). For a model whose ``bio_spec.trained_window_bp`` is null it is the span actually read, so its width shows how much flank was used.

## Other responses

- `202`: Async submission accepted — returned only when ``Prefer: respond-async`` was sent. Poll ``GET /v1/tasks/jobs/{job_id}``; ``Content-Location`` carries the same path.
- `400`: Bad request — e.g. `Prefer: respond-async` combined with a text format.
- `401`: Missing or invalid bearer key (`unauthorized`).
- `403`: Key disabled or insufficient tier (`forbidden`).
- `404`: Unknown task, unknown job, or a job owned by another key (`not_found`).
- `409`: Conflict (`conflict`).
- `410`: Async job result expired out of the job store (`job_expired`).
- `413`: Either the 16 MiB request-body hard cap was exceeded (`payload_too_large`, enforced before parsing), or the body is over the endpoint's synchronous-delivery cap (`sync_too_large`; `error.details` carries `{sequence_length, threshold}` — retry the same request with `Prefer: respond-async`).
- `415`: The requested output format is not one this task can render (`unsupported_format`). The message lists the supported formats; the server never silently falls back to JSON.
- `422`: Validation failed (`validation_failed`) — sequence length outside the task's bounds, a non-ACGTN character, an unknown key under `options` (every options object is closed), or a sequence below the *selected task's published `minLength`/`maxLength` bounds.
- `429`: Rate or concurrency cap reached (`too_many_requests`). `Retry-After` and the `RateLimit-*` headers say when to retry.
- `500`: Server error (`internal_error`).
- `503`: Model still loading (`model_loading`), GPU memory exhausted (`insufficient_memory`), or service unavailable. `insufficient_memory` means the card OOM-ed on this request and its cache has been released — it is transient GPU pressure, not a server bug, so `Retry-After` is honest and a bounded retry is the right response.

---

[API](https://skmtc.dev/genomicintelligence/apis/genomic-intelligence-api.md) · [All operations](https://skmtc.dev/genomicintelligence/apis/genomic-intelligence-api/llms.txt) · [OpenAPI document](https://skmtc.dev/genomicintelligence/apis/genomic-intelligence-api/revisions/72436b86d4fe?raw)
