> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.taktoria.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.taktoria.com/_mcp/server.

# SDKs

> Typed clients for the Taktoria API in TypeScript (@taktoria/sdk) and Python (taktoria-sdk): reading, writing, paging, errors and retries, and the OpenAPI file.

Taktoria publishes a typed client for TypeScript and one for Python. Both are
generated from the same definition as this reference, so every endpoint here
has a method, and every request and response a type.

| Language   | Package                                                        | Install                     |
| ---------- | -------------------------------------------------------------- | --------------------------- |
| TypeScript | [`@taktoria/sdk`](https://www.npmjs.com/package/@taktoria/sdk) | `npm install @taktoria/sdk` |
| Python     | [`taktoria-sdk`](https://pypi.org/project/taktoria-sdk/)       | `pip install taktoria-sdk`  |

The TypeScript SDK runs on Node.js 18 and later, Deno, Bun and Cloudflare
Workers, and ships CommonJS and ES modules. The Python SDK needs Python 3.10
or later, and has a synchronous client and an asynchronous one.

For any other language, import the [OpenAPI file](#the-openapi-file).

## Creating a client

Create an API key in **Settings → API keys** (see Authentication) and give it
to the client. The key is sent as `Authorization: Bearer …` on every request.

```ts
import { TaktoriaClient } from "@taktoria/sdk"

const taktoria = new TaktoriaClient({ token: process.env.TAKTORIA_API_KEY! })
```

```python
import os

from taktoria import Taktoria

taktoria = Taktoria(token=os.environ["TAKTORIA_API_KEY"])
```

In Python, `AsyncTaktoria` takes the same arguments, and each method is awaited.

Requests go to `https://api.taktoria.com/api/v1`. To point a client somewhere
else, such as a development server, pass `baseUrl` (TypeScript) or `base_url`
(Python).

## Reading

Every list endpoint is a method that returns one page:

```ts
const page = await taktoria.launches.list({ status: "IN_DEVELOPMENT", limit: 50 })

for (const launch of page.data) {
  console.log(launch.id, launch.name, launch.plannedLaunchDate)
}
```

```python
page = taktoria.launches.list(status="IN_DEVELOPMENT", limit=50)

for launch in page.data:
    print(launch.id, launch.name, launch.planned_launch_date)
```

Python names are `snake_case`: a field the API calls `plannedLaunchDate` is
`planned_launch_date` on the model and as an argument.

## Writing

```ts
const launch = await taktoria.launches.create({
  name: "Smart Email",
  plannedLaunchDate: "2026-11-14",
  generateSteps: true,
})
```

```python
launch = taktoria.launches.create(
    name="Smart Email",
    planned_launch_date="2026-11-14",
    generate_steps=True,
)
```

A key needs the scope each endpoint names in its reference: `launches:read`
for the list above, `launches:write` for creating one.

## Paging

Lists are pages of at most `limit` items (50 unless asked, 200 at most), with
`nextCursor` to ask for the next one (see Errors and pagination). The SDKs
return a page at a time, so walk them with a loop:

```ts
let cursor: string | undefined
do {
  const page = await taktoria.launches.list({ limit: 200, cursor })
  for (const launch of page.data) {
    console.log(launch.name)
  }
  cursor = page.hasMore ? page.nextCursor : undefined
} while (cursor)
```

```python
cursor = None
while True:
    page = taktoria.launches.list(limit=200, cursor=cursor)
    for launch in page.data:
        print(launch.name)
    if not page.has_more:
        break
    cursor = page.next_cursor
```

## Errors

A response that is not a success is thrown as an error. The errors an
endpoint lists in its reference have their own classes, and every one
carries the status, the headers and the body, `{ error: { code, message,
details } }`:

```ts
import { Taktoria, TaktoriaError } from "@taktoria/sdk"

try {
  await taktoria.launches.create({ name: "" })
} catch (err) {
  if (err instanceof Taktoria.ValidationError) {
    console.error("Invalid:", err.body.error.details)
  } else if (err instanceof TaktoriaError) {
    console.error(err.statusCode, err.body)
  } else {
    throw err
  }
}
```

```python
from taktoria import ValidationError
from taktoria.core.api_error import ApiError

try:
    taktoria.launches.create(name="")
except ValidationError as err:
    print("Invalid:", err.body.error.details)
except ApiError as err:
    print(err.status_code, err.body)
```

`TaktoriaError` and `ApiError` are the base classes, so the last branch also
catches a status the endpoint does not list, such as a `403` for a missing
scope. `code` is the stable part: compare it, not `message`.

## Rate limits

When a rate limit is used up, the API answers `429` with `Retry-After` (see
Rate limits under Errors and pagination). The SDKs wait and send the request
again on their own: they retry a `429`, a `408` and any `5xx` up to twice,
waiting `Retry-After` seconds (at most 60) or, without it, backing off
exponentially. Python also retries a `409`. Raise or lower the number with
`maxRetries` (TypeScript) or `max_retries` (Python), on the client or on one
request:

```ts
const taktoria = new TaktoriaClient({ token: process.env.TAKTORIA_API_KEY!, maxRetries: 5 })

await taktoria.launches.list({}, { maxRetries: 0 })
```

```python
taktoria = Taktoria(token=os.environ["TAKTORIA_API_KEY"], max_retries=5)

taktoria.launches.list(request_options={"max_retries": 0})
```

When the retries run out, the error is a `TooManyRequestsError`, with
`Retry-After` still on its headers:

```ts
if (err instanceof Taktoria.TooManyRequestsError) {
  const seconds = Number(err.rawResponse?.headers.get("Retry-After") ?? 60)
}
```

```python
from taktoria import TooManyRequestsError

try:
    taktoria.launches.list()
except TooManyRequestsError as err:
    seconds = int((err.headers or {}).get("retry-after", "60"))
```

A request refused for a rate limit did nothing, so it is safe to send again.
For bulk work, spread requests out rather than raising `maxRetries`, and give
each integration its own key.

## The OpenAPI file

The API is also described in OpenAPI 3.1, generated from the same definition
and served with the API itself, so it always describes the API as it is:

```
https://www.taktoria.com/openapi.yml
```

Import it into Postman, Insomnia or Bruno, or generate a client for another
language with a tool such as OpenAPI Generator.