From Clicking to Scripting · Subtopic 6 of 7 · 11 min
The Formats Databases Emit
Every database in this course hands you back a file, not a live database record — and the file's shape tells you as much as the database it came from. This lesson maps six formats to the databases that default to emitting them, so you can recognize a GenBank flat file, a GFF3, or a VCF on sight and reach for the right parser instead of guessing.
Same molecule, many containers
A single BRCA1 record can leave NCBI as at least three different files depending on what you ask for: a bare sequence, an annotated flat file, or a table of genomic coordinates. None of these is "the" BRCA1 file — each is a projection of the same underlying record, shaped for a different job. Batch Retrieval Without Code already had you choosing an export format from a "Send to" dropdown; this lesson explains what you were actually choosing between.
FASTA and GenBank: two ways NCBI hands you a sequence
FASTA is the leanest format there is: a header line starting with > followed by wrapped sequence, nothing else. Bio.SeqIO reads it with the "fasta" format string. NCBI's EFetch, Ensembl's /sequence REST endpoint, and UniProt's download button all offer it, because sometimes a sequence is all you need.
The GenBank flat file is FASTA's richer sibling. Fetch NM_007294 with rettype="gb" and you get a LOCUS line, a FEATURES table listing every gene, CDS, and exon with their coordinates, db_xref cross-references back to GeneID:672 and other databases (the mechanism the db_xref lesson walked through), and only then an ORIGIN block with the sequence itself. Parse it with "genbank", not "fasta" — the format string tells Biopython which sections to expect:
from Bio import Entrez, SeqIO
Entrez.email = "[email protected]"
handle = Entrez.efetch(db="nuccore", id="NM_007294", rettype="gb", retmode="text")
record = SeqIO.read(handle, "genbank")
handle.close()
print(record.id, len(record.seq))
for feature in record.features:
if feature.type == "CDS":
print(feature.qualifiers.get("db_xref"))Try it yourself: fetch NM_007294 twice, once with rettype="fasta" and once with rettype="gb". The sequence is identical; only the second call gives you record.features. Ask for the annotation you actually need instead of parsing it back out of a plain sequence.
GFF3, GTF, and VCF: coordinates and variants, not sequence
Gene models — where an exon starts, where a transcript's UTR ends — travel as GFF3 (General Feature Format, version 3) or the older GTF. Both are tab-separated, one feature per line, with columns for seqid, source, type, start, end, score, strand, and phase. The difference is the final attributes column: GTF packs it as gene_id "..."; transcript_id "...";, while GFF3 uses a hierarchical ID=/Parent= scheme that lets a file express "this exon belongs to this transcript belongs to this gene" directly. Ensembl's FTP site and its /overlap REST endpoint both serve GFF3. Neither format carries sequence, and Bio.SeqIO does not parse either — you reach for pandas.read_csv with sep="\t" and a comment filter, or the dedicated gffutils package.
VCF (Variant Call Format) is the coordinate-carrying format's cousin for variation data: ClinVar and dbSNP both emit it as a header block of ## metadata lines, then one row per variant with CHROM, POS, ID, REF, ALT, and an INFO column packed with key=value pairs. A ClinVar VCF for a BRCA1 pathogenic variant carries GENEINFO=BRCA1:672 and a CLNSIG field recording its clinical classification, tying the variant record straight back to the NCBI Gene entry for that gene. Biopython's SeqIO does not touch VCF either — the field standard there is PyVCF, cyvcf2, or, for anything past a quick look, bcftools. The VCF file format explained post covers every column in depth if you have not worked with one before.
mmCIF/PDBx: atoms, not bases
Structures are a different animal entirely: an mmCIF (macromolecular Crystallographic Information File, also called PDBx) record lists atomic coordinates, not a linear sequence of letters. RCSB PDB and PDBe both default to mmCIF now — the older fixed-column PDB format cannot represent large or multi-chain assemblies cleanly. Two real BRCA1-related entries show the range: 1JNX, the crystal structure of BRCA1's tandem BRCT repeat, and 1JM7, the NMR solution structure of the BRCA1/BARD1 RING-domain heterodimer. Both parse with Bio.PDB.MMCIFParser:
from Bio.PDB import MMCIFParser
parser = MMCIFParser(QUIET=True)
structure = parser.get_structure("BRCA1_BRCT", "1jnx.cif")
for chain in structure[0]:
print(chain.id, len(list(chain.get_residues())))SOFT/MINiML and the odd one out: GEO
GEO (the Gene Expression Omnibus) breaks the pattern because it is not distributing a sequence, a coordinate, or a variant — it is distributing a matrix of expression values plus the experimental metadata describing them. Its native export is SOFT (Simple Omnibus Format in Text), a line-oriented format with ^SERIES, ^SAMPLE, and ! metadata prefixes, or MINiML, the same content as XML. Neither is a Bio.SeqIO format; the community tool for pulling a GSE series into a usable table is GEOparse in Python or GEOquery in R.
Matching database to format to parser
| Database emits | Format | Carries | Reach for |
|---|---|---|---|
| GenBank/RefSeq (NCBI) | GenBank flat file | sequence + annotation | SeqIO.parse(..., "genbank") |
| Nucleotide/Protein/UniProt | FASTA | sequence only | SeqIO.parse(..., "fasta") |
| Ensembl / UCSC | GFF3 / GTF | genomic coordinates | pandas / gffutils |
| dbSNP / ClinVar | VCF | variant calls | cyvcf2 / bcftools |
| RCSB PDB / PDBe | mmCIF (PDBx) | atomic coordinates | Bio.PDB.MMCIFParser |
| GEO | SOFT / MINiML | expression matrix + metadata | GEOparse |
The pattern underneath all six: a format's job is to carry exactly what its database is built to hold, no more. Knowing that in advance is what turns "the download looks wrong" into "I asked for the wrong rettype" — the difference between a BRCA1 dossier built from GenBank, VCF, and mmCIF files that actually say what you think they say, and one that quietly does not.