# How to Use Ensembl's REST API for Variant Annotation

By the end of this guide you will be able to annotate human variants against the GRCh38 assembly with Ensembl's REST API, pull transcript-level consequences from the Variant Effect Predictor (VEP), convert dbSNP rsIDs into HGVS and SPDI notation, batch up to 200 variants per request, and parse the JSON responses in Python. You need Python 3.8 or later with the requests library (install with `pip install requests`), curl, and an internet connection. jq is optional but handy for skimming JSON in the terminal.

A REST API (Representational State Transfer application programming interface) is a web service that programs query with plain HTTP requests instead of a graphical interface. Ensembl, the genome annotation project run by EMBL-EBI, publishes its API at https://rest.ensembl.org. Variant annotation means attaching biological meaning to a raw variant: which gene and transcript it touches, whether it changes a protein, and how the reference and alternate alleles differ. The API performs this annotation through VEP, which classifies each variant against every transcript it overlaps using terms from the Sequence Ontology, a controlled vocabulary for describing sequence features.

## What the REST API Does for Variant Annotation

The service exposes several endpoints (URL paths, each with its own job) that matter for variant work:

- `/vep/{species}/...` annotates a variant and returns per-transcript consequences. It accepts an existing rsID, a region with alleles, HGVS notation, or a VCF-style string.
- `/variant_recoder/{species}/{id}` converts between rsID, HGVS, SPDI, and VCF representations of the same variant.
- `/variation/{species}/{id}` returns the variant record: location, allele string, variant class, and the most severe consequence.
- `/overlap/region/{species}/{region}?feature=variation` lists every known variant in a coordinate range.
- `/sequence/region/{species}/{region}` returns reference bases so you can check an allele before annotating.

Three properties shape how you use the service. First, it is free and needs no API key. Second, it is rate limited to 55 requests per second per IP address, and responses carry X-RateLimit headers you can use to stay inside the limit. Third, batch POST requests accept up to 200 variants, which makes the practical ceiling for one REST session a few thousand variants.

That ceiling defines when to choose REST over the standalone VEP command line tool. Use the REST API for interactive checks, for lists of tens to a few thousand variants, and for scripts that need no local installation. Use standalone VEP when you have a VCF (Variant Call Format) file with thousands to millions of records, when you need plugins such as LOFTEE, SpliceAI, or dbNSFP, or when you must annotate locally for privacy or speed. The two share the same consequence logic, so a spot check through REST agrees with a full VEP run.

## Setup, Conventions, and a Smoke Test

Every request goes to https://rest.ensembl.org over HTTPS. Send these two headers with each call:

- `Content-Type: application/json` (what you are sending, required for POST bodies)
- `Accept: application/json` (what you want back)

JSON (JavaScript Object Notation) is the default response format, so the Accept header is technically optional for these examples, but setting it protects you if defaults ever change. Most endpoints also accept `content-type=application/json` as a query parameter if you prefer to keep everything in the URL.

The species segment uses Ensembl's Latin names: `homo_sapiens` for human, `mus_musculus` for mouse. The default human assembly is GRCh38, the current major assembly. If your coordinates are GRCh37, send requests to https://grch37.rest.ensembl.org instead, which mirrors the API for the older assembly. Mixing assemblies is the most common way to get silently wrong answers, so decide once per project and write the choice into your scripts.

Run this smoke test to confirm everything works:

```bash
curl -s -H "Content-Type: application/json" -H "Accept: application/json" \
  "https://rest.ensembl.org/variation/homo_sapiens/rs699"
```

curl is a command line HTTP client, `-s` suppresses its progress meter, and `-H` sets a header. You should see JSON that begins like this (trimmed):

```json
{
  "id": "rs699",
  "source": "dbSNP",
  "var_class": "SNP",
  "most_severe_consequence": "missense_variant",
  "mappings": [
    {
      "location": "1:230838793-230838793",
      "seq_region_name": "1",
      "start": 230838793,
      "end": 230838793,
      "assembly_name": "GRCh38",
      "allele_string": "T/C",
      "strand": 1
    }
  ]
}
```

