# How to Retrieve a Protein Sequence from UniProt in FASTA Format

By the end of this tutorial you will be able to find any protein entry in UniProt, download its sequence in FASTA format from the website or the REST API, retrieve hundreds or millions of sequences in one command, and verify that the file you saved is the sequence you actually wanted. You need a web browser for the first method and either `curl`, `wget`, or a small Python script for the command line methods. Nothing else is required. The FASTA format and the UniProt database are the two workhorses of sequence-based biology, and nearly every downstream tool, from mutation viewers to machine learning pipelines, consumes sequences in this form [1][2].

## What UniProt Is and How to Use It

UniProt, short for the Universal Protein Resource, is a freely accessible database of protein sequences and functional annotations maintained by the UniProt Consortium. Each protein record is called an entry, and each entry has a stable identifier called an accession, such as P04637 for human cellular tumor antigen p53. If you are learning how to use UniProt for the first time, the single most useful distinction is between its two sections:

- **UniProtKB/Swiss-Prot (reviewed)**. Entries checked by human curators, with verified sequence, function, domain annotations, and literature links. This section contains several hundred thousand entries.
- **UniProtKB/TrEMBL (unreviewed)**. Computationally annotated entries, drawn largely from the translation of coding sequences in the nucleotide databases. This section is orders of magnitude larger, with well over 200 million entries.

For most analyses you want the reviewed section first, because a curated sequence is far less likely to contain fragments, sequencing errors, or redundancy. Published pipelines follow this logic. One recent study of β-catenin sequence modeling downloaded its protein sequences from UniProt and explicitly quality-checked them as FASTA input before model training [3]. Another study, on AIM-2 inflammasome proteins in periodontal inflammation, retrieved its UniProt sequences as FASTA at three similarity cutoffs, 100%, 90%, and 50%, before deep learning analysis [2].

## FASTA Format Basics

FASTA is a plain text format for nucleotide or protein sequences. The name comes from the original FASTA sequence comparison program of Pearson and Lipman, and the format predates most of modern bioinformatics. A FASTA record has two parts:

1. A header line, also called a description line, that starts with a greater-than character.
2. The sequence itself, written in the standard one-letter amino acid code, usually wrapped across lines of fixed width.

A real UniProt record for human p53 begins like this:

```
>sp|P04637|P53_HUMAN RecName: Full=Cellular tumor antigen p53; Short=p53 ...
MEEPQSDPSIEPPLSQETFSDLWKLLPENNVLSPLPSQAMDDLMLSPDDIEQWFTEDPGP
...
```

The header shown is truncated. Modern UniProt headers carry the database tag (`sp` for reviewed, `tr` for unreviewed), the accession, the entry name, and a condensed functional description. The sequence line length in UniProt output is typically 60 residues, but well-behaved parsers should not rely on any specific wrap width.

## Step by Step: Downloading a Single Sequence from the UniProt Website

This is the fastest method when you need one or a few sequences. The walkthrough uses human p53 as the worked example.

### 1. Search for the protein

Go to `https://www.uniprot.org`. Type the gene name, protein name, or accession into the search box. For the example, type `TP53`. Alternatively, go directly to `https://www.uniprot.org/uniprotkb/P04637`.

If you search by a gene symbol, you will usually get hits from many species, so filter the results:

- Under **Reviewed**, select **Reviewed (Swiss-Prot)**.
- Under **Organism**, select **Human** or your organism of choice.

For `TP53`, these two filters leave one human reviewed entry, P04637.

### 2. Confirm you have the right entry

On the entry page, check four things before downloading:

- The **Entry name** and **Protein name** match what you intend (P53_HUMAN, Cellular tumor antigen p53).
- The **Gene** field matches your gene of interest (TP53).
- The **Organism** matches.
- The **Sequence** section shows the length you expect (393 amino acids for canonical human p53) and lists the sequence version.

### 3. Download the FASTA

Scroll to the **Sequence** tab on the entry page. Click **Download**, then choose **FASTA (uncompressed)**. Your browser saves a file named something like `P04637.fasta`. If you open it, it looks like the record shown above. That completes a single uniprot fasta sequence download through the web interface.

A second web-based option is the REST API opened in a browser. Visiting `https://rest.uniprot.org/uniprotkb/P04637.fasta` returns the raw FASTA text directly, which you can save with your browser's save function. This URL pattern is worth memorizing because it is also exactly what you call programmatically in the next section.

## Step by Step: Downloading a Single Sequence with the REST API

REST stands for Representational State Transfer, and an API is an Application Programming Interface. In practice, REST here means you retrieve data by constructing a URL and fetching it with a standard HTTP request. UniProt documents this interface in its Help portal, and the uniprotkb endpoint pattern is stable and scriptable.

### 1. Fetch one accession

```bash
curl -s https://rest.uniprot.org/uniprotkb/P04637.fasta
```

Expected output:

```
>sp|P04637|P53_HUMAN RecName: Full=Cellular tumor antigen p53; Short=p53 ...
MEEPQSDPSIEPPLSQETFSDLWKLLPENNVLSPLPSQAMDDLMLSPDDIEQWFTEDPGP
DEAPRMPEAAPPVAPAAPAAPAAP...
```

The `-s` flag silences curl's progress meter. To save the file instead of printing it:

```bash
curl -s https://rest.uniprot.org/uniprotkb/P04637.fasta -o P04637.fasta
```

### 2. Verify the HTTP status

If the accession is wrong, UniProt returns an HTTP 404 status with a JSON error body rather than a sequence. Check the status with:

```bash
curl -s -o /dev/null -w "%{http_code}\n" https://rest.uniprot.org/uniprotkb/P04637.fasta
```

Expected output for a valid accession:

```
200
```

A `200` means success. A `400` usually means the query string was malformed, and a `404` means the accession does not exist.

### 3. Do the same in Python

```python
import urllib.request

accession = "P04637"
url = f"https://rest.uniprot.org/uniprotkb/{accession}.fasta"
urllib.request.urlretrieve(url, "P04637.fasta")
```

This three-line script is the smallest reproducible uniprot fasta sequence download you can write, and it belongs in any laboratory notebook script that needs the sequence of a specific entry.

## Step by Step: Downloading Many Sequences with a Query

