---
type: AI Model
id: mai-voice
title: MAI Voice API
provider: Microsoft
description: "Microsoft AI's speech family on Azure Speech. MAI-Voice-2 turns text into expressive, multilingual speech across 47 voices and 18 locales with SSML emotion control; MAI-Transcribe-1.5 transcribes audio across 43 languages."
resource: https://www.pixazo.ai/models/mai-voice
docs_url: https://www.pixazo.ai/models/mai-voice
latest_version: v2
tags:
  - text-to-speech
  - speech-to-text
  - microsoft
variants:
  - id: mai-voice-2
    name: MAI Voice 2
    version: 2
    capabilities:
      - Text to Speech
  - id: mai-transcribe
    name: MAI Transcribe 1.5
    version: 1.5
    capabilities:
      - Speech to Text
timestamp: 2026-08-31T16:52:32.726Z
---

# MAI Voice API

> Provider: **Microsoft**
> Source: https://www.pixazo.ai/models/mai-voice

Microsoft AI's speech family on Azure Speech. MAI-Voice-2 turns text into expressive, multilingual speech across 47 voices and 18 locales with SSML emotion control; MAI-Transcribe-1.5 transcribes audio across 43 languages.

## MAI Voice 2

### Text to Speech

## Microsoft MAI Voice 2 API Documentation

Generate expressive speech from text with Microsoft MAI-Voice-2 on Azure Speech. Asynchronous: submit returns a `request_id`; poll the status endpoint until the request is `COMPLETED`. The speech is returned as a 24 kHz mono MP3 file.

```
POST https://gateway.pixazo.ai/mai-voice-2/v1/text-to-speech
```

## Authentication

All requests require an API key passed via header.

Header

Type

Required

Description

Ocp-Apim-Subscription-Key

string

Yes

Your API subscription key

## Text to Speech - Microsoft MAI Voice 2

## Request Code

HTTP Python JavaScript cURL

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

{
  "text": "Hello from Pixazo. This is MAI Voice 2.",
  "voice": "en-US-Harper:MAI-Voice-2",
  "style": "excited",
  "styledegree": 1
}
```

```
import requests

url = "https://gateway.pixazo.ai/mai-voice-2/v1/text-to-speech"
headers = {
    "Content-Type": "application/json",
    "Ocp-Apim-Subscription-Key": "YOUR_SUBSCRIPTION_KEY"
}
data = {
  "text": "Hello from Pixazo. This is MAI Voice 2.",
  "voice": "en-US-Harper:MAI-Voice-2",
  "style": "excited",
  "styledegree": 1
}

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

```
const res = await fetch("https://gateway.pixazo.ai/mai-voice-2/v1/text-to-speech", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Ocp-Apim-Subscription-Key": "YOUR_SUBSCRIPTION_KEY"
  },
  body: JSON.stringify({
  "text": "Hello from Pixazo. This is MAI Voice 2.",
  "voice": "en-US-Harper:MAI-Voice-2",
  "style": "excited",
  "styledegree": 1
})
});
console.log(await res.json());
```

```
curl -X POST 'https://gateway.pixazo.ai/mai-voice-2/v1/text-to-speech' \
  -H 'Content-Type: application/json' \
  -H 'Ocp-Apim-Subscription-Key: YOUR_SUBSCRIPTION_KEY' \
  --data-raw '{"text": "Hello from Pixazo. This is MAI Voice 2.", "voice": "en-US-Harper:MAI-Voice-2", "style": "excited", "styledegree": 1}'
```

## Output

```
{
  "request_id": "microsoft-mai-voice-2_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "status": "QUEUED",
  "polling_url": "https://gateway.pixazo.ai/v2/requests/status/microsoft-mai-voice-2_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
```

