Metadata-Version: 2.4
Name: transcribe-api
Version: 0.1.10
Summary: Official Python SDK for Transcribe API.
Author-email: Transcribe API <admin@transcribeapi.com>
License-Expression: MIT
Project-URL: Homepage, https://transcribeapi.com
Keywords: transcription,speech-to-text,audio,api
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: OS Independent
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Requires-Dist: requests>=2.31.0

# Transcribe API Python SDK

Official Python SDK for Transcribe API.

Use this SDK to send one file, many files, local uploads, remote audio URLs, webhook jobs, and large multipart uploads to the Transcribe API from Python.

## Installation

```bash
pip install transcribe-api
```

## Import

```python
from transcribe_api import TranscribeAPI, TranscribeAPIError
```

## Quick start

```python
import os
from transcribe_api import TranscribeAPI

client = TranscribeAPI(
    apiKey=os.environ["TRANSCRIBE_API_KEY"],
    polling={
        "interval": 10,
        "timeout": 15 * 60,
    },
)

result = client.transcribe(
    language="en",
    files=[
        {"reference_id": "meeting_001", "file": "./meeting.mp3"},
    ],
)

print(result)
```

`client.transcribe(files=[...])` is the main API for both single-file and multi-file transcription.

> Important: the current SDK uses camelCase option names such as `apiKey`, `baseUrl`, `uploadConcurrency`, `showLogs`, and `webhookUrl`. It does not use `api_key`, `base_url`, `upload_concurrency`, or `client.batch.transcribe(...)`.

## How `transcribe` routes work

The SDK automatically chooses the right API flow:

- One small local file is sent through the direct synchronous upload path.
- One remote URL is sent through the direct `/v1/transcribe` endpoint and the API decides whether it can finish immediately or should continue as an async job.
- Multiple files, files larger than 30 MB, and files estimated over 10 minutes are sent through the async job flow.
- Large local async uploads use signed R2 upload URLs returned by the API.
- Multipart upload is used automatically when the backend returns multipart upload instructions.
- If `polling` is configured, async calls wait until the job reaches a terminal status.
- If `polling` is not configured, async calls return the upload-completion/job response. Use `client.jobs.get(job_id)` or `client.waitForJobCompletion(job_id, ...)` to check later.

Terminal job statuses are `completed`, `failed`, and `insufficient_funds`. Completed job responses include `result_url` when the API has a result file ready.

## Client options

```python
client = TranscribeAPI(
    apiKey="YOUR_API_KEY",
    baseUrl="https://api.transcribeapi.com/v1",
    uploadConcurrency=4,
    autoReload={
        "amount": 10,
        "if_balance_below": 20,
    },
    showLogs=True,
    logger=print,
    polling={
        "interval": 10,
        "timeout": 15 * 60,
    },
)
```

| Option | Description |
| --- | --- |
| `apiKey` | Required. Your Transcribe API key. |
| `baseUrl` | Optional. Defaults to `https://api.transcribeapi.com/v1`. |
| `uploadConcurrency` | Optional. Number of in-flight upload PUTs. Defaults to `1` and is capped at `32`. |
| `autoReload` | Optional. Automatically calls `POST /v1/add-funds` after a transcription result when `remaining_balance` is below `if_balance_below`. |
| `showLogs` | Optional. Prints upload progress, upload completion, polling status, and final result info. |
| `logger` | Optional. Callable or logger-like object. Defaults to `print`. |
| `polling` | Optional. `{"interval": 10, "timeout": 900}` in seconds. `interval` must be at least `10`. Omit or set to `None`/`False` to disable automatic polling. |

## File inputs

Each item in `files` must be a dictionary with either `file` or `url`:

```python
{"reference_id": "episode_1", "file": "./episode.mp3"}
{"reference_id": "episode_2", "url": "https://example.com/signed-audio-url.mp3"}
```

Supported local/upload inputs:

- Local path strings, such as `"./audio.mp3"`.
- `pathlib.Path` objects.
- `bytes` or `bytearray`.
- File-like objects with a `.read()` method.

Supported remote inputs:

- Public or signed URLs using `{"url": "https://..."}`.

Do not include both `file` and `url` in the same item.

Local path inputs are streamed from disk. `bytes`, `bytearray`, and file-like inputs are read into memory by the SDK.

## Single local file

```python
result = client.transcribe(
    files=[
        {"reference_id": "call_001", "file": "./call.mp3"},
    ],
    language="en",
)
```

For one small local file, this usually uses the direct upload path. Larger files automatically become async jobs.

## File-like objects and bytes