When you need more than one sequence, use the search or stream endpoints with a query. The query syntax follows UniProt's standard query grammar, in which the Boolean operators `AND`, `OR`, and `NOT` must be written in uppercase.

### The search endpoint

```bash
curl -s "https://rest.uniprot.org/uniprotkb/search?query=(gene:TP53) AND (organism_id:9606) AND (reviewed:true)&format=fasta&size=500" -o tp53_set.fasta
```

Note three practical details in this command:

- The entire URL is wrapped in double quotes so the shell does not interpret the `&` or the parentheses. Without quotes, the command after `&` would run in the background and you would download nothing useful.
- Spaces inside the query are written as `+` or `%20`. The safest habit is to wrap the URL in quotes and use literal spaces, which curl handles correctly.
- `organism_id:9606` is the NCBI taxonomy identifier for human, which UniProt accepts in queries. `reviewed:true` restricts output to Swiss-Prot.

### The stream endpoint

The search endpoint paginates its results, returning at most 500 records per request and providing a `Link` response header that points to the next page. For FASTA output there is a simpler choice, the stream endpoint, which returns all matching records in one response:

```bash
curl -s "https://rest.uniprot.org/uniprotkb/stream?query=(organism_id:9606) AND (reviewed:true)&format=fasta" -o human_reviewed.fasta
```

This one command downloads every reviewed human [protein sequence](/blog/guides/protein-sequence), roughly 20,000 records, as a single FASTA file. If you ever need the paginated search endpoint for large results, inspect the pagination with a head request:

```bash
curl -sI "https://rest.uniprot.org/uniprotkb/search?query=reviewed:true&format=fasta&size=500"
```

The response includes a `Link` header with `rel="next"` when more pages exist. Most practitioners avoid this bookkeeping entirely by using `stream` for FASTA.

### Add gzip compression for large downloads

```bash
curl -s "https://rest.uniprot.org/uniprotkb/stream?query=(organism_id:9606) AND (reviewed:true)&format=fasta&compressed=true" -o human_reviewed.fasta.gz
```

The `compressed=true` parameter returns gzip-compressed output, which cuts transfer size for genome-scale downloads.

## Downloading a Complete Proteome

A proteome is the full set of protein sequences produced by one organism, strain, or assembly. UniProt assigns each reference proteome an identifier starting with `UP`, for example UP000005640 for the human reference proteome.

To find a proteome identifier, open any entry of interest, look at the **Proteomes** section on the entry page, and follow the proteome link, or use the proteomes endpoint directly:

```bash
curl -s https://rest.uniprot.org/proteomes/UP000005640
```

Once you have the identifier, retrieve the whole proteome as FASTA through the stream endpoint with a proteome query:

```bash
curl -s "https://rest.uniprot.org/uniprotkb/stream?query=(proteome:UP000005640)&format=fasta&compressed=true" -o human_proteome.fasta.gz
```

This is the standard way to build a reference database for local sequence similarity searches, and it guarantees that every record came from one consistent proteome definition rather than from an ad hoc search.

## Converting a List of Accessions into FASTA Sequences

When a paper, a spreadsheet, or a collaborator gives you a list of UniProt accessions, the tidy way to convert them is the UniProt ID mapping service, which is documented in the UniProt Help portal and exposed both on the website and through the REST API.

### The website route

1. Go to the **ID Mapping** tool on uniprot.org.
2. Paste your accessions, for example `P04637, P25098, O00206`, one per line or comma separated.
3. Set the source database to **UniProtKB_AC-ID**.
4. Set the target database to **UniProtKB**.
5. Submit the job, then choose **Download** and select **FASTA** from the results page.

### The API route

For reproducible pipelines, the same operation runs as three HTTP calls. First, submit the job:

```bash
curl -s -X POST https://rest.uniprot.org/idmapping/run \
  -d "from=UniProtKB_AC-ID" \
  -d "to=UniProtKB" \
  -d "ids=P04637,P25098"
```

The response is JSON containing a `jobId`. Poll the status endpoint with that identifier until it reports results, then stream the sequences:

```bash
curl -s "https://rest.uniprot.org/idmapping/uniprotkb/results/stream?format=fasta&jobId=YOUR_JOB_ID" -o mapped.fasta
```

For modest lists, a shortcut also works. Build an OR query from the accessions and send it to the stream endpoint:

```bash
curl -s "https://rest.uniprot.org/uniprotkb/stream?query=accession:P04637+OR+accession:P25098&format=fasta" -o two.fasta
```

The ID mapping service scales to very large lists and also translates identifiers between databases, for example from Ensembl transcript identifiers or gene names to UniProt accessions.

## Key API Options at a Glance

| Option or pattern | Endpoint | What it does | Example |
|--|--|--|--|
| `.fasta` suffix | uniprotkb | Returns one entry as FASTA | `uniprotkb/P04637.fasta` |
| `query=` | uniprotkb search, stream | Filters entries by field, requires uppercase `AND`, `OR`, `NOT` | `query=(reviewed:true) AND (organism_id:9606)` |
| `format=fasta` | uniprotkb search, stream | Sets the output format | `format=fasta` |
| `size=` | uniprotkb search | Records per page, maximum 500 | `size=500` |
| `compressed=true` | uniprotkb search, stream | Returns gzip output | `compressed=true` |
| `proteome:` field | uniprotkb stream | Restricts to one proteome identifier | `query=(proteome:UP000005640)` |
| `versions=` | unisave | Retrieves a historical sequence version | `unisave/P04637?format=fasta&versions=2` |

## Choosing the Right Sequence: Reviewed Status, Canonical Form, and Isoforms

Three choices inside every entry determine which amino acid string you get, and getting them wrong is the most common silent error in a uniprot fasta sequence download.

**Reviewed versus unreviewed.** Swiss-Prot entries are curated by human annotators. TrEMBL entries are annotated automatically. For any analysis where sequence identity matters, filter with `reviewed:true` or the Reviewed checkbox on the website.

**Canonical sequence versus isoforms.** Many genes produce several protein products called isoforms, generated by [alternative splicing](/blog/guides/alternative-splicing) or alternative initiation. UniProt displays one canonical sequence per entry, the most common or most representative form, and lists alternatives separately. The plain accession download, such as `P04637.fasta`, always returns the canonical sequence. To obtain a specific isoform, look at the **Isoforms** section of the entry page, note the isoform identifier, and check the **Sequences** display there, or download the entry in full text format and extract the isoform coordinates.

