> ## Documentation Index
> Fetch the complete documentation index at: https://docs.strandai.com/llms.txt
> Use this file to discover all available pages before exploring further.

# SDK quickstart

> Create a Strand API key and run the H&E-to-spatial-proteomics workflow from Python or R.

This guide covers the developer path through upload, side-effect-free pricing,
submission, waiting, and result download. For the standard browser workflow,
start with the [Platform quickstart](/quickstart).

<Warning>
  **For research use only.** Strand AI predictions are model outputs intended
  for research and hypothesis generation. They are not validated for, and must
  not be used in, clinical diagnosis, treatment selection, or patient care
  decisions.
</Warning>

## 1. Create an API key

Sign in to [app.strandai.com](https://app.strandai.com), select the organization
your integration should use, and open [Settings, API
keys](https://app.strandai.com/settings/api-keys). Choose **Create key** and
copy the `sk-strand-…` value when it appears. The full value is shown only once.

```bash theme={"dark"}
export STRAND_API_KEY=sk-strand-XXXXXXXXXXXXXXXXXXXXXXXX
```

<Tip>
  Treat the API key like a password. It is scoped to the selected organization
  and uses that organization's sample access and credit balance.
</Tip>

## 2. Install a client

<CodeGroup>
  ```bash Python theme={"dark"}
  pip install "strand-sdk[anndata]"
  ```

  ```r R theme={"dark"}
  install.packages(
    "strandai",
    repos = c("https://strand-ai.r-universe.dev", "https://cloud.r-project.org")
  )
  BiocManager::install("SpatialExperiment")
  ```
</CodeGroup>

## 3. Upload and price the run

<CodeGroup>
  ```python Python theme={"dark"}
  import time

  from strand import Client

  client = Client()  # reads STRAND_API_KEY
  upload = client.uploads.upload_file("biopsy.svs", mpp=0.26)
  markers = ["CD8", "PanCK", "Ki67"]

  # Automatic ingest preprocessing has started. Optional de-identification may
  # also run when enabled for the organization. Wait for prediction readiness.
  while True:
      sample = client.samples.get(upload.id)
      if sample.ownership == "mine" and sample.status == "ready":
          break
      if sample.ownership == "mine" and sample.status.endswith("failed"):
          raise RuntimeError(f"ingest failed: {sample.status}")
      time.sleep(2)

  estimate = client.predict.submit(upload.id, markers, dry_run=True)
  print(estimate.estimated_credits, estimate.org_balance)
  ```

  ```r R theme={"dark"}
  library(strandai)

  client <- strand_client()  # reads STRAND_API_KEY
  upload <- strand_upload_file(client, "biopsy.svs", progress = TRUE, mpp = 0.26)
  markers <- c("CD8", "PanCK", "Ki67")

  # Automatic ingest has started, but the sample may not be prediction-ready.
  repeat {
    sample <- strand_samples_get(client, upload$id)
    if (identical(sample$status, "ready")) break
    if (grepl("failed$", sample$status)) stop("ingest failed: ", sample$status)
    Sys.sleep(2)
  }

  estimate <- strand_predict(client, upload$id, markers, dry_run = TRUE)
  message(estimate$estimated_credits, " credits; balance ", estimate$org_balance)
  ```
</CodeGroup>

`strand_upload_file()` returns after the storage event starts ingest, so the
readiness loop is required before an immediate prediction. Omit `mpp` when the
slide's embedded calibration is authoritative. A dry run validates the exact
request and reports its price without creating a job or reserving credits.

## 4. Submit and wait

<CodeGroup>
  ```python Python theme={"dark"}
  job = client.predict.submit(upload.id, markers)
  status = job.wait(timeout=1800)
  print(status.status)
  ```

  ```r R theme={"dark"}
  job <- strand_predict(client, upload$id, markers)
  status <- strand_job_wait(job, timeout = 1800, progress = TRUE)
  message(status$status)
  ```
</CodeGroup>

Submission reserves credits atomically. The job continues server-side if the
client disconnects.

## 5. Download results

<CodeGroup>
  ```python Python theme={"dark"}
  adata = job.download_results()
  job.download_export("ome-tiff", "result.ome.tiff", timeout=1800)
  ```

  ```r R theme={"dark"}
  spe <- strand_download_results(job)
  strand_export_download(job, "ome-tiff", "result.ome.tiff")
  ```
</CodeGroup>

The in-memory Python result is an `AnnData`. The R result is a
`SpatialExperiment`. Both contain the requested marker channels aligned to the
slide coordinate system.

## Next steps

<CardGroup cols={2}>
  <Card title="Python SDK" icon="python" href="/sdks/python">
    Typed samples, jobs, events, exports, and selective result reads.
  </Card>

  <Card title="R SDK" icon="r-project" href="/sdks/r">
    `strand_*` functions and Bioconductor result handling.
  </Card>

  <Card title="Command line" icon="terminal" href="/sdks/cli">
    Run the workflow from a shell without writing a script.
  </Card>

  <Card title="REST API" icon="rectangle-terminal" href="/api/overview">
    Use the canonical HTTP contract directly.
  </Card>
</CardGroup>
