---
type: AI Model
id: kotoba
title: Kotoba Speech-to-Text API
provider: Kotoba Technologies
description: "Kotoba Speech-to-Text turns a recording of 2 minutes or less into text. It handles Japanese, English, Korean, Chinese and Spanish, and can add word timings and speaker labels."
resource: https://www.pixazo.ai/models/kotoba
docs_url: https://www.pixazo.ai/models/kotoba
latest_version: STT v1
tags:
  - speech-to-text
  - kotoba-technologies
variants:
  - id: kotoba-stt
    name: Kotoba Speech-to-Text
    version: v1
    capabilities:
      - Speech to Text
timestamp: 2026-11-13T14:00:47.313Z
---

# Kotoba Speech-to-Text API

> Provider: **Kotoba Technologies**
> Source: https://www.pixazo.ai/models/kotoba

Kotoba Speech-to-Text turns a recording of 2 minutes or less into text. It handles Japanese, English, Korean, Chinese and Spanish, and can add word timings and speaker labels.

## Kotoba Speech-to-Text

### Speech to Text

## Kotoba Speech-to-Text API Documentation

Transcribe speech with Kotoba Speech-to-Text, tuned for Japanese and other East Asian languages: send one audio file of 2 minutes or less in `audio_url` and its language in `language`. Add `with_timestamps` for word timings, `diarization` to tell speakers apart and `keywords` to suggest the spelling of names and terms. Asynchronous: submit returns a `request_id`; poll the status endpoint until the request is `COMPLETED`, then read the transcript from `output.text`.

```
POST https://gateway.pixazo.ai/kotoba-stt/v1/speech-to-text
```

## Authentication

All requests require an API key passed via header.

Header

Type

Required

Description

Ocp-Apim-Subscription-Key

string

Yes

Your API subscription key

## Speech to Text - Kotoba Speech-to-Text

## Request Code

HTTP Python JavaScript cURL

```
POST https://gateway.pixazo.ai/kotoba-stt/v1/speech-to-text
Content-Type: application/json
Ocp-Apim-Subscription-Key: YOUR_SUBSCRIPTION_KEY

{
  "audio_url": "https://pub-582b7213209642b9b995c96c95a30381.r2.dev/doc-assets/audio/speech-17s.mp3",
  "language": "en"
}
```

```
import requests

url = "https://gateway.pixazo.ai/kotoba-stt/v1/speech-to-text"
headers = {
    "Content-Type": "application/json",
    "Ocp-Apim-Subscription-Key": "YOUR_SUBSCRIPTION_KEY"
}
data = {
  "audio_url": "https://pub-582b7213209642b9b995c96c95a30381.r2.dev/doc-assets/audio/speech-17s.mp3",
  "language": "en"
}

resp = requests.post(url, json=data, headers=headers)
print(resp.json())
```

```
const res = await fetch("https://gateway.pixazo.ai/kotoba-stt/v1/speech-to-text", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Ocp-Apim-Subscription-Key": "YOUR_SUBSCRIPTION_KEY"
  },
  body: JSON.stringify({
  "audio_url": "https://pub-582b7213209642b9b995c96c95a30381.r2.dev/doc-assets/audio/speech-17s.mp3",
  "language": "en"
})
});
console.log(await res.json());
```

```
curl -X POST 'https://gateway.pixazo.ai/kotoba-stt/v1/speech-to-text' \
  -H 'Content-Type: application/json' \
  -H 'Ocp-Apim-Subscription-Key: YOUR_SUBSCRIPTION_KEY' \
  --data-raw '{"audio_url": "https://pub-582b7213209642b9b995c96c95a30381.r2.dev/doc-assets/audio/speech-17s.mp3", "language": "en"}'
```

## Output

```
{
  "request_id": "kotoba-stt_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "status": "QUEUED",
  "polling_url": "https://gateway.pixazo.ai/v2/requests/status/kotoba-stt_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
```

