API Reference
Documentation
Physon exposes a small, sharp REST API. Every endpoint requires an X-API-Key header. Get one for free at /signup.
Base URL
https://sharoncs888--physon-mvp-api.modal.run
Authentication
Every request needs an X-API-Key header. Keys are per-account, rotatable via the dashboard.
curl -X POST https://sharoncs888--physon-mvp-api.modal.run/molecular/generate \
-H "Content-Type: application/json" \
-H "X-API-Key: physon_YOUR_KEY_HERE" \
-d '{"smiles": "CC(=O)Oc1ccccc1C(=O)O", "n_conformers": 20}'Endpoints
/auth/signupGet an API key
Sign up with an email address. Returns an existing key if the email already has one.
curl -X POST https://sharoncs888--physon-mvp-api.modal.run/auth/signup \
-H "Content-Type: application/json" \
-d '{"email": "you@example.com"}'
# Response
{
"email": "you@example.com",
"api_key": "physon_...",
"plan": "free",
"daily_limit": 20
}/molecular/prepareTautomer + charge state enumeration
Prepare a SMILES for docking-ready conformer generation. Returns physiological-pH protonation states + canonical tautomers. Feed each state to /molecular/generate.
curl -X POST https://sharoncs888--physon-mvp-api.modal.run/molecular/prepare \
-H "Content-Type: application/json" \
-H "X-API-Key: physon_..." \
-d '{"smiles": "CC(=O)Oc1ccccc1C(=O)O",
"include_tautomers": true,
"include_charges": true,
"max_tautomers": 3}'
# Response
{
"smiles": "CC(=O)Oc1ccccc1C(=O)O",
"n_states": 4,
"states": [
{"smiles": "CC(=O)Oc1ccccc1C(=O)[O-]",
"kind": "neutralized+protonated",
"weight": 0.25},
...
],
"model": "physon-prep"
}/molecular/generateGenerate 3D conformers
Distribution-aware conformer generation. Auto-dispatches based on molecule type — catalog molecules go to the diffusion arch, macrocycles to CSD-augmented ETKDG.
curl -X POST https://sharoncs888--physon-mvp-api.modal.run/molecular/generate \
-H "Content-Type: application/json" \
-H "X-API-Key: physon_..." \
-d '{"smiles": "CC(=O)Oc1ccccc1C(=O)O",
"n_conformers": 20,
"seed": 42}'
# Response fields
{
"smiles": "...",
"n_conformers": 20,
"conformers": [{"xyz": [[x,y,z], ...],
"atoms": ["C", "H", ...]}],
"inference_ms": 425,
"model": "physon-molecular"
}
# model values you'll see:
# physon-molecular — diffusion arch (catalog molecule)
# physon-molecular-ring — diffusion + ring pucker diversity
# physon-macrocycle-hybrid — CSD-augmented ETKDG for macrocycles
# physon-molecular-dev — MOCK fallback (unsupported SMILES)/molecular/generate/batchBatch conformer generation
Generate ensembles for up to 50 SMILES per request. Each SMILES counts as one quota unit; a 50-SMILES batch uses 1 quota unit.
curl -X POST https://sharoncs888--physon-mvp-api.modal.run/molecular/generate/batch \
-H "Content-Type: application/json" \
-H "X-API-Key: physon_..." \
-d '{"items": [
{"smiles": "CC(=O)Oc1ccccc1C(=O)O", "n_conformers": 10},
{"smiles": "CCCCCCC", "n_conformers": 10}
]}'/molecular/propertiesPhysicochemical properties + ensemble geometry
Lipinski Rule-of-5 (MW, log P, TPSA, HBD/HBA), plus ensemble radius-of-gyration and bounding box computed across a generated conformer set.
curl -X POST https://sharoncs888--physon-mvp-api.modal.run/molecular/properties \
-H "Content-Type: application/json" \
-H "X-API-Key: physon_..." \
-d '{"smiles": "CC(=O)Oc1ccccc1C(=O)O", "n_conformers": 10}'/molecular/catalogList supported molecules
Public catalog of SMILES with real (non-MOCK) inference. Any SMILES outside the catalog returns MOCK or the macrocycle-hybrid mode depending on structure.
curl https://sharoncs888--physon-mvp-api.modal.run/molecular/catalog
/3d/generate3D voxel scene generation
Physon.3D — physics-valid 3D scene evolution. See /3d for available presets.
curl -X POST https://sharoncs888--physon-mvp-api.modal.run/3d/generate \
-H "Content-Type: application/json" \
-H "X-API-Key: physon_..." \
-d '{"preset_name": "bouncing_sphere", "horizon": 8}'Rate limits
- Per-key daily quota — see pricing
- Per-IP: 30 inference calls / minute (all plans)
- Per-IP: 5 signups / hour (abuse mitigation)
- Batch endpoints count as 1 quota unit per request, regardless of item count
Error responses
401 — missing or invalid X-API-Key
400 — invalid SMILES, malformed request body, or unsupported preset
429 — rate limit or daily quota exceeded
500 — internal error; response includes request_id for support tickets
Docking pipeline integration
Physon's SDF output is docking-ready — MMFF energies + rank + provenance tags are already in the file. Drop straight into AutoDock Vina, Schrödinger Glide, or OpenEye FRED.
End-to-end: SMILES → Physon → Vina
Python example. Assumes you have AutoDock Vina installed (brew install autodock-vina or apt), a receptor prepared as receptor.pdbqt, and a grid box defined.
import httpx, subprocess, tempfile
from rdkit import Chem
from rdkit.Chem import AllChem
API = "https://sharoncs888--physon-mvp-api.modal.run"
KEY = "physon_YOUR_KEY_HERE"
SMILES = "CC(=O)Oc1ccccc1C(=O)O" # aspirin
# 1. Ask Physon for a docking-ready SDF (ranked by MMFF energy)
r = httpx.post(
f"{API}/molecular/generate/download",
headers={"X-API-Key": KEY, "Content-Type": "application/json"},
json={"smiles": SMILES, "n_conformers": 20, "format": "sdf"},
timeout=60.0,
)
r.raise_for_status()
sdf_text = r.text
# 2. Convert SDF → PDBQT (Vina input format) via RDKit + Meeko or obabel
with tempfile.NamedTemporaryFile(suffix=".sdf", delete=False) as f:
f.write(sdf_text.encode())
sdf_path = f.name
pdbqt_path = sdf_path.replace(".sdf", ".pdbqt")
subprocess.run([
"obabel", sdf_path, "-O", pdbqt_path, "--gen3d",
], check=True)
# 3. Dock
subprocess.run([
"vina",
"--receptor", "receptor.pdbqt",
"--ligand", pdbqt_path,
"--config", "vina_config.txt", # grid box definition
"--out", "docked.pdbqt",
"--exhaustiveness", "8",
], check=True)
# 4. Parse Vina scores — best pose is first
with open("docked.pdbqt") as f:
for line in f:
if line.startswith("REMARK VINA RESULT:"):
score = float(line.split()[3])
print(f"Vina score: {score:.2f} kcal/mol")
breakSchrödinger Glide (LigPrep input)
Physon SDF drops straight into LigPrep. Skip conformer generation in LigPrep — Physon already produced them.
# 1. Fetch the SDF as above → aspirin.sdf
# 2. Skip LigPrep's conformer step (Physon already generated + ranked):
$SCHRODINGER/ligprep -inp physon.sdf -omae aspirin_prepped.mae \
-nc 0 \
-bff 16 \
-epik # ionization + tautomerization
# 3. Glide docking (grid + config as usual):
$SCHRODINGER/glide docking.in -HOST localhost -NJOBS 4Complete workflow — tautomer prep → conformer gen → rank → dock
This is the full enterprise flow. Prepare ionization states, generate conformer ensembles per state, rank by cluster, take medoids into docking.
import httpx
API = "https://sharoncs888--physon-mvp-api.modal.run"
KEY = "physon_YOUR_KEY_HERE"
SMILES = "CC(=O)Oc1ccccc1C(=O)O"
# Step 1 — enumerate protonation states + tautomers
prep = httpx.post(
f"{API}/molecular/prepare",
headers={"X-API-Key": KEY},
json={"smiles": SMILES, "include_tautomers": True, "include_charges": True},
).json()
print(f"{prep['n_states']} docking-ready states")
# Step 2 — rank conformer families per state, keep medoids
all_medoids = []
for state in prep["states"]:
ranked = httpx.post(
f"{API}/molecular/rank",
headers={"X-API-Key": KEY},
json={"smiles": state["smiles"], "n_conformers": 20,
"rmsd_threshold": 0.75},
).json()
for cluster in ranked["clusters"]:
all_medoids.append({
"smiles": state["smiles"],
"state_kind": state["kind"],
"state_weight": state["weight"],
"cluster_id": cluster["cluster_id"],
"cluster_size": cluster["size"],
"medoid_energy_kJ_mol": cluster["medoid_energy_kJ_mol"],
"medoid_xyz": cluster["medoid_xyz"],
"medoid_atoms": cluster["medoid_atoms"],
})
print(f"{len(all_medoids)} unique conformers to dock")
# → feed all_medoids into your docking pipeline
# → weight scores by state_weight for population-corrected affinityEnterprise plans include curated integrations for KNIME, Pipeline Pilot, and Databricks. Talk to us at /enterprise.
Python SDK — coming soon
First-party Python client with async support, retries, and typed responses. Meanwhile, httpx or requests work fine — every endpoint is plain JSON.