# How to Save and Export UniProt Search Results

By the end of this article you will be able to run a search on UniProt, choose exactly which columns of data you want, and export those results as a file you can open in Excel, load into Python or R, or feed into a sequence analysis tool. You will learn to do this two ways: through the point-and-click interface on the UniProt website, and through the UniProt REST API with `curl` or Python so the export is fully reproducible. You will also learn how to paginate large result sets, how to verify that a downloaded file is complete, and how to fix the errors that most often break an export.

What you need on hand:

- A web browser for the website workflow. No account is required to search or download.
- A terminal with `curl` installed (standard on macOS, Linux, and Windows 10 or later) if you want the command-line workflow.
- Python 3 with the `requests` library if you want the scripted workflow. Install it with `pip install requests` if needed.

## What UniProt Is and What You Can Export From It

UniProt is a publicly available protein knowledgebase maintained by a consortium that includes EMBL-EBI and the SIB Swiss Institute of Bioinformatics. The core database is UniProtKB, the UniProt Knowledgebase, which is split into two sections. Swiss-Prot contains entries that have been manually reviewed by professional curators, with evidence-based annotations for function, domains, and subcellular location. TrEMBL contains computationally analyzed entries that have not yet been manually reviewed. When you export, knowing which section you are querying is the single most important decision you will make, because it controls the quality and volume of what you get back.

Every UniProtKB entry has a stable accession number, such as P38398 for human BRCA1, plus a text identifier called the entry name, such as BRCA1_HUMAN. Exports preserve these accessions, which is what lets you join your exported table back to other data later.

Exporting search results is not just a convenience. It is the standard way to move proteins from a web resource into a real analysis. Downstream tools build directly on UniProt data. SMART, the Simple Modular Architecture Research Tool, provides identification and annotation of protein domains and protein domain architectures, and its underlying protein databases are synchronized with UniProt, Ensembl, and STRING, covering more than 100 million annotated features [1]. When you export a set of UniProt sequences or accessions, tools like SMART can annotate them for domain content without you re-creating any of the underlying mapping [1]. A clean, well-chosen export is therefore the input side of most protein annotation pipelines.

## Exporting Search Results From the UniProt Website, Step by Step

This walkthrough uses a concrete example throughout: collecting all reviewed human BRCA1 entries and their basic annotations. Swap in your own gene, organism, or keyword at each step.

### Step 1. Open UniProtKB and run your search

1. Go to `https://www.uniprot.org` in your browser.
2. Under the search box, make sure the database selector is set to UniProtKB. UniProt hosts several databases (UniProtKB, UniRef, UniParc, Proteomes, and others), and searching the wrong one gives you results you cannot export the way you expect.
3. Type a query. For the worked example, enter:

```
gene:BRCA1 AND organism_id:9606 AND reviewed:true
```

4. Press Enter. The results page header shows the number of hits. For this query the expected result is a single entry, the canonical human BRCA1 record with accession P38398. If you see thousands of hits, you probably left out `reviewed:true` or the organism filter.

The query syntax matters. `gene:BRCA1` searches the gene name field. `organism_id:9606` restricts to the NCBI taxonomy identifier for humans. `reviewed:true` restricts to Swiss-Prot. The operators `AND`, `OR`, and `NOT` must be uppercase. Phrases with spaces need double quotes, for example `protein_name:"insulin receptor"`. Ranges use square brackets, for example `length:[0 TO 300]`.

### Step 2. Inspect and refine the results

Before exporting, confirm the result set is what you intend. The filter panel on the left lets you narrow by reviewed status, organism, and other facets, and each filter visibly updates the query shown at the top of the page. Read that query line. One common surprise is that clicking a filter silently rewrites your query, so the query in the box and the results on screen can drift apart from what you first typed. If you want a specific query, type it directly and avoid further filter clicks, or copy the query text out of the search box before downloading.

To save a search for later, simply bookmark the page. The full query lives in the URL, so reopening the bookmark reruns the exact search. There is no login or saved-search feature to configure.

### Step 3. Choose the columns you want

1. Above the results table, click "Customize columns". UniProt also offers a "Custom columns" option that lets you build a field set beyond the standard presets.
2. Tick the fields you want, such as Entry (the accession), Entry Name, Protein Names, Gene Names, Organism, Length, and specific annotation categories such as Keywords or cross-references.
3. The table refreshes immediately, and the downloaded file will match these columns.

Choosing columns before you export is more efficient than downloading everything and pruning later, especially for large result sets where every extra column inflates the file.

### Step 4. Download the results

1. Click the "Download" button above the results table.
2. A dialog appears with three choices to make: which entries (all results or a selected subset), which format, and whether the file should be compressed.
3. Pick the format from the list. The main options are:
   - FASTA, sequence-only text for BLAST, alignments, and most sequence tools
   - TSV, tab-separated values, one row per entry, ideal for spreadsheets and pandas
   - Excel, a spreadsheet format for direct viewing
   - TXT, the full UniProt flat file with all annotations
   - XML, the structured full record for programmatic parsing
   - GFF, features mapped onto the sequence in a genomics-friendly format
   - RDF, linked-data output for semantic web workflows
4. Choose uncompressed or `.gz` compressed. Compressed output is usually the right choice above a few thousand entries.
5. Click the download action. The browser saves a file, typically named with a pattern like `uniprotkb_<query fragment>_<format>_<date>`.

For the worked example, downloading TSV with the default columns produces a file whose first line is the header:

