Skip to main content

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.

The Python SDK wraps the REST API and adds:
  • a resumable chunked uploader for large WSIs,
  • typed dataclasses for requests and responses,
  • SSE streaming for live job status,
  • and an optional AnnData round-trip for results.
The source lives in sdks/python/strand-sdk/. See the Quickstart for the end-to-end happy path.

Install

pip install strand-sdk
# with bioinformatics extras (AnnData / zarr) for downloading results:
pip install "strand-sdk[anndata]"
Requires Python 3.10+.

Configuration

SourceVariable / argumentDefault
EnvSTRAND_API_KEYrequired
EnvSTRAND_BASE_URLhttps://app.strandai.com
ArgClient(api_key=..., base_url=..., timeout=..., max_retries=...)n/a
from strand import Client

client = Client()                      # picks up STRAND_API_KEY from env
client = Client(api_key="sk-strand-…") # explicit

Uploads

upload = client.uploads.upload_file(
    "biopsy.svs",
    progress=lambda done, total: print(f"{done}/{total}"),
)
print(upload.id, upload.width_px, upload.height_px)
upload_file runs the three-step resumable flow under the hood (initiate → chunked PUT to GCS → complete). Chunks are 8 MiB by default and can be tuned via chunk_size.

Predict

# Optional cost estimate before submitting.
est = client.predict.estimate(upload.id, markers=["CD8", "PanCK", "Ki67"])
print(est.estimated_credits, est.org_balance)

# Submit the job. Reserves credits atomically.
job = client.predict.submit(upload.id, markers=["CD8", "PanCK", "Ki67"])
MethodWhat it does
client.predict.estimate(upload_id, markers)Compute credit cost. No reservation.
client.predict.submit(upload_id, markers)Reserve credits and enqueue. Returns a Job.

Jobs

# Block until terminal (uses SSE under the hood, falls back to polling):
status = job.wait(timeout=1800)

# Or stream events yourself:
for event in job.stream_events():
    print(event.status, event.progress)

# Download results as AnnData (requires the `anndata` extra):
adata = job.download_results()

# Or write the raw OME-Zarr store to disk:
path = job.download_results(path="./results-zarr")
MethodWhat it does
job.refresh()Refetch the latest JobStatus snapshot.
job.wait(timeout=...)Block until completed or failed.
job.stream_events()Iterate over SSE JobEvents.
job.download_results()Returns an AnnData (default) or writes to a path.

Errors

All errors inherit from StrandError. Typed subclasses map HTTP codes 1:1 so you can except precisely:
from strand import (
    InsufficientCreditsError, RateLimitError,
    JobFailedError, JobTimeoutError,
)

try:
    job = client.predict.submit(upload.id, markers=["CD8"])
    job.wait(timeout=600)
except InsufficientCreditsError as e:
    print(f"need {e.required} credits; top up first")
except RateLimitError as e:
    print(f"hold off {e.retry_after}s before retrying")
except JobFailedError as e:
    print(f"job failed: {e}")
ExceptionHTTP
AuthError401
BadRequestError400
InsufficientCreditsError402 (carries .required)
NotFoundError404
RateLimitError429 (carries .retry_after)
JobFailedErrorn/a (terminal status failed)
JobTimeoutErrorn/a (wait() timeout)
UploadErrorn/a (GCS resumable session failed)

Public surface

from strand import (
    Client, Job, JobEvent, JobStatus, JobResults,
    Upload, Estimate,
    AuthError, BadRequestError, InsufficientCreditsError,
    JobFailedError, JobTimeoutError, NotFoundError,
    RateLimitError, StrandError, UploadError,
)

See also