API documentation
The Iosis API lets you submit strategy runs, fetch results, render graphs, and inspect datasets programmatically. All endpoints accept API key authentication via a bearer token.
Authentication
API keys are workspace-scoped credentials used to authenticate programmatic access. Create and manage keys in the Account page (/app/account). A key is shown in full only once at creation - copy it immediately and store it as IOSIS_API_KEY.
Key format
iosis_3f9c2a71_kJ9zT2vQ5xDn8sLmP4wRbYcHgNfE1aUbiosis_- fixed prefix identifying an Iosis API key.- Next 8 hex characters - the key prefix, used to look the key up server-side.
- Remainder - the secret token. Only the prefix is ever shown in the UI again.
Using a key
Send the key as a bearer token in the Authorization header on every request:
Authorization: Bearer iosis_3f9c2a71_kJ9zT2vQ5xDn8sLmP4wRbYcHgNfE1aUbThe Python client handles this automatically from the IOSIS_API_KEY environment variable:
from iosisclient import IosisClient
client = IosisClient() # reads IOSIS_API_KEY from environmentInvalid key response
Missing, invalid, or revoked keys return 401:
{
"error": "invalid_api_key",
"message": "API key is invalid."
}Security
- Treat a key like a password - it grants full API access to the workspace it belongs to.
- Never commit keys to source control, print them in logs, or embed them in client-side code.
- Store keys in an environment variable or secret manager.
- There is a limit of 1 key per workspace. Revoke unused keys instead of accumulating them.
- Revoking a key takes effect immediately - requests using it fail with
401right away.
Rate limits and credits
API key requests are rate limited per key in a fixed one-minute window. Limits apply independently of the credit allowance.
| Bucket | Endpoints | Limit |
|---|---|---|
| Get | GET endpoints (runs, charts, datasets, tsfns) | 20/min |
| Render | POST /api/graphs/render | 10/min |
| Runs | POST /api/runs | 10/min |
Over-limit requests return 429 with a Retry-After header:
{
"error": "rate_limit_exceeded",
"message": "Rate limit exceeded.",
"retry_after_seconds": 12
}Credits
Compute is metered in credits. 1 credit = 6 seconds of worker compute. Runs served from the result cache cost zero credits. Credits are a per-workspace monthly allowance - the free plan includes a small default, paid tiers include their plan allowance.
When the month's allowance is exhausted, POST /api/runs returns:
{
"error": "insufficient_credits",
"message": "Monthly credit allowance exhausted."
}Check your current credits and usage in the Account page (/app/account).
Installation
Install the Python client from PyPI:
pip install iosisclientRequires Python 3.11+. The client module uses only stdlib; the package depends on iosislib.
Endpoints
All endpoints accept API key authentication unless noted otherwise.
| Method | Endpoint | Description |
|---|---|---|
POST | /api/runs | Submit a strategy run |
GET | /api/runs/:runId | Get a run with signed artifact URLs |
GET | /api/runs/:runId/charts | Get chart URLs for a run |
GET | /api/runs/charts | List succeeded runs with charts |
POST | /api/graphs/render | Render a strategy graph as SVG |
GET | /api/datasets | List published datasets |
GET | /api/datasets/lookup | Look up a dataset by name/version |
GET | /api/datasets/manifest | List datasets with full manifests |
GET | /api/tsfns | List allowed time-series functions |
GET | /api/schema/strategy | Strategy JSON Schema (v0.1.0) |
Runs
Submit a run
curl -X POST https://iosis.dev/api/runs \
-H "Authorization: Bearer $IOSIS_API_KEY" \
-H "Content-Type: application/yaml" \
-H "Idempotency-Key: $(uuidgen)" \
--data-binary @strategy.yamlThe request body is a strategy YAML document (the iosis.strategy format, version 0.1.0). A malformed document returns 400.
Always send an Idempotency-Key header (a UUID) to make submission idempotent. Reusing the same key returns the original run instead of creating a duplicate. The Python client generates one automatically.
A successful submission returns 202 Accepted:
{
"id": "8f3c…",
"status": "queued"
}Get a run
curl https://iosis.dev/api/runs/8f3c… \
-H "Authorization: Bearer $IOSIS_API_KEY"Returns the run status (queued, running, succeeded, or failed), compute time, result summary, and short-lived signed URLs to result artifacts.
Artifact URLs expire after 60 seconds for results and 5 minutes for charts. Download them immediately - do not cache the URLs.
A completed run returns:
{
"run": {
"id": "8f3c…",
"status": "succeeded",
"computeMs": 150,
"resultSummary": { "graph_id": "…", "charts": ["…"] },
"result": {
"kind": "result",
"name": "result.parquet",
"url": "https://…/result.parquet?X-Amz-…",
"sha256": "…",
"expiresAt": "2026-08-20T…Z"
},
"artifacts": ["…"]
}
}If the run failed, status is failed and run.error explains why.
Wait for a run
The Python client includes a polling helper with exponential backoff:
result = client.wait_for_run(run["id"])
print(result) # {"run": {"status": "succeeded", ...}}Get charts for a run
curl https://iosis.dev/api/runs/8f3c…/charts \
-H "Authorization: Bearer $IOSIS_API_KEY"Returns signed URLs to the run's chart artifacts. URLs expire after 5 minutes.
List runs with charts
GET https://iosis.dev/api/runs/charts
Authorization: Bearer <key>Returns recently succeeded runs that produced charts.
Graphs
Render a graph
curl -X POST https://iosis.dev/api/graphs/render \
-H "Authorization: Bearer $IOSIS_API_KEY" \
-H "Content-Type: application/yaml" \
--data-binary @strategy.yaml \
--output graph.svgThe request body is a strategy YAML document - the same shape accepted by POST /api/runs. The response renders the strategy graph as an SVG in the app's ReactFlow visual language: nodes, ports, and edges are laid out server-side, so no browser is required. Unknown operations render as generic nodes, so the endpoint works with any valid strategy YAML.
A successful render returns 200 with Content-Type: image/svg+xml. The SVG uses the dark theme with a transparent background: node bodies are #101010 with warm borders (model nodes get a blue #4E7CFF border), edge strokes are red #E31520, and input/output handles sit on the node edges.
Error responses
| Status | Cause |
|---|---|
400 | Malformed or empty strategy graph |
401 | Missing or invalid API key |
413 | Request body too large |
415 | Non-YAML content type |
Datasets
List published datasets
curl https://iosis.dev/api/datasets \
-H "Authorization: Bearer $IOSIS_API_KEY"Returns the names of published datasets.
Any name returned here can be used directly as the name parameter in a source.dataset node (see Strategy Format below). For example, if list_datasets() returns "prices", you can reference it with:
nodes:
prices:
op: source.dataset
version: 0.1.0
params:
name: pricesLook up a dataset
curl "https://iosis.dev/api/datasets/lookup?name=<name>" \
-H "Authorization: Bearer $IOSIS_API_KEY"name- required.
Returns the dataset manifest: glob path, row count, resolution, coverage window, and schema.
List datasets with manifests
curl https://iosis.dev/api/datasets/manifest \
-H "Authorization: Bearer $IOSIS_API_KEY"Returns every published dataset with its full manifest:
{
"datasets": [
{
"name": "prices",
"manifest": {
"format": "iosis.cloud-dataset-v1",
"path": "s3://.../datasets/prices/**/*.parquet",
"row_count": 1000000,
"resolution": "1h",
"time_range": { "start": "...", "end": "..." },
"schema": {
"time": "timestamp",
"columns": { "close": "float64" }
}
}
}
]
}Capabilities
List allowed TSFNs
curl https://iosis.dev/api/tsfns \
-H "Authorization: Bearer $IOSIS_API_KEY"Returns the catalog of time-series functions (TSFNs) allowed in strategy YAML, generated from the iosislib built-in registry. Each entry carries the operation name and version, category (transform, source, model, backtest), parameters (name, type, required flag, default), null/lookahead policy, and the input/output frame signature.
{
"format": "iosis.tsfn-catalog",
"version": "0.1.0",
"tsfns": [
{
"op": "transform.logit",
"version": "0.1.0",
"category": "transform",
"parameters": [],
"signature": {
"input": { "columns": [["value", "Float64"]] },
"output": { "columns": [["logit", "Float64"]] }
}
}
]
}Sources and transforms whose signature depends on data (e.g. source.dataset_source, backtest.backtest) resolve to "signature": null. To reference an allowed op in a strategy, use op + version as op@version.
Errors
All endpoints return errors as JSON with an error code and a human-readable message:
| Status | Meaning |
|---|---|
400 | Malformed request (invalid YAML, missing parameter, bad ID) |
401 | Missing, invalid, or revoked API key |
403 | Account not approved, workspace not active, or monthly credits exhausted |
404 | Run or dataset not found |
409 | Conflict (duplicate idempotency usage, strategy integrity failure) |
413 | Request body too large |
415 | Unsupported media type |
429 | Rate limit exceeded (see Retry-After header) |
500 | Internal error |
The Python client raises IosisError with .status, .code, and .message attributes:
from iosisclient import IosisError
try:
client.get_run("bad-id")
except IosisError as e:
print(e.status) # 400
print(e.code) # "invalid_run_id"
print(e.message) # "Run ID must be a valid UUID."Strategy format
Strategies are YAML documents conforming to the iosis.strategy format, version 0.1.0. Each strategy declares nodes (sources, transforms, models, backtests) and outputs.
Fetch the JSON Schema to validate strategies programmatically:
curl https://iosis.dev/api/schema/strategy \
-H "Authorization: Bearer $IOSIS_API_KEY"format: iosis.strategy
version: 0.1.0
name: prices-signal
description: Rolling z-scores of close-to-close returns on the prices dataset.
nodes:
prices:
op: source.dataset
version: 0.1.0
params:
name: prices
schema:
time: timestamp
columns:
close: float64
volume: float64
forward_return: float64
returns:
op: transform.pct_change
version: 0.1.0
inputs:
close: prices.close
params:
input_column: close
output_column: returns
zscore:
op: transform.rolling_z_score
version: 0.1.0
inputs:
returns: returns.returns
params:
input_column: returns
output_column: zscore
periods: 20
min_samples: 10
outputs:
close: prices.close
zscore: zscore.zscoreFields
format- must be exactlyiosis.strategy.version- SemVer document contract version. Current:0.1.0.name- human-readable strategy name.descriptionandmetadata- optional, no execution semantics.nodes- maps stable identifiers to node declarations. Declaration order has no meaning.outputs- gives public names tonode.outputreferences and determines which nodes belong to the strategy.
Node structure
op- stable operation contract name (e.g.transform.logit,source.dataset).version- SemVer operation-contract version, separate fromop.params- operation-specific, JSON-compatible values.inputs- maps the operation's input names tonode.outputreferences.materialize- optional boolean. Omission lets the operation contract choose.
source.dataset params
For source.dataset nodes, the name param accepts a dataset name from GET /api/datasets or list_datasets(). The platform resolves the name to the actual storage location automatically.
name- dataset name (e.g.prices).schema- declares the time column and value columns with their dtypes.
Schema parameter format
The schema parameter tells the platform about your data's shape. It requires a time key naming the timestamp column, and a columns key mapping column names to dtypes.
schema:
time: timestamp
columns:
close: float64
volume: float64Supported dtypes: bool, float32, float64, int32, int64, string.
For array columns (required by backtest nodes), use the shaped form:
schema:
time: timestamp
columns:
bid:
dtype: float64
shape: [8]Input forms
The short form is normally enough:
inputs:
probability: prices.probabilityUse the expanded form when an input needs consumer-owned behavior:
inputs:
probability:
from: prices.probability
tolerance: 5m
nulls: fill
fill: 0.0Expanded input fields:
from- required source reference.tolerance- non-negative number or Polars-style duration string (e.g.5m). Omission means unbounded backward as-of match.nulls-error,propagate,drop,fill, orpass.fill- scalar, required only whennullsisfill.
Identifiers and references
Identifiers begin with a letter and contain only letters, digits, _, or -. Dots are reserved as the separator in references (e.g. prices.close).
Outputs
The outputs map declares the final result columns. The result.parquet file contains the frame of the alphabetically-first declared output. Every declared output and materialized node is also rendered as an SVG chart.
Notes
- Walk-forward models (e.g.
model.light_gbm@0.3.0) emitNaNpredictions until the first retrain boundary. Exclude those rows before computing metrics over the full series.
Strategy construction guide
This guide walks through building a strategy from scratch. A strategy is a directed acyclic graph of nodes: sources load data, transforms process it, and outputs declare which columns to return.
1. Pick a dataset
Call GET /api/datasets or list_datasets() to see available datasets. Each entry has a name. Pick one that matches your use case.
2. Declare a source node
Reference the dataset by name using source.dataset. The platform resolves the name to the actual storage location automatically.
nodes:
prices:
op: source.dataset
version: 0.1.0
params:
name: prices
schema:
time: timestamp
columns:
close: float64
volume: float64The schema parameter declares your data shape. Use time to name the timestamp column, and columns to map column names to dtypes.
3. Add transforms
Compute derived columns by chaining transforms. Each transform takes inputs from previous nodes and produces new outputs.
returns:
op: transform.pct_change
version: 0.1.0
inputs:
close: prices.close
params:
input_column: close
output_column: returns
zscore:
op: transform.rolling_z_score
version: 0.1.0
inputs:
returns: returns.returns
params:
input_column: returns
output_column: zscore
periods: 20
min_samples: 10The inputs map connects this node to upstream outputs. The key is the input name expected by the operation; the value is a node.output reference.
4. Declare outputs
The outputs map declares which columns appear in the resultparquet file. Reference node outputs as node.output.
outputs:
close: prices.close
zscore: zscore.zscore5. Optional: add a model
For machine learning workflows, add a model node that consumes features and produces predictions. Walk-forward models emit NaN until the first retrain boundary.
model:
op: model.light_gbm
version: 0.3.0
inputs:
features: zscore.zscore
params:
target_column: forward_return
materialize: truePutting it together
format: iosis.strategy
version: 0.1.0
name: rolling-zscore
description: Rolling z-scores of close-to-close returns.
nodes:
prices:
op: source.dataset
version: 0.1.0
params:
name: prices
schema:
time: timestamp
columns:
close: float64
returns:
op: transform.pct_change
version: 0.1.0
inputs:
close: prices.close
params:
input_column: close
output_column: returns
zscore:
op: transform.rolling_z_score
version: 0.1.0
inputs:
returns: returns.returns
params:
input_column: returns
output_column: zscore
periods: 20
min_samples: 10
outputs:
close: prices.close
zscore: zscore.zscoreTips
- Browse available transforms with
GET /api/tsfnsorlist_tsfns(). - Validate strategies with
render_graph()before submitting. - Use an
Idempotency-Keyheader to make submission idempotent.
Backtest reference
The backtest.backtest operation runs a signal-driven backtest over historical data. It requires three declarative sub-mappings: feed, policy, and optionallyrisk_policy. Use kind to select the implementation.
Feed
Describes the market data source. Currently only l1 is supported.
feed:
kind: l1
venue:
name: venue_a
universe: [AAPL, MSFT]
bid_column: bid # optional, default "bid"
ask_column: ask # optional, default "ask"Policy
Controls order execution logic.
| Kind | Parameters | Description |
|---|---|---|
signal | signal_column (default "signal") | Generates orders proportional to the signal value. |
threshold | signal_column, threshold (default 0), long_qty (default 1), short_qty (default 1) | Generates fixed-size orders when signal exceeds threshold. |
policy:
kind: signal
signal_column: zscoreRisk policy
Optional position sizing guardrails.
| Kind | Parameters | Description |
|---|---|---|
fractional_limit | fraction (in (0, 1]) | Limits position to a fraction of portfolio value. |
fractional_kelly | custom_fraction (in (0, 1]) | Kelly-optimal sizing with a fractional multiplier. |
risk_policy:
kind: fractional_limit
fraction: 0.1Full backtest example
nodes:
prices:
op: source.dataset
version: 0.1.0
params:
name: prices
schema:
time: timestamp
columns:
bid: { dtype: float64, shape: [8] }
ask: { dtype: float64, shape: [8] }
signal: float64
backtest:
op: backtest.backtest
version: 0.1.0
inputs:
bid: prices.bid
ask: prices.ask
signal: prices.signal
params:
feed:
kind: l1
venue:
name: venue_a
universe: [AAPL, MSFT]
policy:
kind: signal
signal_column: signal
outputs:
pnl: backtest.pnl