```
Entry	Reviewed	Entry Name	Protein Names	Gene Names	Organism	Length
P38398	reviewed	BRCA1_HUMAN	Breast cancer type 1 susceptibility protein ...	BRCA1 BRCC1 RNF53	Homo sapiens (Human)	1863
```

Your column set will differ depending on what you ticked in step 3, which is expected.

## Exporting Search Results With the UniProt REST API

The website is fine for a one-off. For anything you will rerun, share with colleagues, or repeat after a database update, use the REST API. The current programmatic endpoint for UniProtKB is `https://rest.uniprot.org/uniprotkb/search`. Older programmatic URLs on `www.uniprot.org` now redirect, so point your scripts at the REST host directly.

### A minimal search with curl

```
curl -s "https://rest.uniprot.org/uniprotkb/search?query=gene:BRCA1+AND+organism_id:9606+AND+reviewed:true&format=tsv&fields=accession,id,protein_name,organism_name,length&size=100"
```

Expected output:

```
Entry	Entry Name	Protein Names	Gene Names	Organism	Length
P38398	BRCA1_HUMAN	Breast cancer type 1 susceptibility protein	BRCA1 BRCC1 RNF53	Homo sapiens (Human)	1863
```

Three parameters do most of the work. `query` uses the same syntax as the website. `format` sets the output format, with `tsv`, `fasta`, `txt`, and `xml` among the supported values. `fields` selects the TSV columns as a comma-separated list. If you omit `fields` with `format=tsv`, you get a default column set. Passing `fields=all` returns every available column. The `size` parameter sets entries per page, with a maximum of 500.

In a shell, quote the whole URL. Unquoted URLs containing brackets, colons, or ampersands are the most frequent source of confusing command-line failures, because the shell interprets those characters before curl ever sees them.

### Exporting sequences as FASTA

For a BLAST-ready FASTA of short reviewed human proteins:

```
curl -s "https://rest.uniprot.org/uniprotkb/search?query=organism_id:9606+AND+reviewed:true+AND+length:%5B0+TO+300%5D&format=fasta&size=500"
```

Expected output, one record per protein:

```
>sp|Q9Y6C0|LEMD3_HUMAN LEM domain-containing protein 3 OS=Homo sapiens OX=9606 GN=LEMD3 PE=1 SV=2
MADGSK...
```

The FASTA header encodes the accession, entry name, description, organism (`OS=`), organism taxonomy ID (`OX=`), gene (`GN=`), and evidence tag (`PE=`). Everything after the first space in each header is descriptive, so if a downstream tool requires a simple identifier, parse up to the first space.

### Paginating large results with a cursor

The `search` endpoint returns at most 500 entries per request. Result sets larger than that are paginated with a cursor. Each response carries a `Link` HTTP header containing the URL for the next page. Check it directly:

```
curl -s -D headers.txt "https://rest.uniprot.org/uniprotkb/search?query=reviewed:true&format=tsv&fields=accession,length&size=500" -o page1.tsv
grep -i '^link' headers.txt
```

Expected output is a header line containing `rel="next"` followed by a URL that includes a `cursor` parameter. Follow that URL for page two, then again for page three, until the response has no `next` link. That absence is the signal that you have every result.

### Downloading everything in one request

For very large sets you can avoid the pagination loop entirely with the `stream` endpoint, which returns the entire result set in a single response:

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

The `-compressed` flag tells curl to decompress on the fly if the server sends gzip content. The stream endpoint trades interactive feedback for simplicity. You get one complete file, but no per-page progress, so for truly enormous downloads prefer running it in the background and checking file size as it grows.

### A scripted export in Python

This script pages through a query, keeps the header row once, and writes a single TSV file:

```python
import csv
import io
import requests

url = "https://rest.uniprot.org/uniprotkb/search"
params = {
    "query": "reviewed:true AND organism_id:9606",
    "format": "tsv",
    "fields": "accession,id,protein_name,length",
    "size": 500,
}

rows = []
while url:
    response = requests.get(url, params=params, timeout=60)
    response.raise_for_status()
    reader = csv.reader(io.StringIO(response.text), delimiter="\t")
    lines = list(reader)
    if not rows:
        rows.append(lines)          # keep the header exactly once
    rows.extend(lines[1:])
    url = response.links.get("next", {}).get("url")
    params = None                      # the next URL already carries the query

with open("human_reviewed.tsv", "w", newline="", encoding="utf-8") as fh:
    csv.writer(fh, delimiter="\t").writerows(rows)

print(f"{len(rows) - 1} entries written")
```

Expected output for a successful run:

```
20401 entries written
```

The exact count grows with every UniProt release, so treat any specific number as a snapshot rather than a constant. The structure of the script does not change.

### Controlling which annotation fields you export

The `fields` parameter accepts field names that mirror the website columns. Reliable, commonly used names include `accession`, `id`, `protein_name`, `gene_names`, `organism_name`, `length`, `sequence`, `keywords`, `go_id`, `cc_function`, and cross-reference fields for resources such as PDB. If you are unsure of a field name, build the column set interactively on the website, download a small TSV, and read the header row. Those header names are the API field vocabulary.

## Format Reference and When to Use Each One

