---
title: "Ask questions about your media with the AI chat API"
description: "Ask a question about the media stored in Speak AI and read the answer back, and list the AI chat prompts that have already run in your account."
---

> Documentation Index
> Fetch the complete documentation index at: https://docs.speakai.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Ask questions about your media with the AI chat API


import EndpointIndex from "@/components/api/EndpointIndex.astro";

The Speak AI API exposes 3 ai chat endpoints under the base URL `https://api.speakai.co/v1`. Every request needs the `x-speakai-key` and `x-access-token` headers described in [Authentication](/api/authentication/).

Fetch all your Speak AI Chat Responses.

## What can you do with the ai chat endpoints?

Speak AI groups these 3 endpoints under the ai chat resource. Each entry below links to the full reference for that endpoint further down this page.

| Method | Path | What it does |
| --- | --- | --- |
| `GET` | [`/prompt`](#get-prompt) | All AI Chats |
| `POST` | [`/prompt`](#post-prompt) | Ask AI Chat |
| `GET` | [`/prompt/analysisQuote`](#get-prompt-analysis-quote) | Analysis Quote |

<h2 id="get-prompt">All AI Chats</h2>

Get AI Chat History.

<div slot="code">

<CodePanel label="Example request">

```bash
curl -X GET 'https://api.speakai.co/v1/prompt' \
  -H 'x-speakai-key: sk_test_speak_0000000000000000' \
  -H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000'
```

</CodePanel>

<ResponsePanel id="get-prompt" statuses={[{"code":"200","description":"OK"}]}>

<div slot="status-200">

**`200` OK**

Response body, `application/json`.

| Field | Type |
| --- | --- |
| `status` | string |
| `data` | object |
| `data.totalCount` | integer |
| `data.pages` | integer |
| `data.history` | object[] |

Deeper nested fields are not listed. See the example response below for the full shape.

Example response (List of Prompts), `application/json`. Arrays are shortened to one entry and long strings are cut.

```json
{
  "status": "success",
  "data": {
"totalCount": 2,
"pages": 1,
"history": [
  {
    "id": "64b04ce76db5f05cba07a3ff",
    "prompt": "Identify the top 5 action items to prioritize for maximum impact",
    "answer": "",
    "createdAt": "2023-07-13T19:13:43.953Z",
    "assistantType": "general",
    "mediaName": "How To Upload A Video File On Desktop",
    "mediaIds": [
      "de737309a4e9"
    ],
    "state": "failed",
    "link": "https://app.speakai.co/media/insight/MEDIA_URL"
  }
]
  }
}
```

</div>

</ResponsePanel>

</div>

<h2 id="post-prompt">Ask AI Chat</h2>

AI Chat API allows you to run your own prompt via API.

`Prompt` (**required**) - It will be your query - please be as descriptive as possible to get an accurate output

`assistantType` (**default: general**) - Allow values are - general, researcher, marketer, sales, recruiter

`mediaIds` (**required**) - You can pass multiple `mediaIds` to run your prompt OR pass a single mediaId in an array

**Request rules.** `prompt` is the only field you must send. `assistantTemplateId` becomes required when you set `assistantType` to `custom`; for every other value it is optional. You do not have to send `folderId` or `mediaIds`: a body with only `prompt` is accepted. Send one of them anyway, or the assistant has no material to read.

**Streaming.** Send `isStream: true` to get the answer as Server-Sent Events instead of one JSON body. The response comes back as `text/event-stream` with `Cache-Control: no-cache` and `Connection: keep-alive`. Every frame is written as `data: <json>` followed by a blank line, with no `event:` or `id:` field, so read them as default message events. The stream ends on an `end` frame, or an `error` frame if it failed, and there is no `[DONE]` sentinel. `isStream: true` cannot be combined with `isIndividualPrompt: true`; that returns 400 with errorCode `STREAMING_NOT_SUPPORTED_FOR_INDIVIDUAL`.

**Audio and video analysis.** Send `analysisMediaId` and `analysisInput` together to have the model take in the recording itself rather than only its transcript. `analysisMediaId` must be one of `mediaIds`. This is a premium feature billed in credits per hour of media on top of the normal chat cost, so price it with `GET /prompt/analysisQuote` before you commit to a run. When the file turns out to be ineligible the chat still answers from the transcript rather than failing, and no analysis credits are charged.

### Request body

Fields marked **required** are the ones the server rejects the request without. Anything conditional, where a field becomes required only alongside another, is described under Request rules above rather than marked here.

| Field | Type | Description |
| --- | --- | --- |
| `prompt` | string, **required** | The question or instruction you want the AI to answer. Send a non-empty string, and be specific to get a useful answer. Required. the schema.required rejects an empty string. No maximum length. |
| `isStream` | boolean | Set this to true to stream the answer back as Server-Sent Events instead of waiting for one JSON response. Optional. Schema default is false, but see the note on defaults. When true, send an Accept header of text/event-stream. Cannot be combined with isIndividualPrompt set to true: the route returns 400 with errorCode STREAMING_NOT_SUPPORTED_FOR_INDIVIDUAL. |
| `folderId` | string | The folder you want the AI to read from. Everything in that folder becomes the context for your prompt. Optional. Accepts an empty string or null. Schema default is an empty string. |
| `mediaIds` | string[] | The specific media items you want the AI to read. Pass one or more media ids, even when you only have one. Optional. Accepts null and an empty array. No item cap and no per-item length limit. |
| `folderIds` | string[] | Several folders you want the AI to read from at once. Use this instead of folderId when the context spans more than one folder. Optional. Accepts null and an empty array. No item cap. No default. |
| `assistantType` | string | The assistant persona that shapes the answer. Optional, and restricted to the values listed here: any other string is rejected, and so is an empty string. Omit it to use the default persona saved on your user profile, which falls back to "general" when you have not saved one. Speak resolves that fallback when it creates the chat, so it fixes the persona for the whole conversation. Sending "custom" makes assistantTemplateId required. One of: `researcher`, `marketer`, `sales`, `general`, `recruiter`, `custom`. |
| `assistantTemplateId` | string | The id of your custom assistant template. Send this when you set assistantType to "custom". Conditionally required: required when assistantType is "custom", otherwise optional, accepts an empty string, and carries a schema default of an empty string. Does not accept null in either branch. |
| `tags` | string[] | Tags that narrow the media the AI reads to only items carrying those tags. Optional. Accepts null and an empty array. No item cap. No default. |
| `speakers` | string[] | Speaker names that narrow the transcript content the AI reads to only those speakers. Optional. Accepts null and an empty array. No item cap. No default. |
| `promptId` | string | The id of an existing chat you want to continue. Leave it out to start a new chat. Optional. Accepts an empty string or null. Schema default is an empty string. |
| `fieldId` | string | A single field you want the AI to fill or reference. Optional. Accepts an empty string. Does not accept null. No default. |
| `fieldIds` | string[] | Several fields you want the AI to fill or reference. Optional. Accepts null. Schema default is null. Maximum 10 items, and each item is at most 200 characters. |
| `isIndividualPrompt` | boolean | Set this to true to run the prompt separately against each media item instead of once across all of them. Optional. Schema default is false. Cannot be combined with isStream set to true; that combination returns 400. |
| `filters` | object | Extra filters that narrow which media the AI reads. Optional. Accepts null. The object's keys are not validated, so any object is accepted at the top level. No default. |
| `startDate` | string | Only include media created on or after this date. Optional. Send an ISO 8601 date string or a timestamp. Accepts null and carries a schema default of null. |
| `endDate` | string | Only include media created on or before this date. Optional. Send an ISO 8601 date string or a timestamp. Accepts null and carries a schema default of null. |
| `modelId` | string | The language model you want to answer this chat. Leave it out, or send an empty string or null, to use your company default. The models Speak AI offers today are `gemini-2.5-flash`, `gemini-3-flash-preview`, `gemini-3.5-flash`, `gpt-5.6-terra`, `gpt-5.6-sol`, `gpt-5.5`, `gpt-5.4-mini-2026-03-17`, `claude-sonnet-5`, `claude-opus-4-8`, `claude-sonnet-4-6`, `x-ai/grok-4.5` and `z-ai/glm-5.2`. The full list below also holds older model ids that are still accepted so existing integrations keep working; treat those as deprecated and do not build against them. One of: `gpt-3.5`, `gpt-3.5-turbo-16k`, `gpt-3.5-turbo-0125`, `gpt-4`, `gpt-4-1106-preview`, `gpt-4-turbo`, `gpt-4o-2024-05-13`, `gpt-4o`, `gpt-4o-mini`, `gpt-4o-2024-08-06`, `gpt-4o-mini-2024-07-18`, `gpt-4.1-2025-04-14`, `gpt-5.1-2025-11-13`, `gpt-5.2`, `gpt-5.4`, `gpt-5.4-mini`, `gpt-5.4-mini-2026-03-17`, `gpt-5.4-nano`, `gpt-5.5`, `gpt-5.5-thinking`, `gpt-5.6-sol`, `gpt-5.6-terra`, `gpt-5.6-luna`, `claude-2`, `claude-3-5-sonnet`, `claude-3-5-sonnet-20241022`, `claude-3-7-sonnet-latest`, `claude-haiku-4-5`, `claude-sonnet-4-6`, `claude-sonnet-5`, `claude-opus-4-8`, `gemini-1.5-pro`, `gemini-1.5-flash`, `gemini-2.0-flash`, `gemini-2.5-pro`, `gemini-2.5-flash`, `gemini-2.5-flash-lite`, `gemini-3-flash-preview`, `gemini-3.1-flash-lite`, `gemini-3.1-pro-preview`, `gemini-3.5-flash`, `x-ai/grok-4.5`, `z-ai/glm-5.2`. |
| `attachments` | object[] | Images or PDFs you want the AI to look at alongside your prompt. Optional. Accepts null. Maximum 10 items. Each item is an object with the keys described below, and unknown keys inside an item are rejected. After schema validation the route checks that every attachment path is an S3 URL under your company's prefix and returns 400 if it is not. |
| `attachments[].type` | string | The kind of file you are attaching. Required on every attachment. One of: `image`, `pdf`. |
| `attachments[].path` | string | The URL of the attached file, which must be a file your company owns. Required on every attachment. Must be a valid URI, and surrounding whitespace is trimmed. Ownership is checked after validation. |
| `attachments[].mimeType` | string | The MIME type of the attached file. Optional. Accepts an empty string or null. Whitespace is trimmed. Not restricted to a value list on this endpoint. |
| `attachments[].name` | string | A display name for the attached file. Optional. Accepts an empty string or null. Whitespace is trimmed. |
| `analysisMediaId` | string | The media item you want the model to listen to or watch, instead of only reading its transcript. Optional. Accepts an empty string or null. Must also appear in mediaIds, and must be sent together with analysisInput. Sending one without the other is rejected with "analysisMediaId and analysisInput must be provided together". Sending an id that is not in mediaIds is rejected with "analysisMediaId must be one of mediaIds". Audio and video analysis is a premium feature: an account that is not enabled for it gets a 400. Call GET /prompt/analysisQuote first to check eligibility and price. |
| `analysisInput` | string | What the model should take in for analysisMediaId. Send "audio" to have it hear the recording, or "video" to have it watch the file. Optional. Accepts an empty string or null. Must be sent together with analysisMediaId. Asking for "video" on an audio-only file is refused. Asking for "audio" on a video extracts the audio track first, which makes the request run longer. Omit both fields for a normal transcript-only chat. One of: `audio`, `video`. |

<div slot="code">

<CodePanel label="Example request">

```bash
curl -X POST 'https://api.speakai.co/v1/prompt' \
  -H 'x-speakai-key: sk_test_speak_0000000000000000' \
  -H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000' \
  -H 'Content-Type: application/json' \
  -d '{
  "prompt": "Tell me how my speaking pace was. Did I speak too fast, at a good pace or too slowly? A good pace is between 140 and 170 words per minute. Less than 140 words per minute is too slow and more than 170",
  "mediaIds": [
"923884998a91"
  ],
  "assistantType": "general"
}'
```

</CodePanel>

<ResponsePanel id="post-prompt" statuses={[{"code":"200","description":"OK"}]}>

<div slot="status-200">

**`200` OK**

Response body, `application/json`.

| Field | Type | Description |
| --- | --- | --- |
| `status` | string |  |
| `data` | object |  |
| `data.promptId` | string | The chat this answer belongs to, a 12-character hex id such as `a3f19c47be02`. Pass it back as `promptId` to ask a follow-up question in the same chat. |
| `data.messageId` | string | The message within that chat, a 12-character hex id such as `7d2e845b1fa9`. Use it to retry, export or give feedback on this answer. |
| `data.state` | string |  |
| `data.answer` | string |  |
| `data.totalMedia` | integer |  |
| `data.references` | any[] |  |

Example response (Success Response), `application/json`. Arrays are shortened to one entry and long strings are cut.

```json
{
  "status": "success",
  "data": {
"promptId": "a3f19c47be02",
"messageId": "7d2e845b1fa9",
"state": "completed",
"answer": "Main discussion points:\n\n- The video demonstrates how to upload a video file through Speak.\n- The process involves signing in, accessing the dashboard, and hitting the upload butto...",
"totalMedia": 1,
"references": []
  }
}
```

</div>

</ResponsePanel>

</div>

<h2 id="get-prompt-analysis-quote">Analysis Quote</h2>

Check whether a media item can be analysed as audio or video, and what that will cost, before running a chat with `analysisInput`.

This is the only check that reflects both the account's premium opt-in and the platform-wide switch, so treat it as the answer on whether a run will work. An ineligible file is a normal `200` with `eligible: false` and a plain-English `reason`, not an error.

The quote shares its eligibility rules, duration cap, model resolution and credit conversion with the run itself, so the figure you get back is the figure the run is charged against.

### Parameters

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `mediaId` | query | string | Yes | The media item you want to price. Must belong to your company. |
| `analysisInput` | query | string | Yes | What the model would take in. Use "audio" to have it hear the recording, or "video" to have it watch the file. Audio on a video file is priced for the extracted audio track, which costs a fraction of a video pass and reaches longer files. |
| `modelId` | query | string | No | Price the run against a specific model. Optional. Leave it out and your company default is used, which is what the run itself would use. |

<div slot="code">

<CodePanel label="Example request">

```bash
curl -X GET 'https://api.speakai.co/v1/prompt/analysisQuote?mediaId=de737309a4e9&analysisInput=audio&modelId=gemini-2.5-flash' \
  -H 'x-speakai-key: sk_test_speak_0000000000000000' \
  -H 'x-access-token: eyJhbGciOiJIUzI1NiJ9.test-access-token.0000000000'
```

</CodePanel>

<ResponsePanel id="get-prompt-analysis-quote" statuses={[{"code":"200","description":"OK"},{"code":"404","description":"Not Found"}]}>

<div slot="status-200">

**`200` OK**

Response body, `application/json`.

| Field | Type | Description |
| --- | --- | --- |
| `status` | string |  |
| `data` | object |  |
| `data.eligible` | boolean | Whether this file can be analysed with this analysisInput right now. |
| `data.reason` | string | Why the file cannot be analysed, in language you can show a user. Present only when eligible is false. |
| `data.credits` | number | Credits the analysis would cost, on top of the normal chat cost. Zero when the file is not eligible. |
| `data.seconds` | number | Duration of the media the quote was priced on, in seconds. Zero when the file is not eligible. |

Example response (Eligible), `application/json`.

```json
{
  "status": "success",
  "data": {
"eligible": true,
"credits": 42,
"seconds": 1830
  }
}
```

Example response (Ineligible - too long for this account), `application/json`.

```json
{
  "status": "success",
  "data": {
"eligible": false,
"reason": "The file is longer than the analysis limit on this account. Raise the limit or trim the file.",
"credits": 0,
"seconds": 0
  }
}
```

</div>

<div slot="status-404">

**`404` Not Found**

Response body, `application/json`.

| Field | Type |
| --- | --- |
| `status` | string |
| `requestId` | string |
| `code` | integer |
| `message` | string |
| `hints` | string[] |

Example response (Analysis Quote - Media not found), `application/json`.

```json
{
  "status": "failed",
  "requestId": "ca665625-645e-438a-867d-366a6e3f133a",
  "code": 404,
  "message": "Requested media not found",
  "hints": [
"Requested media not found"
  ]
}
```

</div>

</ResponsePanel>

</div>

## Related pages

- [API reference](/api/) for the base URL, authentication, and the error format.
- [Authenticate with the Speak AI API using access tokens](/api/authentication/)
- [Upload audio and video to Speak AI and read insights](/api/media/)
- [Create and update live transcription sessions in Speak AI](/api/live-transcription/)
- [Analyze text notes with the Speak AI text endpoints](/api/text/)

Get an API key on the [Speak AI developer page](https://app.speakai.co/developers?utm_source=docs&utm_medium=referral&utm_campaign=api-reference&utm_content=api-ai-chat).

Source: https://docs.speakai.co/api/ai-chat/index.mdx