[Try Now](https://api.pixazo.ai/api-details#api=kotoba-stt&operation=speech-to-text)

## Webhook (Optional)

Instead of polling, you can receive a Webhook callback when the request reaches a terminal state. Provide a Webhook URL via header on the submit request.

Header

Required

Description

X-Webhook-URL

To enable

HTTPS URL to receive the Webhook callback.

X-Webhook-Mode

No

`terminal` (default, one callback on COMPLETED/ERROR) or `sync` (per-poll callbacks).

### Example: enable Webhook

```
curl -X POST 'https://gateway.pixazo.ai/kotoba-stt/v1/speech-to-text' \
  -H 'Content-Type: application/json' \
  -H 'Ocp-Apim-Subscription-Key: YOUR_SUBSCRIPTION_KEY' \
  -H 'X-Webhook-URL: https://your-server.com/webhook' \
  --data-raw '{"audio_url": "https://pub-582b7213209642b9b995c96c95a30381.r2.dev/doc-assets/audio/speech-17s.mp3", "language": "en"}'
```

### Callback Payload (success)

```
{
  "request_id": "kotoba-stt_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "status": "COMPLETED",
  "model_id": "kotoba-stt",
  "error": null,
  "output": {
    "media_url": [
      "https://pub-582b7213209642b9b995c96c95a30381.r2.dev/v1/{request_id}/output.json"
    ],
    "media_type": "application/json",
    "text": "The transcript of the audio.",
    "words": [
      {
        "text": "The",
        "start": null,
        "end": null,
        "type": "word",
        "speaker_id": null
      }
    ],
    "language_code": "en",
    "language_probability": 0.98,
    "audio_duration_secs": 17.03
  },
  "created_at": "2026-10-09T10:04:54.387Z",
  "completed_at": "2026-10-09 10:06:21"
}
```

### Failure callback shape

```
{
  "request_id": "kotoba-stt_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "status": "ERROR",
  "model_id": "kotoba-stt",
  "error": "Description of the failure"
}
```

#### Delivery semantics

-   **terminal** mode: one Webhook callback when the request is COMPLETED or ERROR.
-   **sync** mode: a Webhook callback on every poll while the request is processing (status `PROCESSING`), plus the final COMPLETED/ERROR callback.
-   Callbacks are idempotent on `request_id` — de-duplicate on it.
-   Respond `200` within a few seconds; the Webhook endpoint must be HTTPS.

## Request Parameters

Parameter

Required

Type

Default

Allowed values / range

Description

`audio_url`

Yes

string

—

A publicly reachable `https` URL (recommended) or a base64 data URI of an audio file of 2 minutes or less, for example MP3, WAV or M4A

The speech to transcribe. A file that states a longer length is refused at submit.

`language`

Yes

string

—

`ja`, `en`, `ko`, `zh`, `es`, `zh-hk`, `zh-tw`

The language spoken in the audio. The model does not detect it.

`with_timestamps`

No

boolean

`false`

`true`, `false`

Return a start and end time for each word in `output.words`.

`diarization`

No

boolean

`false`

`true`, `false`

Attribute each word to a speaker (`speaker_id`). Word times stay empty unless `with_timestamps` is also `true`.

`keywords`

No

array

—

Up to 1000 terms of at most 64 characters each

Names or product words whose spelling the transcript should prefer.

`kana`

No

boolean

`false`

`true`, `false`

Write personal and place names in katakana (Japanese).

### Good to know

-   Each request returns **one transcript**. `batch_size`, `num_images`, `number_of_images`, `n` and `count` are rejected — send one request per transcript.
-   This API has no length setting: the transcript covers the whole audio file (2 minutes or less). `duration`, `seconds`, `num_frames`, `frames_per_second` and `music_length_ms` are rejected.
-   Send every field at the top level of the JSON body; `parameters`, `content` and `text` are rejected.
-   Send one audio file as `audio_url`: a publicly reachable `https` URL (recommended) or a base64 data URI, 2 minutes or less. A file that states a longer length is refused at submit, with nothing charged; a file that cannot be downloaded fails the request with `status: "ERROR"`, and nothing is charged.
-   `language` is required: `ja`, `en`, `ko`, `zh`, `es`, `zh-hk` or `zh-tw`. The model does not detect the language, so send the one spoken in the audio.
-   `keywords` takes at most 1000 terms of at most 64 characters each; a longer term fails the request with `status: "ERROR"`, and nothing is charged.
-   Audio without speech returns an empty `output.text`; the request is still billed for the length of the audio.
-   `audio_urls` is rejected (send one file per request, as `audio_url`).
-   `prompt` is rejected (use `keywords` to suggest the spelling of names and terms).
-   `language_code` is rejected (send the language as `language`).
-   Use the JSON types shown: `audio_url` and `language` are strings, `with_timestamps`, `diarization` and `kana` are `true` or `false` (not `"true"`), and `keywords` is a list of strings.
-   Omit a field instead of sending `null`.
-   Results are always returned as a URL in `output.media_url`; `sync_mode` is accepted only as `false`.
-   Webhooks are set with the `X-Webhook-URL` / `X-Webhook-Mode` headers; `webhook_url`, `webhook_mode` and `callback_url` in the JSON body are rejected.
-   `stream` is not supported: the result is delivered when the job completes.
-   Options this API does not offer are rejected: `input` and `model`.

## Timestamps, Speakers and Keywords

-   With `with_timestamps` `true`, each entry of `output.words` carries `start` and `end` in seconds.
-   With `diarization` `true`, each word carries a `speaker_id`; send `with_timestamps` too if you need the times.
-   `output.words` holds words, the spaces between them (`type` `spacing`) and sound events such as laughter (`type` `audio_event`).
-   `keywords` only suggests spellings: names, brands and product words the transcript should prefer. It does not change the amount billed.

### Example: word timings and speakers

```
{
  "audio_url": "https://pub-582b7213209642b9b995c96c95a30381.r2.dev/doc-assets/audio/speech-17s.mp3",
  "language": "en",
  "with_timestamps": true,
  "diarization": true
}
```

### Example: suggest spellings

```
{
  "audio_url": "https://pub-582b7213209642b9b995c96c95a30381.r2.dev/doc-assets/audio/speech-17s.mp3",
  "language": "en",
  "keywords": [
    "Pixazo",
    "Kotoba"
  ]
}
```

## Example Request

```
{
  "audio_url": "https://pub-582b7213209642b9b995c96c95a30381.r2.dev/doc-assets/audio/speech-17s.mp3",
  "language": "en"
}
```

## Example Response

```
{
  "request_id": "kotoba-stt_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "status": "QUEUED",
  "polling_url": "https://gateway.pixazo.ai/v2/requests/status/kotoba-stt_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
```

## Request Headers

Header

Required

Description

Content-Type

Yes

`application/json`

Ocp-Apim-Subscription-Key

Yes

Your API subscription key.

X-Webhook-URL

No

Enable Webhook callbacks (see Webhook section).

X-Webhook-Mode

No

`terminal` (default) or `sync` (see Webhook section).

## Response Handling

Status Code

Meaning

202

Accepted — request queued; returns `request_id` and `polling_url`.

400

Invalid request — a required field is missing, a value has the wrong type, is too long, out of range or not one of the allowed values, or a field this operation does not accept was sent. The message says what to fix.

401

Unauthorized — missing or invalid subscription key.

402

Insufficient balance for the hold this request needs.

429

Too many requests.

500

Internal server error.

Parameter problems are caught when you submit and returned as a `400`, before any hold is placed on your balance. Problems that need the model to run are reported through the status endpoint as `status: "ERROR"`, with a message in `error` saying what went wrong. Failed requests are not billed — the hold is released in full.

Condition

How it surfaces

Missing or invalid subscription key

synchronous `401`

Balance too low for the hold

synchronous `402`

Missing or empty `audio_url`, `audio_url` not a string

synchronous `400`

Missing or empty `language`, `language` not a string

synchronous `400`

`keywords` not a list, or more than 1000 items

synchronous `400`

`with_timestamps` or `diarization` or `kana` not `true` or `false` — for example the string `"true"`

synchronous `400`

`sync_mode` set to anything other than `false` (results are always returned as a URL in `output.media_url`)

synchronous `400`

`audio_urls` (send one file per request, as `audio_url`)

synchronous `400`

`prompt` (use `keywords` to suggest the spelling of names and terms)

synchronous `400`

`language_code` (send the language as `language`)

synchronous `400`

`batch_size`, `num_images`, `number_of_images`, `n`, `count` (one transcript per request)

synchronous `400`

`duration`, `seconds`, `num_frames`, `frames_per_second`, `music_length_ms` (the length is the audio file's own)

synchronous `400`

`parameters`, `content`, `text` (send the fields at the top level of the JSON body)

synchronous `400`

`webhook_url`, `webhook_mode`, `callback_url` in the JSON body (use the `X-Webhook-URL` / `X-Webhook-Mode` headers)

synchronous `400`

`stream` (the result is delivered when the job completes)

synchronous `400`

`input`, `model` (not accepted by this operation)

synchronous `400`

An `audio_url` whose file states a length over 2 minutes (the response names the measured length)

synchronous `400`

An audio file that cannot be downloaded, is larger than 32 MB, or that the model cannot decode

`status: "ERROR"`

Audio longer than 2 minutes whose file did not state its length

`status: "ERROR"`

A `keywords` term longer than 64 characters

`status: "ERROR"`

### Example error responses

A refused request returns `error`, a `message` that joins every problem found, and the same problems one by one in `violations`.

#### Missing `audio_url` (synchronous `400`)

```
{
  "error": "Invalid request parameters",
  "message": "'audio_url' is required",
  "violations": [
    "'audio_url' is required"
  ]
}
```

#### A language that is not offered (synchronous `400`)

```
{
  "error": "Invalid request parameters",
  "message": "'language' must be one of: \"ja\", \"en\", \"ko\", \"zh\", \"es\", \"zh-hk\", \"zh-tw\" (got \"fr\")",
  "violations": [
    "'language' must be one of: \"ja\", \"en\", \"ko\", \"zh\", \"es\", \"zh-hk\", \"zh-tw\" (got \"fr\")"
  ]
}
```

#### More than one file per request (synchronous `400`)

```
{
  "error": "Invalid request parameters",
  "message": "'audio_urls' must be one of:  (got [\"https://pub-582b7213209642b9b995c96c95a30381.r2.dev/doc-assets/audio/speech-17s.mp3\",\"https://pub-582b7213209642b9b995c96c95a30381.r2.dev/doc-assets/audio/speech-17s.mp3\"]); 'audio_urls' is not accepted: each request transcribes one audio file, as 'audio_url'.",
  "violations": [
    "'audio_urls' must be one of:  (got [\"https://pub-582b7213209642b9b995c96c95a30381.r2.dev/doc-assets/audio/speech-17s.mp3\",\"https://pub-582b7213209642b9b995c96c95a30381.r2.dev/doc-assets/audio/speech-17s.mp3\"])",
    "'audio_urls' is not accepted: each request transcribes one audio file, as 'audio_url'."
  ]
}
```

#### `with_timestamps` as a string (synchronous `400`)

```
{
  "error": "Invalid request parameters",
  "message": "'with_timestamps' must be boolean (got string)",
  "violations": [
    "'with_timestamps' must be boolean (got string)"
  ]
}
```

#### A length setting (synchronous `400`)

```
{
  "error": "Invalid request parameters",
  "message": "'duration' must be one of:  (got 10); 'duration' is not accepted: the length is the audio file's own (2 minutes or less).",
  "violations": [
    "'duration' must be one of:  (got 10)",
    "'duration' is not accepted: the length is the audio file's own (2 minutes or less)."
  ]
}
```

## Retrieving Results

Poll the status endpoint with the `request_id` from the submit response until `status` is `COMPLETED` or `ERROR`, then read the transcript in `output.text` (the same JSON is stored as a file at `output.media_url[0]`). Treat any status other than `QUEUED`, `PROCESSING` or `COMPLETED` as a failure.

### Endpoint

```
GET https://gateway.pixazo.ai/v2/requests/status/{request_id}
Ocp-Apim-Subscription-Key: YOUR_SUBSCRIPTION_KEY
```

### cURL example

```
curl 'https://gateway.pixazo.ai/v2/requests/status/kotoba-stt_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' \
  -H 'Ocp-Apim-Subscription-Key: YOUR_SUBSCRIPTION_KEY'
```

### Completed response

```
{
  "request_id": "kotoba-stt_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "status": "COMPLETED",
  "model_id": "kotoba-stt",
  "error": null,
  "output": {
    "media_url": [
      "https://pub-582b7213209642b9b995c96c95a30381.r2.dev/v1/{request_id}/output.json"
    ],
    "media_type": "application/json",
    "text": "The transcript of the audio.",
    "words": [
      {
        "text": "The",
        "start": null,
        "end": null,
        "type": "word",
        "speaker_id": null
      }
    ],
    "language_code": "en",
    "language_probability": 0.98,
    "audio_duration_secs": 17.03
  },
  "created_at": "2026-10-09T10:04:54.387Z",
  "completed_at": "2026-10-09 10:06:21"
}
```

## Response Fields

Field

Type

Description

request\_id

string

Unique request identifier.

status

string

QUEUED, PROCESSING, COMPLETED or ERROR.

model\_id

string

The model that handled the request — `kotoba-stt` for this endpoint.

output.text

string

The transcript. It is empty for audio without speech.

output.words

array of objects

The transcript word by word: `text`, `type` (`word`, `spacing` or `audio_event`), and `start` / `end` in seconds with `with_timestamps`, `speaker_id` with `diarization`.

output.language\_code

string

The language used: the `language` you sent.

output.language\_probability

number

`1`, because the language comes from your request.

output.audio\_duration\_secs

number

The length of the audio in seconds: what the request is billed for.

output.truncated

boolean

Present and `true` only when a text longer than 32 KB was shortened in this response; the file at `output.media_url[0]` holds it in full.

output.media\_url

array of strings

URL of a JSON file holding the same fields in full.

output.media\_type

string

`application/json`.

created\_at

string

Request creation timestamp.

completed\_at

string

Completion timestamp.

error

string

Error message when `status` is ERROR.

polling\_url

string

Status URL for this request (submit response only).

## Status Values & Flow

`QUEUED` → `PROCESSING` → `COMPLETED` (success) or `ERROR` (failure).

### Typical workflow

1.  Send the request to `POST https://gateway.pixazo.ai/kotoba-stt/v1/speech-to-text`.
2.  Save the `request_id` from the `202` response.
3.  Poll `GET /v2/requests/status/{request_id}` every 10 seconds or so, or use a Webhook instead.
4.  When `status` is `COMPLETED`, read the transcript in `output.text` (the same JSON is stored as a file at `output.media_url[0]`).

## How Billing Is Measured

Billing is measured **per second of audio**: the length of the file you send, rounded up to the next whole second, 2 minutes at most. A hold for the longest file the API accepts is placed when you submit; when the transcript is ready, the charge is set from the audio's length, shown in `output.audio_duration_secs`, and the rest of the hold is released. Timestamps, speakers and keywords do not change the amount. A failed request is not charged. The current rate is shown in the pricing panel on this page.
