Skip to main content
The R SDK (strandai) provides the same surface as the Python SDK with idiomatic R conventions: all functions are prefixed strand_* and results can be returned as a SpatialExperiment for downstream Bioconductor pipelines.
The package strandai avoids a CRAN clash with the unrelated strand package.

Install

# From r-universe (when published):
install.packages(
  "strandai",
  repos = c("https://strand-ai.r-universe.dev", "https://cloud.r-project.org")
)

# Or directly from GitHub (bleeding edge):
remotes::install_github("Strand-AI/strand-sdk-r")
To get results as a SpatialExperiment (recommended) you’ll also need:
BiocManager::install("SpatialExperiment")

Configuration

SourceArgument / variableDefault
Argstrand_client(api_key = ...)reads env
EnvSTRAND_API_KEYrequired
EnvSTRAND_BASE_URLhttps://app.strandai.com
library(strandai)
client <- strand_client()                       # reads STRAND_API_KEY
client <- strand_client(api_key = "sk-strand-…") # explicit

End-to-end

upload <- strand_upload_file(client, "biopsy.svs", progress = TRUE)

est <- strand_estimate(client, upload$id, c("CD8", "PanCK", "Ki67"))
message("will cost ~", est$estimated_credits, " credits")

job <- strand_predict(client, upload$id, c("CD8", "PanCK", "Ki67"))
status <- strand_job_wait(job, progress = TRUE)

spe <- strand_download_results(job)   # SpatialExperiment

Selecting a model

strand_predict() and strand_run() accept an optional model =. Live Lattice versions:
  • "v0.4": 192-marker panel, original training.
  • "v0.5": 192-marker panel, retrained (current default).
Both share the same GenePT embeddings, so the marker vocabulary is identical between them. Picking a version swaps the model weights while keeping the same vocabulary. Omit model (or pass NULL) to let the platform pick the current default ("v0.5").
result <- strand_run(
  client, "slide.svs",
  markers = c("CD8", "Ki67", "PanCK"),
  model = "v0.5"
)
The earlier "v10", "v10-fullpanel", and "v10-fullpanel-v2" names are still accepted as deprecated aliases with a warning(); pin the canonical v0.X id to silence the warning and forward-protect against the 2026-12-01 alias sunset. The "v10" alias resolved to the now-sunset v0.3 and is rejected by the server with 400 unknown_model.

Submit without waiting

A full pipeline run blocks for 15+ minutes. To kick a job off and continue with other work, pass wait = FALSE, and strand_run() returns the strand_job handle as soon as upload + submit complete:
job <- strand_run(client, "slide.svs", c("CD3", "CD8"), wait = FALSE)
message("submitted ", job$id)

# ...do other work, or shut down the process. The job runs server-side.

# Later (same process or a fresh one via `strand_job_get(client, job$id)`):
status <- strand_job_wait(job)
spe <- strand_download_results(job)
When wait = FALSE, the "wait" and "download" progress stages don’t fire, and timeout_sec / poll_interval_sec / output_dir are ignored.

Function reference

FunctionWhat it does
strand_client(api_key, base_url)Construct a client handle.
strand_upload_file(client, path, progress)Resumable chunked upload of a WSI to GCS.
strand_estimate(client, upload_id, markers)Compute credit cost without reserving.
strand_predict(client, upload_id, markers, model)Submit a prediction; reserves credits.
strand_run(client, image_path, markers, model, wait, ...)Full pipeline; wait = FALSE returns a strand_job after submit.
strand_job_get(client, job_id)Refetch a job snapshot.
strand_job_wait(job, progress, timeout)Block until terminal status.
strand_download_results(job)Return results as SpatialExperiment.

Error handling

Documented HTTP error codes map to typed conditions you can dispatch on:
HTTPCondition class
400strand_bad_request_error
401strand_auth_error
402strand_insufficient_credits_error (carries required)
404strand_not_found_error
429strand_rate_limit_error (carries retry_after)
tryCatch(
  strand_predict(client, upload$id, markers),
  strand_insufficient_credits_error = function(e) {
    message("need ", e$required, " credits; top up the org first.")
  },
  strand_rate_limit_error = function(e) {
    message("wait ", e$retry_after, "s before retrying.")
  }
)

See also