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
| Source | Variable / argument | Default |
|---|
| Env | STRAND_API_KEY | required |
| Env | STRAND_BASE_URL | https://app.strandai.com |
| Arg | Client(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"])
| Method | What 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")
| Method | What 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}")
| Exception | HTTP |
|---|
AuthError | 401 |
BadRequestError | 400 |
InsufficientCreditsError | 402 (carries .required) |
NotFoundError | 404 |
RateLimitError | 429 (carries .retry_after) |
JobFailedError | n/a (terminal status failed) |
JobTimeoutError | n/a (wait() timeout) |
UploadError | n/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