**Entry version versus sequence version.** Every UniProt entry carries two version numbers. The entry version tracks changes to the whole record, including annotations. The sequence version tracks changes to the amino acid string itself. The FASTA response always gives the current sequence, and the header does not state the version, so record the accession, the sequence version, and the UniProt release date in your methods or your project log. When a protocol pins a historical sequence, the unisave service serves any past version, as shown in the table above.

## How to Check Your Download Is Correct

Verification takes one minute and prevents the most expensive class of error, a downstream analysis run on the wrong molecule.

1. **Count the records.** For multi-record files:

```bash
grep -c "^>" human_reviewed.fasta
```

2. **Check the header of each record** against the accessions you intended. The first pipe-delimited field after `sp` or `tr` is the accession.

3. **Check the sequence length** against the entry page:

```python
seq = []
with open("P04637.fasta") as fh:
    for line in fh:
        if not line.startswith(">"):
            seq.append(line.strip())
print(len("".join(seq)))
```

Expected output:

```
393
```

4. **Check the alphabet.** Protein FASTA should contain only the 20 standard amino acid letters plus `X` for unknown, `U` for selenocysteine, `B`, `Z` for ambiguous residues, and `*` as a translation stop marker. A quick validation script:

```python
allowed = set("ACDEFGHIKLMNPQRSTVWYXBZUJ*")
bad = set(seq_string.upper()) - allowed
print(bad if bad else "clean")
```

5. **Spot-check the mass or a short peptide.** The entry page reports the sequence mass and lists tryptic peptides. If your file matches those, you have the same sequence the curators verified.

6. **Search the sequence back.** Paste your downloaded sequence into the BLAST tool on uniprot.org against the reviewed database. The top hit at 100% identity should be the entry you started from. If it is not, you downloaded a different record somewhere upstream.

## Using the Download in Real Workflows

A FASTA file from UniProt is an input format for nearly everything. Two documented examples show the pattern. In the β-catenin bone formation study, sequences were downloaded from UniProt, converted to FASTA, and quality-checked for duplicates, outliers, and missing values before being split 80:20 into training and testing sets for attention-based peptide sequence prediction, with the best model reaching 89% accuracy [3]. In the AIM-2 inflammasome study, UniProt served the same role at three sequence similarity levels, with transformer and graph attention models reaching 86% accuracy compared with 64% for a simpler recurrent convolutional architecture [2].

Mutation interpretation tools follow the same input convention. HPMV, the Human Protein Mutation Viewer, accepts protein mutations supplied as UniProt accessions, genome coordinates, or raw FASTA sequences, and maps them onto domains, targeting signals, and post-translational modification sites with an interactive display [1]. In practice this means the accession you download today is also the key that unlocks structural and functional context tomorrow.

## Common Mistakes and Limitations

These are the errors seen most often in real projects, each with its fix.

**Downloading an unreviewed or wrong-species entry.** Searching a gene symbol alone returns every ortholog and every unreviewed duplicate. Filter by organism and reviewed status before downloading, and confirm the gene field on the entry page.

**Assuming the accession alone defines the sequence.** Isoforms, sequence versions, and processing events mean that several plausible amino acid strings exist for one accession. Decide explicitly whether you need the canonical precursor, a mature chain, or a numbered isoform, and record which one you used.

**Mixing entry and sequence versions.** Annotation updates change entry versions constantly, sequence versions rarely. If your result depends on an exact amino acid string, pin the sequence version with unisave rather than re-downloading the current file months later.

**Using retired URL patterns.** Old bookmarks and old scripts sometimes point at the legacy website URLs. UniProt now redirects those addresses to the REST endpoints, but redirects are slower and can fail in some HTTP client configurations. Point scripts directly at `rest.uniprot.org`.

**Unquoted URLs in the shell.** A command like `curl ... query=A AND B&format=fasta` without quotes splits into two shell jobs. The download either fails or truncates. Always wrap the full URL in double quotes.

**Forgetting URL encoding.** Quotation marks inside a query must become `%22`, and spaces become `%20` or `+`, when a URL is constructed by hand. Queries built with string concatenation in Python need the same encoding.

**Concatenating paginated results incorrectly.** If you loop over the paginated search endpoint without following the `Link` header correctly, you can save duplicate pages or miss records entirely. Use the stream endpoint for FASTA, or a library that handles cursors for you.

**Long headers breaking legacy tools.** Some older scripts parse the header by fixed position or expect a single identifier after the `>`. Strip headers to their first token when needed:

```bash
sed "s/ .*//" input.fasta > simple_headers.fasta
```

**Expecting mature chains in the canonical sequence.** UniProt's canonical sequence is the translated precursor. Secreted proteins carry signal peptides that are removed in vivo, and many proteins are processed further. The **Sequence processing** section of the entry page states the chain boundaries, so read it before trimming or modeling.

**Ignoring the limitation of the format itself.** FASTA carries sequence and a one-line description, nothing more. Annotations, cross-references, isoform definitions, and proteomics evidence live on the entry page and in UniProt's other export formats. If an analysis needs those data, download the full entry text or tabular fields in addition to the FASTA.

## Anatomy of a UniProt FASTA Header: Reading and Parsing It Reliably

The header line is the only metadata a FASTA file carries, so learning to read it precisely pays off every time you debug a pipeline. The draft showed a truncated header; here is what a complete current UniProt header for human p53 actually looks like:

```
>sp|P04637|P53_HUMAN RecName: Full=Cellular tumor antigen p53; Short=p53 OX=9606 GN=TP53 PE=1 SV=1
```

Breaking this apart field by field:

