Query runs, metrics, and objects
Use the SDK query API for bounded notebook and automation reads.
Use im.Api(...) when a script or notebook needs to query runs, fetch bounded metric series, inspect logged objects, or download artifacts — everything a training script's write path does not cover.
Prerequisites
- An API key with a read scope such as
export:readfor tenant read routes.
Create an API helper
import instantml as im
api = im.Api(
base_url="https://api.instantml.ai",
api_key="instantml_...",
)Query run summaries
runs = api.query_runs(
project="cartpole",
q="tag:baseline status:finished",
sort_by="metric-best",
metric_key="eval/return_mean",
limit=25,
)
for run in runs.items:
print(run["id"], run.get("name"))Accepted filters:
| Parameter | Meaning |
|---|---|
project | Project name. Omit or use all for all projects. |
status | running, finished, failed, or all. |
q | Shared run search query. Bare text remains implicit AND; field filters, exact tags/status, ID prefixes, uppercase booleans, phrases, grouping, field/group exclusion such as -tag:debug, and explicit Rust re:/.../ regex are supported. |
sort_by | created, name, status, duration, metric-latest, or metric-best. |
metric_key | Metric used for metric sorts. |
limit | Page size, 1..100. |
cursor | Cursor returned by the previous Page. |
max_pages | Number of pages to read into one Page; defaults to 1. |
query_runs() returns Page(items, next_cursor, raw, limit) and preserves the server's order exactly. Use iter_runs() when you want lazy pagination:
for run in api.iter_runs(project="cartpole", q="tag:candidate", page_size=50, max_pages=2):
print(run["id"])Useful q examples:
seed 13
tag:baseline status:finished
name:"long context" -tag:debug
(tag:baseline OR tag:candidate) notes:ablated
re:/seed-(13|14)/Fetch metric series
series = api.query_metrics(
[run["id"] for run in runs.items],
["eval/return_mean", "train/loss"],
step_min=0,
step_max=1000,
point_limit=1000,
buckets=400,
)
for item in series.series:
print(item["run_id"], item["key"], len(item["metrics"]))query_metrics() accepts 1..100 run IDs and 1..25 keys. The SDK calls the bounded metric-series route once per key, preserves caller run and key order, omits empty run/key pairs, and keeps each run/key bounded by point_limit before server-side effective caps. buckets requests the same M4 downsampling used by high-density dashboard charts.
Metric timestamp windows are not exposed by the current API route. The SDK accepts the future-facing parameters but raises ValueError if time_min, time_max, or x_axis="time" are used, so notebooks do not silently receive a different slice than requested.
Query rich objects
objects = api.query_objects(
project="cartpole",
kind="table",
key="eval/samples",
limit=25,
)
rows = api.object_rows(objects.items[0]["id"], limit=100)query_objects() uses the cross-run GET /api/objects/explorer route when the backend exposes it. On older backends it falls back only when you pass a single run_id:
objects = api.query_objects(run_id="run-id", kind="table", key="eval/samples")The SDK does not emulate cross-run object search by paging every matching run. That keeps notebook code from accidentally turning a small query into an unbounded fan-out.
Download artifact bytes
path = api.download_artifact(
"artifact-id",
"checkpoints/policy.pt",
)
print(path)The helper creates parent directories and returns the written path. It is the same restore primitive used by checkpoint resume snippets in the dashboard.
Stay inside read bounds
InstantML keeps list and series reads bounded:
- Run summary and rich-object pages default to 100 rows.
- Raw metric history is requested through metric-specific series endpoints.
- Metric query inputs are capped at 100 run IDs and 25 metric keys.
- Table row reads are capped at 1,000 rows per request.
- Artifact lists are capped.
- Export responses include truncation metadata when limits are hit.
If you need a bulk data movement path, use export or an importer/exporter flow instead of repeatedly fetching every chart series from a notebook.
Write to an existing run
Use im.attach_run(...) when a resume script should continue logging into an existing run, such as a checkpoint fork created by the dashboard or Api.fork_run(...).
run = im.attach_run("child-run-id")
run.log({"train/loss": 0.1}, step=1001)
run.finish()By default, attach_run(validate=True) reads /runs/:id before returning a run handle. That validation needs read access such as export:read. Use validate=False only for intentionally write-only credentials or offline attach flows.