Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Run sequence similarity searches via the NCBI BLAST REST API
.claude/skills/brycewang-stanford-ncbi-blast-api/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-07 | ✗→✓ | ▲ Improved | 271% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 207% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 41% | 0% |
| case-12 | ✗→✓ | ▲ Improved | 295% | 0% |
| case-15 | ✗→✓ | ▲ Improved | 61% | 0% |
BLAST (Basic Local Alignment Search Tool) is the most widely used bioinformatics tool, comparing nucleotide or protein sequences against databases to find regions of similarity. The NCBI BLAST REST API enables programmatic submission of searches, status polling, and result retrieval. Free, no authentication required (but rate-limited).
BLAST searches are asynchronous: submit → poll → retrieve.
bash# Nucleotide BLAST (blastn) curl -X POST "https://blast.ncbi.nlm.nih.gov/blast/Blast.cgi" \ -d "CMD=Put&PROGRAM=blastn&DATABASE=nt&QUERY=ATGCGATCGATCG..." # Protein BLAST (blastp) curl -X POST "https://blast.ncbi.nlm.nih.gov/blast/Blast.cgi" \ -d "CMD=Put&PROGRAM=blastp&DATABASE=nr&QUERY=MKTLLLTLVVVTIVCL..." # BLAST with specific parameters curl -X POST "https://blast.ncbi.nlm.nih.gov/blast/Blast.cgi" \ -d "CMD=Put&PROGRAM=blastn&DATABASE=nt&QUERY=SEQUENCE&\ EXPECT=0.001&WORD_SIZE=11&HITLIST_SIZE=50"
bash# Poll for completion (returns XML with Status field) curl "https://blast.ncbi.nlm.nih.gov/blast/Blast.cgi?CMD=Get&FORMAT_OBJECT=SearchInfo&RID=YOUR_RID"
bash# Get results in XML curl "https://blast.ncbi.nlm.nih.gov/blast/Blast.cgi?CMD=Get&FORMAT_TYPE=XML&RID=YOUR_RID" # Get results in JSON curl "https://blast.ncbi.nlm.nih.gov/blast/Blast.cgi?CMD=Get&FORMAT_TYPE=JSON2_S&RID=YOUR_RID" # Get results in tabular format curl "https://blast.ncbi.nlm.nih.gov/blast/Blast.cgi?CMD=Get&FORMAT_TYPE=Tabular&RID=YOUR_RID"
| Program | Query → Database | Use case | |---------|-----------------|----------| | blastn | Nucleotide → Nucleotide | DNA/RNA similarity | | blastp | Protein → Protein | Protein homology | | blastx | Translated nuc → Protein | Find protein homologs of DNA | | tblastn | Protein → Translated nuc | Find DNA encoding similar protein | | tblastx | Translated nuc → Translated nuc | Compare at protein level |
| Database | Content | |----------|---------| | nt | All GenBank nucleotide sequences | | nr | Non-redundant protein sequences | | refseq_rna | RefSeq RNA sequences | | refseq_protein | RefSeq protein sequences | | swissprot | UniProtKB/Swiss-Prot (curated) | | pdb | Protein Data Bank sequences |
| Parameter | Description | Default | |-----------|-------------|---------| | PROGRAM | BLAST program | Required | | DATABASE | Target database | Required | | QUERY | Sequence or accession | Required | | EXPECT | E-value threshold | 10 | | WORD_SIZE | Word size | 11 (blastn), 6 (blastp) | | HITLIST_SIZE | Max results | 100 | | MATRIX | Scoring matrix (protein) | BLOSUM62 | | FILTER | Low complexity filter | L | | ENTREZ_QUERY | Restrict to organism | Homo sapiens[ORGN] |
pythonimport time import requests from xml.etree import ElementTree BLAST_URL = "https://blast.ncbi.nlm.nih.gov/blast/Blast.cgi" def submit_blast(sequence: str, program: str = "blastn", database: str = "nt", evalue: float = 0.001) -> str: """Submit a BLAST search, return Request ID.""" resp = requests.post(BLAST_URL, data={ "CMD": "Put", "PROGRAM": program, "DATABASE": database, "QUERY": sequence, "EXPECT": evalue, "HITLIST_SIZE": 50, }) resp.raise_for_status() for line in resp.text.split("\n"): if "RID = " in line: return line.split("=")[1].strip() raise ValueError("No RID in response") def wait_for_results(rid: str, poll_interval: int = 15, max_wait: int = 300) -> bool: """Poll until BLAST search completes.""" elapsed = 0 while elapsed < max_wait: resp = requests.get(BLAST_URL, params={ "CMD": "Get", "FORMAT_OBJECT": "SearchInfo", "RID": rid, }) if "Status=READY" in resp.text: return True if "Status=FAILED" in resp.text: raise RuntimeError("BLAST search failed") time.sleep(poll_interval) elapsed += poll_interval raise TimeoutError(f"BLAST timed out after {max_wait}s") def get_results(rid: str) -> list: """Retrieve BLAST results as parsed hits.""" resp = requests.get(BLAST_URL, params={ "CMD": "Get", "FORMAT_TYPE": "XML", "RID": rid, }) resp.raise_for_status() root = ElementTree.fromstring(resp.text) ns = "" hits = [] for hit in root.iter(f"{ns}Hit"): hsps = hit.find(f"{ns}Hit_hsps") hsp = hsps.find(f"{ns}Hsp") if hsps is not None else None hits.append({ "accession": hit.findtext(f"{ns}Hit_accession", ""), "description": hit.findtext(f"{ns}Hit_def", ""), "length": int(hit.findtext(f"{ns}Hit_len", "0")), "evalue": float(hsp.findtext(f"{ns}Hsp_evalue", "999")) if hsp is not None else 999, "identity": float(hsp.findtext(f"{ns}Hsp_identity", "0")) if hsp is not None else 0, "score": float(hsp.findtext(f"{ns}Hsp_bit-score", "0")) if hsp is not None else 0, }) return hits # Example: BLAST a short DNA sequence rid = submit_blast("ATGCGATCGATCGATCGATCGATCG", program="blastn") print(f"Submitted BLAST search: {rid}") wait_for_results(rid) hits = get_results(rid) for h in hits[:5]: print(f"{h['accession']}: {h['description'][:60]}...") print(f" E-value: {h['evalue']:.2e} | Identity: {h['identity']}")
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-07 | fail→pass | 10,792 | 3,599 | -67% | 1 | 1 | 0% | 697 | 2,585 | +271% | 0 | 0 | — |
case-01 | pass→pass | 24,991 | 24,311 | -3% | 1 | 1 | 0% | 2,477 | 3,851 | +55% | 0 | 0 | — |
case-02 | pass→pass | 8,873 | 8,135 | -8% | 1 | 1 | 0% | 1,649 | 3,476 | +111% | 0 | 0 | — |
case-03 | pass→pass | 9,126 | 8,515 | -7% | 1 | 1 | 0% | 1,780 | 3,292 | +85% | 0 | 0 | — |
case-04 | pass→pass | 17,053 | 17,527 | +3% | 1 | 1 | 0% | 3,365 | 5,478 | +63% | 0 | 0 | — |
case-05 | pass→pass | 12,630 | 6,716 | -47% | 1 | 1 | 0% | 2,425 | 3,265 | +35% | 0 | 0 | — |
case-06 | pass→pass | 10,072 | 7,294 | -28% | 1 | 1 | 0% | 1,653 | 3,319 | +101% | 0 | 0 | — |
case-08 | pass→pass | 5,181 | 2,811 | -46% | 1 | 1 | 0% | 752 | 2,431 | +223% | 0 | 0 | — |
case-09 | pass→pass | 9,237 | 6,034 | -35% | 1 | 1 | 0% | 1,561 | 2,962 | +90% | 0 | 0 | — |
case-10 | fail→pass | 10,932 | 3,339 | -69% | 1 | 1 | 0% | 790 | 2,423 | +207% | 0 | 0 | — |
case-11 | fail→pass | 12,534 | 6,086 | -51% | 1 | 1 | 0% | 2,184 | 3,086 | +41% | 0 | 0 | — |
case-12 | fail→pass | 13,024 | 5,811 | -55% | 1 | 1 | 0% | 739 | 2,922 | +295% | 0 | 0 | — |
case-13 | pass→pass | 11,872 | 11,534 | -3% | 1 | 1 | 0% | 2,045 | 4,013 | +96% | 0 | 0 | — |
case-14 | pass→pass | 11,283 | 10,907 | -3% | 1 | 1 | 0% | 2,031 | 3,988 | +96% | 0 | 0 | — |
case-15 | fail→pass | 8,942 | 3,337 | -63% | 1 | 1 | 0% | 1,569 | 2,533 | +61% | 0 | 0 | — |
case-16 | fail→pass | 7,549 | 3,592 | -52% | 1 | 1 | 0% | 1,281 | 2,549 | +99% | 0 | 0 | — |
case-17 | fail→pass | 7,266 | 2,609 | -64% | 1 | 1 | 0% | 1,284 | 2,390 | +86% | 0 | 0 | — |
case-18 | fail→pass | 20,742 | 3,424 | -83% | 1 | 1 | 0% | 1,307 | 2,531 | +94% | 0 | 0 | — |
case-19 | pass→pass | 12,135 | 4,779 | -61% | 1 | 1 | 0% | 2,281 | 2,792 | +22% | 0 | 0 | — |
case-20 | pass→pass | 7,149 | 5,123 | -28% | 1 | 1 | 0% | 1,401 | 2,982 | +113% | 0 | 0 | — |
case-21 | pass→pass | 4,195 | 4,478 | +7% | 1 | 1 | 0% | 902 | 2,902 | +222% | 0 | 0 | — |
case-22 | pass→pass | 8,309 | 5,893 | -29% | 1 | 1 | 0% | 1,300 | 2,936 | +126% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted, and 18 counted toward the lift figure. The other 4 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +36 percentage points is the difference between those two pass rates over the 18 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.