- `sp` is the database tag. Reviewed Swiss-Prot entries begin with `sp`, unreviewed TrEMBL entries with `tr`. A single glance at this tag tells you the curation status of every record in a mixed file.
- `P04637` is the primary accession, the stable identifier you should use in scripts, notebooks, and publications.
- `P53_HUMAN` is the entry name, a mnemonic constructed from the protein name and species code. Entry names are stable in practice but historically have changed during merges, so scripts should key on the accession rather than the entry name.
- `RecName: Full=...` is the condensed recommended protein name. Unreviewed records often carry less structured text here instead.
- `OX=9606` is the NCBI taxonomy identifier of the source organism, embedded directly in the header. This is a gift for downstream filtering: you can separate species in a multi-FASTA file without consulting any external table.
- `GN=TP53` is the ordered gene name, when one is known.
- `PE=1` is the protein evidence tag, where 1 means experimental evidence at the protein level and higher numbers indicate decreasing levels of evidence, down to predicted records.
- `SV=1` is the sequence version. This is worth dwelling on, because it means the sequence version you learned to record in your methods section is usually sitting in the header of the very file you downloaded. When you archive a download, capture the `SV` value and you have pinned the exact amino acid string automatically.

A practical consequence of the `SV` field: if you re-download an accession months later and the `SV` in the header has changed, the amino acid string itself changed at some point in between. That single character comparison is the cheapest possible update check.

To extract accessions from any UniProt-style multi-FASTA file with awk:

```bash
awk '/^>/{split($0,a,"|"); print a[1]}' input.fasta > accessions.txt
```

For the record above this prints `P04637`. The same trick extracts the taxonomy identifier when the header carries `OX=`:

```bash
awk '/^>/{for(i=1;i<=NF;i++) if($i ~ /^OX=/){split($i,b,"="); print b[1]}}' input.fasta | sort -u
```

In Python, a small parsing function that returns a dictionary keyed by accession makes any multi-FASTA file addressable:

```python
def parse_uniprot_fasta(path):
    records = {}
    header, seq_lines = None, []
    for line in open(path):
        line = line.rstrip("\n")
        if line.startswith(">"):
            if header:
                records[header.split("|")[3]] = "".join(seq_lines)
            header = line[1:]
            seq_lines = []
        else:
            seq_lines.append(line.strip())
    if header:
        records[header.split("|")[3]] = "".join(seq_lines)
    return records
```

Two caveats belong next to any parsing code. First, this pipe-splitting logic assumes UniProt's own three-part headers; FASTA files from other sources may place the identifier differently, so a portable parser should treat everything after the `>` up to the first whitespace as the identifier and treat the rest as description. Second, headers are free text by design, and annotation updates can reword the description while the accession and `SV` stay fixed. Treat description text as display information, and treat accession plus sequence version as the machine-readable identity.

## A Worked Python Pipeline for Robust Batch Downloads

The three-line `urllib` script shown earlier is fine for one accession. Real projects need error handling, polite pacing, and a log of what was fetched. The pipeline below reads a plain text file of accessions, one per line, downloads each sequence, records failures, and writes a manifest you can cite in your methods.

```python
import time
import json
import urllib.request
import urllib.error

BASE = "https://rest.uniprot.org/uniprotkb/"

with open("accessions.txt") as fh:
    accessions = [line.strip() for line in fh if line.strip()]

ok, failed = [], []

for acc in accessions:
    url = f"{BASE}{acc}.fasta"
    for attempt in range(3):
        try:
            with urllib.request.urlopen(url, timeout=60) as resp:
                data = resp.read().decode()
            with open(f"sequences/{acc}.fasta", "w") as out:
                out.write(data)
            ok.append(acc)
            break
        except urllib.error.HTTPError as e:
            if e.code in (404, 400):
                failed.append((acc, f"HTTP {e.code}"))
                break
            time.sleep(2 ** attempt)
        except urllib.error.URLError:
            time.sleep(2 ** attempt)
    else:
        failed.append((acc, "retries exhausted"))
    time.sleep(0.5)

with open("manifest.txt", "w") as m:
    m.write("accession\tstatus\n")
    for acc in ok:
        m.write(f"{acc}\tok\n")
    for acc, why in failed:
        m.write(f"{acc}\t{why}\n")

print(f"downloaded {len(ok)}, failed {len(failed)}")
```

Points worth internalizing from this script:

- **Failures are data.** A 404 for one accession should not abort the run; it should be logged so you can decide later whether the accession was mistyped, merged into another entry, or deleted.
- **Retries are for transient faults.** Network resets and temporary server strain are common on long runs. Retrying with a growing pause handles them without hammering the service.
- **Pacing matters.** The short sleep between requests keeps a loop of a few thousand accessions well behaved. If your list is that large, though, the query-based stream approach from the earlier section is almost always better, because one request replaces thousands.
- **The manifest is your provenance record.** Combined with the `SV` value captured from each header, the manifest is enough for a methods statement that a reviewer can trust.

One refinement that turns this from good to publication-grade: fetch the JSON representation of each entry alongside the FASTA, because the JSON includes an audit block reporting both the entry version and the sequence version. Recording those two numbers in your manifest means every downloaded sequence is pinned to an exact historical state of the database.

## A Field Guide to UniProt Query Syntax

The stream and search endpoints accept the full UniProt query grammar, and most retrieval problems reduce to writing a better query rather than writing more code. The fields you will use daily are `reviewed`, `organism_id`, `organism_name`, `gene`, `protein_name`, `accession`, and `proteome`. Boolean operators `AND`, `OR`, and `NOT` must be uppercase; lowercase `and` is not recognized as an operator and will silently change your results.

Worked queries, each of which can be dropped into the stream endpoint from the earlier section:

**All reviewed mouse entries for a gene family, multiple genes at once:**

```
query=(gene:TP53 OR gene:CDKN1A OR gene:MDM2) AND (organism_id:10090) AND (reviewed:true)
```

**A phrase search on the protein name.** Phrases with spaces need quotation marks, URL-encoded as `%22`:

```
query=(protein_name:%22tumor%20suppressor%20p53%22) AND (reviewed:true)
```

**Excluding a species while searching a broad taxon.** Parentheses group terms, and `NOT` removes them:

```
query=(gene:INS) AND (reviewed:true) NOT (organism_id:9606)
```

**Everything from one organism that has a specific gene name, regardless of review status, useful when surveying an understudied genome:**

```
query=(organism_name:Escherichia%20coli) AND (gene:recA)
```

Three habits prevent most query frustration:

1. **Parenthesize every clause.** Operator precedence across mixed `AND` and `OR` is a classic source of silently wrong result sets. Explicit parentheses cost nothing and remove the ambiguity.
2. **Test the count before downloading.** The search endpoint returns JSON by default, and the response reports how many records matched. Issue the query without `format=fasta` first, look at the match count, and only then stream the FASTA. A count of 400 when you expected 4 means a filter is missing.
3. **Prefer taxonomy identifiers over names.** `organism_id:9606` is unambiguous; a name string can match unexpected entries through annotation quirks. Names are convenient for exploration, identifiers are what belongs in a script.

