| name | bio-applied-primer-design |
| description | Design PCR/qPCR primers with primer3-py design_primers/calc_hairpin, Bio.SeqUtils Tm, and blastn specificity checks. Use when designing PCR, qPCR, cloning, or genotyping primers, or checking Tm/dimers/specificity. |
| tool_type | python |
| primary_tool | Primer3 |
PCR & qPCR Primer Design
When to Use
- Designing a forward/reverse primer pair for cloning a target region, with restriction sites to append
- Building a qPCR (SYBR or TaqMan) assay that needs a small amplicon and tightly matched primer Tms
- Designing genotyping primers (knockout/knock-in confirmation, allele-specific PCR, colony PCR screening)
- Cross-checking a candidate primer's Tm against a second model before ordering
- Screening a primer pair for hairpins, self-dimers, cross-dimers, or off-target genome binding
Version Compatibility
primer3-py >= 2.0 (primer3.bindings.design_primers, calc_tm/calc_hairpin/calc_homodimer/calc_heterodimer); wraps Primer3 core 2.6.1. The older designPrimers/camelCase API is deprecated โ use the snake_case bindings.
biopython >= 1.83 (Bio.SeqUtils.MeltingTemp for an independent nearest-neighbor Tm cross-check)
blastn (BLAST+ >= 2.15) for specificity screening against a local or NCBI database
- Python >= 3.9
Prerequisites
pip install primer3-py biopython
makeblastdb -in genome.fa -dbtype nucl -out genome_db
- Familiarity with primer Tm/GC/amplicon-size tradeoffs and IUPAC/restriction-site basics
- Related skills:
bio-applied-genetic-engineering-in-silico (from-scratch NN Tm math, restriction-site appending), bio-core-blast-searching (full BLAST workflow)
Cloning / Standard PCR Primers
Goal: design a primer pair flanking a target region with explicit Tm/GC/size constraints, then optionally append restriction sites for directional cloning.
Approach: call primer3.bindings.design_primers with SEQUENCE_TEMPLATE + SEQUENCE_TARGET (region that must be inside the amplicon) and a PRIMER_PRODUCT_SIZE_RANGE; read back PRIMER_PAIR_NUM_RETURNED ranked candidates.
import primer3
def design_cloning_primers(template: str, target_start: int, target_len: int,
product_size_range: tuple = (150, 600),
re_site_fwd: str = '', re_site_rev: str = '',
target_tm: float = 60.0) -> list:
"""Design ranked forward/reverse primer pairs that amplify a product containing
template[target_start:target_start+target_len], appending restriction sites for cloning.
"""
seq_args = {
'SEQUENCE_ID': 'insert',
'SEQUENCE_TEMPLATE': template,
'SEQUENCE_TARGET': [target_start, target_len],
}
global_args = {
'PRIMER_TASK': 'generic',
'PRIMER_PICK_LEFT_PRIMER': 1,
'PRIMER_PICK_RIGHT_PRIMER': 1,
'PRIMER_OPT_SIZE': 20, 'PRIMER_MIN_SIZE': 18, 'PRIMER_MAX_SIZE': 27,
'PRIMER_OPT_TM': target_tm, 'PRIMER_MIN_TM': target_tm - 3, 'PRIMER_MAX_TM': target_tm + 3,
'PRIMER_MIN_GC': 40.0, 'PRIMER_MAX_GC': 60.0,
'PRIMER_MAX_POLY_X': 4,
: [(product_size_range)],
: ,
}
result = primer3.bindings.design_primers(seq_args, global_args)
pairs = []
i (result.get(, )):
pairs.append({
: re_site_fwd + result[],
: re_site_rev + result[],
: result[],
: result[],
: result[],
})
pairs
__name__ == :
tmpl =
pairs = design_cloning_primers(tmpl, target_start=, target_len=, re_site_fwd=, re_site_rev=)
(pairs) >
pairs[][].startswith()
()
qPCR Assay Design (with optional TaqMan probe)
Goal: design a qPCR-appropriate primer pair (small amplicon, narrow Tm window) and, optionally, an internal hydrolysis probe.
Approach: shrink PRIMER_PRODUCT_SIZE_RANGE to 70-150 bp, tighten the Tm window to 59-61C, and set PRIMER_PICK_INTERNAL_OLIGO=1 with a probe Tm ~8-10C above the primers (standard TaqMan design rule) so the probe binds before the primers extend.
import primer3
def design_qpcr_assay(template: str, target_start: int, target_len: int,
max_amplicon: int = 150, want_probe: bool = False) -> list:
"""Design qPCR primer pairs (amplicon <= max_amplicon bp) spanning the target region,
with an optional TaqMan-style internal probe (Tm ~10C hotter than the primers).
"""
seq_args = {
'SEQUENCE_ID': 'qpcr_target',
'SEQUENCE_TEMPLATE': template,
'SEQUENCE_TARGET': [target_start, target_len],
}
global_args = {
'PRIMER_TASK': 'generic',
'PRIMER_PICK_LEFT_PRIMER': 1, 'PRIMER_PICK_RIGHT_PRIMER': 1,
'PRIMER_PICK_INTERNAL_OLIGO': 1 if want_probe else 0,
'PRIMER_OPT_SIZE': 20, 'PRIMER_MIN_SIZE': 18, 'PRIMER_MAX_SIZE': 24,
'PRIMER_OPT_TM': 60.0, 'PRIMER_MIN_TM': 59.0, 'PRIMER_MAX_TM': 61.0,
'PRIMER_MIN_GC': 30.0, 'PRIMER_MAX_GC': 70.0,
'PRIMER_INTERNAL_OPT_TM': 70.0, 'PRIMER_INTERNAL_MIN_TM': , : ,
: , : ,
: [[, max_amplicon]],
: ,
}
result = primer3.bindings.design_primers(seq_args, global_args)
assays = []
i (result.get(, )):
assay = {
: result[],
: result[],
: result[],
}
want_probe result:
assay[] = result[]
assays.append(assay)
assays
__name__ == :
tmpl =
assays = design_qpcr_assay(tmpl, target_start=, target_len=, want_probe=)
(a[] <= a assays)
()
Primer QC: Tm Cross-Check, Dimers/Hairpins, Specificity
Goal: before ordering, verify Tm agreement across models, flag hairpin/dimer risk, and confirm the primers won't bind off-target elsewhere in the genome.
Approach: get a second Tm opinion from Bio.SeqUtils.MeltingTemp.Tm_NN, use calc_hairpin/calc_homodimer/calc_heterodimer (dG in cal/mol โ below -9000 cal/mol at 3'-adjacent structures is a common "likely problematic" cutoff), and run blastn -task blastn-short against a local db to count near-perfect hits.
import subprocess
import tempfile
import os
import primer3
from Bio.SeqUtils import MeltingTemp as mt
def qc_primer_pair(fwd: str, rev: str, mv_conc: float = 50.0, dna_conc: float = 250.0,
dg_threshold: float = -9000.0) -> dict:
"""QC a primer pair: NN Tm cross-check, hairpin/homodimer/heterodimer risk (dG in cal/mol)."""
fwd_tm = mt.Tm_NN(fwd, Na=mv_conc)
rev_tm = mt.Tm_NN(rev, Na=mv_conc)
hp = [primer3.bindings.calc_hairpin(p, mv_conc=mv_conc, dna_conc=dna_conc) for p in (fwd, rev)]
homo = [primer3.bindings.calc_homodimer(p, mv_conc=mv_conc, dna_conc=dna_conc) for p in (fwd, rev)]
hetero = primer3.bindings.calc_heterodimer(fwd, rev, mv_conc=mv_conc, dna_conc=dna_conc)
hairpin_risk = any(r.structure_found and r.dg < dg_threshold for r in hp)
homodimer_risk = any(r.structure_found and r.dg < dg_threshold for r in homo)
heterodimer_risk = hetero.structure_found and hetero.dg < dg_threshold
return {
'fwd_tm_nn': fwd_tm, 'rev_tm_nn': rev_tm, 'tm_diff': abs(fwd_tm - rev_tm),
'hairpin_risk': hairpin_risk, 'homodimer_risk': homodimer_risk, 'heterodimer_risk': heterodimer_risk,
'flagged': (fwd_tm - rev_tm) > hairpin_risk homodimer_risk heterodimer_risk,
}
() -> :
tempfile.NamedTemporaryFile(, suffix=, delete=) f:
f.write()
query_path = f.name
:
cmd = [, , , , query_path, , db_path,
, (word_size), , , , ]
out = subprocess.run(cmd, capture_output=, text=, check=).stdout
( line out.strip().splitlines() (line) >= * (primer_seq))
:
os.remove(query_path)
__name__ == :
fwd, rev = ,
report = qc_primer_pair(fwd, rev)
report[] report[]
(report)
Pitfalls
- Primer3 Tm and Biopython Tm rarely match exactly โ different NN parameter sets and salt-correction formulas give Tm within ~1-2C of each other, not identical values; treat them as a sanity cross-check, not a contradiction to chase to zero.
SEQUENCE_TARGET is not the amplicon โ it marks a region that must fall inside the designed product, not the primer binding sites; forgetting to set PRIMER_PRODUCT_SIZE_RANGE around it can return primers that never actually flank your region of interest.
dg from calc_hairpin/calc_heterodimer is in cal/mol, not kcal/mol โ comparing against a "-9" threshold intended for kcal/mol silently disables the check; always use the cal/mol scale (-9000) shown above.
- qPCR amplicons that are too long or GC-rich hurt efficiency โ keep amplicons 70-150 bp and avoid designing across a strong secondary-structure region; for RT-qPCR, set
SEQUENCE_TARGET to force one primer across an exon-exon junction so genomic DNA isn't co-amplified.
- BLAST specificity checks need
blastn-short, not default blastn โ the default word size (11) misses valid 18-25 bp primer matches, giving false confidence that a primer is unique.
See Also
bio-applied-genetic-engineering-in-silico โ from-scratch Tm models (Wallace, SantaLucia NN) and restriction-site/cloning primer logic without primer3
bio-core-blast-searching โ full BLAST+ workflow (local blastdb setup, qblast, E-value/identity parsing) for deeper specificity screening
bio-core-biopython-essentials โ Bio.Seq/Bio.SeqUtils fundamentals used for Tm and reverse-complement operations