Skip to main content

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

SDK documentation: doc.jinko.ai/sdk. Learn more about the platform at doc.jinko.ai.

Installation

pip install jinko-sdk

Optional integrations are installed as extras:

pip install "jinko-sdk[pandas]"  # DataFrame helpers
pip install "jinko-sdk[pdf]"     # Native PDF quote extraction

Requirements:

  • Python 3.11+
  • A Jinko API key, for one project or for an organization
  • A target Jinko project id, unless you start from an organization key

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,
)

When a credential is neither passed explicitly nor provided through the environment, the SDK prompts for it:

client = JinkoClient()

Validate credentials and the key's declared scope for the configured project:

check = client.auth_check()
print(check.status, check.api_key.role)

client.auth_check() raises AuthenticationError when the project is outside the key's declared scope. A successful check does not prove that the project exists or that its resources are accessible. Session responses without API key metadata receive a credential check only. Request a project resource to verify access.

Organization API keys

JINKO_API_KEY holds either a project key or an organization key. An organization key reaches several projects. JinkoOrgClient needs no project id and accepts both kinds of key:

from jinko import JinkoOrgClient

org = JinkoOrgClient()  # reads JINKO_API_KEY and JINKO_BASE_URL
check = org.auth_check()  # validates credentials without a project ID
print(check.status, check.api_key.scope)
for project in org.list_projects():
    print(project.id, project.name)

client = org.project(project.id)  # a regular JinkoClient for that project
client.search(limit=5)

list_projects() accepts a name and a group id to narrow that list. The same client creates a project in a group, and lists, reads, creates, and renames groups.

The list shows the projects visible to the key owner, which can be more than the key reaches. A project key reaches only its own project. Call client.auth_check() on the returned project client to check its declared key scope before accessing resources. SingleProject and MultipleProjects keys must include the project in check.api_key.project_ids. AllProjects has no fixed project list, but the key owner must still have access to the project.

JinkoClient() works with both kinds of key when JINKO_PROJECT_ID names a project in the key scope.

Command-line tools

The SDK ships a set of jinko.cli command-line tools for common workflows — tagging model components, creating protocol designs or Vpops, inspecting project items, and more. Each previews its changes by default; pass --apply after reviewing the plan.

python -m jinko.cli.tag_model_components --model-sid cm-... --tag clearance=i::vpop
python -m jinko.cli.create_vpop_from_csv --csv patients.csv --apply
python -m jinko.cli.inspect_protocol_design --protocol-design-sid pd-... --summary

Each tool is also installed as a console script (for example jinko-tag-model-components) if its directory is on PATH.

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 example model.rename(...), trial.run())
  • types.ProjectItemMetadata: common typed metadata envelope (sid, type, core_id, folders, version, ...)
  • Page[T]: paginated list() 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>(...) or create_raw_<type>(...) when available
  • delete(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 (for example model.components)

Folder handling follows a single-folder public interface:

  • public filters and create methods accept folder= with either a Folder object or folder id string
  • project-item wrappers expose .folder for the common single-folder case
  • .folders remains available for full API compatibility when an existing item belongs to multiple folders
  • use move_to_folder(folder) as the public folder mutator to place an item in one folder, and move_to_folder(None) to clear folder assignments

Public client surface

Project-wide services

  • client.list_folders(...), client.get_folder(...), client.get_folder_by_name(...), client.create_folder(...): folder retrieval and creation; rename, move, delete, or list the items of a folder through the returned Folder
  • client.delete_folder(folder): delete a folder, given a Folder or a folder ID, with its descendant folders and their project items; use it when you only hold the ID, and folder.delete() when the Folder is already resolved
  • client.folder_tree(...), client.folder_tree_nodes(...), client.folder_tree_dict(...): project-wide folder-tree exploration
  • client.raw_request(...): authenticated low-level HTTP escape hatch
  • client.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_simple_output_sets(...), client.iter_simple_output_sets(...), client.get_simple_output_set(...)
  • client.list_protocol_designs(...), client.iter_protocol_designs(...), client.get_protocol_design(...)
  • client.list_advanced_output_sets(...), client.iter_advanced_output_sets(...), client.get_advanced_output_set(...)
  • client.list_vpops(...), client.iter_vpops(...), client.get_vpop(...)
  • client.list_vpop_designs(...), client.iter_vpop_designs(...), client.get_vpop_design(...)
  • client.list_extracts(...), client.iter_extracts(...), client.get_extract(...)
  • 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.find_references_by_doi(doi): the non-deleted references with a DOI, compared without case, a doi: prefix, or a doi.org URL prefix
  • 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

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.folder_tree())

root = client.get_folder_by_name("Program A", exact_match_only=True)
if root:
    print(root.tree(max_depth=2, include_project_items=True))
    reports = root.find_child("Reports", exact_match_only=True)
    children = root.children()
    tree_data = root.tree_dict(include_project_items=False)
    tree_nodes = root.tree_nodes(include_project_items=False)

For a project-wide structured forest:

tree_nodes = client.folder_tree_nodes()
tree_data = client.folder_tree_dict()

You can also list by folder:

modeling_folder = client.get_folder_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)

folder = model.folder
all_api_folders = model.folders

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="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="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:

  • ConfigurationError
  • AuthenticationError, AuthorizationError
  • NotFoundError, ValidationError, ConflictError
  • RateLimitError, 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

From the repository root, run just test-sdk-unit for unit tests, just test-sdk-e2e for live e2e tests, or just test-sdk-all for both. See tests/README.md for live-test setup.

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

Metadata

Release files for jinko-sdk 1.15.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for jinko-sdk 1.15.1
File Size Uploaded Provenance Actions
jinko_sdk-1.15.1.tar.gz 606.0 kB Without provenance Details for jinko_sdk-1.15.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for jinko-sdk 1.15.1
File Interpreter ABI Platform Actions
jinko_sdk-1.15.1-py3-none-any.whl Python 3 none any Details for jinko_sdk-1.15.1-py3-none-any.whl

Total release size: 1.3 MB

Release files / jinko_sdk-1.15.1.tar.gz

Download URL jinko_sdk-1.15.1.tar.gz
Size 606.0 kB
Tags Source
SHA-256 checksum
How to use checksums
a8768dfa92c190963419d8816ebeeb54475fae1e6ddb95cf30a8bdebbeb000e2
BLAKE2b-256 checksum
How to use checksums
62e52a623e0a36d79e5e532826e74744340af1218d8be0e61dc9c24d1b07e3e6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.22 {"installer":{"name":"uv","version":"0.12.22","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Alpine Linux","version":"3.24.2","id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / jinko_sdk-1.15.1-py3-none-any.whl

Download URL jinko_sdk-1.15.1-py3-none-any.whl
Size 650.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
76e51f82eef52f7b89d6997d4a055f174382dd46672e9c30118e6e4fb632a236
BLAKE2b-256 checksum
How to use checksums
f4d67d6e143e776f9df8fc8c9ceeb66b36212a6934490bb08d1900c4d0c24af8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.22 {"installer":{"name":"uv","version":"0.12.22","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Alpine Linux","version":"3.24.2","id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for jinko-sdk 1.15.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

1.15.1 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page