For long lists of accessions, an `OR` query built from a file works up to the point where the URL itself becomes impractically long. Build the query with a small shell fragment:

```bash
Q=$(sed 's/^/accession:/' accessions.txt | paste -sd+)
curl -s "https://rest.uniprot.org/uniprotkb/stream?query=${Q}&format=fasta&compressed=true" -o batch.fasta.gz
```

Split very long lists into chunks of a few hundred accessions, or switch to the ID mapping service, which was designed for exactly this scale and avoids URL length limits entirely.

## Pinning a Historical Sequence with the unisave Service

The version archive service, unisave, is the tool you reach for when a protocol, a figure, or a trained model depends on the sequence as it existed at a particular time. The draft introduced unisave in a table; here is the full workflow.

First, list the versions that exist for an entry. Requesting the archive in JSON returns a version history rather than a sequence:

```bash
curl -s "https://rest.uniprot.org/unisave/P04637?format=json"
```

The response enumerates the available entry versions with their dates. Choose the version that matches your project's date or a version number you recorded previously.

Second, download the exact historical sequence. The `versions` parameter takes one version number, and the output is ordinary FASTA:

```bash
curl -s "https://rest.uniprot.org/unisave/P04637?format=fasta&versions=2" -o P04637_v2.fasta
```

Third, compare the historical sequence with the current one to see precisely what changed:

```bash
diff <(grep -v "^>" P04637_v2.fasta | tr -d '\n') \
     <(grep -v "^>" P04637.fasta    | tr -d '\n')
```

This joins each sequence into one line and shows any differing characters. For proteins whose sequences were corrected after publication, this diff is the difference between an explainable result and a mysterious one.

When does this matter in practice? Three recurring situations. A reviewer asks which sequence version your alignment used; a machine learning dataset must be exactly regenerable; or a paper you are reproducing predates a sequence correction. In all three, the unisave URL plus the version number is a complete, compact answer that belongs in your supplementary methods.

## Choosing Between Download Methods: A Comparison

![Decision tree leading from the shape of your request to website, single accession URL, stream query, proteome query or ID mapping.](/article-images/illustrations/uniprot-fasta-sequence-download-method-choice.webp)

With several retrieval routes available, the practical question becomes which route fits which job. The table summarizes the tradeoffs.

| Method | Best for | Typical scale | Reproducibility |
|-|-|-|-|
| Website entry page | One sequence, interactive browsing | 1 entry | Manual; record accession and SV |
| REST single accession URL | Scripts and notebooks touching a few entries | 1 to dozens | Excellent; the URL is the method |
| Stream endpoint with query | Filtered sets by gene, organism, annotation | Hundreds to tens of thousands | Excellent; the query is the method |
| Proteome query | A complete, consistent organism dataset | Thousands to millions | Excellent; proteome ID pins the set |
| UniRef clusters | Redundancy-reduced sequence sets | Any | Good; record the cluster level |
| ID mapping service | Converting foreign identifiers to sequences | Up to very large lists | Good; record from, to, and job parameters |
| Database dumps | Mirror-scale or offline institutional use | Entire database | Excellent; snapshot is fixed |

A few decision rules emerge from the table.

If you can express what you want as a query, use the stream endpoint. Queries are text, text diffs cleanly in version control, and a colleague can rerun your retrieval by pasting one line. This is why the machine learning studies discussed earlier could describe their input data precisely: their sequences came from definable UniProt retrieval steps, quality-checked before model training [3][2].

If your input is a list of accessions from elsewhere, the stream endpoint with an `OR` query handles small lists, and ID mapping handles large ones. Resist the urge to loop one-request-per-accession for thousands of entries; the batched routes are faster, kinder to the service, and produce one coherent file.

If you need a stable local reference database, use a proteome query or a database dump. Ad hoc search results drift as the database changes, while a proteome identifier defines a fixed, self-consistent set.

If your colleagues or your future self will rerun this, prefer any method whose complete description fits in one line of text. The website is wonderful for exploration and verification, but a click sequence is hard to reproduce; a URL is not.

## Post-Download File Manipulation with Standard Shell Tools

Once a multi-FASTA file is on disk, most manipulation requires nothing beyond awk, sed, and standard text tools. These recipes assume UniProt's pipe-delimited headers.

**Join wrapped lines into two-line records.** Many modern tools prefer one sequence per line:

```bash
awk '!/^>/{seq = seq $0; next} /^>/{if (hdr) print hdr "\n" seq; hdr = $0; seq = ""} END{if (hdr) print hdr "\n" seq}' input.fasta > two_line.fasta
```

**Count total residues across all records:**

```bash
awk '!/^>/{n += length($0)} END{print n}' human_reviewed.fasta
```

**Subset a file to a list of wanted accessions.** With the accessions in `ids.txt`, one per line:

```bash
awk 'NR==FNR{want[$1]; next} /^>/{n = split($0, a, "|"); keep = (a[1] in want)} keep' ids.txt input.fasta > subset.fasta
```

The flag logic works because every record's first line decides membership, and all wrapped sequence lines follow it until the next header.

**Remove exact duplicate sequences.** Duplicates are the most common blemish in retrieved sets, and near-duplicates are subtler still. Exact deduplication with awk, keeping the first occurrence:

```bash
awk '!/^>/{hdr = $0; next} {seq = seq $0} END{}' input.fasta > /dev/null
```

That one-liner pattern gets awkward in pure awk because the header precedes the sequence; a short Python script is clearer and worth keeping in your toolbox:

```python
seen = set()
out = open("unique.fasta", "w")
hdr, chunk = None, []
for line in open("input.fasta"):
    if line.startswith(">"):
        if hdr and "".join(chunk) not in seen:
            seen.add("".join(chunk))
            out.write(hdr + "\n" + "".join(chunk) + "\n")
        hdr, chunk = line.rstrip(), []
    else:
        chunk.append(line.strip())
if hdr and "".join(chunk) not in seen:
    out.write(hdr + "\n" + "".join(chunk) + "\n")
```