| Format | What it contains | Best for | Watch out for |
|--|--|--|--|
| FASTA | Sequences only, with annotation-rich headers | BLAST, multiple alignments, most sequence tools | No [tabular data](/blog/research-skills/tabular-data-what-it-is-and-how-to-analyze-it), so you cannot join it back to other tables without parsing headers |
| TSV | One row per entry, selected columns | Excel, R, pandas, tidy workflows | Sequences can be a wide column, keep them only if you need them |
| Excel | Spreadsheet workbook from the website download | Handoff to non-programmatic collaborators | Less convenient for scripting than TSV |
| TXT | Full UniProt flat file | Complete record, parsing with Biopython's SwissProt reader | Verbose, one entry per block, larger files |
| XML | Structured full record | Programmatic pipelines needing nested annotation | Requires an XML parser, heavier than TSV |
| GFF | Sequence features on a coordinate system | Genome browsers, feature track work | Not a general-purpose record export |
| RDF | Linked-data triples | Semantic web and graph database use | Steep learning curve for most users |

A good default for tabular work is TSV with a deliberately short field list plus `accession`. A good default for sequence work is FASTA. You can always re-fetch full records later by accession, because accessions are stable identifiers, but you cannot un-download a bloated file you did not need.

## Tips for Better UniProt Exports

- Decide reviewed versus unreviewed first. `reviewed:true` gives Swiss-Prot, the manually curated subset. Leaving it out pulls in TrEMBL and can inflate your result set by orders of magnitude, often with near-duplicate sequences.
- Quote phrases and use uppercase Boolean operators. `protein_name:"insulin receptor"` finds the phrase, `protein_name:insulin receptor` does not behave the way you expect. Lowercase `and` and `or` are treated as search terms, not operators.
- Use taxonomy identifiers for precision. `organism_id:9606` is human. Searching organism by name string can catch unexpected species with similar names.
- Use length ranges to keep sets manageable. `length:[0 TO 300]` is a legitimate query component and a fast way to exclude giant proteins you do not intend to analyze.
- For domain-focused work, export accessions and sequences, then send the sequences to a domain tool. SMART identifies and annotates protein domains and domain architectures, supports high-resolution export of protein schematics, and synchronizes its protein databases with UniProt [1], so your exported set moves into domain annotation without reformatting gymnastics [1].
- Bookmark the search URL on the website as your saved query. Because the query is fully encoded in the URL, the bookmark is both your saved search and your documentation of what you did.
- Record your query string, the date, and the UniProt release in a readme next to the exported file. UniProt updates continuously, so a reproducible export needs its query preserved even more than its results.
- If a collaborator needs Excel, download XLSX from the website for them and keep the TSV for yourself. There is no reason to force one format on both audiences.

## Common Mistakes and Limitations

- Assuming the first page is the whole result. A `search` call returns at most 500 entries. If you did not paginate or use the `stream` endpoint, you silently have a slice, not the set. Check the `Link` header for `rel="next"` every time.
- Writing field names from memory. The API returns an HTTP 400 with a JSON body containing a `messages` array when a field name or query syntax is wrong. The message usually names the offending token, so read the response body rather than assuming curl failed silently.
- Unquoted URLs in the shell. Brackets in `length:[0 TO 300]` and ampersands in multi-parameter URLs are shell syntax. Quote the URL or percent-encode special characters, as shown in the examples above.
- Mixing up UniProtKB with UniRef and UniParc. UniRef clusters related sequences at fixed identity thresholds, and UniParc stores every unique sequence ever seen. Search and export in the wrong database and your accession lists will not join to anything sensibly.
- Forgetting isoforms. Many human genes have multiple protein isoforms. The canonical entry carries one sequence, and FASTA exports include isoform sequences as additional records with their own accession suffixes. If your downstream analysis requires exactly one sequence per gene, filter deliberately rather than assuming the default.
- Overriding your query with filters, then downloading. Filter clicks rewrite the query line. Before clicking download, read the query in the search box and confirm it says what you think it says.
- Treating exported annotation as a permanent truth. UniProt annotations change between releases as evidence accumulates. An export is a snapshot with a date. Re-running an old query a year later can return different annotation text, additional entries, or merged accessions.
- Expecting a search export to be a domain analysis. UniProt exports annotations that curated sources already deposited, but if you need systematic domain architecture for arbitrary sequences, a dedicated domain resource is the right next step. SMART provides identification and extensive annotation of protein domains and domain architectures across a large model set [1], which is exactly the layer UniProt exports are commonly fed into.

## How to Check That Your Export Is Right

Verification takes two minutes and prevents most downstream confusion.

For a TSV, count rows and compare against the hit count the website or API reported:

```
wc -l human_reviewed.tsv
```

The count minus one for the header should equal the number of entries reported. In pandas, confirm uniqueness and length sanity:

```python
import pandas as pd

df = pd.read_csv("human_reviewed.tsv", sep="\t")
print(df.shape)
print(df["Entry"].is_unique)
print(df["Length"].describe())
```

For a FASTA file, count records and inspect the first header:

```
grep -c '^>' human_reviewed.fasta
head -n 1 human_reviewed.fasta
```

The record count should match your expected entry count, remembering that isoforms add records. Finally, spot-check one accession. Copy an accession from your file, paste it into the search box on the UniProt website, and confirm the annotations in your export match the live record. If the accession resolves and the values agree, your export pipeline is sound.

## Planning an Export Before You Touch the Website

Most bad UniProt exports are not caused by tooling problems. They are caused by skipping five minutes of planning. Before you run any search, write down answers to four questions, because each one maps directly onto a decision you will make in the interface.