[Try Now](https://api.pixazo.ai/api-details#api=mai-voice-2&operation=microsoft-mai-voice-2-request)

## 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/mai-voice-2/v1/text-to-speech' \
  -H 'Content-Type: application/json' \
  -H 'Ocp-Apim-Subscription-Key: YOUR_SUBSCRIPTION_KEY' \
  -H 'X-Webhook-URL: https://your-server.com/webhook' \
  --data-raw '{"text": "Hello from Pixazo. This is MAI Voice 2.", "voice": "en-US-Harper:MAI-Voice-2", "style": "excited", "styledegree": 1}'
```

### Callback Payload (success)

```
{
  "request_id": "microsoft-mai-voice-2_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "status": "COMPLETED",
  "model_id": "microsoft-mai-voice-2",
  "output": {
    "media_url": "https://pub-582b7213209642b9b995c96c95a30381.r2.dev/v1/{request_id}/output.mp3",
    "media_type": "audio/mpeg"
  },
  "created_at": "2026-07-31T11:34:16.102Z",
  "completed_at": "2026-07-31T11:34:20.123Z"
}
```

### Failure callback shape

```
{
  "request_id": "microsoft-mai-voice-2_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "status": "ERROR",
  "model_id": "microsoft-mai-voice-2",
  "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 each status change.
-   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

`text`

Yes

string

—

1–8000 characters

The text to speak. Billing counts these characters, rounded up to the next 1000.

`voice`

No

string

`en-US-Harper:MAI-Voice-2`

any prebuilt MAI-Voice-2 voice id

Which voice speaks the text. 47 prebuilt MAI-Voice-2 voices are available across 18 locales; every id ends in `:MAI-Voice-2`, for example `es-MX-Valeria:MAI-Voice-2`. See Voices and Styles below.

`style`

No

string

— (neutral)

voice-specific — e.g. `excited`, `happy`, `sad`, `angry`, `whispering`, `shouting`

Speaking style, applied as the SSML `mstts:express-as` style. The supported set varies by voice and some voices support no styles at all. Microsoft specifies that when the style is missing or invalid the whole expressive block is ignored and the voice uses its default neutral speech.

`styledegree`

No

number

`1`

0.01–2 inclusive

Intensity of the chosen style; applies only when `style` is set. `1` is the predefined style intensity and `2` doubles it. Microsoft specifies that if the value is missing or the voice does not support it, the attribute is ignored.

### Text limits

-   `text` is required and must be a non-empty string of at most **8000 characters**. Longer text is rejected at submit.
-   Send plain text, not SSML — `voice`, `style` and `styledegree` are applied for you.
-   Cost scales with the number of characters you submit (see Pricing).

## Voices and Styles

47 prebuilt MAI-Voice-2 voices are available across 18 locales (de-DE, en-AU, en-US, es-ES, es-MX, fr-FR, hi-IN, hu-HU, it-IT, ko-KR, nl-NL, pt-BR, pt-PT, ro-RO, ru-RU, th-TH, tr-TR, zh-CN). Pass the full voice id in `voice`; every id ends in `:MAI-Voice-2`. The current list is maintained by Microsoft in the [MAI-Voice documentation](https://learn.microsoft.com/en-us/azure/ai-services/speech-service/mai-voices).

Example voice

Locale

Gender

`en-US-Harper:MAI-Voice-2`

en-US

Female

`en-US-Ethan:MAI-Voice-2`

en-US

Male

`en-AU-Lisa:MAI-Voice-2`

en-AU

Female

`es-MX-Valeria:MAI-Voice-2`

es-MX

Female

`fr-FR-Marc:MAI-Voice-2`

fr-FR

Male

`hi-IN-Kavya:MAI-Voice-2`

hi-IN

Female

`zh-CN-Mei:MAI-Voice-2`

zh-CN

Female

### Styles

Supported styles are per voice. Most voices support the expressive set `angry`, `confused`, `determined`, `disgusted`, `embarrassed`, `excited`, `fearful`, `happy`, `hopeful`, `jealous`, `joyful`, `regretful`, `relieved`, `sad`, `shouting`, `softvoice`, `surprised`, `whispering`. Some voices instead support a narrative set (`adventurous`, `caring`, `empathy`, `curious`, `encouraging`, `excited`, `friendly`, `cheerful`, `nostalgic`, `reflective`, `sad`, `disappointed`, `serious`), and a few voices support no styles at all. Check the voice you are using against Microsoft’s per-voice style table before relying on a style.

### Output

The completed request returns `output.media_url`, a 24 kHz mono MP3 (`audio/mpeg`). Fetch that URL to download the audio.

```
{
  "text": "Welcome to Microsoft Build. MAI Voice 2 supports multilingual expressive synthesis.",
  "voice": "en-US-Harper:MAI-Voice-2",
  "style": "happy",
  "styledegree": 1.2
}
```

## Example Request

```
{
  "text": "Hello from Pixazo. This is MAI Voice 2.",
  "voice": "en-US-Harper:MAI-Voice-2",
  "style": "excited",
  "styledegree": 1
}
```

## Example Response

```
{
  "request_id": "microsoft-mai-voice-2_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "status": "QUEUED",
  "polling_url": "https://gateway.pixazo.ai/v2/requests/status/microsoft-mai-voice-2_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).

## Response Handling

Status Code

Meaning

202

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

400

Bad request — missing `text`, `text` longer than 8000 characters, or a malformed JSON body.

401

Unauthorized — missing or invalid subscription key.

402

Insufficient balance.

429

Too many requests.

500

Internal server error.

A failure during synthesis is reported through the status endpoint as `status: "ERROR"`, not as a synchronous error. Failed requests are not billed.

## Retrieving Results

Poll the status endpoint with the `request_id` from the submit response until `status` is `COMPLETED` (or `FAILED`/`ERROR`), then fetch `output.media_url` for the audio.

```
curl 'https://gateway.pixazo.ai/v2/requests/status/microsoft-mai-voice-2_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' \
  -H 'Ocp-Apim-Subscription-Key: YOUR_SUBSCRIPTION_KEY'
```

### Completed response

```
{
  "request_id": "microsoft-mai-voice-2_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "status": "COMPLETED",
  "model_id": "microsoft-mai-voice-2",
  "output": {
    "media_url": "https://pub-582b7213209642b9b995c96c95a30381.r2.dev/v1/{request_id}/output.mp3",
    "media_type": "audio/mpeg"
  },
  "created_at": "2026-07-31T11:34:16.102Z",
  "completed_at": "2026-07-31T11:34:20.123Z"
}
```

## Response Fields

Field

Type

Description

request\_id

string

Unique request identifier.

status

string

QUEUED, PROCESSING, COMPLETED, FAILED, or ERROR.

model\_id

string

The model that handled the request.

output.media\_url

string

URL of the generated MP3 audio file.

output.media\_type

string

`audio/mpeg`.

created\_at

string

Request creation timestamp.

completed\_at

string

Completion timestamp.

error

string

Error message when `status` is FAILED/ERROR.

## Status Values & Flow

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

### Pricing

Billed at **$0.022 per 1000 characters** of input text, rounded up to the next 1000 characters. Billing is based on the **text you submit**, not on the length of the audio produced.

Characters submitted

Billed units (1000 chars)

Cost

250

1

$0.022

1000

1

$0.022

1001

2

$0.044

8000 (maximum)

8

$0.176

Because the character count is known when you submit, the cost of a request is determined at submit time. Failed requests are not billed.

## MAI Transcribe 1.5

### Speech to Text

## Microsoft MAI Transcribe API Documentation

Transcribe audio into text with Microsoft's MAI-Transcribe-1.5 model, with automatic language detection across 43 languages. Asynchronous: submit returns a `request_id`; poll the status endpoint until the request is `COMPLETED`. The transcript is returned as a JSON file.

```
POST https://gateway.pixazo.ai/mai-transcribe/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 - Microsoft MAI Transcribe

## Request Code

HTTP Python JavaScript cURL

```
POST https://gateway.pixazo.ai/mai-transcribe/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",
  "locales": ["en"],
  "transcribe_style": "verbatim",
  "phrase_list": ["Acme Corp", "Dr. Yamada"]
}
```

```
import requests

url = "https://gateway.pixazo.ai/mai-transcribe/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",
  "locales": ["en"],
  "transcribe_style": "verbatim",
  "phrase_list": ["Acme Corp", "Dr. Yamada"]
}

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

```
const res = await fetch("https://gateway.pixazo.ai/mai-transcribe/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",
  "locales": ["en"],
  "transcribe_style": "verbatim",
  "phrase_list": ["Acme Corp", "Dr. Yamada"]
})
});
console.log(await res.json());
```

```
curl -X POST 'https://gateway.pixazo.ai/mai-transcribe/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", "locales": ["en"], "transcribe_style": "verbatim", "phrase_list": ["Acme Corp", "Dr. Yamada"]}'
```

## Output

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

[Try Now](https://api.pixazo.ai/api-details#api=mai-transcribe&operation=microsoft-mai-transcribe-request)

## 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/mai-transcribe/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", "locales": ["en"], "transcribe_style": "verbatim", "phrase_list": ["Acme Corp", "Dr. Yamada"]}'
```