**Verify file integrity after transfer or compression:**

```bash
md5sum human_proteome.fasta.gz > human_proteome.fasta.gz.md5
md5sum -c human_proteome.fasta.gz.md5
```

Run the checksum at download time and again before any long analysis run. If the checksum matches, every mysterious downstream failure is definitively not a corrupted file, which halves the search space.

**Strip carriage returns from files touched by Windows editors:**

```bash
tr -d '\r' < input.fasta > input_clean.fasta
```

A stray `\r` at line end is invisible in most editors and fatal to strict parsers, so this one-liner belongs in every pre-flight checklist.

## Common Misconceptions About UniProt and FASTA

Misconceptions in this area persist because each one contains a grain of truth. Correcting them explicitly prevents the errors they seed.

**"The accession uniquely identifies a [protein molecule](/blog/guides/protein-molecule)."** An accession identifies an entry, and an entry contains a canonical sequence plus, frequently, alternative isoforms and described processing events. One accession can correspond to several legitimate amino acid strings depending on whether you take the precursor, a mature chain, or a numbered isoform. The molecule you analyze is accession plus a choice, and the choice must be recorded.

**"Line wrapping is part of the format."** The 60-residue wrapping seen in UniProt output is a display convention, not a format requirement. Parsers must accumulate sequence lines until the next header regardless of width. Conversely, a tool that crashes on unusual widths is telling you something true about the tool, not the file.

**"Unreviewed means unreliable."** TrEMBL entries are computationally annotated, not random. They are indispensable when the reviewed section has no coverage, for example in less-studied organisms. The correct framing is risk management: reviewed entries minimize false sequence assignments, unreviewed entries maximize coverage, and studies needing both simply record which section each sequence came from.

**"More sequences always make a model better."** Redundant sequences inflate apparent performance when near-identical relatives land on both sides of a train/test split. This is precisely why published pipelines retrieved sequences at staged similarity cutoffs of 100%, 90%, and 50% before training, letting them evaluate how redundancy level affected reported accuracy [2]. Downloading more is easy; controlling redundancy is what makes results believable.

**"A download today equals a download next year."** Annotations change constantly and sequences occasionally. Any pipeline that re-downloads without pinning versions will, over a long enough interval, process a different dataset while reporting the same method name. Pin sequence versions, whether through the `SV` header field or the unisave archive.

**"The description in the header is a stable machine field."** Description text is condensed prose maintained by curators, and it can be reworded. Scripts that grep for a phrase in headers will eventually break. Match on accession, filter on `OX` and `GN` tags, and read descriptions only with human eyes.

**"A multi-FASTA file is unusual."** Multi-record FASTA is the normal currency of the field; single-record files are the special case. Design scripts around the possibility of many records from line one.

**"Getting the sequence is the analysis."** A retrieved sequence is raw material. The β-catenin study converted its UniProt downloads into FASTA and then invested real effort in quality control before modeling, checking duplicates and outliers and splitting data properly, which is why its reported 89% accuracy means something [3]. The retrieval step and the validation step deserve equal care.

## Troubleshooting Guide: When a Download Fails or Looks Wrong

![Branching chart from an empty or odd file through quoting, HTML content, gzip and length checks to the likely fix.](/article-images/illustrations/uniprot-fasta-sequence-download-download-triage.webp)

Presented as symptom, likely cause, and fix, these cover the failures seen repeatedly in real use.

**The output file is empty or the shell prints a usage message.** The URL almost certainly was not quoted, so the shell split it at `&` or `?`. Confirm by rerunning with the entire URL inside double quotes, and verify status separately with the `curl -s -o /dev/null -w "%{http_code}\n"` pattern from earlier. A `200` with an empty file points to a redirection or quoting problem rather than a server problem.

**The file contains HTML instead of FASTA.** A request landed on the interactive website rather than the REST endpoint, typically because of a legacy bookmark or a mistyped host. Check the first byte:

```bash
head -c 1 file.fasta
```

A `>` means FASTA. Anything else means the wrong content arrived. Point the script at `rest.uniprot.org` directly and drop any dependence on redirect behavior.

**A long batch loop starts failing partway through.** Very rapid repeated requests can be throttled. Batch your accessions into query-based stream requests instead of one-request-per-accession, or add increasing pauses with retry logic as in the Python pipeline above. If the loop must remain, log HTTP codes so throttling is distinguishable from bad accessions.

**A huge stream download dies mid-transfer.** Large uncompressed FASTA bodies are vulnerable to connection drops. Add `compressed=true` so the payload shrinks dramatically, and consider splitting an organism-wide query into per-taxon or per-proteome chunks that each complete independently and can be concatenated afterward.

**gzip refuses to decompress the file.** Usually `compressed=true` was omitted, so the file is plain text with a misleading name. The `file` command reports the truth:

```bash
file human_proteome.fasta.gz
```

If it says ASCII text, rename it, decompress nothing, and fix the command for next time.

**The sequence is the wrong length.** Work through the candidates in order: a different isoform, the precursor including [signal peptide](/knowledge/molecular-biology/signal-peptide) or propeptide, a fragment record from the unreviewed section, a different species ortholog, or a different sequence version. The entry page's sequence processing section and the `SV` tag resolve all five.

**The file has records from an unexpected species mixed in.** The query lacked an organism filter, or a gene symbol matched orthologs. Re-run the retrieval with `organism_id` in the query, and as an independent check, tally the `OX` tags:

```bash
grep "^>" file.fasta | grep -o "OX=[0-9]*" | sort | uniq -c
```

**A parser rejects the file with no clear error.** Check for carriage returns as shown above, check for a missing final newline, and check for stop characters. UniProt protein FASTA should not normally contain `*`, but files assembled from translations of nucleotide data sometimes do, and several alignment tools refuse records containing them:

```bash
grep -c '\*' input.fasta
```

**The accession worked yesterday and 404s today.** Accessions can be merged into other entries or, rarely, deleted. The entry's history is visible through the version archive, and the fix is usually to adopt the successor accession listed in the merged record. This is one more reason the manifest from your batch pipeline earns its keep: it tells you exactly which accession changed.

## From Download to Database: Preparing UniProt FASTA for Local Tools

A downloaded FASTA file becomes dramatically more useful once it feeds a local sequence search or alignment toolkit. The canonical example is building a BLAST database from the human reviewed proteome retrieved earlier.

