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_kJ9zT2vQ5xDn8sLmP4wRbYcHgNfE1aUb
  • iosis_ - 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_kJ9zT2vQ5xDn8sLmP4wRbYcHgNfE1aUb

The Python client handles this automatically from the IOSIS_API_KEY environment variable:

from iosisclient import IosisClient

client = IosisClient()  # reads IOSIS_API_KEY from environment

Invalid 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 401 right 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.

BucketEndpointsLimit
GetGET endpoints (runs, charts, datasets, tsfns)20/min
RenderPOST /api/graphs/render10/min
RunsPOST /api/runs10/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 iosisclient

Requires Python 3.11+. The client module uses only stdlib; the package depends on iosislib.

Endpoints

All endpoints accept API key authentication unless noted otherwise.

MethodEndpointDescription
POST/api/runsSubmit a strategy run
GET/api/runs/:runIdGet a run with signed artifact URLs
GET/api/runs/:runId/chartsGet chart URLs for a run
GET/api/runs/chartsList succeeded runs with charts
POST/api/graphs/renderRender a strategy graph as SVG
GET/api/datasetsList published datasets
GET/api/datasets/lookupLook up a dataset by name/version
GET/api/datasets/manifestList datasets with full manifests
GET/api/tsfnsList allowed time-series functions
GET/api/schema/strategyStrategy 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.yaml

The 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.svg

The 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

StatusCause
400Malformed or empty strategy graph
401Missing or invalid API key
413Request body too large
415Non-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: prices

Look 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:

StatusMeaning
400Malformed request (invalid YAML, missing parameter, bad ID)
401Missing, invalid, or revoked API key
403Account not approved, workspace not active, or monthly credits exhausted
404Run or dataset not found
409Conflict (duplicate idempotency usage, strategy integrity failure)
413Request body too large
415Unsupported media type
429Rate limit exceeded (see Retry-After header)
500Internal 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.zscore

Fields

  • format - must be exactly iosis.strategy.
  • version - SemVer document contract version. Current: 0.1.0.
  • name - human-readable strategy name.
  • description and metadata - optional, no execution semantics.
  • nodes - maps stable identifiers to node declarations. Declaration order has no meaning.
  • outputs - gives public names to node.output references 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 from op.
  • params - operation-specific, JSON-compatible values.
  • inputs - maps the operation's input names to node.output references.
  • 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: float64

Supported 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.probability

Use the expanded form when an input needs consumer-owned behavior:

inputs:
  probability:
    from: prices.probability
    tolerance: 5m
    nulls: fill
    fill: 0.0

Expanded 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, or pass.
  • fill - scalar, required only when nulls is fill.

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) emit NaN predictions 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: float64

The 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: 10

The 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.zscore

5. 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: true

Putting 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.zscore

Tips

  • Browse available transforms with GET /api/tsfns or list_tsfns().
  • Validate strategies with render_graph() before submitting.
  • Use an Idempotency-Key header 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.

KindParametersDescription
signalsignal_column (default "signal")Generates orders proportional to the signal value.
thresholdsignal_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: zscore

Risk policy

Optional position sizing guardrails.

KindParametersDescription
fractional_limitfraction (in (0, 1])Limits position to a fraction of portfolio value.
fractional_kellycustom_fraction (in (0, 1])Kelly-optimal sizing with a fractional multiplier.
risk_policy:
  kind: fractional_limit
  fraction: 0.1

Full 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