> ## 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.

# Prompt A Structured Q&A Response Using LLM Gateway

This cookbook will demonstrate how to use AssemblyAI's [LLM Gateway](/llm-gateway) framework to prompt a structured question and answer response.

## Quickstart

<Tabs groupId="language">
  <Tab language="python" title="Python" default>
    ```python expandable theme={null}
    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("&", "&amp;")

    # -------------------------------
    # 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)
    ```
  </Tab>

  <Tab language="javascript" title="JavaScript">
    ```javascript expandable theme={null}
    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, "&amp;");
    }

    // -------------------------------
    // 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);
    }
    ```
  </Tab>
</Tabs>

## Getting Started

Before we begin, make sure you have an AssemblyAI account and an API key. You can [sign up for an AssemblyAI account](https://www.assemblyai.com/dashboard/home) and get your API key from your dashboard.

Find more details on the current LLM Gateway pricing in the AssemblyAI [pricing page](https://www.assemblyai.com/pricing).

## 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:

<Tabs groupId="language">
  <Tab language="python" title="Python" default>
    ```bash theme={null}
    pip install requests
    ```
  </Tab>

  <Tab language="javascript" title="JavaScript">
    ```bash theme={null}
    npm install @xmldom/xmldom
    ```
  </Tab>
</Tabs>

Import the necessary libraries and set API key.

<Tabs groupId="language">
  <Tab language="python" title="Python" default>
    ```python theme={null}
    import requests
    import time
    import xml.etree.ElementTree as ET

    API_KEY = "YOUR_API_KEY"
    ```
  </Tab>

  <Tab language="javascript" title="JavaScript">
    ```javascript theme={null}
    import { DOMParser } from "@xmldom/xmldom";

    const API_KEY = "YOUR_API_KEY";
    const headers = { authorization: API_KEY };
    ```
  </Tab>
</Tabs>

Next, we'll use AssemblyAI to transcribe a file and save our transcript.

<Tabs groupId="language">
  <Tab language="python" title="Python" default>
    ```python expandable theme={null}
    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)
    ```
  </Tab>

  <Tab language="javascript" title="JavaScript">
    ```javascript expandable theme={null}
    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));
      }
    }
    ```
  </Tab>
</Tabs>

Construct a formatted string to structure the questions. This includes the question text, context, an answer format, any answer options, then returns the formatted string.

<Tabs groupId="language">
  <Tab language="python" title="Python" default>
    ```python expandable theme={null}
    # -------------------------------
    # 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("&", "&amp;")
    ```
  </Tab>

  <Tab language="javascript" title="JavaScript">
    ```javascript expandable theme={null}
    // -------------------------------
    // 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, "&amp;");
    }
    ```
  </Tab>
</Tabs>

Define a list of questions. For each question, you can define additional `context` and specify either an `answer_format` or a list of `answer_options`.

<Tabs groupId="language">
  <Tab language="python" title="Python" default>
    ```python theme={null}
    # -------------------------------
    # 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?"},
    ]
    ```
  </Tab>

  <Tab language="javascript" title="JavaScript">
    ```javascript theme={null}
    // -------------------------------
    // 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?" },
    ];
    ```
  </Tab>
</Tabs>

Construct the formatted question string for all the questions and build the LLM prompt.

<Tabs groupId="language">
  <Tab language="python" title="Python" default>
    ```python theme={null}
    question_str = '\n'.join(construct_question(q) for q in questions)
    ```
  </Tab>

  <Tab language="javascript" title="JavaScript">
    ```javascript theme={null}
    const questionStr = questions.map((q) => constructQuestion(q)).join("\n");
    ```
  </Tab>
</Tabs>

Provide detailed instructions to prompt LLM Gateway to answer a series of questions. This also defines a structured XML template for the responses.

<Tabs groupId="language">
  <Tab language="python" title="Python" default>
    ```python expandable theme={null}
    # -------------------------------
    # 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)
    ```
  </Tab>

  <Tab language="javascript" title="JavaScript">
    ```javascript expandable theme={null}
    // -------------------------------
    // 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);
    }
    ```
  </Tab>
</Tabs>