### Callback Payload (success)

```
{
  "request_id": "microsoft-mai-transcribe_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "status": "COMPLETED",
  "model_id": "microsoft-mai-transcribe",
  "output": {
    "media_url": "https://pub-582b7213209642b9b995c96c95a30381.r2.dev/v1/{request_id}/output.json",
    "media_type": "application/json"
  },
  "created_at": "2026-07-31T11:34:16.102Z",
  "completed_at": "2026-07-31T11:34:20.123Z"
}
```

### Failure callback shape

```
{
  "request_id": "microsoft-mai-transcribe_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "status": "ERROR",
  "model_id": "microsoft-mai-transcribe",
  "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 each status change.
-   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

—

http(s) URL; WAV, MP3 or FLAC, under 50 MB

Public URL of the audio to transcribe. The file is fetched by the gateway, so the URL must be reachable without authentication.

`locales`

No

array of string

— (multilingual auto-detect)

ISO language codes, e.g. `["en"]`, `["hi"]`, `["zh"]`

Optional. Force recognition into a single language. Omit it and the model runs in multilingual mode across the 43 supported languages.

`transcribe_style`

No

string

— (readability-optimised)

`verbatim`

Optional. Set to `verbatim` to preserve the original spoken content including filler words and disfluencies. The default output is cleaned up for readability.

`phrase_list`

No

array of string

—

list of short phrases, e.g. `["Contoso", "Jessie"]`

Names, product terms, acronyms or jargon that appear in the audio. Biases recognition towards this vocabulary, which improves accuracy on proper nouns and domain terminology.

### Audio limits

-   The audio file must be **under 50 MB** and in WAV, MP3 or FLAC format.
-   `audio_url` must be an `http` or `https` URL and must be publicly reachable.
-   Speaker diarization is not supported — the transcript is not split by speaker.
-   Prompt-tuning is not supported. Use `phrase_list` to bias recognition instead.
-   Cost scales with the length of the audio (see Pricing).

## Transcript Format

The completed request returns `output.media_url`, a JSON file containing the transcript. Fetch that URL to read the result.

```
{
  "text": "From Pixazo, this is MAI Voice 2 running through our own gateway.",
  "phrases": [
    {
      "offsetMilliseconds": 0,
      "durationMilliseconds": 5856,
      "text": "From Pixazo, this is MAI Voice 2 running through our own gateway.",
      "locale": "en",
      "confidence": 0
    }
  ],
  "duration_ms": 5860
}
```

Field

Type

Description

text

string

The full transcript, with punctuation and capitalisation.

phrases

array of object

Recognised segments in order. Each entry has `text`, `offsetMilliseconds`, `durationMilliseconds`, `locale` (the language detected for that segment) and `confidence`.

duration\_ms

number

Length of the transcribed audio in milliseconds. This is the quantity you are billed on.

Timestamps are returned per phrase, not per word: each entry in `phrases` carries `offsetMilliseconds` and `durationMilliseconds`.

## Example Request

```
{
  "audio_url": "https://pub-582b7213209642b9b995c96c95a30381.r2.dev/doc-assets/audio/speech-17s.mp3",
  "locales": ["en"],
  "transcribe_style": "verbatim",
  "phrase_list": ["Acme Corp", "Dr. Yamada"]
}
```

## Example Response

```
{
  "request_id": "microsoft-mai-transcribe_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "status": "QUEUED",
  "polling_url": "https://gateway.pixazo.ai/v2/requests/status/microsoft-mai-transcribe_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).

## Response Handling

Status Code

Meaning

202

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

400

Bad request — missing `audio_url`, a non-http(s) URL, or an audio format that could not be determined from the URL and was not supplied.

401

Unauthorized — missing or invalid subscription key.

402

Insufficient balance.

429

Too many requests.

500

Internal server error.

An unreachable `audio_url`, an oversized file, or audio containing no detectable speech is reported through the status endpoint as `status: "ERROR"`, not as a synchronous error. Failed requests are not billed.

## Retrieving Results

Poll the status endpoint with the `request_id` from the submit response until `status` is `COMPLETED` (or `FAILED`/`ERROR`), then fetch `output.media_url` for the transcript.

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

### Completed response

```
{
  "request_id": "microsoft-mai-transcribe_019dxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "status": "COMPLETED",
  "model_id": "microsoft-mai-transcribe",
  "output": {
    "media_url": "https://pub-582b7213209642b9b995c96c95a30381.r2.dev/v1/{request_id}/output.json",
    "media_type": "application/json"
  },
  "created_at": "2026-07-31T11:34:16.102Z",
  "completed_at": "2026-07-31T11:34:20.123Z"
}
```

## Response Fields

Field

Type

Description

request\_id

string

Unique request identifier.

status

string

QUEUED, PROCESSING, COMPLETED, FAILED, or ERROR.

model\_id

string

The model that handled the request.

output.media\_url

string

URL of the transcript JSON file.

output.media\_type

string

`application/json`.

created\_at

string

Request creation timestamp.

completed\_at

string

Completion timestamp.

error

string

Error message when `status` is FAILED/ERROR.

## Status Values & Flow

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

### Pricing

Billed at **$0.006 per minute of audio** ($0.36 per audio hour), rounded up to the next whole minute. Billing is based on the **length of the audio you submit**, not on the size of the transcript.

Audio length

Billed minutes

Cost

20 seconds

1

$0.006

90 seconds

2

$0.012

10 minutes

10

$0.06

1 hour

60

$0.36

A hold is placed when the request is submitted, because the audio length is not known until transcription completes. The hold is reduced to the real cost once the duration is known, and released in full if the request fails.
