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

# Analyze The Sentiment Of A Customer Call using LLM Gateway

In this guide, we'll show you how to use AssemblyAI's LLM Gateway framework to process an audio file and then use [LLM Gateway](/llm-gateway) to automatically detect sentiment analysis from customer calls as "positive", "negative", or "neutral". In addition, we will glean additional insights beyond these three sentiments and learn the reasoning behind these detected sentiments.

## Quickstart

<Tabs groupId="language">
  <Tab language="python" title="Python" default>
    ```python expandable theme={null}
    import requests
    import time

    API_KEY = "YOUR_API_KEY"
    audio_file_path = "./meeting.mp3"

    # ------------------------------------------
    # Step 1: Upload the audio file
    # ------------------------------------------
    def upload_file(filename):
        with open(filename, "rb") as f:
            upload_url = "https://api.assemblyai.com/v2/upload"
            headers = {"authorization": API_KEY}
            response = requests.post(upload_url, headers=headers, data=f)
            response.raise_for_status()
            return response.json()["upload_url"]

    audio_url = upload_file(audio_file_path)
    print(f"Uploaded audio file. URL: {audio_url}")

    # ------------------------------------------
    # Step 2: Request transcription
    # ------------------------------------------
    transcript_request = requests.post(
        "https://api.assemblyai.com/v2/transcript",
        headers={"authorization": API_KEY, "content-type": "application/json"},
        json={"audio_url": audio_url},
    )

    transcript_id = transcript_request.json()["id"]

    # Poll until completed
    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)

    print("\nTranscription complete.\n")

    # ------------------------------------------
    # Step 3: Define questions
    # ------------------------------------------
    agent_context = "The agent is trying to get the customer to go through with the update to their car."
    customer_context = "The customer is calling to check how much it would cost to update the map in his car."

    answer_format = "<answer in one sentence> <reason in one sentence>"

    questions = [
        {
            "question": "What was the overall sentiment of the call?",
            "context": customer_context,
            "answer_format": answer_format,
        },
        {
            "question": "What was the sentiment of the agent in this call?",
            "context": agent_context,
            "answer_format": answer_format,
        },
        {
            "question": "What was the sentiment of the customer in this call?",
            "context": customer_context,
            "answer_format": answer_format,
        },
        {
            "question": "What quote best demonstrates the customer's level of interest?",
            "context": customer_context,
            "answer_format": answer_format,
        },
        {
            "question": "Provide a quote from the agent that demonstrates their level of enthusiasm.",
            "context": agent_context,
            "answer_format": answer_format,
        },
    ]

    # ------------------------------------------
    # Step 4: Build prompt for the LLM
    # ------------------------------------------
    question_strs = []
    for q in questions:
        q_str = f"Question: {q['question']}"
        if q.get("context"):
            q_str += f"\nContext: {q['context']}"
        if q.get("answer_format"):
            q_str += f"\nAnswer Format: {q['answer_format']}"
        question_strs.append(q_str)

    questions_prompt = "\n\n".join(question_strs)

    prompt = f"""
    You are an expert at analyzing call transcripts.
    Given the series of questions below, answer them accurately and concisely.
    When context or answer format is provided, use it to guide your answers.

    Transcript:
    {{{{ transcript }}}}

    Questions:
    {questions_prompt}
    """

    # ------------------------------------------
    # Step 5: Query the 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 display the results
    # ------------------------------------------
    print("\n--- LLM Responses ---\n")
    print(llm_output)
    ```
  </Tab>

  <Tab language="javascript" title="JavaScript">
    ```javascript expandable theme={null}
    import fs from "fs-extra";

    const API_KEY = "YOUR_API_KEY";
    const audioFilePath = "./meeting.mp3";
    const headers = { authorization: API_KEY };

    // ------------------------------------------
    // Step 1: Upload the audio file
    // ------------------------------------------
    const audioData = await fs.readFile(audioFilePath);
    let res = await fetch("https://api.assemblyai.com/v2/upload", {
      method: "POST",
      headers,
      body: audioData,
    });
    if (!res.ok) throw new Error(`Error: ${res.status}`);
    const uploadResponse = await res.json();
    const audioUrl = uploadResponse.upload_url;
    console.log(`Uploaded audio file. URL: ${audioUrl}`);

    // ------------------------------------------
    // Step 2: Request transcription
    // ------------------------------------------
    res = await fetch("https://api.assemblyai.com/v2/transcript", {
      method: "POST",
      headers: { ...headers, "Content-Type": "application/json" },
      body: JSON.stringify({ audio_url: audioUrl }),
    });
    if (!res.ok) throw new Error(`Error: ${res.status}`);
    const transcriptRequest = await res.json();

    const transcriptId = transcriptRequest.id;

    // Poll until completed
    while (true) {
      res = await fetch(`https://api.assemblyai.com/v2/transcript/${transcriptId}`, { headers });
      if (!res.ok) throw new Error(`Error: ${res.status}`);
      const pollingResponse = await res.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));
      }
    }

    console.log("\nTranscription complete.\n");

    // ------------------------------------------
    // Step 3: Define questions
    // ------------------------------------------
    const agentContext = "The agent is trying to get the customer to go through with the update to their car.";
    const customerContext = "The customer is calling to check how much it would cost to update the map in his car.";

    const answerFormat = "<answer in one sentence> <reason in one sentence>";

    const questions = [
      {
        question: "What was the overall sentiment of the call?",
        context: customerContext,
        answer_format: answerFormat,
      },
      {
        question: "What was the sentiment of the agent in this call?",
        context: agentContext,
        answer_format: answerFormat,
      },
      {
        question: "What was the sentiment of the customer in this call?",
        context: customerContext,
        answer_format: answerFormat,
      },
      {
        question: "What quote best demonstrates the customer's level of interest?",
        context: customerContext,
        answer_format: answerFormat,
      },
      {
        question: "Provide a quote from the agent that demonstrates their level of enthusiasm.",
        context: agentContext,
        answer_format: answerFormat,
      },
    ];

    // ------------------------------------------
    // Step 4: Build prompt for the LLM
    // ------------------------------------------
    const questionStrs = [];
    for (const q of questions) {
      let qStr = `Question: ${q.question}`;
      if (q.context) {
        qStr += `\nContext: ${q.context}`;
      }
      if (q.answer_format) {
        qStr += `\nAnswer Format: ${q.answer_format}`;
      }
      questionStrs.push(qStr);
    }

    const questionsPrompt = questionStrs.join("\n\n");

    const prompt = `
    You are an expert at analyzing call transcripts.
    Given the series of questions below, answer them accurately and concisely.
    When context or answer format is provided, use it to guide your answers.

    Transcript:
    {{ transcript }}

    Questions:
    ${questionsPrompt}
    `;

    // ------------------------------------------
    // Step 5: Query the 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 display the results
    // ------------------------------------------
    console.log("\n--- LLM Responses ---\n");
    console.log(llmOutput);
    ```
  </Tab>
</Tabs>

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

See our [pricing page](https://www.assemblyai.com/pricing) for LLM Gateway pricing rates.

## Step-by-Step Instructions

In this guide, we will ask five questions to learn about the sentiment of the customer and agent. You can adjust the questions to suit your project's needs.

### Install dependencies

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 fs-extra
    ```
  </Tab>