First, what biological question does the export serve? "Get all human proteins" is not a question. "Get every reviewed human protein annotated with a kinase activity so I can screen my phosphoproteomics hits against it" is. The question determines whether you need Swiss-Prot only, whether you need sequence or annotation or both, and whether your result set should be dozens of entries or tens of thousands.

Second, what is your scope of organisms and evidence? Reviewed status and taxonomy are the two filters that change result counts by orders of magnitude. Decide them up front and encode them in the query string rather than clicking filters later, for the reasons described earlier about filter clicks rewriting your query.

Third, what does the downstream tool actually require? This is the planning step people skip most often, and it is expensive to get wrong. If your next step is a BLAST database, you need FASTA and nothing else. If your next step is a spreadsheet comparison against a mass spectrometry result table, you need TSV with accessions, gene names, and protein names, and you do not need sequences at all because they will make the file unwieldy. If your next step is domain architecture visualization, you need sequences, because the domain tool will compute the architecture itself; resources such as SMART provide identification and extensive annotation of protein domains and domain architectures and keep their protein databases synchronized with UniProt [1], which means the burden of re-annotation falls on the tool, not on your export.

Fourth, how will you know the export succeeded? Decide your verification check before you download: an expected entry count, a sanity range on protein lengths, a spot-check accession. An export without a planned check is an export you cannot trust when a collaborator asks about it three months later.

A short planning template you can paste into your [lab notebook](/knowledge/molecular-biology/lab-notebook) or project readme:

```
Question:      identify reviewed human kinases for phosphoproteomics screen
Scope:         organism_id:9606, reviewed:true
Query:         reviewed:true AND organism_id:9606 AND keyword:Kinase
Format:        TSV (accession, gene_names, protein_name, length, sequence)
Verification:  row count matches website hit count; all Length values 100 to 5000
Downstream:    join against phosphosite table on accession
```

Filling in that template takes less time than debugging a mismatched export after the fact, and it produces the documentation you will want anyway.

## Building More Precise Queries: A Field Guide with Worked Examples

The single query box on UniProt accepts a rich fielded syntax, and most users barely scratch it. This section walks through query components that matter in practice, each with a concrete example and an explanation of what the query does and does not return.

**Exact gene matching.** The plain `gene:` field matches tokens across the gene name field, which can catch synonyms, symbols from other species, and partial matches. When you want a specific symbol and nothing that merely contains it, the exact-match variant is the safer choice:

```
gene_exact:BRCA1 AND organism_id:9606 AND reviewed:true
```

The difference shows up with short or generic symbols. A plain search for a three-letter gene symbol can pull in unrelated entries where that string appears as a substring or as an alternate name somewhere in the gene names field. Exact matching reduces that noise, at the cost of missing entries where your symbol is listed only as a synonym, so always sanity-check the hit count against your expectation.

**Keyword fields.** UniProt assigns controlled vocabulary keywords to entries, and these are queryable. For example:

```
reviewed:true AND organism_id:9606 AND keyword:Kinase
```

returns reviewed human entries carrying the Kinase keyword, which is assigned by curators based on evidence for kinase activity. Keyword queries are broader than enzyme commission numbers and narrower than free-text searches, which makes them a good middle option for functional family pulls. Note the distinction between searching the keyword field and searching the word anywhere: `keyword:Kinase` restricts to the controlled term, whereas a bare `kinase` in the query box searches across text fields and will match descriptions, names, and more.

**Enzyme commission numbers.** For enzymatic families, the EC field is precise:

```
reviewed:true AND organism_id:9606 AND ec:2.7.11.1
```

This returns reviewed human entries annotated with that exact EC class. EC queries are only as complete as the annotations, and annotation coverage differs between Swiss-Prot and TrEMBL, which is one more reason the reviewed decision comes first.

**Annotation type constraints.** You can restrict entries to those carrying a specific category of annotation, such as a [signal peptide](/knowledge/molecular-biology/signal-peptide) or a transmembrane region, using annotation type filters. The practical value is that you can build a set like "reviewed human proteins annotated with a signal peptide" without exporting everything and filtering afterward. When you combine an annotation-type constraint with a length range, you get tight, manageable sets, for example a query that combines a signal peptide annotation type with `length:[0 TO 600]` to capture candidate secreted peptides in a size window you can actually work with.

**Cross-reference constraints.** If you only care about proteins with structural data, constrain on a cross-reference to a structural database in the query itself. This avoids the frustrating pattern of exporting ten thousand rows and then discovering only two hundred have the cross-reference you needed. The cross-reference fields are also available as export columns, so you can pull the actual identifiers in the same download.

**Combining and grouping.** Boolean operators must be uppercase, and parentheses group subexpressions. A query such as:

```
(reviewed:true) AND (organism_id:9606 OR organism_id:10090) AND length:[0 TO 400]
```

returns reviewed human or mouse entries up to 400 residues. Read every compound query aloud to yourself as a sentence before running it. Ambiguity about whether an AND binds tighter than an OR is the most common logic error in fielded search, and parentheses remove the ambiguity entirely.

**A caution on over-filtering.** Every additional constraint trades recall for precision. If your query returns a suspiciously small number of hits, relax one constraint at a time and observe which one is doing the pruning. The advanced search interface on the website lets you assemble these constraints visually, and the resulting query string appears in the search box, which is a convenient way to learn the syntax for fields you will later reuse in scripts.

## A Second Worked Example: Exporting a Protein Family from Start to Finish

