This page as a plain-text brief for an AI coding assistant: the endpoints, the units, the TSV columns and the rules to follow. Give the file to the assistant together with what you need (for example, “download every binding pocket with a pocket RMSD above 2 Å, with its structure files”) and it can write the download code.
Download smartflexdb_agent_prompt.txt ↓A runnable Jupyter notebook: loads /api/pairs into a
DataFrame, summarizes the geometry columns and fetches one full record. Set BASE in the
first code cell, then run all.
import requests BASE = "https://aidd.rc.ufl.edu/app/smartflexdb" # hosted service PAIR = "1EI2_NMY_A_B_26_pocket" # a binding-pocket id (see /api/pairs)
smartflexdb_pairs.tsv: one row per
pocket-level comparison (one binding pocket against one apo structure): 1,187 rows, 32 columns in
schema_version 12 (column list).
smartflexdb_pairs.meta.json
defines every column. Derived data are
CC-BY-4.0;
atomic coordinates come from the RCSB PDB.
import pandas as pd
df = pd.read_csv(f"{BASE}/downloads/smartflexdb_pairs.tsv", sep="\t")
print(len(df)) # 1187 pocket-level comparisons
print(df.groupby(["holo_pdb", "apo_pdb"]).ngroups) # 734 entries
| Unit | Count | Where |
|---|---|---|
binding pocket — one ligand site, measured against its own apo_pdb |
434 | /api/pairs (one record each), /api/pair/{id} |
| pocket-level comparison — one binding pocket against one apo structure | 1,187 | one TSV row; the pocket-level CSV of a Database page download |
| entry — one (holo structure, apo structure) pair | 734 | Database page cards and entry-level CSV |
| holo structure — one holo PDB ID | 266 | Database page “Apo-Holo Relationship” view, /api/classification |
23 further (holo, apo) pairings failed our sequence check.
Neither the site nor the TSV includes them; /api/pairs still lists them in
apo_alts, flagged seq_qualified: false.
id is the entry id
<binding pocket id>__apo_<APO PDB>, the binding pocket’s
own apo structure included. /api/pair/{id} and /api/bundle/{id} accept it.
Group on (holo_pdb, apo_pdb) for the 734 entries; count distinct binding
pocket ids for the 434 binding pockets.ligand, lig_resseq,
pocket_rmsd, pocket_size, hbonds, mw,
logp and the two motif columns are the binding pocket’s own. rmsd,
host_rmsd, host_rmsd_local, tm_score and rg_delta
belong to the host strand (holo_chain), so binding pockets on one strand of one entry
share them.pandas.read_csv(path, sep="\t"). Header, in file order: id, holo_pdb,
holo_chain, lig_auth_chain, ligand, lig_resseq, apo_pdb, apo_chain, rna_type, method,
resolution, identity, global_identity, holo_len, apo_len, num_chains, has_protein, has_dna, rmsd,
host_rmsd, host_rmsd_local, tm_score, pocket_rmsd, pocket_size, hbonds, mw, logp, rg_delta,
pocket_motifs, pocket_motifs_fine, apo_qualified, holo_rna_chain.| Column | Meaning (host chain = the RNA chain the ligand sits on) |
|---|---|
id | the entry id: the binding pocket the row’s values come from and the row’s apo structure (see above) |
holo_pdb, apo_pdb | PDB IDs of the holo and the apo structure |
holo_chain | host RNA chain; identical to holo_rna_chain, the
last column, which repeats it under the row key’s name |
lig_auth_chain | chain the ligand is deposited on (equal to
holo_chain on every row) |
apo_chain | apo chain paired with holo_chain |
ligand | CCD code of this binding pocket’s ligand |
lig_resseq | ligand residue number |
rna_type | Functional type of the host RNA, from NAKB (“Unannotated” if none) |
method, resolution | experimental method; resolution in Å (empty for NMR) |
identity | local sequence identity (%), BLAST pident over the aligned region; ≥ 90 on every row |
global_identity | global identity (%),
Needleman–Wunsch
over the full declared (SEQRES) sequences; every mismatch and gap counts. Both identities are
integers measured on lig_auth_chain, so 100 can mean ≥ 99.5. Per-chain values (one
decimal) and the raw counts are on /api/pair/{id}: seq_identity_chains,
global_identity_chains, global_identity_detail |
holo_len, apo_len | RNA length (nt) of the host chain and of the apo chain |
num_chains | RNA chains within 8 Å of the ligand |
has_protein, has_dna | holo structure contains a protein / DNA chain (False on every row: the database is RNA-only) |
rmsd | C3′ RMSD (Å) over the binding-site RNA chains matched to an apo chain, after one superposition fitted on them |
host_rmsd | host chain only, in that same superposition |
host_rmsd_local | host chain fitted on its own; ≤ host_rmsd,
and the gap shows the chains moving relative to each other |
tm_score | US-align TM-score (0–1) of the host chain against the apo chain, normalised by the holo chain |
pocket_rmsd | C3′ RMSD (Å) over the binding-site residues (≤ 8 Å of the ligand, all chains) |
rg_delta | radius of gyration, holo − apo (Å), over the same
residues as rmsd |
pocket_size | binding-site residues (≤ 8 Å) |
hbonds | binding-site residues hydrogen-bonded to the ligand |
mw, logp | ligand molecular weight (Da) and logP (RDKit) |
pocket_motifs | x3dna-dssr motif types of the host chain that overlap the binding site,
"; "-joined, every N-way junction written junction: the values
the Database “Pocket motif” filter uses. Empty = no motif overlaps. |
pocket_motifs_fine | the same, with the junction type kept
(4-way junction) |
apo_qualified | sequence check of this row’s apo: qualified
(all 1,187 rows) or unjudged (not decided, not a refusal; 0 rows). A refused apo has no
row |
holo_rna_chain | = holo_chain (see above) |
GET /api/pairs
→ {"pairs": [ … ]}All 434 binding-pocket records, each measured against its own apo_pdb: IDs, ligand,
method, resolution, Functional type, the RMSD family, TM-score, Rg, H-bond count, ligand
MW/logP. apo_alts lists the other apo structures the pocket is paired with, each with its
own apo-side values. Each record and each apo_alts item carries its
entry_id (<binding pocket id>__apo_<APO PDB>).
Start here.
pairs = requests.get(f"{BASE}/api/pairs").json()["pairs"]
print(len(pairs)) # 434 binding pockets
print(pairs[0]["id"], pairs[0]["pocket_rmsd"], pairs[0]["tm_score"])
# the Database page's entries: every (holo, apo) pair once, without the refused apo structures
entries = {(p["holo_pdb"], p["apo_pdb"]) for p in pairs}
entries |= {(p["holo_pdb"], a["pdb"]) for p in pairs
for a in p["apo_alts"] if a["seq_qualified"] is not False}
print(len(entries)) # 734
GET /api/pair/{id}
→ full pair objectEverything the pair page shows for one binding pocket: sequences and 2D (holo_2d,
apo_2d), per-residue displacement, RNA–ligand interactions (interactions3d),
DSSR base pairs and motifs, ligand properties (lig_props), pocket_dyn,
geom and per-chain RMSD. Returns 404 for an unknown id.
Path · id - a binding-pocket id from /api/pairs, or an
entry id {id}__apo_{pdb} (entry_id in /api/pairs), which
selects that apo structure.
Query · apo=<pdb> - the same selection. {id}__apo_{pdb}, the older
{id}__alt_{pdb} and {id}?apo={pdb} are the same request. A value the record
does not carry is a 400, never a silent fall back to the record’s own apo.
With another apo structure · the apo-side fields (apo_pdb, rmsd,
displacement, bp_rewire, seq_identity / global_identity
with the per-chain global_identity_chains / global_identity_detail) describe
the selected apo; qc, apo_dssr_pairs, apo_clean and
apo_pocket_ligand are null:
they are measured against the record’s own apo_pdb only.
pair = requests.get(f"{BASE}/api/pair/{PAIR}").json()
print(pair["holo_2d"]["seq"]) # holo sequence
print(pair["pocket_dyn"]["pocket_rmsd"]) # pocket C3′ RMSD
other = requests.get(f"{BASE}/api/pair/2L8H_L8H_A_C_2_pocket__apo_7JU1").json()
print(other["apo_pdb"], other["rmsd"]) # 7JU1 3.9
GET /api/bundle/{id}
→ application/zipOne binding pocket against one apo structure as a ZIP: pair.json (the full record, with its
entry_id), holo and apo mmCIF, the superposed apo (pocket-trimmed, exactly the overlay the
viewer draws), ligand.sdf and contacts.csv.
Path / Query · an entry id {id}__apo_{pdb} or
apo=<pdb>, as for /api/pair/{id}. The ZIP is named after the request:
{id}__apo_<pdb>.zip, or {id}__alt_<pdb>.zip for apo=.
z = requests.get(f"{BASE}/api/bundle/{PAIR}").content
open(f"{PAIR}.zip", "wb").write(z)
GET /api/classification
→ cluster + t-SNE JSONSequence, structure and pocket clusters of the 266 holo structures, with t-SNE coordinates, for redundancy-controlled train/test splits. One row per holo structure, taken from its binding pocket with the most binding-site residues.
Sequence · MMseqs2, ≥ 70 % identity over ≥ 80 % coverage.
Structure (host chain) and pocket (residues ≤ 8 Å) ·
RMalign
(Zheng et al., 2019) RMscore ≥ 0.75, single linkage.
Fields · pairs[] = {id, holo_pdb, apo_pdb, apo_pdbs, ligand, rna_type,
seq_cluster, struct_cluster, pocket_cluster}. seq_apo_cluster /
struct_apo_cluster / pocket_apo_cluster merge the clusters that share an apo
structure, so a split on them keeps each apo structure on one side.
cls = requests.get(f"{BASE}/api/classification").json()
print(cls["n_pairs"]) # 266 holo structures
print(cls["pairs"][0]) # ids, ligand, rna_type and the cluster IDs
POST /api/query
→ {"job_id": …}Multipart form: apo and holo (mmCIF, ≤30 MB each) and
ligand — a HET code (e.g. CNY) or, for a ligand with several copies,
one copy as LIG:CHAIN:RESSEQ (e.g. CNY:C:41). Poll
GET /api/query/{job_id}/status until state is done (or
error, with a message), then fetch the result from
GET /api/query/{job_id}/pair, in the /api/pair format. A validation failure is
a 400 with the message in detail.
import time
files = {"apo": open("apo.cif", "rb"), "holo": open("holo.cif", "rb")}
r = requests.post(f"{BASE}/api/query", files=files, data={"ligand": "CNY:C:41"})
r.raise_for_status()
job_id = r.json()["job_id"]
while requests.get(f"{BASE}/api/query/{job_id}/status").json()["state"] not in ("done", "error"):
time.sleep(2)
pair = requests.get(f"{BASE}/api/query/{job_id}/pair").json()