</Tabs>

Import the required libraries and set your AssemblyAI API key.

<Tabs groupId="language">
  <Tab language="python" title="Python" default>
    ```python theme={null}
    import requests
    import time

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

  <Tab language="javascript" title="JavaScript">
    ```javascript theme={null}
    import fs from "fs-extra";

    const API_KEY = "YOUR_API_KEY";
    ```
  </Tab>
</Tabs>

Next, you'll upload your audio file to AssemblyAI's servers.
Once the upload is complete, the API will return a temporary URL that can be used to start the transcription.

After submitting the transcription request, your script will poll the API until the transcription is finished.

<Tabs groupId="language">
  <Tab language="python" title="Python" default>
    ```python expandable theme={null}
    audio_file_path = "./meeting.mp3"
    # ------------------------------------------
    # Step 1: Upload the audio file
    # ------------------------------------------
    def upload_file(filename):
        with open(filename, "rb") as f:
            upload_url = "https://api.assemblyai.com/v2/upload"
            headers = {"authorization": API_KEY}
            response = requests.post(upload_url, headers=headers, data=f)
            response.raise_for_status()
            return response.json()["upload_url"]
    audio_url = upload_file(audio_file_path)
    print(f"Uploaded audio file. URL: {audio_url}")
    # ------------------------------------------
    # Step 2: Request transcription
    # ------------------------------------------
    transcript_request = requests.post(
        "https://api.assemblyai.com/v2/transcript",
        headers={"authorization": API_KEY, "content-type": "application/json"},
        json={"audio_url": audio_url},
    )
    transcript_id = transcript_request.json()["id"]
    # Poll until completed
    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)
    print("\nTranscription complete.\n")
    ```
  </Tab>

  <Tab language="javascript" title="JavaScript">
    ```javascript expandable theme={null}
    const audioFilePath = "./meeting.mp3";
    const headers = { authorization: API_KEY };
    // ------------------------------------------
    // Step 1: Upload the audio file
    // ------------------------------------------
    const audioData = await fs.readFile(audioFilePath);
    let res = await fetch("https://api.assemblyai.com/v2/upload", {
      method: "POST",
      headers,
      body: audioData,
    });
    if (!res.ok) throw new Error(`Error: ${res.status}`);
    const uploadResponse = await res.json();
    const audioUrl = uploadResponse.upload_url;
    console.log(`Uploaded audio file. URL: ${audioUrl}`);
    // ------------------------------------------
    // Step 2: Request transcription
    // ------------------------------------------
    res = await fetch("https://api.assemblyai.com/v2/transcript", {
      method: "POST",
      headers: { ...headers, "Content-Type": "application/json" },
      body: JSON.stringify({ audio_url: audioUrl }),
    });
    if (!res.ok) throw new Error(`Error: ${res.status}`);
    const transcriptRequest = await res.json();
    const transcriptId = transcriptRequest.id;
    // Poll until completed
    while (true) {
      res = await fetch(`https://api.assemblyai.com/v2/transcript/${transcriptId}`, { headers });
      if (!res.ok) throw new Error(`Error: ${res.status}`);
      const pollingResponse = await res.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));
      }
    }
    console.log("\nTranscription complete.\n");
    ```
  </Tab>
</Tabs>

Once you have the transcript, you'll define short context strings for both the agent and the customer.
These will help the model better understand the roles and perspectives in the conversation.

<Tabs groupId="language">
  <Tab language="python" title="Python" default>
    ```python theme={null}
    agent_context = "The agent is trying to get the customer to go through with the update to their car."
    customer_context = "The customer is calling to check how much it would cost to update the map in his car."
    ```
  </Tab>

  <Tab language="javascript" title="JavaScript">
    ```javascript theme={null}
    const agentContext = "The agent is trying to get the customer to go through with the update to their car.";
    const customerContext = "The customer is calling to check how much it would cost to update the map in his car.";
    ```
  </Tab>
</Tabs>

You can now specify the exact questions you want the LLM Gateway to answer.
Each question can include optional context and an answer format that tells the model how to structure its response.

<Tabs groupId="language">
  <Tab language="python" title="Python" default>
    ```python expandable theme={null}
    answer_format = "<answer in one sentence> <reason in one sentence>"
    questions = [
        {
            "question": "What was the overall sentiment of the call?",
            "context": customer_context,
            "answer_format": answer_format,
        },
        {
            "question": "What was the sentiment of the agent in this call?",
            "context": agent_context,
            "answer_format": answer_format,
        },
        {
            "question": "What was the sentiment of the customer in this call?",
            "context": customer_context,
            "answer_format": answer_format,
        },
        {
            "question": "What quote best demonstrates the customer's level of interest?",
            "context": customer_context,
            "answer_format": answer_format,
        },
        {
            "question": "Provide a quote from the agent that demonstrates their level of enthusiasm.",
            "context": agent_context,
            "answer_format": answer_format,
        },
    ]
    ```
  </Tab>

  <Tab language="javascript" title="JavaScript">
    ```javascript expandable theme={null}
    const answerFormat = "<answer in one sentence> <reason in one sentence>";
    const questions = [
      {
        question: "What was the overall sentiment of the call?",
        context: customerContext,
        answer_format: answerFormat,
      },
      {
        question: "What was the sentiment of the agent in this call?",
        context: agentContext,
        answer_format: answerFormat,
      },
      {
        question: "What was the sentiment of the customer in this call?",
        context: customerContext,
        answer_format: answerFormat,
      },
      {
        question: "What quote best demonstrates the customer's level of interest?",
        context: customerContext,
        answer_format: answerFormat,
      },
      {
        question: "Provide a quote from the agent that demonstrates their level of enthusiasm.",
        context: agentContext,
        answer_format: answerFormat,
      },
    ];
    ```
  </Tab>
</Tabs>

Now that the questions are defined, combine them into a single formatted prompt.
This prompt includes both the call transcript and the questions you want the model to address.
The model will use these details to generate accurate and concise responses.

<Tabs groupId="language">
  <Tab language="python" title="Python" default>
    ```python expandable theme={null}
    # ------------------------------------------
    # Step 4: Build prompt for the LLM
    # ------------------------------------------
    question_strs = []
    for q in questions:
        q_str = f"Question: {q['question']}"
        if q.get("context"):
            q_str += f"\nContext: {q['context']}"
        if q.get("answer_format"):
            q_str += f"\nAnswer Format: {q['answer_format']}"
        question_strs.append(q_str)
    questions_prompt = "\n\n".join(question_strs)
    prompt = f"""
    You are an expert at analyzing call transcripts.
    Given the series of questions below, answer them accurately and concisely.
    When context or answer format is provided, use it to guide your answers.
    Transcript:
    {{{{ transcript }}}}
    Questions:
    {questions_prompt}
    """
    ```
  </Tab>

  <Tab language="javascript" title="JavaScript">
    ```javascript expandable theme={null}
    // ------------------------------------------
    // Step 4: Build prompt for the LLM
    // ------------------------------------------
    const questionStrs = [];
    for (const q of questions) {
      let qStr = `Question: ${q.question}`;
      if (q.context) {
        qStr += `\nContext: ${q.context}`;
      }
      if (q.answer_format) {
        qStr += `\nAnswer Format: ${q.answer_format}`;
      }
      questionStrs.push(qStr);
    }
    const questionsPrompt = questionStrs.join("\n\n");
    const prompt = `
    You are an expert at analyzing call transcripts.
    Given the series of questions below, answer them accurately and concisely.
    When context or answer format is provided, use it to guide your answers.
    Transcript:
    {{ transcript }}
    Questions:
    ${questionsPrompt}
    `;
    ```
  </Tab>
</Tabs>

With the prompt prepared, query LLM Gateway then extract and print the answers returned by the LLM Gateway.
This step displays the model's assessment of each question, including the identified sentiments and their reasoning.

<Tabs groupId="language">
  <Tab language="python" title="Python" default>
    ```python expandable theme={null}
    # ------------------------------------------
    # Step 5: Query the 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 display the results
    # ------------------------------------------
    print("\n--- LLM Responses ---\n")
    print(llm_output)
    ```
  </Tab>

  <Tab language="javascript" title="JavaScript">
    ```javascript expandable theme={null}
    // ------------------------------------------
    // Step 5: Query the LLM Gateway
    // ------------------------------------------
    let 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 display the results
    // ------------------------------------------
    console.log("\n--- LLM Responses ---\n");
    console.log(llmOutput);
    ```
  </Tab>
</Tabs>
