Metadata-Version: 2.4
Name: typeless-sdk
Version: 0.1.0
Summary: Official Python SDK for the Typeless External Transcript API
Keywords: transcription,speech-to-text,api,typeless,websocket
Author: Typeless
Author-email: Typeless <hello@typeless.com>
License-Expression: LicenseRef-Proprietary
License-File: LICENSE
License-File: THIRD_PARTY_NOTICES.md
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Dist: httpx>=0.24,<0.29
Requires-Dist: websockets>=14.0,<17.0
Requires-Python: >=3.10
Project-URL: Homepage, https://www.typeless.com/
Project-URL: Documentation, https://docs.typelessapi.com
Project-URL: Console, https://platform.typelessapi.com
Project-URL: Changelog, https://docs.typelessapi.com/sdk/python#changelog
Project-URL: Terms of Service, https://www.typeless.com/terms
Description-Content-Type: text/markdown

# Typeless Python SDK

Official Python SDK for the [Typeless External Transcript API](https://docs.typelessapi.com).

## Installation

Requires Python 3.10 or later.

```bash
pip install typeless-sdk
```

## Quick Start

```python
from typeless import TypelessClient

client = TypelessClient(api_key="tls_sk_xxx")
result = client.transcribe("recording.wav", model="typeless-1.0-max")

print(result.transcript)
print(result.detected_language)
print(result.duration_seconds)
print(result.request_id)
print(result.usage.billed_audio_seconds)
```

## Authentication

Read the API key from the environment:

```bash
export TYPELESS_API_KEY=tls_sk_xxx
```

```python
from typeless import TypelessClient

client = TypelessClient()
```

You can also override the API base URL:

```bash
export TYPELESS_BASE_URL=https://api.typelessapi.com
```

## Audio Input

`transcribe()` accepts a file path, in-memory bytes, or a readable binary file object.
The API detects the audio format from the file content.
Supported container formats include WAV, OGG (Opus), and WebM.

```python
# file path
result = client.transcribe("recording.wav", model="typeless-1.0-max")

# in-memory bytes
result = client.transcribe(audio_bytes, model="typeless-1.0-max")

# binary file object
with open("recording.wav", "rb") as audio_file:
    result = client.transcribe(audio_file, model="typeless-1.0-max")
```

Optional language hint:

```python
result = client.transcribe("recording.wav", model="typeless-1.0-max", language="zh")
```

## Models

The `model` parameter is required on every request.
The SDK does not inject a default model.

| Tier | Model name |
| --- | --- |
| Lite | `typeless-1.0-lite` |
| Pro | `typeless-1.0-pro` |
| Max | `typeless-1.0-max` |

Unknown model values are rejected by the API with `INVALID_REQUEST`.

## Limits

Per-request limits are enforced by the API and may vary by account.
Typical defaults include a maximum file size of about 50 MB and a maximum audio
duration of about 300 seconds per request.

## WebSocket Streaming

Use `stream()` for real-time raw PCM16 audio such as microphone capture.
For offline files in WAV, OGG, or WebM format, use HTTP `transcribe()` instead.

```python
from typeless import AudioReceived, StreamResult
from typeless.exceptions import TypelessError

client = TypelessClient(api_key="tls_sk_xxx")

with client.stream(
    model="typeless-1.0-max",
    sample_rate=16000,
    channels=1,
    read_timeout=None,
) as conn:
    conn.send(pcm_chunk_bytes)
    conn.send(pcm_chunk_bytes)
    conn.finish()

    try:
        for msg in conn:
            if isinstance(msg, AudioReceived):
                print(f"processed {msg.duration_seconds}s")
            elif isinstance(msg, StreamResult):
                print(msg.transcript)
                print(msg.detected_language)
    except TypelessError as exc:
        print(f"stream failed: [{exc.code}] {exc.message}")
```

`send()` accepts raw PCM16 bytes only (16-bit signed integer, little-endian).
The SDK sends keep-alive messages automatically while the connection is open.

| Parameter | Description |
| --- | --- |
| `channels` | Number of audio channels from 1 to 2 (default `1`) |
| `read_timeout` | Maximum seconds to wait for each server message during iteration, or `None` for no limit |

## Async Client

```python
from typeless import AsyncTypelessClient
import asyncio

async def main() -> None:
    async with AsyncTypelessClient(api_key="tls_sk_xxx") as client:
        result = await client.transcribe("audio.wav", model="typeless-1.0-max")

        results = await asyncio.gather(
            client.transcribe("a.wav", model="typeless-1.0-max"),
            client.transcribe("b.wav", model="typeless-1.0-max"),
            client.transcribe("c.wav", model="typeless-1.0-max"),
        )

asyncio.run(main())
```

Async streaming:

```python
from typeless import AsyncTypelessClient, StreamResult
from typeless.exceptions import TypelessError

async with AsyncTypelessClient(api_key="tls_sk_xxx") as client:
    async with client.stream(model="typeless-1.0-max", sample_rate=16000) as conn:
        await conn.send(pcm_chunk_bytes)
        await conn.finish()
        try:
            async for msg in conn:
                if isinstance(msg, StreamResult):
                    print(msg.transcript)
        except TypelessError as exc:
            print(f"stream failed: [{exc.code}] {exc.message}")
```

## Error Handling

```python
from typeless.exceptions import (
    APIConnectionError,
    APITimeoutError,
    AudioFormatError,
    AudioTooLargeError,
    AudioTooLongError,
    AuthenticationError,
    ConcurrentLimitError,
    InsufficientBalanceError,
    InvalidRequestError,
    RateLimitError,
    TypelessError,
)

try:
    result = client.transcribe("audio.wav", model="typeless-1.0-max")
except AuthenticationError:
    print("invalid api key")
except InvalidRequestError as exc:
    print(f"invalid request: {exc.message}")
except RateLimitError as exc:
    print(f"rate limited, retry after {exc.retry_after}s")
except InsufficientBalanceError:
    print("insufficient account balance")
except AudioTooLongError as exc:
    print(
        f"audio too long: limit {exc.max_duration_seconds}s, "
        f"actual {exc.actual_duration_seconds}s"
    )
except AudioFormatError as exc:
    print(f"unsupported format: {exc.supported_formats}")
except AudioTooLargeError as exc:
    print(f"file too large: limit {exc.max_size_bytes} bytes")
except ConcurrentLimitError as exc:
    print(f"concurrent limit exceeded: {exc.message}")
except APIConnectionError as exc:
    print(f"connection failed: {exc.message}")
except APITimeoutError as exc:
    print(f"request timed out: {exc.message}")
except TypelessError as exc:
    print(f"[{exc.code}] {exc.message} (req_id={exc.request_id})")
```

## Configuration

```python
client = TypelessClient(
    api_key="tls_sk_xxx",
    base_url="https://api.typelessapi.com",
    timeout=300.0,
    max_retries=3,
)
```

| Parameter | Description |
| --- | --- |
| `api_key` | API key, or read from `TYPELESS_API_KEY` when omitted |
| `base_url` | API base URL, or read from `TYPELESS_BASE_URL` when omitted |
| `timeout` | Request timeout in seconds (default `300`) |
| `max_retries` | Maximum retries for transient HTTP failures (default `3`) |

## Resource Management

Reuse a single client instance across multiple requests so the HTTP connection pool can reuse TCP connections.

```python
with TypelessClient(api_key="tls_sk_xxx") as client:
    result = client.transcribe("audio.wav", model="typeless-1.0-max")
```

```python
client = TypelessClient(api_key="tls_sk_xxx")
try:
    result = client.transcribe("audio.wav", model="typeless-1.0-max")
finally:
    client.close()
```

Async client:

```python
async with AsyncTypelessClient(api_key="tls_sk_xxx") as client:
    result = await client.transcribe("audio.wav", model="typeless-1.0-max")
```

## Response Fields

A successful HTTP transcription returns a `TranscriptResult`:

| Field | Description |
| --- | --- |
| `transcript` | Final transcript text |
| `detected_language` | Detected language code, or `None` when unknown |
| `duration_seconds` | Audio duration in seconds |
| `request_id` | Request identifier for support |
| `usage.billed_audio_seconds` | Billed audio duration in seconds |
| `usage.output_token_count` | Output token count reported by the API |

## Logging

The SDK uses the standard library `logging` module with logger name `typeless`.
By default it attaches a `NullHandler` and does not write to stderr.

```python
import logging

logging.getLogger("typeless").setLevel(logging.DEBUG)
```

## Development

Install the package and development dependencies in your local environment:

```bash
pip install typeless-sdk
uv sync
uv run ruff check src/typeless tests
uv run mypy
uv run pytest -m "not integration" -q
uv build --no-sources
```

### Integration tests

Unit tests run by default and do not require API credentials.
Integration tests call a live API endpoint.

```bash
export TYPELESS_API_KEY=tls_sk_xxx
export TYPELESS_BASE_URL=https://your-test-endpoint
uv run pytest tests/integration -m integration -v
```

## Support

Questions, bug reports, or access requests: email [hello@typeless.com](mailto:hello@typeless.com)
and include the `request_id` from any failed response.

## License

Proprietary. See the [LICENSE](LICENSE) file. Use of this SDK is also subject to
the Typeless [Terms of Service](https://www.typeless.com/terms) and
[Privacy Policy](https://www.typeless.com/privacy).

You may install the SDK and distribute it, unmodified, as an embedded dependency
of your own applications (including commercial products) that call the Typeless
API, and to the end users of those applications. Standalone redistribution,
modification, and reverse engineering are not permitted. See the LICENSE file for
the full terms.

Third-party open-source components are listed in
[THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
