> ## 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.

# REST API overview

> Reference for the Strand AI REST API: base URL, bearer authentication, the four-step prediction lifecycle, error model, and per-org concurrency limits.

The Strand AI REST API is a thin wrapper around the platform's job
infrastructure. It's the same surface the Python and R SDKs use under the
hood, so you can hit it directly from any language.

The OpenAPI spec is hosted at
[`https://app.strandai.com/api/v1/openapi.json`](https://app.strandai.com/api/v1/openapi.json).
A Markdown variant for LLM agents is at
[`https://app.strandai.com/docs/api.md`](https://app.strandai.com/docs/api.md).

## Base URL

```text theme={null}
https://app.strandai.com/api/v1
```

## Authentication

All endpoints require a bearer API key:

```http theme={null}
Authorization: Bearer sk-strand-XXXXXXXXXXXXXXXXXXXXXXXX
```

Generate keys at [/settings/api-keys](https://app.strandai.com/settings/api-keys).
Keys are org-scoped. They inherit the credit balance and concurrent-job
quota of the org that created them.

## Lifecycle

A prediction flows through four endpoints:

<Steps>
  <Step title="Initiate an upload">
    `POST /uploads` with the slide's filename, size, and content type.
    Returns a **resumable upload URL** (signed GCS). `PUT` the slide
    bytes there directly.
  </Step>

  <Step title="Complete the upload">
    `POST /uploads/{id}/complete` once the bytes are uploaded. The
    platform reads the slide dimensions (needed for credit estimates).
  </Step>

  <Step title="Estimate, then submit">
    Optional: `POST /predict/estimate` to see how many credits the job
    will cost. `POST /predict` reserves credits atomically and enqueues
    inference, returning a `jobId`.
  </Step>

  <Step title="Poll or stream the job">
    `GET /jobs/{id}` for snapshots, or `GET /jobs/{id}/stream` for an
    SSE stream. When `status` reaches `succeeded`, download the OME-Zarr
    results via `GET /jobs/{id}/results/files/{path}` or
    `GET /jobs/{id}/results` (signed GCS URL).
  </Step>
</Steps>

## Error model

Errors are returned as JSON in a consistent shape:

```json theme={null}
{
  "error": "insufficient_credits",
  "message": "Org has 120 credits; this job requires 450.",
  "required": 450
}
```

| HTTP | Meaning                                                               |
| ---- | --------------------------------------------------------------------- |
| 400  | Bad request: invalid body, unknown marker, etc.                       |
| 401  | Missing or invalid API key.                                           |
| 402  | Insufficient credits. `required` carries the credit cost.             |
| 404  | Resource not found, or scoped to a different org.                     |
| 409  | Resource not ready (e.g. upload not finalized).                       |
| 429  | Per-org concurrent-job cap exceeded. `Retry-After` header in seconds. |

## Rate limits

There is a **per-org concurrent inference job cap** (currently soft, surfaced
as 429 with `Retry-After`). Other endpoints are not rate-limited beyond
ordinary abuse protections. Get in touch if you have a high-throughput
use case and we'll raise your cap.

## SDKs

The platform ships first-party SDKs that wrap this API:

<CardGroup cols={2}>
  <Card title="Python" icon="python" href="/sdks/python">
    `pip install strand-sdk`
  </Card>

  <Card title="R" icon="r-project" href="/sdks/r">
    `install.packages("strandai", ...)`
  </Card>
</CardGroup>