rs699 is the well-studied angiotensinogen (AGT) missense variant often called M235T, and dbSNP is NCBI's public archive of known variants. The mappings block lists where the variant sits on each available assembly. `allele_string` gives reference/alternate on the forward genomic strand, and `strand` 1 means forward. If you get an HTML error page or a connection error, check the URL and your network proxy settings before continuing.

## Step-by-Step Ensembl REST API Variant Annotation

The walkthrough below uses bash for requests and Python 3 for parsing. Example responses are trimmed to the fields being discussed, and exact values can shift as Ensembl updates its data releases.

### Step 1. Annotate a known variant by rsID

An rsID is a variant's identifier in dbSNP. If your input is a list of rsIDs, this is the simplest entry point:

```bash
curl -s -H "Content-Type: application/json" \
  "https://rest.ensembl.org/vep/homo_sapiens/id/rs699"
```

The response is a JSON array with one object per input variant. Trimmed:

```json
[
  {
    "input": "rs699",
    "id": "rs699",
    "seq_region_name": "1",
    "start": 230838793,
    "end": 230838793,
    "allele_string": "T/C",
    "strand": 1,
    "assembly_name": "GRCh38",
    "most_severe_consequence": "missense_variant",
    "transcript_consequences": [
      {
        "transcript_id": "ENST00000537654",
        "gene_id": "ENSG00000135744",
        "gene_symbol": "AGT",
        "variant_allele": "C",
        "consequence_terms": ["missense_variant"],
        "codons": "Atg/Acg",
        "amino_acids": "M/T",
        "protein_start": 268,
        "protein_end": 268
      }
    ]
  }
]
```

`transcript_consequences` holds one entry per overlapping transcript. In this example the variant changes one codon of AGT: `codons` shows the change with the altered base in uppercase (Atg becomes Acg), `amino_acids` shows methionine to threonine (M/T), and `protein_start` and `protein_end` locate the change at residue 268.

### Step 2. Request the fields you need

The default response is deliberately lean. Query parameters switch on extra annotations:

```bash
curl -s -H "Content-Type: application/json" \
  "https://rest.ensembl.org/vep/homo_sapiens/id/rs699?HGVS=1&canonical=1&biotype=1&protein=1"
```

The consequence entry now also carries fields like these:

```json
{
  "transcript_id": "ENST00000537654",
  "gene_symbol": "AGT",
  "biotype": "protein_coding",
  "canonical": 1,
  "consequence_terms": ["missense_variant"],
  "amino_acids": "M/T",
  "protein_start": 268,
  "protein_end": 268,
  "hgvsc": "ENST00000537654.1:c.803T>C"
}
```

`canonical` equal to 1 marks the representative transcript for the gene, `biotype` tells you the transcript type, and `HGVS=1` adds HGVS ([Human Genome](/blog/guides/human-genome) Variation Society) nomenclature strings for the coding change and, where relevant, the protein change. The full parameter set is listed in the table and section below.

### Step 3. Annotate a novel variant by region

For variants not yet in dbSNP, pass the position and alleles directly using the format `{chromosome}:{start}:{end}:{reference}/{alternate}`:

```bash
curl -s -H "Content-Type: application/json" \
  "https://rest.ensembl.org/vep/homo_sapiens/region/1:230838793:230838793:T/C"
```

The response has the same shape as the rsID call, with `input` echoing your region string and `id` set to a coordinate label such as `1_230838793_T/C`. Insertions and deletions (indels) use a dash for the missing allele: a two-base deletion of AG spanning positions 3400 to 3401 on chromosome 12 is written `12:3400:3401:AG/-`, and for an insertion the reference allele is the dash while the alternate carries the inserted sequence.

### Step 4. Annotate from HGVS notation

If your input is already HGVS, skip the conversion step:

```bash
curl -s -H "Content-Type: application/json" \
  "https://rest.ensembl.org/vep/homo_sapiens/hgvs/NC_000001.11:g.230838793T%3EC"
```