```python
with open("./meeting.wav", "rb") as audio:
    result = client.transcribe(
        files=[
            {"reference_id": "meeting", "file": audio},
        ],
        language="en",
    )
```

```python
audio_bytes = Path("./clip.mp3").read_bytes()

result = client.transcribe(
    files=[
        {"reference_id": "clip", "file": audio_bytes},
    ],
)
```

When the SDK receives raw bytes, it uses the fallback filename `audio.mp3`. For best content-type detection, prefer passing a path or file-like object with a useful `.name`.

## Remote URL transcription

```python
job = client.transcribe(
    files=[
        {"reference_id": "remote_001", "url": "https://example.com/audio.mp3"},
    ],
    language="en",
)
```

For one remote URL, the SDK submits the request to the direct `/v1/transcribe` endpoint and lets the API decide the final route. Small remote files may complete immediately, while larger or longer remote files automatically fall back to the async job flow.

## Batch and mixed input transcription

```python
job = client.transcribe(
    language="en",
    uploadConcurrency=8,
    files=[
        {"reference_id": "episode_1", "file": "./episode-1.mp3"},
        {"reference_id": "episode_2", "file": "./episode-2.wav"},
        {"reference_id": "episode_3", "url": "https://example.com/episode-3.m4a"},
    ],
)
```

Local files and remote URLs can be mixed in the same batch. Provide a unique `reference_id` for every item so uploads and results can be matched reliably.

## Per-file language

Set `language` at the top level to apply a default to the whole job. Set `language` on a file item to override the default for that file.

```python
job = client.transcribe(
    language="en",
    files=[
        {"reference_id": "intro", "file": "./intro.mp3"},
        {"reference_id": "french_segment", "file": "./segment.m4a", "language": "fr"},
    ],
)
```

`language` must be a supported language code such as `en`, `fr`, or `yue`. Omit it, pass an empty value, or pass `"auto"` to avoid sending an explicit language.

## Excluding outputs or features

Use `exclude` to pass API exclusions. Lists are joined with commas before sending. Valid values are only `vtt`, `segments`, `metadata`, `billing`, and `detected_language`.

```python
result = client.transcribe(
    files=[
        {"reference_id": "meeting", "file": "./meeting.mp3"},
    ],
    exclude=["vtt", "segments"],
)
```

You can also pass a comma-separated string:

```python
exclude="metadata,billing"
```

For single-file outputs, if `exclude` leaves only one transcript field and removes metadata, billing, and detected language, the API returns that remaining value directly instead of a JSON object. Text-only responses return a plain string, VTT-only responses return a plain string, and segments-only responses return a list. Multi-file outputs still return JSON so each result stays associated with its `reference_id`.

If `autoReload` is enabled, the SDK always keeps billing in the request even if you included `billing` in `exclude`. It needs `remaining_balance` to decide whether to call `POST /v1/add-funds`.

## Auto reload

```python
client = TranscribeAPI(
    apiKey="YOUR_API_KEY",
    autoReload={
        "amount": 10,
        "if_balance_below": 20,
    },
)
```

When `autoReload` is enabled, the SDK:

- checks `remaining_balance` after a transcription result
- only calls `POST /v1/add-funds` when the balance is below `if_balance_below`
- sends `{"amount": amount, "if_balance_below": if_balance_below}` to the API for backend-side protection too
- ignores `billing` inside `exclude` so the SDK can always read `remaining_balance`

Auto reload only runs when the transcription result returned to the SDK includes `remaining_balance`. If you disable polling for async jobs, immediate non-terminal async responses do not have enough billing data for the SDK to auto reload yet.

### Manual add_funds for webhooks

When using webhooks with batch jobs, the transcription result goes to your webhook URL instead of the SDK. The `autoReload` config won't fire because the SDK never sees the result. Call `client.add_funds()` inside your webhook handler instead:

```python
# Webhook handler (Flask example)
@app.route('/webhook', methods=['POST'])
def webhook():
    payload = request.get_json()
    remaining_balance = payload.get('remaining_balance', 0)

    if remaining_balance < 20:
        client = TranscribeAPI(apiKey=os.environ['TRANSCRIBE_API_KEY'])
        client.add_funds(10, if_balance_below=20)

    return '', 200
```

`add_funds(amount, *, if_balance_below=None)` calls `POST /v1/add-funds`:

- `amount` — whole dollar amount from 5 to 100.
- `if_balance_below` — server-side guard. The API only charges if your current balance is actually below this threshold, making it safe against duplicate webhook deliveries.

## Webhooks