Decompress, then build the database:

```bash
gunzip -c human_proteome.fasta.gz > human_reviewed.fasta
mkdir -p db
makeblastdb -in human_reviewed.fasta -dbtype prot -out db/human_reviewed -title "Human reviewed proteome (UniProt)"
```

Then search your sequences of interest against it, with tabular output that is easy to load into a spreadsheet or a notebook:

```bash
blastp -query candidates.fasta -db db/human_reviewed \
  -outfmt "6 qseqid sseqid pident length evalue bitscore" \
  -out hits.tsv
```

The identifiers appearing in the `sseqid` column are the UniProt accessions from your headers, which closes a convenient loop: hits can be piped straight back into the retrieval workflows from the earlier sections to fetch full records, or pasted into tools such as the Human Protein Mutation Viewer, which accepts UniProt accessions and FASTA sequences and maps them onto domains, targeting signals, and modification sites [1].

Before feeding any downloaded file into alignment or search tools, three preparatory checks prevent the most common tool crashes:

1. **Stop characters.** As above, scan for `*` and remove or investigate affected records, because some tools reject them outright.
2. **Nonstandard residues.** The letters beyond the standard twenty, including `U` for selenocysteine and `X` for unknown, are legal FASTA but not universally handled. Scan for their frequency with the alphabet-checking approach shown in the verification section and decide per tool whether they are acceptable.
3. **Header complexity.** Long descriptive headers with spaces break position-based parsers and some indexing tools. When in doubt, produce a simplified-header copy alongside the original and feed the simplified one to the tool, keeping the original as the record of provenance.

For local similarity searches at very large scale, redundancy control matters again. A proteome-derived database is consistent but contains every isoform and every similar paralog; if search speed or database size is a concern, retrieve a UniRef cluster representation instead and build the database from that, recording the clustering level in your methods.

## Building a Reproducible Download Manifest

Everything in this guide converges on one deliverable: a manifest that lets you or anyone else reconstruct exactly what was downloaded, when, and from where. A minimal manifest has one row per retrieved record with these columns:

- accession
- retrieval URL or query string
- retrieval date
- sequence version, from the `SV` header field or the entry JSON audit block
- sequence length
- md5 checksum of the saved file
- intended use, for example `canonical precursor`, `isoform X`, or `proteome member`

A compact script that produces most of this automatically:

```python
import hashlib, json, urllib.request

acc = "P04637"
url = f"https://rest.uniprot.org/uniprotkb/{acc}.fasta"
fasta = urllib.request.urlopen(url).read().decode()

seq = "".join(l.strip() for l in fasta.splitlines() if not l.startswith(">"))
sv = None
for token in fasta.splitlines().split():
    if token.startswith("SV="):
        sv = token[3:]

entry = json.loads(urllib.request.urlopen(
    f"https://rest.uniprot.org/uniprotkb/{acc}.json").read())
audit = entry["entryAudit"]

row = [acc, url, sv or str(audit["sequenceVersion"]),
       str(len(seq)),
       hashlib.md5(fasta.encode()).hexdigest()]
print("\t".join(row))
```

Run this over a batch, write the rows to `manifest.tsv`, and commit both the manifest and the script to version control alongside the sequences or, for large files, alongside the manifest alone with the retrieval commands.

The written methods section practically composes itself from the manifest. A serviceable template: protein sequences were retrieved from UniProt on a stated date using the REST interface at rest.uniprot.org, restricted to reviewed entries for a stated organism, with sequence versions recorded per record; files were checksummed at download and again before analysis. Compare that with the alternative, "sequences were obtained from UniProt," and the value is obvious. The published machine learning studies cited throughout this guide make their inputs auditable in exactly this spirit, retrieving sequences from UniProt as FASTA and documenting the quality checks applied before any modeling [3][2], and interpretation tools such as HPMV accept the resulting accessions directly, so a well-documented accession list stays useful far beyond the initial download [1].

## Species, Strain, and Proteome Selection Pitfalls

Retrieval queries interact with biology in ways that produce subtle wrong answers, and species selection is where they concentrate.

**Taxon queries are hierarchical in effect.** Filtering by an organism identifier at a high taxonomic level can pull in entries from many species within that group. If you want one species, use its own identifier; if you want a genus or broader group deliberately, say so in your methods, because the resulting set size and redundancy both change.

**Reference proteomes are curated subsets, not everything.** A proteome query with an identifier such as the human `UP000005640` returns the proteins of one defined assembly, which is what you want for a self-consistent reference database. An `organism_id` query without a proteome filter returns every entry attributed to that organism, including entries from multiple strains and isolates for microbes, which is a different and often redundant set. Choose deliberately: proteome queries for reference databases and comparative work, organism queries for surveying everything known about a taxon.

**Strain-level proteomes matter for microbes.** Two strains of the same bacterial species can have proteomes differing by hundreds of proteins. When reproducing a study on a specific strain, retrieve that strain's proteome identifier from one of its entry pages and query on it, rather than settling for the species-level reference and hoping it matches.

**Paralogs and gene families require explicit decisions.** A gene-symbol query in a genome with many paralogs returns all of them, and every one is a correct answer to the query even though only one answers your biological question. The filter sequence from the step-by-step guide, reviewed status then organism, narrows the field, but the final choice among paralogs is a scientific decision that belongs to you, documented in your notes rather than hidden in a query string.

**Watch the organism tag against your expectation.** Entries occasionally reflect sequences from a different source than the entry's headline organism suggests, for laboratory strains or modified constructs. The `OX` tag in the header is the authoritative organism identifier per record, so a one-line tally of `OX` values across your final file, as shown in the troubleshooting section, is the last word on what species your dataset actually contains.

## Practice Exercises

Work these in order; each builds on the previous and every expected result derives from facts already covered in this guide.

**Exercise 1. Single retrieval and verification.** Download P04637 through the REST endpoint, then confirm with the Python length-checking snippet that the sequence contains 393 residues, and read the `SV` value from the header. Record the accession, sequence version, and download date in a one-row manifest.

**Exercise 2. Query construction.** Using the stream endpoint, retrieve all reviewed human entries for gene `CDKN1A`. Before downloading, run the query without `format=fasta` and confirm the match count is a small number, then download the FASTA and verify the record count with `grep -c "^>"`. Every header should begin `sp|` and carry `OX=9606`.