`%3E` is the URL encoding of the greater-than sign, which HTTP requires inside paths and query strings. The endpoint accepts genomic HGVS on RefSeq chromosome accessions such as NC_000001.11, coding HGVS on Ensembl transcript accessions such as ENST00000537654, and protein HGVS on Ensembl protein (ENSP) accessions. Coding notation needs the transcript accession because the same c. position means different things on different transcripts.

### Step 5. Batch annotate with POST

Single GET requests take one variant each. For lists, switch to POST:

```bash
curl -s -X POST -H "Content-Type: application/json" -H "Accept: application/json" \
  -d '{"ids": ["rs699", "rs334", "rs429358"]}' \
  "https://rest.ensembl.org/vep/homo_sapiens/id"
```

The response is a JSON array of three objects in the same order as the input. The cap is 200 IDs per call. For novel variants, the region POST takes the same colon-delimited strings:

```bash
curl -s -X POST -H "Content-Type: application/json" \
  -d '{"variants": ["1:230838793:230838793:T/C"]}' \
  "https://rest.ensembl.org/vep/homo_sapiens/region"
```

Query parameters still ride on the URL, for example `?canonical=1&HGVS=1` appended to either endpoint, and they apply to every variant in the batch.

### Step 6. Convert identifiers with Variant Recoder

Mixed input lists (some rsIDs, some HGVS strings) cause subtle failures. Normalize them first:

```bash
curl -s -H "Content-Type: application/json" \
  "https://rest.ensembl.org/variant_recoder/homo_sapiens/rs699"
```

Trimmed response:

```json
{
  "input": "rs699",
  "hgvsg": ["NC_000001.11:g.230838793T>C"],
  "spdi": ["NC_000001.11:230838792:T:C"],
  "vcf_string": ["1 230838793 . T C"]
}
```

The full response also includes protein-level HGVS. SPDI is NCBI's Sequence Position Deletion Insertion notation, which is 0-based, so its position is one less than the 1-based genomic coordinate. A single rsID can expand to several HGVS strings when multiple transcripts are affected, which is exactly the kind of surprise you want to catch before an annotation run rather than during one.

### Step 7. List all variants in a region

To pull every known variant across a gene or candidate interval:

```bash
curl -s -H "Content-Type: application/json" \
  "https://rest.ensembl.org/overlap/region/homo_sapiens/1:230838700-230838900?feature=variation"
```

Each entry looks like this (trimmed):

```json
{
  "id": "rs699",
  "start": 230838793,
  "end": 230838793,
  "allele_string": "T/C",
  "most_severe_consequence": "missense_variant"
}
```

The overlap endpoints cap region size at 4.9 Mb. A common pattern is to collect the rsIDs across a gene with this endpoint, then feed them to the batch VEP POST from Step 5.

### Step 8. Verify the reference allele

Before trusting an annotation, confirm the reference base at your position:

```bash
curl -s -H "Content-Type: application/json" \
  "https://rest.ensembl.org/sequence/region/homo_sapiens/1:230838793:230838793:1"
```

The response returns the reference base (here T) with its coordinates. If your file claims the reference at this position is C, your coordinates come from a different assembly, or the file has a strand or offset problem. Spot-checking a sample of variants this way prevents the most confusing failure mode, an annotation that looks plausible but describes the wrong molecular change.

### Step 9. Parse and filter results in Python

```python
import requests

server = "https://rest.ensembl.org"
headers = {"Content-Type": "application/json", "Accept": "application/json"}

r = requests.get(server + "/vep/homo_sapiens/id/rs699", headers=headers)
r.raise_for_status()
result = r.json()[0]

print(result["most_severe_consequence"])
for csq in result.get("transcript_consequences", []):
    if csq.get("canonical") == 1:
        print(csq["transcript_id"],
              csq.get("gene_symbol"),
              ",".join(csq["consequence_terms"]),
              csq.get("amino_acids"),
              csq.get("protein_start"))
```

Expected terminal output:

```
missense_variant
ENST00000537654 AGT missense_variant M/T 268
```