```python
job = client.transcribe(
    webhookUrl="https://example.com/transcribe-webhook",
    files=[
        {"reference_id": "upload_001", "file": "./long-audio.mp3"},
    ],
)
```

For a single file or URL, `webhookUrl` is sent through the direct `/v1/transcribe` endpoint and the API decides whether it can finish immediately or should continue as a job. Multi-file requests still use the async batch flow.

## Progress and logs

Use `onProgress` to receive structured progress events:

```python
def on_progress(event):
    print(event)

job = client.transcribe(
    files=[
        {"reference_id": "episode_1", "file": "./episode-1.mp3"},
        {"reference_id": "episode_2", "file": "./episode-2.mp3"},
    ],
    uploadConcurrency=4,
    onProgress=on_progress,
)
```

Common event names:

- `upload_urls_received`
- `upload_started`
- `upload_progress`
- `upload_completed`

Progress events may include `jobId`, `jobStatus`, `referenceId`, `loaded`, `total`, `fileLoaded`, `fileTotal`, `batchLoaded`, `batchTotal`, `uploadType`, `partNumber`, `totalParts`, and `multipartConcurrency`.

Set `showLogs=True` on the client or on a single call to print built-in progress and polling logs:

```python
client = TranscribeAPI(
    apiKey=os.environ["TRANSCRIBE_API_KEY"],
    showLogs=True,
)
```

## Manual async jobs

`transcribe` uploads automatically. Use `createBatchJob` when you want to separate job creation from upload.

```python
job = client.createBatchJob(
    language="en",
    files=[
        {"reference_id": "part_1", "file": "./part-1.mp3"},
        {"reference_id": "part_2", "url": "https://example.com/part-2.mp3"},
    ],
)

print(job.jobId)

completion = job.upload()
print(completion)
```

For a single large local file, you can also use `createBigFileJob`:

```python
job = client.createBigFileJob(
    file={"reference_id": "large_001", "file": "./large.wav"},
)

completion = job.upload()
```

## Job helpers

```python
job = client.jobs.get("job_id_here")
same_job = client.jobs.result("job_id_here")

final_job = client.waitForJobCompletion(
    "job_id_here",
    polling={"interval": 10, "timeout": 15 * 60},
)
```

Available job helpers:

| Method | Description |
| --- | --- |
| `client.jobs.get(jobId)` | Fetches `GET /v1/transcribe/{job_id}`. |
| `client.jobs.result(jobId)` | Alias for `jobs.get`. |
| `client.jobs.uploadCompleted(jobId)` | Calls `POST /v1/transcribe/{job_id}/upload-completed`. |
| `client.jobs.complete(jobId)` | Alias for `jobs.uploadCompleted`. |
| `client.jobs.createBatch(options)` | Creates a batch job and returns a `BatchJob`. |
| `client.jobs.createBigFile(options)` | Creates a one-file async upload job and returns a `BatchJob`. |
| `client.waitForJobCompletion(jobId, options)` | Polls until a terminal job status or timeout. |

## Direct upload method

Most applications should use `client.transcribe(files=[...])`. If you specifically need to force the direct upload endpoint for one file, use `transcribeDirect`:

```python
result = client.transcribeDirect(
    file="./short.mp3",
    referenceId="short_001",
    language="en",
)
```

## Limits and validation

- Batch jobs support up to `10,000` files.
- Batch local upload payloads support up to `10 GB` total local file size and 50 hours total batch duration.
- Per file up to 10 GB and up to 10 hours duration.
- Direct sync routing is used only for one local file up to `30 MB` and about `10 minutes`.
- Multipart upload starts when the backend returns multipart upload instructions. The SDK sends `size_bytes` for local files at least `128 MB` so the backend can choose multipart.
- `uploadConcurrency` defaults to `1` and is capped at `32`.
- `polling["interval"]` must be at least `10` seconds.
- Batch local uploads support `mp3`, `mpeg`, `mpga`, `m4a`, `wav`, and `webm`.
- `.mp4` is not supported.

## Error handling

```python
try:
    result = client.transcribe(
        files=[
            {"reference_id": "bad_file", "file": "./missing.mp3"},
        ],
    )
    print(result)
except TranscribeAPIError as error:
    print(str(error))
    print(error.code)
    print(error.status)
    print(error.response)
    print(error.to_json())
```

`TranscribeAPIError` includes `message`, `code`, `status`, `response`, and any extra fields returned by the API or generated by the SDK.

## CLI

The package exposes a small CLI entrypoint:

```bash
transcribe-api --version
```

At the moment, the CLI only prints the package version or a basic placeholder message. Use the Python SDK for transcription work.
