Benchling R&D Python SDK: CRUD on registry entities (DNA, RNA, proteins, custom), inventory, ELN, workflow automation. Needs Benchling account and API key. Use biopython for local sequence analysis; pubchem for chemical DBs.
Benchling R&D Python SDK: CRUD on registry entities (DNA, RNA, proteins, custom), inventory, ELN, workflow automation. Needs Benchling account and API key. Use biopython for local sequence analysis; pubchem for chemical DBs.
license
Apache-2.0
Benchling Integration — R&D Platform SDK
Overview
Benchling is a cloud platform for life sciences R&D. The Python SDK provides programmatic access to registry entities (DNA, proteins), inventory, electronic lab notebooks, and workflows. All operations require a Benchling tenant URL and API key or OAuth credentials.
When to Use
Creating, updating, or querying biological sequences (DNA, RNA, proteins) in Benchling registry
API rate limits: Benchling enforces per-tenant rate limits. The SDK automatically retries on 429 responses with exponential backoff (up to 5 retries by default). For bulk operations, add time.sleep(0.5) between batches.
Quick Start
from benchling_sdk.benchling import Benchling
from benchling_sdk.auth.api_key_auth import ApiKeyAuth
from benchling_sdk.models import DnaSequenceCreate
import os
benchling = Benchling(
url="https://your-tenant.benchling.com",
auth_method=ApiKeyAuth(os.environ["BENCHLING_API_KEY"])
)
# Create a DNA sequence
seq = benchling.dna_sequences.create(
DnaSequenceCreate(name="GFP-insert", bases="ATGGTGAGCAAGGGC", is_circular=False, folder_id="fld_abc123")
)
print(f"Created: {seq.name} ({seq.id})")
Core API
1. Registry — Entity CRUD
Registry entities include DNA sequences, RNA sequences, AA sequences, custom entities, and mixtures. All entity types follow the same create/read/update/archive pattern.
# Register entity in registry (with auto-generated ID)
registered = benchling.dna_sequences.create(
DnaSequenceCreate(
name="Production Plasmid",
bases="ATCGATCG",
is_circular=True,
folder_id="fld_abc123",
entity_registry_id="src_abc123",
naming_strategy="NEW_IDS"# or "IDS_FROM_NAMES"
)
)
print(f"Registry ID: {registered.entity_registry_id}")
# Entity types available via SDK:# benchling.dna_sequences, benchling.rna_sequences,# benchling.aa_sequences, benchling.custom_entities, benchling.mixtures
2. Registry — Listing and Pagination
All list operations return paginated generators for memory efficiency.
# List with pagination
sequences = benchling.dna_sequences.list()
total = sequences.estimated_count()
print(f"Total sequences: {total}")
for page in sequences:
for seq in page:
print(f" {seq.name} ({seq.id}): {len(seq.bases)} bp")
# Filter by schema
filtered = benchling.dna_sequences.list(schema_id="ts_abc123")
for page in filtered:
for seq in page:
print(f" {seq.name}")
3. Inventory Management
Manage physical samples, containers, boxes, and locations.
from benchling_sdk.models import ContainerCreate, BoxCreate
# Create container (sample tube)
container = benchling.containers.create(
ContainerCreate(
name="Sample Tube 001",
schema_id="cont_schema_abc123",
parent_storage_id="box_abc123",
fields=benchling.models.fields({"concentration": "100 ng/uL"})
)
)
print(f"Container: {container.id}, Barcode: {container.barcode}")
# Create box
box = benchling.boxes.create(
BoxCreate(
name="Freezer Box A1",
schema_id="box_schema_abc123",
parent_storage_id="loc_abc123"
)
)
# Transfer container to new location
benchling.containers.transfer(
container_id=container.id,
destination_id="box_xyz789"
)
print(f"Transferred {container.name} to new box")
4. Notebook Entries (ELN)
Create and manage electronic lab notebook entries.
Benchling entities use schema-defined custom fields. Always use the fields() helper:
# Correct: use fields() helper
fields = benchling.models.fields({
"concentration": "100 ng/uL",
"date_prepared": "2026-02-17",
"passage_number": 5
})
# Fields are typed by schema — string, number, date, entity link, dropdown
Pagination Pattern
All list() calls return paginated generators. Never call list() without iterating:
# Correct: iterate through pagesfor page in benchling.dna_sequences.list():
for item in page:
process(item)
# Get count without loading all data
count = benchling.dna_sequences.list().estimated_count()
Common Workflows
Workflow: Bulk Import from FASTA
import os, time
from Bio import SeqIO
from benchling_sdk.benchling import Benchling
from benchling_sdk.auth.api_key_auth import ApiKeyAuth
from benchling_sdk.models import DnaSequenceCreate
benchling = Benchling(
url="https://your-tenant.benchling.com",
auth_method=ApiKeyAuth(os.environ["BENCHLING_API_KEY"])
)
created = []
for record in SeqIO.parse("sequences.fasta", "fasta"):
seq = benchling.dna_sequences.create(
DnaSequenceCreate(
name=record.id,
bases=str(record.seq),
is_circular=False,
folder_id="fld_abc123",
fields=benchling.models.fields({
"description": record.description,
"source": "FASTA import"
})
)
)
created.append(seq.id)
time.sleep(0.5) # Rate limit complianceprint(f"Created: {record.id} -> {seq.id}")
print(f"Imported {len(created)} sequences")
Workflow: Inventory Audit Report
import os, csv
from benchling_sdk.benchling import Benchling
from benchling_sdk.auth.api_key_auth import ApiKeyAuth
benchling = Benchling(
url="https://your-tenant.benchling.com",
auth_method=ApiKeyAuth(os.environ["BENCHLING_API_KEY"])
)
audit = []
containers = benchling.containers.list(parent_storage_id="loc_freezer01")
for page in containers:
for c in page:
audit.append({
"id": c.id,
"name": c.name,
"barcode": c.barcode,
"location": c.parent_storage_id,
"created": str(c.created_at)
})
withopen("inventory_audit.csv", "w", newline="") as f:
writer = csv.DictWriter(f, fieldnames=audit[0].keys())
writer.writeheader()
writer.writerows(audit)
print(f"Audit complete: {len(audit)} containers")
Workflow: Automated QC Workflow
List pending workflow tasks: benchling.workflow_tasks.list(workflow_id=..., status="pending")
For each task, read associated entity via benchling.dna_sequences.get_by_id()
Run automated validation checks (sequence length, GC content, restriction sites)
Update task status to "complete" or "failed" via benchling.workflow_tasks.update()
Log results to a notebook entry via benchling.entries.create()
Key Parameters
Parameter
Function/Endpoint
Default
Options
Effect
folder_id
All create operations
Required
fld_...
Target folder for new entity
schema_id
All create operations
Optional
ts_..., cont_...
Schema defining custom fields
entity_registry_id
Entity registration
Optional
src_...
Registry to register entity in
naming_strategy
Entity registration
—
NEW_IDS, IDS_FROM_NAMES
How registry IDs are generated
parent_storage_id
Containers, boxes
Optional
box_..., loc_...
Storage location for inventory
max_retries
RetryStrategy
5
0–10
Number of retry attempts on failure
interval_wait_seconds
wait_for_task
2
1–60
Polling interval for async tasks
max_wait_seconds
wait_for_task
300
10–3600
Maximum wait for async completion
Best Practices
Always use environment variables for credentials: Never hardcode API keys. Use os.environ["BENCHLING_API_KEY"].
Use the fields() helper for custom schema fields: Raw dicts will not work — the SDK requires typed Fields objects.
Anti-pattern — loading all entities into memory: Use the paginated generator pattern. Never convert list() to a Python list for large datasets.
Add rate limit delays for bulk operations: Insert time.sleep(0.5) between create/update calls when processing >50 entities.
Use OAuth for production apps, API keys for scripts: API keys are user-scoped; OAuth allows app-level permissions and rotation.
Anti-pattern — using both entity_registry_id and naming_strategy: These are mutually exclusive on create. Use one or the other.
Handle BenchlingError explicitly: Catch SDK exceptions and log the status code and message for debugging.
Common Recipes
Recipe: Export Sequences by Schema
import csv
export = []
for page in benchling.dna_sequences.list(schema_id="ts_target_schema"):
for seq in page:
export.append({
"registry_id": seq.entity_registry_id,
"name": seq.name,
"length": len(seq.bases),
"bases": seq.bases[:50] + "..."iflen(seq.bases) > 50else seq.bases
})
withopen("sequences_export.csv", "w", newline="") as f:
writer = csv.DictWriter(f, fieldnames=export[0].keys())
writer.writeheader()
writer.writerows(export)
print(f"Exported {len(export)} sequences")
Recipe: Find Entities by Custom Field
# Search entities with specific field values# Note: SDK list() supports limited filtering; for complex queries use Data Warehouse
results = []
for page in benchling.custom_entities.list(schema_id="ts_cell_lines"):
for entity in page:
fields = entity.fields or {}
if fields.get("organism", {}).get("value") == "Human":
results.append(entity)
print(f"Found: {entity.name} ({entity.id})")
print(f"Total human cell lines: {len(results)}")
Recipe: Batch Archive Old Entities
import time
from datetime import datetime, timedelta
cutoff = datetime.now() - timedelta(days=365)
to_archive = []
for page in benchling.custom_entities.list():
for entity in page:
if entity.modified_at and entity.modified_at < cutoff:
to_archive.append(entity.id)
# Archive in batches
batch_size = 50for i inrange(0, len(to_archive), batch_size):
batch = to_archive[i:i+batch_size]
benchling.custom_entities.archive(ids=batch, reason="RETIRED")
print(f"Archived batch {i//batch_size + 1}: {len(batch)} entities")
time.sleep(1)
print(f"Total archived: {len(to_archive)}")
Troubleshooting
Problem
Cause
Solution
401 Unauthorized
Invalid or expired API key
Regenerate key in Benchling Profile Settings; check env var is set
403 Forbidden
Insufficient permissions
API key inherits user permissions; check user role in Benchling admin
404 Not Found
Wrong entity ID or tenant URL
Verify ID format (seq_, fld_, etc.); check tenant URL matches
429 Too Many Requests
Rate limit exceeded
SDK auto-retries; add time.sleep() between bulk operations
fields ignored on create
Using raw dict instead of fields() helper
Use benchling.models.fields({...}) for custom schema fields
naming_strategy error
Used with entity_registry_id
These are mutually exclusive — use one or the other
Pagination memory issues
Collecting all items into a list
Iterate page-by-page with for page in .list() pattern
OAuth token expired
Client credentials not refreshing
SDK handles refresh automatically; check client_id/secret are valid
Related Skills
biopython-molecular-biology — local sequence analysis (BLAST, alignment) before uploading to Benchling
opentrons-protocol-api — automate lab protocols that feed samples into Benchling inventory