---
title: Python SDK
description: "The official Python SDK — syntext on PyPI."
---

# Python SDK

Python client for the Syntext API with sync and async support. Requires Python 3.9+.

## Installation

<CodeGroup>
```bash pip
pip install syntext
```

```bash uv
uv add syntext
```

```bash poetry
poetry add syntext
```
</CodeGroup>

## Quick Start

```python
import os
from syntext import Syntext

client = Syntext(os.environ["SYNTEXT_API_KEY"])

# Get a project
project = client.projects.get("prj_abc123")

# Trigger a build
build = client.builds.trigger(project.id, branch="main")

# Search the docs
results = client.search.query(project.id, "authentication")
```

## Async Client

```python
from syntext import AsyncSyntext

async def main():
    client = AsyncSyntext(os.environ["SYNTEXT_API_KEY"])
    build = await client.builds.trigger("prj_abc123", branch="main")
```

## Resources

| Resource | Methods |
|----------|---------|
| `client.projects` | `list()`, `get(id)`, `create(...)`, `update(id, ...)`, `delete(id)` |
| `client.builds` | `list(project_id, ...)`, `get(project_id, build_id)`, `trigger(project_id, ...)`, `cancel(project_id, build_id)` |
| `client.search` | `query(project_id, query)`, `reindex(project_id)` |
| `client.chat` | `ask(project_id, question)` — streaming |
| `client.domains` | `add(project_id, domain)`, `verify(...)`, `remove(...)` |
| `client.api_keys` | `list(project_id)`, `create(...)`, `rotate(...)`, `revoke(...)` |

## Streaming AI Chat

```python
for chunk in client.chat.ask("prj_abc123", question="How do I add a custom domain?"):
    print(chunk.delta, end="", flush=True)
```

## Error Handling

```python
from syntext import SyntextError, NotFoundError, RateLimitError

try:
    client.builds.trigger("prj_abc123")
except RateLimitError as e:
    time.sleep(e.retry_after_seconds)
except NotFoundError:
    print("project does not exist")
except SyntextError as e:
    print(e.code, e.message)
```

`429` and `5xx` responses are retried automatically with exponential backoff before an exception is raised.

## Polling a Build to Completion

```python
import time

build = client.builds.trigger("prj_abc123")

while build.status in ("queued", "building"):
    time.sleep(5)
    build = client.builds.get("prj_abc123", build.id)

print("deployed" if build.status == "deployed" else f"failed: {build.status}")
```