Note the two-level structure. Variant-level keys such as `most_severe_consequence` sit at the top, and transcript-level detail sits inside `transcript_consequences`. Filtering on `canonical` gives one line per gene, while filtering on `consequence_terms` selects specific effects such as `stop_gained`.

### Step 10. Wrap it in a rate-limited annotator

```python
import json
import time
import requests

SERVER = "https://rest.ensembl.org"
HEADERS = {"Content-Type": "application/json", "Accept": "application/json"}

def annotate_ids(ids, chunk_size=200):
    annotated = []
    for i in range(0, len(ids), chunk_size):
        chunk = ids[i:i + chunk_size]
        attempt = 0
        while True:
            r = requests.post(SERVER + "/vep/homo_sapiens/id",
                              headers=HEADERS,
                              data=json.dumps({"ids": chunk}))
            if r.status_code in (429, 503):
                attempt += 1
                time.sleep(min(2 ** attempt, 60))
                continue
            r.raise_for_status()
            annotated.extend(r.json())
            break
        time.sleep(0.05)
    return annotated

results = annotate_ids(["rs699", "rs334", "rs429358"])
print(len(results), "variants annotated")
```

The loop chunks the list into groups of 200, retries with exponential backoff when the server returns 429 (rate limited) or 503 (temporarily unavailable), and pauses briefly between chunks. A few thousand variants finish in well under a minute. Save the raw JSON to disk so you never annotate the same list twice.

## Parameters That Matter

Boolean parameters take the value 1 and combine with the ampersand in the query string. These are the ones that change how you interpret results:

| Parameter | What it adds to each transcript consequence | Why you would use it |
|--|--|--|
| `HGVS=1` | HGVS strings (`hgvsc`, and `hgvsp` where applicable) | Standardized variant names for reports and databases |
| `canonical=1` | `canonical=1` on the representative transcript | One primary line per gene |
| `biotype=1` | Transcript biotype such as `protein_coding` | Separates real coding effects from pseudogene noise |
| `protein=1` | [Protein sequence](/blog/guides/protein-sequence) plus `protein_start` and `protein_end` | Locates the exact residue change |
| `domains=1` | Overlapping protein domains (Pfam, PROSITE, and others) | Judges whether a change lands in a functional region |
| `numbers=1` | Exon and intron position, for example exon 3 of 15 | Maps variants onto transcript structure |
| `distance=1` | Distance to the transcript for upstream and downstream calls | Quantifies how far away an intergenic effect sits |
| `utr=1` | Full detail for 5' and 3' UTR consequences | Untranslated region effects are otherwise summarized |
| `shift_hgvs=1` | HGVS strings shifted to the 3' end per HGVS rules | Matches the convention used by clinical databases |
| `CADD=1` | `cadd_phred`, a PHRED-scaled deleteriousness score | Higher scores predict larger effect |
| `content-type` | Response format | Pass as a header or query parameter |

The Ensembl REST documentation lists the complete, current parameter set for the `/vep` endpoints, and the set grows between releases, so check the documentation page when a field you want is missing. POST batches accept the same query parameters as GET requests.

## Reading the Output

`most_severe_consequence` is the single worst term across all transcripts. It is ideal for triage because one glance tells you whether a variant is likely to matter, but it collapses per-transcript detail. A variant that is a missense change in one transcript and synonymous in another reports only the missense term at the top level.

`transcript_consequences` is where the real content lives. The recurring fields are:

- `transcript_id`, `gene_id`, and `gene_symbol`: stable Ensembl identifiers for the transcript and its gene.
- `biotype`: transcript class, for example `protein_coding` or `retained_intron`.
- `canonical`: present and equal to 1 on the representative transcript when requested.
- `variant_allele`: the alternate allele this consequence refers to.
- `consequence_terms`: a list of Sequence Ontology terms for this transcript.
- `codons` and `amino_acids`: the codon and residue changes, written on the transcript strand.
- `protein_start`, `protein_end`, `cds_start`, `cds_end`, `cdna_start`, `cdna_end`: coordinates of the change at protein, coding sequence, and cDNA level.
- `hgvsc` and `hgvsp`: HGVS names when requested.
- `cadd_phred`: the CADD score when requested. CADD (Combined Annotation Dependent Depletion) is a PHRED-like scale where higher values predict stronger effects.