The BRCA1 walkthrough in the main article shows a single-entry export. Here is a complete second example with a realistic result set size, run both ways, so you can see the whole pipeline for a family-scale query.

The goal: every reviewed human protein annotated with the Kinase keyword, with names, lengths, subcellular location, and the sequences, ready for both a spreadsheet summary and downstream sequence analysis.

**Website route.** Search UniProtKB with:

```
reviewed:true AND organism_id:9606 AND keyword:Kinase
```

The results header will show a count in the hundreds. Customize columns to include Entry, Entry Name, Protein Names, Gene Names, Length, and Keywords. Because you also want sequences for a separate sequence-based step, run the download twice from the same query: once as TSV with your chosen columns, once as FASTA. Two downloads from one bookmarked query is cleaner than trying to force sequences into a tabular file, and it keeps each file purpose-built for its consumer. Save the query URL in your notes; that bookmark is the reproducible record of the selection.

**API route.** The same selection as one TSV request:

```
curl -s "https://rest.uniprot.org/uniprotkb/search?query=reviewed:true+AND+organism_id:9606+AND+keyword:Kinase&format=tsv&fields=accession,id,protein_name,gene_names,length&size=500"
```

Remember from the main article that this returns at most 500 entries per page and larger sets require cursor pagination or the stream endpoint. A family-scale set may fit in one page, but do not assume it: check the hit count the website reported and compare it against your row count.

For the sequence side, request FASTA with the same query, swapping `format=fasta`. FASTA ignores the `fields` parameter because the format carries only headers and sequences.

**Verification for this example.** Count the TSV rows and subtract the header; the number should equal the website hit count. Count the FASTA records with a record-count grep; that number should equal the TSV row count, unless isoforms add records, in which case the FASTA count legitimately exceeds the entry count. Spot-check two accessions, one at the top of the file and one random, by pasting them into the UniProt search box and confirming the protein name and length match your exported row.

**Taking it downstream.** With sequences in hand, sending the set to a domain annotation resource is the natural next step. Because domain resources such as SMART synchronize their underlying protein databases with UniProt and annotate protein domains and domain architectures directly [1], the accessions and sequences you exported are already in the vocabulary those tools expect, and the domain architectures you get back can be joined back to your TSV on accession without any identifier remapping [1]. That join, export table on the left, tool output on the right, keyed by accession, is the workhorse pattern of protein informatics, and it only works if your export preserved accessions faithfully, which is why every planning decision above keeps accession in the field list.

## Parsing and Cleaning Exports in Python and R

Exports are rarely analysis-ready the moment they land. Multi-value fields,isoform suffixes, and FASTA headers all need light parsing. This section gives you the standard manipulations for the two most common environments.

**Splitting multi-value columns in pandas.** Gene names arrive as a single space-separated string, and organisms arrive with the common name in parentheses. A typical cleaning pass:

```python
import pandas as pd

df = pd.read_csv("kinases.tsv", sep="\t")

# one row per gene name synonym
genes = df.assign(gene=df["Gene Names"].str.split()).explode("gene")

# strip the parenthetical from organism names
df["Organism"] = df["Organism"].str.replace(r"\s*\(.*\)$", "", regex=True)

# flag isoform rows by suffix pattern
df["is_isoform"] = df["Entry"].str.contains("-", regex=False)
```

The explode operation is the key move for joins. If your external data keys on gene symbols, joining against the exploded table catches entries through any of their synonyms, which you can then deduplicate back to one row per accession if needed.

**Parsing FASTA headers.** The header carries structured key-value pairs after the first space. A parser that extracts accession, gene, and organism:

```python
def parse_header(line):
    fields = dict(kv.split("=", 1) for kv in line.split()[1:] if "=" in kv)
    identifier = line.split()          # e.g. sp|P38398|BRCA1_HUMAN
    parts = identifier.split("|")
    return {
        "db": parts,
        "accession": parts[1],
        "entry_name": parts,
        "gene": fields.get("GN"),
        "organism": fields.get("OS"),
        "taxid": fields.get("OX"),
    }
```

Note that the `OS` and `OX` and `GN` tags only appear when the export includes them; sequence-only FASTA from some tools may have bare headers, so code defensively.

**Reading full TXT records in Python.** When you exported the flat file format, the Biopython Swiss-Prot parser reads it record by record without you touching the format details:

```python
from Bio import SeqIO

for record in SeqIO.parse("uniprot_flat.txt", "swiss"):
    print(record.id, len(record.seq), record.description)
```

This is the format of choice when you need nested annotations such as feature tables, because the parser exposes them as structured objects rather than flattened columns.

**Reading TSV in R.** Base R and the tidyverse both handle UniProt TSV directly:

```r
df <- read.delim("kinases.tsv", sep = "\t", quote = "", comment.char = "")

library(dplyr)
library(tidyr)
genes <- df %>%
  mutate(gene = strsplit(`Gene Names`, " ")) %>%
  unnest(gene)
```

Setting `quote` to empty and disabling comment characters matters more than people expect. Free-text protein description fields occasionally contain characters that the default reader treats as quote or comment syntax, and the symptom is silently shifted columns, which you will only notice when a downstream join fails.

## Troubleshooting Exports: A Symptom to Fix Reference

When an export misbehaves, the symptom usually points to one of a small number of causes. Work through this table before resorting to trial and error.

