# Use the OpenAI SDK with Paxa

POST /v1/audio/speech and POST /v1/audio/transcriptions are drop-in replacements for the OpenAI speech and transcription endpoints: point the SDK's base URL at the Paxa API and existing calls work unchanged.

The Paxa API serves the OpenAI audio.speech request shape at [POST /v1/audio/speech](https://paxalabs.com/docs/api/speech). Set the SDK's base URL to `https://api.paxalabs.com/v1` and authenticate with a Paxa API key. Existing `audio.speech.create` calls then work unchanged. Pricing, headers, and error codes match [POST /v1/tts](https://paxalabs.com/docs/api/text-to-speech). The [transcription endpoint](#transcription) is served the same way.

Python:

```python
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.paxalabs.com/v1",
    api_key=os.environ["PAXA_API_KEY"],
)

with client.audio.speech.with_streaming_response.create(
    model="paxa-tts-flash-v2",
    voice="khanomkrok",
    input="สวัสดีครับ ยินดีต้อนรับสู่ Paxa Labs",
) as response:
    response.stream_to_file("speech.mp3")
```

TypeScript:

```typescript
import { writeFile } from "node:fs/promises";
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://api.paxalabs.com/v1",
  apiKey: process.env.PAXA_API_KEY,
});

const response = await client.audio.speech.create({
  model: "paxa-tts-flash-v2",
  voice: "khanomkrok",
  input: "สวัสดีครับ ยินดีต้อนรับสู่ Paxa Labs",
});

await writeFile("speech.mp3", Buffer.from(await response.arrayBuffer()));
```

curl:

```bash
curl -X POST https://api.paxalabs.com/v1/audio/speech \
  -H "Authorization: Bearer $PAXA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "paxa-tts-flash-v2",
    "input": "สวัสดีครับ ยินดีต้อนรับสู่ Paxa Labs",
    "voice": "khanomkrok"
  }' \
  --output speech.mp3
```

## Field mapping

| Field | Behavior |
| --- | --- |
| model | Required, same as on /v1/tts. A Paxa model id, for example `paxa-tts-flash-v2`. OpenAI's `tts-1`, `tts-1-hd`, `gpt-4o-mini-tts`, `gpt-4o-mini-tts-2025-03-20`, `gpt-4o-mini-tts-2025-12-15` also work and run on `paxa-tts-flash-v2`. |
| input | The text to synthesize. Same limits, pricing, [direction tags](https://paxalabs.com/docs/directions), and [emotion tags](https://paxalabs.com/docs/emotion-tags) on `paxa-tts-flash-v1` as `text` on /v1/tts. |
| voice | The voice id from [GET /v1/voices](https://paxalabs.com/docs/api/voices), for example `"khanomkrok"`; case is ignored, as for `voice` on /v1/tts. This endpoint also takes OpenAI's voice names, such as `alloy`, mapped to Paxa voices, and the `x-voice-id` response header names the voice that spoke. |
| response_format | `mp3` (the default), `opus`, `wav`, `pcm` (16-bit little-endian mono at 24 kHz, as OpenAI's, unless `sample_rate` names another rate), `mulaw`, or `alaw`, as `format` on /v1/tts. Paxa TTS Flash answers `mulaw` and `alaw` with unsupported_format, and other OpenAI formats answer 400. |
| sample_rate | A Paxa extension, read for `pcm` and `wav` as on /v1/tts and ignored with other formats, though any value must be one of Paxa TTS Flash v2's rates. It sets their rate in Hz, where Paxa TTS Flash v2 serves 8000, 16000, 22050, 24000, 32000, 44100, 48000 and Paxa TTS Flash only 24000. Set it when the platform playing the audio expects a fixed rate. |
| temperature | A Paxa extension, read as on /v1/tts: how much the reading varies from one request to the next, from 0 to 2 on Paxa TTS Flash v2. [Temperature](https://paxalabs.com/docs/temperature) covers it. |
| stream | Defaults to `true`, matching OpenAI delivery. Set `false` for a complete body with a `content-length`. |

`speed` is served at this model's range. A value outside that range answers 400. A rate the model cannot reach is never applied silently. Fields the OpenAI API defines but Paxa does not serve, for example `instructions`, are ignored, and existing calls carrying them keep working. The reverse also holds. The [timestamps](https://paxalabs.com/docs/timestamps) extension exists only on `/v1/tts`, and timed speech requires the native endpoint.

Apps that fill a voice or model picker from the base URL read [GET /v1/audio/voices](https://paxalabs.com/docs/api/audio-voices) and [GET /v1/audio/models](https://paxalabs.com/docs/api/audio-models). They list Paxa TTS Flash v2 and its voices, and need no key.

## Differences from POST /v1/tts

The compatible endpoint covers the plain case: text in, audio out, streamed by default. Everything beyond it lives on the native endpoint: [`timestamps`](https://paxalabs.com/docs/timestamps) and `language` exist only on `/v1/tts` and `/v1/tts/live`, and the native request shape is what the [API reference](https://paxalabs.com/docs/api/text-to-speech) documents field by field. A `timestamps` or `language` sent here is ignored: the response is plain audio, and every request reads as `auto`.

Both endpoints accept an [Idempotency-Key](https://paxalabs.com/docs/idempotency), report the exact charge in `x-credits-charged`, and answer failures from the same [error catalog](https://paxalabs.com/docs/errors). Retry and billing code written against one endpoint works against the other.

## Transcription

[POST /v1/audio/transcriptions](https://paxalabs.com/docs/api/transcriptions) serves the OpenAI `audio.transcriptions.create` shape, under the same base URL and the same key. The recording rides as a multipart file part. Nothing is base64 encoded.

Python:

```python
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.paxalabs.com/v1",
    api_key=os.environ["PAXA_API_KEY"],
)

with open("meeting.m4a", "rb") as file:
    subtitles = client.audio.transcriptions.create(
        file=file,
        model="paxa-stt-lite-v1-preview",
        response_format="srt",
    )

with open("meeting.srt", "w") as out:
    out.write(subtitles)
```

TypeScript:

```typescript
import { createReadStream } from "node:fs";
import { writeFile } from "node:fs/promises";
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://api.paxalabs.com/v1",
  apiKey: process.env.PAXA_API_KEY,
});

const subtitles = await client.audio.transcriptions.create({
  file: createReadStream("meeting.m4a"),
  model: "paxa-stt-lite-v1-preview",
  response_format: "srt",
});

await writeFile("meeting.srt", subtitles);
```

curl:

```bash
# The recording rides as a file part; nothing is base64 encoded.
curl -X POST https://api.paxalabs.com/v1/audio/transcriptions \
  --max-time 300 \
  -H "Authorization: Bearer $PAXA_API_KEY" \
  -F file=@meeting.m4a \
  -F model=paxa-stt-lite-v1-preview \
  -F response_format=srt \
  -o meeting.srt
```

| Field | Behavior |
| --- | --- |
| file | The recording as a file part, in any of 7 containers; the format and the length come from the bytes. Same ceilings and pricing as `audio` on /v1/stt. |
| model | Required. A Paxa model id, for example `paxa-stt-lite-v1-preview`. OpenAI's `whisper-1`, `gpt-4o-transcribe`, `gpt-4o-mini-transcribe`, `gpt-4o-mini-transcribe-2025-03-20`, `gpt-4o-mini-transcribe-2025-12-15` also work and run on `paxa-stt-lite-v1-preview`. |
| language | A BCP 47 hint, the same field as on /v1/stt. Omitted, the model detects the language itself. |
| response_format | `json` (the default), `verbose_json`, `text`, `srt`, or `vtt`. `verbose_json` adds the duration, the segments, and the words when `timestamp_granularities` names `word`. |

The charge rides `x-credits-charged`, since OpenAI's body has nowhere to report it. `prompt` and `temperature` are ignored, and `timestamp_granularities` is read with `verbose_json` alone. `stream` set to `true` answers [400 validation](https://paxalabs.com/docs/errors#validation); [WS /v1/stt/live](https://paxalabs.com/docs/api/stt-live) transcribes as the audio arrives. The two transcript conventions, the two styles, speaker turns, the vocabulary, and the subtitle line length are extensions of [POST /v1/stt](https://paxalabs.com/docs/api/stt) and are not read here. A recording long enough to run past a proxy's idle limit belongs on the native endpoint, whose JSON body is kept alive while it works.

## Switch an existing integration

- Point the SDK's base URL at `https://api.paxalabs.com/v1`.
- Swap the OpenAI key for an API key from the [dashboard](https://paxalabs.com/app/keys).
- Keep OpenAI's model names, which run on `paxa-tts-flash-v2`, or set `model` to `paxa-tts-flash-v2` or `paxa-tts-flash-v1`.
- Keep OpenAI's voice names, or set `voice` to an id the chosen model serves, from [GET /v1/voices](https://paxalabs.com/docs/api/voices). A voice off that model's roster answers [400 unknown_voice](https://paxalabs.com/docs/errors#unknown_voice).
- Set `response_format` to `mp3`, `opus`, `wav`, or `pcm`, or `mulaw` or `alaw` for phone lines; the other OpenAI containers are not served.

> Failures follow this API's [error model](https://paxalabs.com/docs/errors). The SDK raises its usual HTTP error, and the response body carries the stable code in `title`.