The consequence terms follow VEP's impact classes. High-impact terms include `stop_gained`, `frameshift_variant`, `splice_donor_variant`, `splice_acceptor_variant`, `start_lost`, and `stop_lost`. Moderate-impact terms include `missense_variant`, `inframe_insertion`, `inframe_deletion`, and `protein_altering_variant`. Low-impact terms include `synonymous_variant` and `coding_sequence_variant`. Modifier terms include `intron_variant`, `upstream_gene_variant`, `downstream_gene_variant`, `5_prime_UTR_variant`, and `3_prime_UTR_variant`. Variants that overlap no transcript return an `intergenic_consequences` block instead, and variants in regulatory regions can return regulatory feature consequences alongside the transcript list.

## Cross-Check Results in the Ensembl Genome Browser

The genome browser and the REST API read the same underlying database, so agreement between them is expected, and disagreement points to an input or assembly problem. A short browser tutorial for spot checks:

1. Open https://www.ensembl.org in a browser.
2. Type `rs699` into the search box and choose the human result.
3. The variant page shows the location, the allele string, the most severe consequence, and a table of affected transcripts.
4. Compare that table with your `transcript_consequences`: transcript IDs, consequence terms, and protein changes should match your API output field for field.
5. Open the Location tab to see the surrounding genes, which explains upstream and downstream calls that look surprising in the JSON.
6. For a code-free annotation of a small list, the VEP web interface at https://www.ensembl.org/Tools/VEP runs the same predictor through a form and returns a table you can compare against a REST run.

Doing this once per new project, on two or three variants you already understand, builds the confidence to trust (or debug) a large automated run.

## Common Errors and Fixes

![Decision path from an HTTP status code to the usual cause and fix for 400, 404, 429 and 503 responses.](/article-images/illustrations/ensembl-rest-api-variant-annotation-error-triage.webp)

Failures come back as HTTP status codes with a JSON body carrying an `error` key. Print both in your scripts rather than guessing:

```python
if r.status_code != 200:
    print(r.status_code, r.json().get("error"))
```

| HTTP status | What you see | Usual cause | Fix |
|--|--|--|--|
| 400 | An `error` key describing the bad input | Malformed region, allele, or HGVS string, or a POST body that is not valid JSON | Recheck the format `chr:start:end:ref/alt`, use a dash for missing indel alleles, URL-encode the greater-than sign as `%3E`, validate the POST body |
| 404 | A body like `{"error": "ID 'rs69999999' not found."}` (wording varies) | Unknown rsID, misspelled species, wrong endpoint path | Confirm the ID in the Ensembl browser, check the species segment, compare the URL against the documentation |
| 429 | An `error` key | More than 55 requests per second | Batch with POST, add sleeps, retry with exponential backoff |
| 503 | An `error` key | Server maintenance or temporary overload | Retry with backoff, plan large runs around announced Ensembl downtime |

The failures that do not produce an error code are the dangerous ones. An empty `transcript_consequences` array usually means the variant overlaps no transcript on that assembly, most often because the coordinates come from a different assembly. A reference allele that disagrees with the assembly produces missing or misleading consequences rather than a clean error. Both are caught by the checks in Steps 7 and 8.

## Common Mistakes and Limitations

