This cookbook will demonstrate how to use AssemblyAI’s LLM Gateway framework to prompt a structured question and answer response.Documentation Index
Fetch the complete documentation index at: https://assemblyai.com/docs/llms.txt
Use this file to discover all available pages before exploring further.
Quickstart
- Python
- JavaScript
import requests
import time
import xml.etree.ElementTree as ET
API_KEY = "YOUR_API_KEY"
audio_url = "https://storage.googleapis.com/aai-web-samples/meeting.mp4"
# -------------------------------
# Step 1: Transcribe the audio
# -------------------------------
transcript_request = requests.post(
"https://api.assemblyai.com/v2/transcript",
headers={"authorization": API_KEY, "content-type": "application/json"},
json={"audio_url": audio_url, "speech_models": ["universal-3-pro"]},
)
transcript_id = transcript_request.json()["id"]
# Poll for completion
while True:
polling_response = requests.get(
f"https://api.assemblyai.com/v2/transcript/{transcript_id}",
headers={"authorization": API_KEY},
)
status = polling_response.json()["status"]
if status == "completed":
break
elif status == "error":
raise RuntimeError(f"Transcription failed: {polling_response.json()['error']}")
else:
print(f"Transcription status: {status}")
time.sleep(3)
# -------------------------------
# Step 2: Build question helper functions
# -------------------------------
def construct_question(question):
question_str = f"Question: {question['question']}"
if question.get("context"):
question_str += f"\nContext: {question['context']}"
# Default answer_format
if not question.get("answer_format"):
question["answer_format"] = "short sentence"
question_str += f"\nAnswer Format: {question['answer_format']}"
if question.get("answer_options"):
options_str = ", ".join(question["answer_options"])
question_str += f"\nOptions: {options_str}"
return question_str + "\n"
def escape_xml_characters(xml_string):
return xml_string.replace("&", "&")
# -------------------------------
# Step 3: Define questions
# -------------------------------
questions = [
{
"question": "What are the top level KPIs for engineering?",
"context": "KPI stands for key performance indicator",
"answer_format": "short sentence",
},
{
"question": "How many days has it been since the data team has gotten updated metrics?",
"answer_options": ["1", "2", "3", "4", "5", "6", "7", "more than 7"],
},
{"question": "What are the future plans for the project?"},
]
question_str = "\n".join(construct_question(q) for q in questions)
# -------------------------------
# Step 4: Build the LLM prompt
# -------------------------------
prompt = f"""You are an expert at giving accurate answers to questions about texts.
No preamble.
Given the series of questions, answer the questions.
Each question may follow up with answer format, answer options, and context for each question.
It is critical that you follow the answer format and answer options for each question.
When context is provided with a question, refer to it when answering the question.
You are useful, true and concise, and write in perfect English.
Only the question is allowed between the <question> tag. Do not include the answer format, options, or question context in your response.
Only text is allowed between the <question> and <answer> tags.
XML tags are not allowed between the <question> and <answer> tags.
End your response with a closing </responses> tag.
For each question-answer pair, format your response according to the template provided below:
Template for response:
<responses>
<response>
<question>The question</question>
<answer>Your answer</answer>
</response>
<response>
...
</response>
...
</responses>
These are the questions:
{question_str}
Transcript:
{{{{ transcript }}}}
"""
# -------------------------------
# Step 5: Query LLM Gateway
# -------------------------------
headers = {"authorization": API_KEY}
response = requests.post(
"https://llm-gateway.assemblyai.com/v1/chat/completions",
headers=headers,
json={
"model": "claude-sonnet-4-5-20250929",
"messages": [{"role": "user", "content": prompt}],
"transcript_id": transcript_id,
"max_tokens": 2000,
},
)
response_json = response.json()
llm_output = response_json["choices"][0]["message"]["content"]
# -------------------------------
# Step 6: Parse and print XML response
# -------------------------------
clean_response = escape_xml_characters(llm_output).strip()
try:
root = ET.fromstring(clean_response)
for resp in root.findall("response"):
question = resp.find("question").text
answer = resp.find("answer").text
print(f"Question: {question}")
print(f"Answer: {answer}\n")
except ET.ParseError as e:
print("Could not parse XML response.")
print("Raw model output:\n", llm_output)
import { DOMParser } from "@xmldom/xmldom";
const API_KEY = "YOUR_API_KEY";
const headers = { authorization: API_KEY };
const audioUrl = "https://storage.googleapis.com/aai-web-samples/meeting.mp4";
// -------------------------------
// Step 1: Transcribe the audio
// -------------------------------
let res = await fetch("https://api.assemblyai.com/v2/transcript", {
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify({ audio_url: audioUrl, speech_models: ["universal-3-pro"] }),
});
if (!res.ok) throw new Error(`Error: ${res.status}`);
const transcriptRequest = await res.json();
const transcriptId = transcriptRequest.id;
// Poll for completion
while (true) {
const pollingRes = await fetch(`https://api.assemblyai.com/v2/transcript/${transcriptId}`, { headers });
if (!pollingRes.ok) throw new Error(`Error: ${pollingRes.status}`);
const pollingResponse = await pollingRes.json();
const status = pollingResponse.status;
if (status === "completed") {
break;
} else if (status === "error") {
throw new Error(`Transcription failed: ${pollingResponse.error}`);
} else {
console.log(`Transcription status: ${status}`);
await new Promise((resolve) => setTimeout(resolve, 3000));
}
}
// -------------------------------
// Step 2: Build question helper functions
// -------------------------------
function constructQuestion(question) {
let questionStr = `Question: ${question.question}`;
if (question.context) {
questionStr += `\nContext: ${question.context}`;
}
// Default answer_format
if (!question.answer_format) {
question.answer_format = "short sentence";
}
questionStr += `\nAnswer Format: ${question.answer_format}`;
if (question.answer_options) {
const optionsStr = question.answer_options.join(", ");
questionStr += `\nOptions: ${optionsStr}`;
}
return questionStr + "\n";
}
function escapeXmlCharacters(xmlString) {
return xmlString.replace(/&/g, "&");
}
// -------------------------------
// Step 3: Define questions
// -------------------------------
const questions = [
{
question: "What are the top level KPIs for engineering?",
context: "KPI stands for key performance indicator",
answer_format: "short sentence",
},
{
question: "How many days has it been since the data team has gotten updated metrics?",
answer_options: ["1", "2", "3", "4", "5", "6", "7", "more than 7"],
},
{ question: "What are the future plans for the project?" },
];
const questionStr = questions.map((q) => constructQuestion(q)).join("\n");
// -------------------------------
// Step 4: Build the LLM prompt
// -------------------------------
const prompt = `You are an expert at giving accurate answers to questions about texts.
No preamble.
Given the series of questions, answer the questions.
Each question may follow up with answer format, answer options, and context for each question.
It is critical that you follow the answer format and answer options for each question.
When context is provided with a question, refer to it when answering the question.
You are useful, true and concise, and write in perfect English.
Only the question is allowed between the <question> tag. Do not include the answer format, options, or question context in your response.
Only text is allowed between the <question> and <answer> tags.
XML tags are not allowed between the <question> and <answer> tags.
End your response with a closing </responses> tag.
For each question-answer pair, format your response according to the template provided below:
Template for response:
<responses>
<response>
<question>The question</question>
<answer>Your answer</answer>
</response>
<response>
...
</response>
...
</responses>
These are the questions:
${questionStr}
Transcript:
{{ transcript }}
`;
// -------------------------------
// Step 5: Query LLM Gateway
// -------------------------------
res = await fetch("https://llm-gateway.assemblyai.com/v1/chat/completions", {
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify({
model: "claude-sonnet-4-5-20250929",
messages: [{ role: "user", content: prompt }],
transcript_id: transcriptId,
max_tokens: 2000,
}),
});
if (!res.ok) throw new Error(`Error: ${res.status}`);
const response = await res.json();
const llmOutput = response.choices[0].message.content;
// -------------------------------
// Step 6: Parse and print XML response
// -------------------------------
const cleanResponse = escapeXmlCharacters(llmOutput).trim();
try {
const root = new DOMParser().parseFromString(cleanResponse, "text/xml");
const responses = Array.from(root.getElementsByTagName("response"));
for (const resp of responses) {
const question = resp.getElementsByTagName("question")[0].textContent;
const answer = resp.getElementsByTagName("answer")[0].textContent;
console.log(`Question: ${question}`);
console.log(`Answer: ${answer}\n`);
}
} catch (e) {
console.log("Could not parse XML response.");
console.log("Raw model output:\n", llmOutput);
}
Getting Started
Before we begin, make sure you have an AssemblyAI account and an API key. You can sign up for an AssemblyAI account and get your API key from your dashboard. Find more details on the current LLM Gateway pricing in the AssemblyAI pricing page.Step-by-Step Instructions
In this guide, we will prompt LLM Gateway with a structured Q&A format and generate an XML response. Install the required packages:- Python
- JavaScript
pip install requests
npm install @xmldom/xmldom
- Python
- JavaScript
import requests
import time
import xml.etree.ElementTree as ET
API_KEY = "YOUR_API_KEY"
import { DOMParser } from "@xmldom/xmldom";
const API_KEY = "YOUR_API_KEY";
const headers = { authorization: API_KEY };
- Python
- JavaScript
audio_url = "https://storage.googleapis.com/aai-web-samples/meeting.mp4"
# -------------------------------
# Step 1: Transcribe the audio
# -------------------------------
transcript_request = requests.post(
"https://api.assemblyai.com/v2/transcript",
headers={"authorization": API_KEY, "content-type": "application/json"},
json={"audio_url": audio_url, "speech_models": ["universal-3-pro"]},
)
transcript_id = transcript_request.json()["id"]
# Poll for completion
while True:
polling_response = requests.get(
f"https://api.assemblyai.com/v2/transcript/{transcript_id}",
headers={"authorization": API_KEY},
)
status = polling_response.json()["status"]
if status == "completed":
break
elif status == "error":
raise RuntimeError(f"Transcription failed: {polling_response.json()['error']}")
else:
print(f"Transcription status: {status}")
time.sleep(3)
const audioUrl = "https://storage.googleapis.com/aai-web-samples/meeting.mp4";
// -------------------------------
// Step 1: Transcribe the audio
// -------------------------------
let res = await fetch("https://api.assemblyai.com/v2/transcript", {
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify({ audio_url: audioUrl, speech_models: ["universal-3-pro"] }),
});
if (!res.ok) throw new Error(`Error: ${res.status}`);
const transcriptRequest = await res.json();
const transcriptId = transcriptRequest.id;
// Poll for completion
while (true) {
const pollingRes = await fetch(`https://api.assemblyai.com/v2/transcript/${transcriptId}`, { headers });
if (!pollingRes.ok) throw new Error(`Error: ${pollingRes.status}`);
const pollingResponse = await pollingRes.json();
const status = pollingResponse.status;
if (status === "completed") {
break;
} else if (status === "error") {
throw new Error(`Transcription failed: ${pollingResponse.error}`);
} else {
console.log(`Transcription status: ${status}`);
await new Promise((resolve) => setTimeout(resolve, 3000));
}
}
- Python
- JavaScript
# -------------------------------
# Step 2: Build question helper functions
# -------------------------------
def construct_question(question):
question_str = f"Question: {question['question']}"
if question.get("context"):
question_str += f"\nContext: {question['context']}"
# Default answer_format
if not question.get("answer_format"):
question["answer_format"] = "short sentence"
question_str += f"\nAnswer Format: {question['answer_format']}"
if question.get("answer_options"):
options_str = ", ".join(question["answer_options"])
question_str += f"\nOptions: {options_str}"
return question_str + "\n"
def escape_xml_characters(xml_string):
return xml_string.replace("&", "&")
// -------------------------------
// Step 2: Build question helper functions
// -------------------------------
function constructQuestion(question) {
let questionStr = `Question: ${question.question}`;
if (question.context) {
questionStr += `\nContext: ${question.context}`;
}
// Default answer_format
if (!question.answer_format) {
question.answer_format = "short sentence";
}
questionStr += `\nAnswer Format: ${question.answer_format}`;
if (question.answer_options) {
const optionsStr = question.answer_options.join(", ");
questionStr += `\nOptions: ${optionsStr}`;
}
return questionStr + "\n";
}
function escapeXmlCharacters(xmlString) {
return xmlString.replace(/&/g, "&");
}
context and specify either an answer_format or a list of answer_options.
- Python
- JavaScript
# -------------------------------
# Step 3: Define questions
# -------------------------------
questions = [
{
"question": "What are the top level KPIs for engineering?",
"context": "KPI stands for key performance indicator",
"answer_format": "short sentence",
},
{
"question": "How many days has it been since the data team has gotten updated metrics?",
"answer_options": ["1", "2", "3", "4", "5", "6", "7", "more than 7"],
},
{"question": "What are the future plans for the project?"},
]
// -------------------------------
// Step 3: Define questions
// -------------------------------
const questions = [
{
question: "What are the top level KPIs for engineering?",
context: "KPI stands for key performance indicator",
answer_format: "short sentence",
},
{
question: "How many days has it been since the data team has gotten updated metrics?",
answer_options: ["1", "2", "3", "4", "5", "6", "7", "more than 7"],
},
{ question: "What are the future plans for the project?" },
];
- Python
- JavaScript
question_str = '\n'.join(construct_question(q) for q in questions)
const questionStr = questions.map((q) => constructQuestion(q)).join("\n");
- Python
- JavaScript
# -------------------------------
# Step 4: Build the LLM prompt
# -------------------------------
prompt = f"""You are an expert at giving accurate answers to questions about texts.
No preamble.
Given the series of questions, answer the questions.
Each question may follow up with answer format, answer options, and context for each question.
It is critical that you follow the answer format and answer options for each question.
When context is provided with a question, refer to it when answering the question.
You are useful, true and concise, and write in perfect English.
Only the question is allowed between the <question> tag. Do not include the answer format, options, or question context in your response.
Only text is allowed between the <question> and <answer> tags.
XML tags are not allowed between the <question> and <answer> tags.
End your response with a closing </responses> tag.
For each question-answer pair, format your response according to the template provided below:
Template for response:
<responses>
<response>
<question>The question</question>
<answer>Your answer</answer>
</response>
<response>
...
</response>
...
</responses>
These are the questions:
{question_str}
Transcript:
{{{{ transcript }}}}
"""
# -------------------------------
# Step 5: Query LLM Gateway
# -------------------------------
headers = {"authorization": API_KEY}
response = requests.post(
"https://llm-gateway.assemblyai.com/v1/chat/completions",
headers=headers,
json={
"model": "claude-sonnet-4-5-20250929",
"messages": [{"role": "user", "content": prompt}],
"transcript_id": transcript_id,
"max_tokens": 2000,
},
)
response_json = response.json()
llm_output = response_json["choices"][0]["message"]["content"]
# -------------------------------
# Step 6: Parse and print XML response
# -------------------------------
clean_response = escape_xml_characters(llm_output).strip()
try:
root = ET.fromstring(clean_response)
for resp in root.findall("response"):
question = resp.find("question").text
answer = resp.find("answer").text
print(f"Question: {question}")
print(f"Answer: {answer}\n")
except ET.ParseError as e:
print("Could not parse XML response.")
print("Raw model output:\n", llm_output)
// -------------------------------
// Step 4: Build the LLM prompt
// -------------------------------
const prompt = `You are an expert at giving accurate answers to questions about texts.
No preamble.
Given the series of questions, answer the questions.
Each question may follow up with answer format, answer options, and context for each question.
It is critical that you follow the answer format and answer options for each question.
When context is provided with a question, refer to it when answering the question.
You are useful, true and concise, and write in perfect English.
Only the question is allowed between the <question> tag. Do not include the answer format, options, or question context in your response.
Only text is allowed between the <question> and <answer> tags.
XML tags are not allowed between the <question> and <answer> tags.
End your response with a closing </responses> tag.
For each question-answer pair, format your response according to the template provided below:
Template for response:
<responses>
<response>
<question>The question</question>
<answer>Your answer</answer>
</response>
<response>
...
</response>
...
</responses>
These are the questions:
${questionStr}
Transcript:
{{ transcript }}
`;
// -------------------------------
// Step 5: Query LLM Gateway
// -------------------------------
const res = await fetch("https://llm-gateway.assemblyai.com/v1/chat/completions", {
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify({
model: "claude-sonnet-4-5-20250929",
messages: [{ role: "user", content: prompt }],
transcript_id: transcriptId,
max_tokens: 2000,
}),
});
if (!res.ok) throw new Error(`Error: ${res.status}`);
const response = await res.json();
const llmOutput = response.choices[0].message.content;
// -------------------------------
// Step 6: Parse and print XML response
// -------------------------------
const cleanResponse = escapeXmlCharacters(llmOutput).trim();
try {
const root = new DOMParser().parseFromString(cleanResponse, "text/xml");
const responses = Array.from(root.getElementsByTagName("response"));
for (const resp of responses) {
const question = resp.getElementsByTagName("question")[0].textContent;
const answer = resp.getElementsByTagName("answer")[0].textContent;
console.log(`Question: ${question}`);
console.log(`Answer: ${answer}\n`);
}
} catch (e) {
console.log("Could not parse XML response.");
console.log("Raw model output:\n", llmOutput);
}