> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nano-gpt.com/llms.txt
> Use this file to discover all available pages before exploring further.

# TTS Status

## Overview

Poll the status of an asynchronous text-to-speech (TTS) job. Use the `runId` and `model` values returned by `POST /api/tts` when the initial response is `202` with `status: "pending"`.

For low‑latency, synchronous TTS without polling, use [Speech](/api-reference/endpoint/speech) (POST `/v1/audio/speech`).

### Query parameters

* `runId` (string, required): Job identifier from the submit response
* `model` (string, required): The model that was used for the job (e.g., `Elevenlabs-Turbo-V2.5`)
* `cost` (number, optional): Cost from the ticket; helps with automatic refunds
* `paymentSource` (string, optional): Currency/source from the ticket (e.g., `USD`)
* `isApiRequest` (boolean, optional): Pass `true` when polling from API clients

Including `cost`, `paymentSource`, and `isApiRequest` from the original ticket helps the platform perform automatic refunds if the upstream provider rejects content after you were charged.

## Usage

<CodeGroup>
  ```python Python theme={null}
  import time
  import requests

  BASE = "https://nano-gpt.com/api"

  def get_tts_status(run_id: str, model: str, api_key: str, *, cost=None, payment_source=None, is_api_request=True) -> dict:
      params = {"runId": run_id, "model": model}
      if isinstance(cost, (int, float)):
          params["cost"] = str(cost)
      if payment_source:
          params["paymentSource"] = str(payment_source)
      if isinstance(is_api_request, bool):
          params["isApiRequest"] = str(is_api_request)

      resp = requests.get(
          f"{BASE}/tts/status",
          headers={"x-api-key": api_key},
          params=params,
          timeout=30,
      )
      resp.raise_for_status()
      return resp.json()


  def wait_for_tts(run_id: str, model: str, api_key: str, *, cost=None, payment_source=None, is_api_request=True, max_attempts: int = 60, delay_s: int = 3) -> str:
      for attempt in range(max_attempts):
          data = get_tts_status(run_id, model, api_key, cost=cost, payment_source=payment_source, is_api_request=is_api_request)
          status = data.get("status")

          if status == "completed" and data.get("audioUrl"):
              return data["audioUrl"]
          if status == "error":
              raise RuntimeError(data.get("error", "TTS generation failed"))

          time.sleep(delay_s)

      raise TimeoutError("Polling timeout")
  ```

  ```javascript JavaScript theme={null}
  const BASE = 'https://nano-gpt.com/api';

  async function getTTSStatus({ runId, model, cost, paymentSource, isApiRequest = true }, apiKey) {
    const qs = new URLSearchParams({ runId, model });
    if (typeof cost === 'number') qs.set('cost', String(cost));
    if (paymentSource) qs.set('paymentSource', String(paymentSource));
    if (typeof isApiRequest === 'boolean') qs.set('isApiRequest', String(isApiRequest));

    const res = await fetch(`${BASE}/tts/status?${qs.toString()}`, {
      headers: { 'x-api-key': apiKey }
    });
    if (!res.ok) {
      const err = await res.json().catch(() => ({}));
      throw new Error(err.error || `Polling failed (${res.status})`);
    }
    return res.json();
  }

  export async function waitForTTS(ticket, apiKey, maxAttempts = 60, delayMs = 3000) {
    for (let i = 0; i < maxAttempts; i++) {
      const data = await getTTSStatus(ticket, apiKey);
      if (data.status === 'completed' && data.audioUrl) return data.audioUrl;
      if (data.status === 'error') throw new Error(data.error || 'TTS generation failed');
      await new Promise(r => setTimeout(r, delayMs));
    }
    throw new Error('Polling timeout');
  }
  ```

  ```bash cURL theme={null}
  # Single status check
  curl -s "https://nano-gpt.com/api/tts/status?runId=RUN_ID&model=Elevenlabs-Turbo-V2.5&cost=0.0050388&paymentSource=USD&isApiRequest=true" \
    -H "x-api-key: YOUR_API_KEY" | jq .

  # Simple poll (every 3s, 3 minutes max)
  for i in {1..60}; do
    RESP=$(curl -s "https://nano-gpt.com/api/tts/status?runId=RUN_ID&model=Elevenlabs-Turbo-V2.5" -H "x-api-key: YOUR_API_KEY")
    STATUS=$(echo "$RESP" | jq -r '.status // empty')
    echo "Attempt $i: status=$STATUS"
    if [ "$STATUS" = "completed" ]; then
      echo "$RESP" | jq .
      break
    fi
    if [ "$STATUS" = "error" ]; then
      echo "$RESP" | jq .
      exit 1
    fi
    sleep 3
  done
  ```
</CodeGroup>

## Response examples

### Pending

```json theme={null}
{
  "status": "pending",
  "runId": "98b0d593-fe8d-49b8-89c9-233022232297",
  "queuePosition": 3
}
```

### Completed

```json theme={null}
{
  "status": "completed",
  "audioUrl": "https://.../file.mp3",
  "contentType": "audio/mpeg",
  "model": "Elevenlabs-Turbo-V2.5"
}
```

### Error (generic)

```json theme={null}
{
  "status": "error",
  "error": "TTS generation failed. Please try again."
}
```

### Error (content policy)

```json theme={null}
{
  "status": "error",
  "code": "CONTENT_POLICY_VIOLATION",
  "error": "Content rejected by provider. Please modify your prompt and try again."
}
```

### Notes

* For Elevenlabs‑family async models (e.g., `Elevenlabs-Turbo-V2.5`, `Elevenlabs-V3`) you will always poll this endpoint until it returns `completed`.
* When the job completes, the response includes an `audioUrl` you can download or play in the browser.
* If available, include `cost`, `paymentSource`, and `isApiRequest` from the original ticket while polling to improve refund handling.
