# SDKs

> Two small clients with no dependencies. They send an Idempotency-Key for you, retry safely, wait for results and check callback signatures.

## Install

```bash
npm install diffusr   # Node 18 or newer
pip install diffusr   # Python 3.9 or newer
```

Both read the key from `DIFFUSR_KEY`. Bodies and answers are the API's own snake_case JSON, passed through unchanged. (Both are published after launch; until then the cURL form of every call on these docs works today.)

## The whole loop

**cURL**

```bash
curl -X POST https://app.diffusr.ai/api/v2/make_video \
  -H "Authorization: Bearer $DIFFUSR_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "prompt": "A woman turns to camera on a sunny street, smiles",
  "duration_sec": 5,
  "dry_run": true
}'
```

**TypeScript**

```ts
import { Diffusr } from "diffusr";

const d = new Diffusr(); // reads DIFFUSR_KEY
const job = await d.makeVideo({
  prompt: "A woman turns to camera on a sunny street, smiles",
  duration_sec: 5,
  dry_run: true,
});
console.log(job); // the price, nothing was made
```

**Python**

```python
from diffusr import Diffusr

d = Diffusr()  # reads DIFFUSR_KEY
job = d.make_video(
    prompt="A woman turns to camera on a sunny street, smiles",
    duration_sec=5,
    dry_run=True,
)
print(job)  # the price, nothing was made
```

**cURL**

```bash
curl -X POST https://app.diffusr.ai/api/v2/make_video \
  -H "Authorization: Bearer $DIFFUSR_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "prompt": "A woman turns to camera on a sunny street, smiles",
  "duration_sec": 5
}'
```

**TypeScript**

```ts
import { Diffusr } from "diffusr";

const d = new Diffusr(); // reads DIFFUSR_KEY
const job = await d.makeVideo({
  prompt: "A woman turns to camera on a sunny street, smiles",
  duration_sec: 5,
});
const result = await d.waitFor(job.id);
console.log(result.output?.url);
```

**Python**

```python
from diffusr import Diffusr

d = Diffusr()  # reads DIFFUSR_KEY
job = d.make_video(
    prompt="A woman turns to camera on a sunny street, smiles",
    duration_sec=5,
)
result = d.wait_for(job["id"])
print(result["output"]["url"])
```

`waitFor` / `wait_for` polls with backoff and gives up with `WAIT_TIMEOUT` if the job takes too long.

## Methods

| TypeScript | Python | REST |
| --- | --- | --- |
| `makeVideo`, `makeImage`, `editImage`, `recast`, `makeMusic`, `creatorVideo`, `runTemplate` `(body, { idempotencyKey? })` | `make_video`, `make_image`, `edit_image`, `recast`, `make_music`, `creator_video`, `run_template` `(**body)` | `POST /{tool}` |
| `getResult(id)`, `listResults({ limit? })`, `cancel(id)` (stops queued work; a step already generating may finish and be charged: check `charge`) | `get_result`, `list_results`, `cancel` | `GET /results/{id}`, `GET /results`, `POST /results/{id}/cancel` |
| `waitFor(id, { timeoutMs?, intervalMs? })` | `wait_for(id, timeout=, interval=)` | polls `GET /results/{id}` |
| `uploadFile(...)`, `uploadFromUrl(url)` | `upload_file(...)`, `upload_from_url(url)` | `POST /uploads` + signed `PUT` |
| `listModels({ kind? })`, `listTemplates()` | `list_models`, `list_templates` | `GET /models`, `GET /templates` |
| `listCallbacks`, `retryCallback`, `getCallbackSecret`, `rotateCallbackSecret` | `list_callbacks`, `retry_callback`, `get_callback_secret`, `rotate_callback_secret` | `/callbacks...` |
| `Diffusr.verifyCallback(rawBody, headers, secret)` | `Diffusr.verify_callback(raw_body, headers, secret)` | |

`dry_run: true` on any make call answers with the price only.

## Retries and errors

- Every request times out after 60 s (`TIMEOUT`). `GET` calls (and `waitFor`) retry on `429`, `500`, `502`, `503`, `504`, network errors and timeouts with backoff, and honour `Retry-After`; make calls retry on `429`, `503`, network errors and timeouts, with the same `Idempotency-Key`. A wait over 30 seconds is not slept: the error is thrown.
- Make calls retry the same way **with the same Idempotency-Key**. Pass your own to make a retry safe across restarts.
- Every failure is a `DiffusrError` with `status`, `code`, `message`, `requestId` and `retryAfter`. The SDK adds the codes `NETWORK_ERROR`, `WAIT_TIMEOUT`, `UPLOAD_FAILED`, `CONTENT_TYPE_REQUIRED` and `MISSING_API_KEY`. Handle codes you do not know.

```ts
try {
  await d.makeVideo({ prompt: "..." });
} catch (e) {
  if (e instanceof DiffusrError) console.log(e.status, e.code, e.requestId, e.retryAfter);
}
```

```python
from diffusr import DiffusrError

try:
    d.make_video(prompt="...")
except DiffusrError as e:
    print(e.status, e.code, e.request_id, e.retry_after)
```

Verifying callbacks: see [Callbacks](https://diffusr.ai/docs/callbacks).

Source: https://diffusr.ai/docs/sdks