1. **Reading only the first transcript consequence.** The array is ordered by transcript, not by importance. Filter on `canonical` or `biotype` before drawing conclusions about a gene.
2. **Mixing assemblies.** The same coordinate points to different sequence in GRCh37 and GRCh38. Annotating GRCh37 coordinates on the default server produces a valid-looking answer for the wrong locus. Pin the host: rest.ensembl.org for GRCh38, grch37.rest.ensembl.org for GRCh37.
3. **Ignoring the rate limit.** A tight loop of single GETs crosses 55 requests per second quickly and starts failing with 429 mid-run. Batch with POST and back off on 429 instead of hammering.
4. **Using REST for whole-[VCF annotation](/blog/guides/vcf-annotation-how-to-read-variant-consequence-reports).** The 200-variant POST cap and the rate cap make REST impractical beyond a few thousand variants. Switch to standalone VEP, which reads VCF files directly and runs offline.
5. **Treating consequence terms as clinical calls.** `missense_variant` describes a protein change, not pathogenicity. Clinical interpretation needs population frequency, conservation, functional evidence, and curated resources such as NCBI's ClinVar.
6. **Ignoring strand.** `codons` and `amino_acids` are given on the transcript strand. For genes on the minus strand, a genomic T-to-C change appears as an A-to-G change on the mRNA.
7. **Caching annotations across Ensembl releases.** Transcript models change between releases, so HGVS strings and consequence calls can shift. Store the release date with your results and re-annotate when you upgrade.
8. **Trusting `most_severe_consequence` alone.** It collapses all transcripts into one term. Read the per-transcript list before deciding what a variant does.
9. **Sending sensitive human data to a public endpoint.** Requests carry variant coordinates to a public server. For identifiable patient data, run standalone VEP locally instead.
10. **Skipping URL encoding.** The greater-than sign in HGVS strings and spaces or tabs in VCF-style inputs must be percent-encoded, or the request fails with a 400.

On limitations: the REST service annotates against Ensembl's current gene set and assemblies, exposes a far smaller plugin set than standalone VEP, caps batches at 200 variants, and changes in detail with each Ensembl release (several per year). For reproducibility, record the API host, the parameters, and the date of every run alongside your results.

## Tips That Save Time and Requests

- Batch with POST. One call with 200 IDs does the work of 200 GETs at a fraction of the request count.
- Watch the `X-RateLimit-Remaining` response header. When it approaches zero, sleep until the window resets instead of waiting for a 429.
- Use jq for quick looks: `curl -s "https://rest.ensembl.org/vep/homo_sapiens/id/rs699" | jq '.transcript_consequences[].consequence_terms'`
- Combine parameters in one request. Once a request is made, each extra parameter rides along for free.
- Normalize mixed input lists with `/variant_recoder` before annotating.
- Cache raw responses keyed by variant and assembly, and re-annotate only after an Ensembl release bump.
- Keep a single assembly constant in your scripts and derive the host from it.
- Spot check two or three variants against the genome browser before trusting a large run.

## Scaling Up and Next Steps

![Decision path asking about variant count, plugins and privacy to choose between the REST API and standalone VEP.](/article-images/illustrations/ensembl-rest-api-variant-annotation-rest-or-vep.webp)

Standalone VEP is the natural next step once variant counts grow. It reads VCF files directly, runs against a local cache with no network dependency, and supports a large plugin ecosystem including LOFTEE for loss-of-function filtering, SpliceAI for splicing predictions, and dbNSFP for aggregated variant scores. Because it shares consequence logic with the REST service, you can prototype in REST and scale out in VEP without changing your interpretation.

For non-vertebrate species, the separate Ensembl Genomes REST server at rest.ensemblgenomes.org serves bacteria, fungi, other metazoa, plants, and protists with the same URL style. Check its documentation for species coverage of the VEP endpoints, since annotation depth varies by division.

For clinical context, pair functional consequences with curated assertions from NCBI's ClinVar, searchable by position or HGVS name. Consequence terms tell you what a variant does to a transcript. ClinVar tells you what has been observed in patients, and the two answer different questions.

Finally, wrap the annotator function from Step 10 in your workflow engine of choice, log request counts and dates, and store raw JSON next to your summaries. The raw responses are cheap to keep and priceless when a reviewer asks how an annotation was made.

## Frequently Asked Questions

### Do I need an API key or an account to use Ensembl's REST API?

No. The service is open, needs no registration, and works with plain HTTP requests. The only constraint is a rate limit of 55 requests per second per IP address, and exceeding it returns HTTP 429.