**Exercise 3. Batch retrieval with a manifest.** Save the accessions `P04637`, `P25098`, and `O00206` into a file and run either the OR-query stream command or the Python batch pipeline. Confirm three records arrive, extract the accessions with the awk one-liner, and check they match your input list exactly.

**Exercise 4. Subsetting and deduplication.** Download the compressed human reviewed proteome with `compressed=true`. Extract a subset containing only P04637 using the awk subset recipe, then run the Python deduplication script on the full proteome and compare record counts before and after with `grep -c "^>"`. Record both counts; the difference is your duplicate burden.

**Exercise 5. Version pinning.** Fetch the version history for P04637 from unisave in JSON, pick the earliest listed version, download it as FASTA, and diff it against the current sequence using the joined-line diff command. If any characters differ, note their positions; that positions file is exactly what you would attach to a reproducibility report.

**Exercise 6. Local database.** Build a BLAST database from the proteome file with `makeblastdb`, search P04637 against it with `blastp` in tabular outfmt, and confirm the top hit's `sseqid` is P04637. This round trip, download, database, search, self-identification, is the fastest end-to-end sanity check that your local setup is sound, and it rehearses the same accession-centric workflow that connects retrieval to interpretation tools such as HPMV [1].

Completing all six leaves you with a verified single sequence, a verified filtered set, a duplicate-free proteome, a pinned historical version, a working local search database, and a manifest tying it all together. That collection is not just practice; it is the actual skeleton of a production sequence-analysis project, and every downstream method, from simple alignments to the attention-based and graph-based models reported in the literature at accuracies near 86 to 89 percent [3][2], starts from exactly these materials.

## Frequently Asked Questions

### Is UniProt free to use?

Yes. UniProt is freely available to everyone, and its sequence data can be downloaded through the website, the REST API, and full database dumps without fees or registration.

### What is the difference between Swiss-Prot and TrEMBL?

Swiss-Prot contains reviewed entries checked by human curators, while TrEMBL contains unreviewed entries annotated by automated methods, so Swiss-Prot is the higher-confidence section to search first.

### How do I download an isoform instead of the canonical sequence?

Open the entry, go to the Isoforms and Sequences sections, identify the isoform you need, and retrieve it through the Sequences display on the entry page, since the plain accession FASTA always returns the canonical form.

### Why is my downloaded sequence longer or shorter than I expected?

The canonical sequence is the full translated precursor, which includes signal peptides and propeptides that are removed during normal processing, while alternative isoforms, sequence versions, or a different species can explain other discrepancies.

### How do I download all protein sequences for one organism?

Find the organism's proteome identifier on any of its entry pages, then query the stream endpoint with that identifier and `format=fasta`, which returns the complete proteome in a single compressed file.

### How do I download sequences grouped at 90% or 50% identity?

Use the UniRef services, which cluster UniProt sequences at 100%, 90%, and 50% identity, and export the cluster members in FASTA format. This is the mechanism behind published pipelines that fetched UniProt sequences at those exact similarity cutoffs [2].

### What is the difference between the entry version and the sequence version?

The entry version counts all changes to the record, including annotation updates, while the sequence version counts only changes to the [amino acid sequence](/blog/guides/amino-acid-sequence) itself, and both are displayed on the entry page and should be recorded with your download.

### How do I simplify long UniProt FASTA headers for older tools?

Remove everything after the first space in each header line with a small text transformation, such as a sed substitution, so each record retains only the database tag, accession, and entry name.

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "Is UniProt free to use?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Yes. UniProt is freely available to everyone, and its sequence data can be downloaded through the website, the REST API, and full database dumps without fees or registration."
      }
    },
    {
      "@type": "Question",
      "name": "What is the difference between Swiss-Prot and TrEMBL?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Swiss-Prot contains reviewed entries checked by human curators, while TrEMBL contains unreviewed entries annotated by automated methods, so Swiss-Prot is the higher-confidence section to search first."
      }
    },
    {
      "@type": "Question",
      "name": "How do I download an isoform instead of the canonical sequence?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Open the entry, go to the Isoforms and Sequences sections, identify the isoform you need, and retrieve it through the Sequences display on the entry page, since the plain accession FASTA always returns the canonical form."
      }
    },
    {
      "@type": "Question",
      "name": "Why is my downloaded sequence longer or shorter than I expected?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "The canonical sequence is the full translated precursor, which includes signal peptides and propeptides that are removed during normal processing, while alternative isoforms, sequence versions, or a different species can explain other discrepancies."
      }
    },
    {
      "@type": "Question",
      "name": "How do I download all protein sequences for one organism?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Find the organism's proteome identifier on any of its entry pages, then query the stream endpoint with that identifier and format=fasta, which returns the complete proteome in a single compressed file."
      }
    },
    {
      "@type": "Question",
      "name": "How do I download sequences grouped at 90% or 50% identity?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Use the UniRef services, which cluster UniProt sequences at 100%, 90%, and 50% identity, and export the cluster members in FASTA format. This is the mechanism behind published pipelines that fetched UniProt sequences at those exact similarity cutoffs."
      }
    },
    {
      "@type": "Question",
      "name": "What is the difference between the entry version and the sequence version?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "The entry version counts all changes to the record, including annotation updates, while the sequence version counts only changes to the amino acid sequence itself, and both are displayed on the entry page and should be recorded with your download."
      }
    },
    {
      "@type": "Question",
      "name": "How do I simplify long UniProt FASTA headers for older tools?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Remove everything after the first space in each header line with a small text transformation, such as a sed substitution, so each record retains only the database tag, accession, and entry name."
      }
    }
  ]
}
</script>

## Sources

1. [HPMV: human protein mutation viewer - relating sequence mutations to protein sequence architecture and function changes.](https://pubmed.ncbi.nlm.nih.gov/26503432/)
2. [Artificial intelligence-powered prediction of AIM-2 inflammasome sequences using transformers and graph attention networks in periodontal inflammation.](https://pubmed.ncbi.nlm.nih.gov/40082687/)
3. [HyperAttention and Linformer-Based β-catenin Sequence Prediction For Bone Formation.](https://pubmed.ncbi.nlm.nih.gov/39376879/)