| Symptom | Most likely cause | Fix |
|-|-|-|
| HTTP 400 with a JSON error body | Bad field name or query syntax | Read the `messages` array in the body; it names the offending token |
| Zero hits when the website shows hits | Query was re-encoded incorrectly, or a filter click changed the scope | Copy the query directly from the website search box |
| Far fewer rows than the reported hit count | Pagination ignored; only the first page arrived | Follow the `rel="next"` link or switch to the stream endpoint |
| HTML or a redirect page instead of data | Script still pointed at an old URL that redirects | Use the REST host directly in scripts |
| curl prints nothing and exits quietly | Unquoted URL; the shell ate the brackets or ampersands | Quote the entire URL |
| File opens with scrambled columns in Excel | Quote, comment, or delimiter characters in free-text fields | Import with explicit delimiter settings, or read via R or Python instead |
| Gene symbols look like dates in Excel | Spreadsheet autocorrect on short symbols | Import the column as text, or avoid Excel for gene-level tables |
| FASTA record count exceeds entry count | Isoform records included | Filter by suffix or count accessions without suffixes |
| Download stalls on a very large set | Single-response size limits on huge sets | Use compressed output, or split the query by length or taxonomy ranges |
| Annotation text differs from what you saw last month | Database update between runs | Re-run and re-verify; treat exports as dated snapshots |

Two of these deserve elaboration because they cost people the most time.

**The silent pagination truncation.** This is the most dangerous failure mode because nothing errors. You get a valid file, open it, see a plausible number of rows, and move on, unaware that the query returned four thousand entries and you have five hundred. The defense is mechanical: every script that uses the search endpoint must loop on the next link until it is absent, as the Python example in the main article does, and every manual use should compare the row count against the website's reported hit count. Make the comparison part of the download ritual, not an occasional audit.

**Excel's treatment of gene symbols.** When a TSV containing gene names is opened directly in Excel, the spreadsheet application applies its default cell type inference. Certain gene symbols that resemble dates or numbers get converted: symbols formatted as month-and-number combinations become dates, symbols with leading zeros lose the zeros, and long numeric identifiers drift into scientific notation. The conversions happen silently at open time and are not reversible from the saved file. The robust habits are to import the file through the data import dialog with columns declared as text, to do gene-level work in R or Python where no type inference occurs, and to treat Excel as a viewing and handoff layer rather than the system of record. This is not a UniProt problem, but it surfaces constantly in UniProt workflows because exports feed spreadsheets so often.

**Transient failures during long downloads.** Large stream downloads occasionally fail partway through for network or server-side reasons. Because a truncated file usually still opens and parses, the failure can hide until your analysis behaves oddly. The defense is the same verification step as always: count records or rows, compare against the reported hit count, and re-download if the numbers disagree. For very large sets, splitting the query into ranges, exporting each range separately, and verifying each file gives you checkpoints that a single monolithic download does not.

## Choosing Between the Website, the Search Endpoint, and the Stream Endpoint

![Decision path asking whether the export is a one-off, must be rerun, or is very large, ending at the website, search endpoint or stream endpoint.](/article-images/illustrations/uniprot-export-search-results-route-choice.webp)

By now you have three ways to get the same data, and the right choice is situational. The tradeoffs worth internalizing:

| Route | Strengths | Weaknesses | Best for |
|-|-|-|-|
| Website download dialog | Zero setup, visual column picker, immediate feedback | Manual, not reproducible by default, filter clicks can alter scope | One-off exploratory pulls, quick checks, handoffs to collaborators |
| Search endpoint with pagination | Precise control of fields and page size, scriptable, resumable via cursor | Requires the pagination loop, 500 per page ceiling | Medium sets, scripted pipelines you may rerun, field-controlled TSV |
| Stream endpoint | Whole result set in one response, no loop to write | No per-page progress, harder to resume if interrupted | Very large sets, fire-and-forget background downloads |

A productive hybrid workflow uses all three at different stages. Build and refine your query on the website, where the filter panel and hit counts give fast feedback and where the finalized query string appears in the search box. Once the query is right, copy it into a script that uses the search endpoint for controlled field selection, or the stream endpoint if the set is large. Your one-off exploration produced a reusable, reproducible command, which is the entire point of the handoff.

One more consideration: the website is the only route that produces the Excel workbook format directly. If a collaborator genuinely needs a spreadsheet file and will not accept TSV, the website download is the path of least resistance, and per the advice in the main article you should still keep the TSV for anything programmatic.

## Reproducibility Practices for UniProt Exports

Because UniProt updates continuously, an exported file is a snapshot whose meaning is anchored to a query, a date, and a database state. Professional practice treats the query, not the file, as the primary artifact. These habits make that real.

**Keep a readme beside every exported file.** A minimal but sufficient template:

```
File:            kinases_2026-05-14.tsv
Source:          UniProtKB, Swiss-Prot section
Query:           reviewed:true AND organism_id:9606 AND keyword:Kinase
Format:          TSV
Fields:          accession,id,protein_name,gene_names,length
Retrieved by:    curl search endpoint, cursor pagination
Hit count:       (record the count the site reported)
Row count:       (record your wc -l result minus header)
Notes:           isoforms included in FASTA companion file
```

**Version the query, not just the results.** Store the query string in your repository next to the script that uses it. When the query changes, even by one term, that change is a scientific event worth a commit message, because it changes the population your analysis describes.

**Checksum the files you keep.** After verification, record a checksum so you can later prove a file has not been modified or corrupted:

```
sha256sum kinases_2026-05-14.tsv > kinases_2026-05-14.tsv.sha256
```