### How many variants can I annotate in one request?

Up to 200. The POST versions of the VEP endpoints accept 200 IDs or region strings per call, and the overall rate limit is 55 requests per second, so chunk longer lists and pause between chunks.

### What is the difference between the REST API and the VEP command line tool?

The REST API suits interactive checks and lists up to a few thousand variants. The command line tool suits large VCF files, offline work, and plugins such as LOFTEE and SpliceAI. Both use the same consequence logic.

### Which genome assembly does the API use by default?

GRCh38 for human on rest.ensembl.org. For GRCh37 coordinates, use the dedicated host grch37.rest.ensembl.org, which serves the same endpoints on the older assembly.

### Can I annotate variants in mouse or other species?

Yes. Replace `homo_sapiens` with the species name, for example `mus_musculus` for mouse. Bacteria, fungi, other metazoa, plants, and protists are served by the separate Ensembl Genomes REST server at rest.ensemblgenomes.org.

### Why does my variant return no transcript_consequences?

Usually because the variant does not overlap any transcript on that assembly, often because the coordinates come from a different assembly. Check `assembly_name` in the response and verify the reference base with the sequence endpoint.

### How do I convert an rsID into HGVS notation?

Use the `/variant_recoder` endpoint. One call returns genomic HGVS, protein HGVS, SPDI, and a VCF-style string for the variant, and the endpoint also accepts HGVS and SPDI strings as input.

### Does a VEP consequence term tell me a variant is pathogenic?

No. Terms such as `missense_variant` or `stop_gained` describe the predicted molecular effect on a transcript. Pathogenicity assessment needs population frequency, conservation, functional and segregation evidence, and curated assertions such as those in NCBI's ClinVar.

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "Do I need an API key or an account to use Ensembl's REST API?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "No. The service is open, needs no registration, and works with plain HTTP requests. The only constraint is a rate limit of 55 requests per second per IP address, and exceeding it returns HTTP 429."
      }
    },
    {
      "@type": "Question",
      "name": "How many variants can I annotate in one request?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Up to 200. The POST versions of the VEP endpoints accept 200 IDs or region strings per call, and the overall rate limit is 55 requests per second, so chunk longer lists and pause between chunks."
      }
    },
    {
      "@type": "Question",
      "name": "What is the difference between the REST API and the VEP command line tool?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "The REST API suits interactive checks and lists up to a few thousand variants. The command line tool suits large VCF files, offline work, and plugins such as LOFTEE and SpliceAI. Both use the same consequence logic."
      }
    },
    {
      "@type": "Question",
      "name": "Which genome assembly does the API use by default?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "GRCh38 for human on rest.ensembl.org. For GRCh37 coordinates, use the dedicated host grch37.rest.ensembl.org, which serves the same endpoints on the older assembly."
      }
    },
    {
      "@type": "Question",
      "name": "Can I annotate variants in mouse or other species?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Yes. Replace homo_sapiens with the species name, for example mus_musculus for mouse. Bacteria, fungi, other metazoa, plants, and protists are served by the separate Ensembl Genomes REST server at rest.ensemblgenomes.org."
      }
    },
    {
      "@type": "Question",
      "name": "Why does my variant return no transcript_consequences?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Usually because the variant does not overlap any transcript on that assembly, often because the coordinates come from a different assembly. Check assembly_name in the response and verify the reference base with the sequence endpoint."
      }
    },
    {
      "@type": "Question",
      "name": "How do I convert an rsID into HGVS notation?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Use the /variant_recoder endpoint. One call returns genomic HGVS, protein HGVS, SPDI, and a VCF-style string for the variant, and the endpoint also accepts HGVS and SPDI strings as input."
      }
    },
    {
      "@type": "Question",
      "name": "Does a VEP consequence term tell me a variant is pathogenic?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "No. Terms such as missense_variant or stop_gained describe the predicted molecular effect on a transcript. Pathogenicity assessment needs population frequency, conservation, functional and segregation evidence, and curated assertions such as those in NCBI's ClinVar."
      }
    }
  ]
}
</script>