Official typed Python SDK for the Jinkō clinical trial simulation platform (jinko.ai) — script models, trials, virtual populations, and results.
Project description
Jinkō Python SDK
Jinkō is a complete solution for clinical trial simulation and protocol design optimization, developed by Nova In Silico. It combines mechanistic ("white-box") modeling, virtual populations, in-silico trials, and analytics in a collaborative platform used by modelers, scientists, and trial managers to accelerate drug development and de-risk clinical decisions.
The Jinkō Python SDK is the official, typed Python client for the Jinkō API. It lets you script, automate, and integrate Jinkō workflows — from browsing project items and editing computational models, to running trials on virtual populations and retrieving simulation results — directly from Python.
Main features
- QSP model development — Create, edit, and version quantitative systems pharmacology models with typed APIs for parameters, events, reactions, and compartments
- Virtual population management — Work with patient populations and generators for trial simulation at scale
- In-silico trial orchestration — Design protocols, run simulations, and analyze outcomes to optimize trial designs before clinical execution
- Collaborative project navigation — Browse folders, search across model libraries, and manage versioned assets in team environments
- Results analytics — Extract simulation summaries, tabular data, and visualizations with optional pandas integration
- Programmatic workflows — Automate repetitive modeling tasks, batch operations, and integrate with existing R&D pipelines
Learn more about the platform at doc.jinko.ai.
Installation
pip install jinko-sdk
Requirements:
- Python 3.12+
- A Jinko API key
- A target Jinko project id
Setup and authentication
The SDK reads configuration from environment variables by default:
export JINKO_API_KEY="..."
export JINKO_PROJECT_ID="..."
export JINKO_BASE_URL="https://api.jinko.ai" # optional
For local developer convenience, keep secrets in a local .env and load them in your shell (for example with direnv allow if you use direnv).
You can also pass values explicitly:
from jinko import JinkoClient
client = JinkoClient(
api_key="...",
project_id="...",
base_url="https://api.jinko.ai", # optional
timeout=30.0,
)
Validate credentials early:
check = client.auth_check()
print(check.status, check.user_email)
Quickstart
from jinko import JinkoClient
client = JinkoClient()
model = client.get_model("cm-...")
print(model.name)
model.rename("Retuned PK model")
model.components.get_parameter("k_clearance").set_formula("CL / V")
trial = client.create_trial(model, name=f"{model.name} - smoke run")
trial.run()
trial.wait_until_completed(timeout=600, poll_interval=5.0)
summary = trial.results.summary()
print(summary.get("status"))
Core concepts
JinkoClient: primary user-facing entrypoint- Client direct methods:
client.get_model(...),client.list_trials(...),client.create_trial(...), ... - Domain wrappers (
Model,Trial,Vpop, ...): typed objects with behavior methods (for examplemodel.rename(...),trial.run()) types.ProjectItem: common typed metadata envelope (sid,type,core_id, folders, version, ...)Page[T]: paginatedlist()result (items,next_cursor,has_next)iter(): auto-paginated iterator for bulk traversal
Most user-facing resources follow this pattern:
list_<types>(...) -> Page[T]iter_<types>(...) -> Iterator[T]get_<type>(sid, revision=None)create_<type>(...)orcreate_raw_<type>(...)when availabledelete(sid)- rich object methods on returned items (
.versions,rename,set_description,run,wait_until_completed, ...)
The intended SDK usage is:
- enter through
client.<direct_method>(...) - continue through the typed object returned by that method
- use object-level services only when explicitly exposed (currently
model.components)
Public client surface
Project-wide services
client.folders: folder CRUD and folder-tree explorationclient.raw_request(...): authenticated low-level HTTP escape hatchclient.delete(sid): delete any project item by SID
Typed project item methods
client.list_models(...),client.iter_models(...),client.get_model(...)client.list_trials(...),client.iter_trials(...),client.get_trial(...)client.list_calibrations(...),client.iter_calibrations(...),client.get_calibration(...)client.list_output_sets(...),client.iter_output_sets(...),client.get_output_set(...)client.list_protocol_designs(...),client.iter_protocol_designs(...),client.get_protocol_design(...)client.list_scorings(...),client.iter_scorings(...),client.get_scoring(...)client.list_vpops(...),client.iter_vpops(...),client.get_vpop(...)client.list_vpop_generators(...),client.iter_vpop_generators(...),client.get_vpop_generator(...)client.list_assertions(...),client.iter_assertions(...),client.get_assertion(...)client.list_data_tables(...),client.iter_data_tables(...),client.get_data_table(...)client.list_documents(...),client.iter_documents(...),client.get_document(...)client.list_raw_files(...),client.iter_raw_files(...),client.get_raw_file(...)client.list_references(...),client.iter_references(...),client.get_reference(...)client.list_subsampling_designs(...),client.iter_subsampling_designs(...),client.get_subsampling_design(...)client.list_trial_visualizations(...),client.iter_trial_visualizations(...),client.get_trial_visualization(...)
Exploring a project
1) Start broad: project-items search
models_page = client.list_models(name="PK")
for model in models_page:
print(model.sid, model.type, model.name)
Use iter() when you want all pages:
all_trial_sids = [trial.sid for trial in client.iter_trials()]
2) Explore folders and folder trees
print(client.folders.tree())
root = client.folders.get_by_name("Program A", exact_match_only=True)
if root:
print(client.folders.tree(root=root, max_depth=2, include_project_items=True))
You can also list by folder:
modeling_folder = client.folders.get_by_name("Modeling", exact_match_only=True)
if modeling_folder:
models = client.list_models(folder=modeling_folder, limit=25)
3) Fetch typed resources and inspect content
model = client.get_model("cm-...")
content = model.content() # typed model interface
print(content.model.modelName)
Versioned resources
By default, get(sid) reads the latest revision.
latest = client.get_model("cm-...")
older = client.get_model("cm-...", revision=3)
List version metadata and label snapshots:
versions_page = latest.versions.list(only_labeled=False, first=20)
for version in versions_page:
print(version.revision, version.label, version.is_latest)
latest.versions.label(revision=3, label="baseline")
latest.versions.unlabel(revision=3)
Models and components
Model.components is the ergonomic typed API for editing components.
model = client.get_model("cm-...")
# Typed read
k_clearance = model.components.get_parameter("k_clearance")
# Immediate multi-field update in one API call
k_clearance.update(formula="CL / V", unit="L/h", description="retuned clearance")
# Typed create (immediate commit)
model.components.create_parameter(id="k_abs", formula=1.2, unit="1/h")
# Event updates are provided as a component->value mapping
model.components.create_event(
id="dose_start",
updates={"Dose": 100},
condition_trigger="t >= 0",
)
# Reaction stoichiometry is also a mapping
model.components.create_mass_action_reaction(
id="binding",
reactants={"Drug": 1, "Target": 1},
products={"Complex": 1},
k_plus="kon",
k_minus="koff",
)
When changing several components, batch commits avoid one-request-per-change:
with model.components.batch(version_name="retune") as batch:
batch.edit_parameter("k_clearance").set_formula("CL2 / V")
batch.create_parameter(id="k_new", formula=0.8, unit="1/h")
with model.components.batch(version_name="event and kinetics") as batch:
batch.edit_event("dose_start").set_updates({"Dose": 120})
batch.edit_reaction("binding").set_general_kinetics(
reactants={"Drug": 1, "Target": 1},
products={"Complex": 1},
rate="kon * Drug * Target - koff * Complex",
)
For unsupported modeling endpoints, use client.raw_request(...) rather than private object internals.
Trials and result retrieval
Create a trial from typed resources, run it, then consume results:
model = client.get_model("cm-...")
vpop = client.get_vpop("vp-...")
protocol = client.get_protocol_design("pd-...")
trial = client.create_trial(
model,
vpop=vpop,
protocol=protocol,
name="model-vpop-protocol run",
)
trial.run()
trial.wait_until_completed(timeout=1800)
summary = trial.results.summary()
scalars_csv = trial.results.scalars(["AUC", "Cmax"]) # TabularDownload
# Optional convenience if pandas is installed in your environment
df = scalars_csv.to_dataframe()
print(df.head())
Pagination patterns
list() returns a Page[T] with cursor metadata:
page = client.list_models(limit=20)
print(len(page), page.has_next, page.next_cursor)
if page.has_next:
next_page = client.list_models(limit=20, after=page.next_cursor)
Use iter() when you want to consume all pages seamlessly.
Raw API escape hatch
Use client.raw_request(...) when an endpoint is not yet wrapped by a typed service.
It reuses SDK authentication, project scoping, transport, and error handling.
payload = client.raw_request(
"GET",
"/app/v1/auth/check",
)
print(payload)
Example with params + JSON body:
result = client.raw_request(
"POST",
"/app/v1/project-item",
params={"first": 10},
json_body={"text": "tumor", "type": "Trial"},
headers={"X-My-Header": "value"},
)
Use this path for uncovered routes. Prefer typed SDK methods when available.
Error handling
The SDK raises typed exceptions from jinko, including:
ConfigurationErrorAuthenticationError,AuthorizationErrorNotFoundError,ValidationError,ConflictErrorRateLimitError,ServerError,TransportError
Minimal example:
from jinko import JinkoClient, NotFoundError
client = JinkoClient()
try:
client.get_model("cm-does-not-exist")
except NotFoundError as exc:
print(f"Model not found: {exc}")
Development tests
See tests/README.md.
License
This project is licensed under the MIT License. See the LICENSE file for details.
Contact & references
For support or inquiries, please contact us at oss@jinko.ai
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file jinko_sdk-1.2.0.tar.gz.
File metadata
- Download URL: jinko_sdk-1.2.0.tar.gz
- Upload date:
- Size: 109.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.17 {"installer":{"name":"uv","version":"0.11.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Alpine Linux","version":"3.23.4","id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0a6dce497f24c96fee514fd2dea587f917953e1008694800f10b4b8a6c52bd89
|
|
| MD5 |
f06246fbc0a2c9a9b83c84d72723ec96
|
|
| BLAKE2b-256 |
fd6f3869d680835d73bd514a2b53d2bc7e59baa29b959a9248ad82dfec2fcd05
|
File details
Details for the file jinko_sdk-1.2.0-py3-none-any.whl.
File metadata
- Download URL: jinko_sdk-1.2.0-py3-none-any.whl
- Upload date:
- Size: 144.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.17 {"installer":{"name":"uv","version":"0.11.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Alpine Linux","version":"3.23.4","id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a6d8875141e4c68c5ed091ce92d70987695a416a0b054e593c272b6d1d7420fc
|
|
| MD5 |
d03a88ade80c97dfe245cce54fbcdafe
|
|
| BLAKE2b-256 |
e578dc6ac74a963a723034628ea2a0c2df8fd79877a990924bf511bb8dba4157
|