Why ADMET Screening Matters
A molecule that binds perfectly to its target protein is worthless as a drug if the body cannot absorb it, if it is metabolized too quickly, if it accumulates in the wrong tissues, or if it is toxic. Roughly 90 percent of drug candidates fail in clinical trials, and poor pharmacokinetic (ADMET) properties are one of the top reasons.
Early ADMET screening catches these problems before you spend months and millions on synthesis and animal studies. By predicting absorption, distribution, metabolism, excretion, and toxicity computationally, you can filter out problematic compounds at the design stage and focus your experimental budget on the candidates most likely to succeed.
The Five ADMET Properties Explained
Absorption
Can the drug get into the bloodstream? Key properties include Caco-2 cell permeability (a proxy for intestinal absorption), human intestinal absorption (HIA), aqueous solubility, and P-glycoprotein substrate status (which affects efflux from cells).
Distribution
Where does the drug go once absorbed? Important properties include plasma protein binding (how much drug is bound to albumin vs. free), volume of distribution (Vd), and blood-brain barrier penetration (relevant for CNS drugs, and a red flag for non-CNS drugs).
Metabolism
How is the drug broken down? The main concern is cytochrome P450 (CYP) enzyme interactions. CYP inhibition can cause dangerous drug-drug interactions. CYP substrate status predicts which enzymes metabolize the compound and how quickly it is cleared.
Excretion
How fast is the drug eliminated? Half-life and clearance rate determine dosing frequency. Drugs cleared too quickly require frequent dosing; drugs cleared too slowly risk accumulation and toxicity.
Toxicity
Is the drug safe? Predictions include hERG channel inhibition (cardiac toxicity risk), AMES mutagenicity (cancer risk), hepatotoxicity (liver damage), and skin sensitization. A single positive toxicity flag can kill a drug candidate.
Prerequisites
You need Python 3.7+ and a SciRouter API key. Sign up at scirouter.ai/register for 500 free credits.
pip install scirouter pandasexport SCIROUTER_API_KEY="sk-sci-your-api-key-here"Step 1: Screen a Single Molecule
Start by screening one molecule to understand the API response format. Here we screen aspirin:
from scirouter import SciRouter
client = SciRouter()
# Screen aspirin
result = client.pharma.adme(smiles="CC(=O)Oc1ccccc1C(=O)O")
print("=== ADMET Profile for Aspirin ===")
print(f"Caco-2 permeability: {result.caco2_permeability:.2f} (log cm/s)")
print(f"Human intestinal absorption: {result.hia:.1f}%")
print(f"Plasma protein binding: {result.ppb:.1f}%")
print(f"BBB penetration: {'Yes' if result.bbb_penetrant else 'No'}")
print(f"CYP2D6 inhibitor: {'Yes' if result.cyp2d6_inhibitor else 'No'}")
print(f"CYP3A4 inhibitor: {'Yes' if result.cyp3a4_inhibitor else 'No'}")
print(f"Half-life: {result.half_life:.1f} hours")
print(f"hERG inhibitor: {'Yes' if result.herg_inhibitor else 'No'}")
print(f"AMES mutagenicity: {'Positive' if result.ames_positive else 'Negative'}")
print(f"Hepatotoxicity: {'Yes' if result.hepatotoxic else 'No'}")
print(f"Drug-likeness (QED): {result.qed:.2f}")Step 2: Screen a Batch of 1000 Molecules
For high-throughput screening, loop through a list of SMILES strings and collect results into a pandas DataFrame. Using concurrent requests keeps the total run time manageable:
import pandas as pd
from concurrent.futures import ThreadPoolExecutor, as_completed
from scirouter import SciRouter
from scirouter.exceptions import SciRouterError
client = SciRouter()
# Load molecules from a CSV file (one SMILES per row)
# For this example, we define a small set inline
molecules = pd.read_csv("candidates.csv") # columns: name, smiles
# Or define inline:
# molecules = pd.DataFrame({
# "name": ["aspirin", "ibuprofen", "caffeine"],
# "smiles": ["CC(=O)Oc1ccccc1C(=O)O", "CC(C)Cc1ccc(cc1)C(C)C(=O)O", "Cn1c(=O)c2c(ncn2C)n(C)c1=O"],
# })
def screen_one(name, smiles):
"""Screen a single molecule and return results as a dict."""
try:
r = client.pharma.adme(smiles=smiles)
return {
"name": name,
"smiles": smiles,
"caco2": r.caco2_permeability,
"hia": r.hia,
"ppb": r.ppb,
"bbb": r.bbb_penetrant,
"cyp2d6_inhibitor": r.cyp2d6_inhibitor,
"cyp3a4_inhibitor": r.cyp3a4_inhibitor,
"half_life": r.half_life,
"herg": r.herg_inhibitor,
"ames": r.ames_positive,
"hepatotoxic": r.hepatotoxic,
"qed": r.qed,
"error": None,
}
except SciRouterError as e:
return {"name": name, "smiles": smiles, "error": str(e)}
# Screen all molecules with 10 parallel workers
results = []
with ThreadPoolExecutor(max_workers=10) as pool:
futures = {
pool.submit(screen_one, row["name"], row["smiles"]): row["name"]
for _, row in molecules.iterrows()
}
for i, future in enumerate(as_completed(futures)):
result = future.result()
results.append(result)
if (i + 1) % 100 == 0:
print(f"Screened {i + 1}/{len(molecules)} molecules...")
df = pd.DataFrame(results)
print(f"\nScreened {len(df)} molecules. Errors: {df['error'].notna().sum()}")
df.to_csv("admet_results.csv", index=False)Step 3: Filter for Drug-Like Candidates
With ADMET predictions in hand, apply filters to identify the most promising compounds. A typical filter cascade removes molecules with toxicity flags, poor absorption, or unfavorable metabolism:
import pandas as pd
df = pd.read_csv("admet_results.csv")
print(f"Starting compounds: {len(df)}")
# Remove molecules with errors
df = df[df["error"].isna()]
print(f"After removing errors: {len(df)}")
# Filter 1: No toxicity flags
df_safe = df[
(df["herg"] == False) & # no cardiac toxicity risk
(df["ames"] == False) & # no mutagenicity
(df["hepatotoxic"] == False) # no liver toxicity
]
print(f"After toxicity filter: {len(df_safe)}")
# Filter 2: Good absorption
df_absorb = df_safe[
(df_safe["hia"] > 70) & # >70% intestinal absorption
(df_safe["caco2"] > -5.5) # reasonable Caco-2 permeability
]
print(f"After absorption filter: {len(df_absorb)}")
# Filter 3: Acceptable metabolism (not CYP inhibitor)
df_metab = df_absorb[
(df_absorb["cyp2d6_inhibitor"] == False) &
(df_absorb["cyp3a4_inhibitor"] == False)
]
print(f"After metabolism filter: {len(df_metab)}")
# Filter 4: Drug-likeness
df_final = df_metab[df_metab["qed"] > 0.4]
print(f"After drug-likeness filter: {len(df_final)}")
# Rank by QED score
df_final = df_final.sort_values("qed", ascending=False)
print(f"\nTop 10 candidates:")
print(df_final[["name", "smiles", "qed", "hia", "half_life"]].head(10).to_string())
df_final.to_csv("drug_candidates.csv", index=False)
print(f"\nSaved {len(df_final)} candidates to drug_candidates.csv")Understanding the Results: A Practical Guide
Here is how to interpret key ADMET values in practice:
- Caco-2 permeability above -5.15 log cm/s: Good intestinal permeability. Below -6.0 is poor.
- HIA above 80%: Well absorbed orally. Below 30% suggests the molecule may need alternative delivery (injection, inhaled).
- Plasma protein binding above 95%: Highly bound, less free drug available. May need higher doses.
- hERG positive: Potential cardiac toxicity. This is a hard red flag — most programs will drop these compounds.
- AMES positive: Mutagenic potential. Another hard red flag for most therapeutic areas.
- Half-life 4 to 12 hours: Ideal range for once or twice daily dosing. Below 2 hours is challenging.
- QED above 0.5: Reasonable drug-likeness. The average approved drug scores around 0.6.
Combining ADMET with Other Screens
ADMET screening is most powerful when combined with other computational filters. Here is a typical multi-stage screening funnel:
from scirouter import SciRouter
client = SciRouter()
# Stage 1: Check drug-likeness with molecular properties
props = client.chemistry.properties(smiles="CC(=O)Oc1ccccc1C(=O)O")
if props.molecular_weight > 500 or props.logp > 5:
print("FAIL: Violates Lipinski rules")
# Stage 2: ADMET screening
admet = client.pharma.adme(smiles="CC(=O)Oc1ccccc1C(=O)O")
if admet.herg_inhibitor or admet.ames_positive:
print("FAIL: Toxicity flag")
# Stage 3: Dock against target (if ADMET passes)
# dock = client.docking.diffdock(protein_pdb=pdb, ligand_smiles=smiles)
# if dock.poses[0].confidence < 0.5:
# print("FAIL: Poor binding prediction")
print("PASS: Candidate for experimental validation")Performance and Throughput
Here is what to expect for batch screening at different scales:
- 100 molecules: About 1 minute with 10 parallel workers
- 1,000 molecules: About 5 to 10 minutes with 10 parallel workers
- 10,000 molecules: About 50 to 100 minutes with 10 parallel workers (Pro tier recommended)
- 100,000+ molecules: Contact SciRouter for enterprise batch processing
Each ADMET prediction costs 1 credit. Free tier accounts receive 500 credits per month. For large screening campaigns, the Pro tier provides higher rate limits and bulk pricing.
Exporting Results for Downstream Analysis
The CSV output from the screening pipeline integrates with standard cheminformatics workflows. Load it into DataWarrior for SAR analysis, feed it into Spotfire for visualization, or use it in a Jupyter notebook with RDKit for further filtering:
import pandas as pd
import matplotlib.pyplot as plt
df = pd.read_csv("admet_results.csv")
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
# Distribution of drug-likeness scores
axes[0].hist(df["qed"].dropna(), bins=30, color="#2563eb", edgecolor="white")
axes[0].set_xlabel("QED Score")
axes[0].set_ylabel("Count")
axes[0].set_title("Drug-Likeness Distribution")
axes[0].axvline(x=0.4, color="red", linestyle="--", label="Cutoff")
axes[0].legend()
# HIA vs Caco-2
axes[1].scatter(df["caco2"], df["hia"], alpha=0.3, s=10, color="#2563eb")
axes[1].set_xlabel("Caco-2 Permeability (log cm/s)")
axes[1].set_ylabel("Human Intestinal Absorption (%)")
axes[1].set_title("Absorption Properties")
# Toxicity flag distribution
tox_counts = pd.DataFrame({
"Flag": ["hERG", "AMES", "Hepatotoxic"],
"Count": [df["herg"].sum(), df["ames"].sum(), df["hepatotoxic"].sum()],
})
axes[2].bar(tox_counts["Flag"], tox_counts["Count"], color=["#ef4444", "#f59e0b", "#8b5cf6"])
axes[2].set_ylabel("Flagged Molecules")
axes[2].set_title("Toxicity Flags")
plt.tight_layout()
plt.savefig("admet_overview.png", dpi=150)
print("Saved admet_overview.png")Next Steps
Now that you can screen molecules for ADMET properties at scale, integrate it into a complete drug discovery workflow. Use ADMET Prediction for pharmacokinetic screening, combine with DiffDock molecular docking to evaluate binding, and check Lipinski drug-likeness rules for basic property filtering.
For an automated pipeline that chains target prediction, molecular generation, docking, and ADMET screening into a single workflow, explore SciRouter Labs — our end-to-end drug discovery platform.
Sign up at scirouter.ai/register for 500 free credits and start screening molecules today.