Re-running the same checksum months later tells you instantly whether the file on disk is the one you verified.

**Expect accessions to be the stable anchor, and handle merges gracefully.** Accessions are designed for stability, but entries do occasionally merge, and a merged accession may be retired while its records live on under the surviving accession. Practically, this means an old export's accession list can contain identifiers that resolve indirectly today. If you join across exports from different dates and some accessions stop matching, a merge is a likely explanation, and re-running the original query against the current database is the cleanest resolution. This is also a reason the readme matters: with the original query preserved, a re-run is one command rather than an archaeology project.

**Schedule re-runs deliberately.** For a pipeline that consumes UniProt data on an ongoing basis, re-run the export on a fixed schedule, verify counts each time, and record the new snapshot date. Expect the counts to move between runs; the movement itself is informative, and the verification step is what distinguishes real database growth from a truncated download.

## Feeding Exports into Downstream Tools

The export is a means, not an end. These are the most common handoffs, with the specific mechanics of each.

**Building a local BLAST database.** A FASTA export becomes a searchable database in one command, and then any sequence can be screened against it:

```
makeblastdb -in human_reviewed.fasta -dbtype prot -out myhumansp
blastp -query candidates.fasta -db myhumansp -outfmt 6 -out hits.tsv
```

The tabular output format keeps results joinable back to your UniProt TSV by accession, preserving the join-on-accession pattern throughout the pipeline. Note that if your FASTA contains isoform records, your BLAST hits will too, so carry the isoform-awareness from the export stage into the interpretation stage.

**Multiple sequence alignments.** For a family-scale FASTA export, the standard next step is an alignment with a tool such as a modern aligner of your choice, followed by tree building or conservation analysis. One practical tip: extremely long sequences, which length-range filtering helps you exclude up front, can dominate alignment runtime, so if you know your analysis only cares about a size window, encode that window in the query rather than trimming after the fact.

**Domain and architecture annotation.** As described earlier, tools that maintain synchronized protein databases can annotate your exported set directly [1]. The typical flow is: export accessions and sequences, submit the sequences to the domain resource, retrieve the domain architecture per protein, and join the architecture results back to your TSV on accession. Because SMART's protein databases are synchronized with UniProt and cover a large annotated feature space [1], this flow requires no identifier translation layer, which is precisely why keeping accessions pristine in your export pays off [1].

**Cross-reference joins.** When your export includes cross-reference columns, those columns become join keys into yet other resources: structural entries, family databases, interaction resources. The practical discipline is to check for empty cells in the cross-reference column before building an analysis on it, because absence of a cross-reference is itself data, and silently dropping empty rows can bias a set in ways that are hard to see later.

## Practical Applications in Real Projects

To make the abstract concrete, here are three recurring project patterns where a well-planned UniProt export is the foundation, each with the specific decisions that matter.

**Proteomics hit annotation.** A mass spectrometry analysis produces a list of protein identifiers. The task is annotating them with function, subcellular location, and keywords. The right export is a TSV restricted to your organism and reviewed status, with a field list heavy on functional annotation and light on sequence, joined against your identifier list on accession. Because reviewed entries carry evidence-based functional annotations, restricting to Swiss-Prot for this use case is usually correct even at the cost of coverage: an unreviewed entry's annotation may be propagated computationally rather than curated, and for a results table you generally want the curated layer.

**Gene family curation.** Building a clean set for a family, such as all reviewed kinases in a set of organisms, is a union of per-organism exports, each verified independently. The family example earlier in this article is the single-organism case; the multi-organism case adds one decision, which is whether to include orthologs from less-annotated species, where the reviewed restriction may thin the set considerably. In family curation work, the sequences usually matter as much as the annotation, since downstream phylogenetics and domain architecture work, including visualization of protein schematics of the kind domain tools export at high resolution [1], both consume the sequences directly.

**Training and benchmark sets for computational methods.** Whether you are training a predictor or benchmarking one, the set definition must be reproducible and its provenance documented, which makes the UniProt query string the natural formal definition of the set. Two constraints matter extra here. First, redundancy: unreviewed sections can contain near-duplicate sequences that leak between train and test partitions, so redundancy reduction happens after export, and the export should preserve enough fields, such as sequence and taxonomy, to do that reduction sensibly. Second, snapshot discipline: methods papers claim results relative to a database state, so the readme template above, with its date and release notes, is not bureaucratic overhead but the difference between a reproducible benchmark and an anecdote.

## Common Misconceptions About UniProt Exports

Beyond the operational mistakes catalogued earlier, several conceptual misconceptions shape how people use exports, and correcting them changes what you do next.

**"The export is the database."** An export is a projection: your query, your fields, one moment in time. Annotations accumulate and change between releases, entries merge, and new evidence revises function calls. Two exports of the same query six months apart are two different datasets, and neither is wrong. The misconception leads people to treat a year-old file as current truth in a manuscript or pipeline. The corrective habit is the dated snapshot plus the preserved query, so that anyone can regenerate the current state and see exactly what changed.

**"All entries carry annotations of equal reliability."** The reviewed and unreviewed split exists precisely because annotation quality differs, and within Swiss-Prot, individual annotations carry their own evidence attributions. An export that flattens everything into columns hides this gradient. When a claim in your analysis rests on a specific annotation, such as a function call or a subcellular location, know which section of the database it came from, and prefer the reviewed layer for claims you will publish.

**"A FASTA export is all I need."** Sequences without the accompanying tabular annotation discard the curation layer, and the FASTA headers, while informative, are a lossy and awkward substitute for a proper table. The header descriptions also change over time in wording even when the underlying biology does not, so parsing meaning out of header text couples your pipeline to phrasing that is not guaranteed stable. Keep the TSV alongside the FASTA, keyed on accession, and let each file do its job.

**"The column set is a permanent contract."** The available fields and their names evolve as the database grows. A script that hard-codes a field list should fail loudly with a 400 error if a name is retired, which is preferable to silently missing a column, but it also means field lists deserve the same version control treatment as queries. The defensive practice of building a column set on the website and reading the resulting TSV header, described in the main article, is also a good periodic audit: run it once per project cycle to catch renames before they bite.

**"One gene means one sequence."** The isoform reality, described in the main article, is worth restating as a planning principle rather than a footnote: any pipeline that assumes a strict one-to-one mapping between genes and sequences will be quietly wrong for organisms with substantial [alternative splicing](/blog/guides/alternative-splicing). Decide at the planning stage whether you work at the gene level, the canonical-sequence level, or the all-isoform level, and encode that decision in how you filter and count. Counting records instead of accessions, as the verification section recommends, is the mechanical expression of that decision.

**"Downloading is the finish line."** The most successful UniProt users treat the export as the first component of a pipeline whose later stages, BLAST databases, alignments, domain annotation through synchronized resources [1], spreadsheet joins, are already sketched before the download begins. When you know the destination, every intermediate choice, from format to field list to verification check, becomes obvious, and the export stops being a file you hope is right and becomes a dataset you know is right.

## Frequently Asked Questions

### Can I export UniProt search results without writing any code?

Yes. Search on the UniProt website, customize your columns, then click the Download button and pick a format such as TSV, FASTA, Excel, TXT, or XML. No account is needed.

### What is the difference between exporting TSV and FASTA?

TSV gives one row per entry with whatever annotation columns you selected, which suits spreadsheets and data frames. FASTA gives sequences only, which suits BLAST, alignments, and sequence tools.

### Why does my API search return only 500 entries when the website shows more?

The search endpoint caps each page at 500 results. Follow the `Link` header with `rel="next"` to page through, or use the `stream` endpoint to get the full result set in one response.

### How do I export only manually reviewed Swiss-Prot entries?

Add `reviewed:true` to your query on either the website or the API. Leaving it out includes TrEMBL, the unreviewed section.

### Can I export cross-references such as PDB or Pfam IDs?

Yes. Add the relevant cross-reference fields to your column selection on the website, or pass them in the `fields` parameter of a TSV API request.

### Is there a limit on how many entries I can download at once?

The website download dialog exports your entire result set, with an optional compressed output for large sets. Through the API, the `stream` endpoint returns a complete result set in a single response, while `search` needs cursor pagination beyond 500 entries per page.

### How do I save a search so I can rerun it later?

Bookmark the results page, because the full query is encoded in the URL. For scripts, keep the query string in your command or configuration file and rerun it whenever you need a fresh export.

### Do FASTA exports include protein isoforms?

Yes. Where isoforms exist, FASTA output includes them as additional records with accession suffixes such as P38398-2, so count records rather than accessions when you expect one sequence per gene.

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "Can I export UniProt search results without writing any code?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Yes. Search on the UniProt website, customize your columns, then click the Download button and pick a format such as TSV, FASTA, Excel, TXT, or XML. No account is needed."
      }
    },
    {
      "@type": "Question",
      "name": "What is the difference between exporting TSV and FASTA?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "TSV gives one row per entry with whatever annotation columns you selected, which suits spreadsheets and data frames. FASTA gives sequences only, which suits BLAST, alignments, and sequence tools."
      }
    },
    {
      "@type": "Question",
      "name": "Why does my API search return only 500 entries when the website shows more?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "The search endpoint caps each page at 500 results. Follow the Link header with rel=\"next\" to page through, or use the stream endpoint to get the full result set in one response."
      }
    },
    {
      "@type": "Question",
      "name": "How do I export only manually reviewed Swiss-Prot entries?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Add reviewed:true to your query on either the website or the API. Leaving it out includes TrEMBL, the unreviewed section."
      }
    },
    {
      "@type": "Question",
      "name": "Can I export cross-references such as PDB or Pfam IDs?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Yes. Add the relevant cross-reference fields to your column selection on the website, or pass them in the fields parameter of a TSV API request."
      }
    },
    {
      "@type": "Question",
      "name": "Is there a limit on how many entries I can download at once?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "The website download dialog exports your entire result set, with an optional compressed output for large sets. Through the API, the stream endpoint returns a complete result set in a single response, while search needs cursor pagination beyond 500 entries per page."
      }
    },
    {
      "@type": "Question",
      "name": "How do I save a search so I can rerun it later?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Bookmark the results page, because the full query is encoded in the URL. For scripts, keep the query string in your command or configuration file and rerun it whenever you need a fresh export."
      }
    },
    {
      "@type": "Question",
      "name": "Do FASTA exports include protein isoforms?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Yes. Where isoforms exist, FASTA output includes them as additional records with accession suffixes such as P38398-2, so count records rather than accessions when you expect one sequence per gene."
      }
    }
  ]
}
</script>

## Sources

1. [SMART: recent updates, new developments and status in 2015.](https://pubmed.ncbi.nlm.nih.gov/25300481/)