Get API key

LLM API Meeting Notes: Transcript to Summary in Python

To get llm api meeting notes you send the transcript to a chat completions endpoint with a short system prompt and read the reply. The pieces that decide quality are the prompt, a fixed output shape and how you handle long recordings.

Updated

Isometric meeting table with speech bubbles condensing into a checklist card above it

Key points

  • One chat completion call turns a transcript into notes; JSON mode gives you a fixed shape.
  • A 100,000-token window holds most meeting transcripts without chunking.
  • Tell the model to write null for missing owners and dates, so it does not invent them.
  • A full key allows 8 concurrent requests, enough to process a day of meetings in parallel.

The pipeline in four steps

  1. Record and transcribe. Use any speech-to-text tool. This API is text only and does not accept audio.
  2. Prepare the text. Add a header with the meeting title, date and attendee names. Speaker labels, if you have them, help a lot.
  3. Call the model. Send the transcript with a system prompt that defines the sections you want.
  4. Store and share the result. Save the JSON, render it as Markdown or HTML, post it where the team reads.

The endpoint is POST /v1/chat/completions on https://api.whisperapis.com/v1, the model ID is uncensored, and any OpenAI SDK works after you change the base URL and key. If you need to set up speech-to-text first, the quickstart docs cover the text half and any Whisper-compatible server can supply the transcript.

Get structured notes with JSON mode

Free-form summaries are fine for reading, but a pipeline wants fields it can store and filter. JSON mode, enabled with response_format set to {"type": "json_object"}, makes the model return a single JSON object. Describe the keys in the system prompt, and say the word JSON there, because the prompt is what defines the shape.

import json
from openai import OpenAI

client = OpenAI(base_url="https://api.whisperapis.com/v1", api_key="YOUR_KEY")

SYSTEM = """You write meeting notes from raw transcripts.
Use only what the transcript says. If an owner or a date is not stated, write null.
Reply with a JSON object with the keys: summary (string), decisions (array of strings),
action_items (array of objects with task, owner, due), open_questions (array of strings)."""

transcript = open("standup.txt", encoding="utf-8").read()

resp = client.chat.completions.create(
    model="uncensored",
    messages=[
        {"role": "system", "content": SYSTEM},
        {"role": "user", "content": "Transcript:\n\n" + transcript},
    ],
    response_format={"type": "json_object"},
    temperature=0.2,
)

notes = json.loads(resp.choices[0].message.content)
for item in notes["action_items"]:
    print(item["owner"], "->", item["task"], item["due"])

Two lines in that prompt do most of the work. "Use only what the transcript says" keeps the model from adding plausible details. "If an owner or a date is not stated, write null" gives it a legal way to say it does not know, which is better than a made-up name. A low temperature such as 0.2 reduces variation between runs, which you want when the same transcript might be processed twice.

Always parse defensively. Wrap json.loads in a try block, and on failure retry once. Failed requests are not charged, and a single retry fixes most one-off formatting errors.

Stream the result to the person waiting

If someone clicks a button and waits for the notes, show text as it arrives. Set stream to true and the API sends server-sent events; usage is included at the end of the stream.

from openai import OpenAI

client = OpenAI(base_url="https://api.whisperapis.com/v1", api_key="YOUR_KEY")
transcript = open("standup.txt", encoding="utf-8").read()

stream = client.chat.completions.create(
    model="uncensored",
    messages=[
        {"role": "system", "content": "Write concise meeting notes in Markdown with headings: Summary, Decisions, Actions."},
        {"role": "user", "content": transcript},
    ],
    stream=True,
    temperature=0.3,
)

for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)

Streaming has one operational detail: a streamed request holds its concurrency slot until the stream ends. A full key allows 8 at once, a trial key 2. If a browser tab closes and your server keeps reading, that slot stays busy, so cancel the upstream request when the client disconnects.

For batch jobs that nobody watches, skip streaming. A plain request is simpler and you get the whole answer in one object.

Long meetings and the 100k window

The context window is 100,000 tokens, counting the prompt and the reply together. A one-hour meeting is roughly 9,000 words, or about 12,000 tokens, so even an all-day workshop fits in one request. That removes the usual map-and-reduce design where you split a transcript, summarize each part and then summarize the summaries. Each pass of that design loses detail, and a decision made at minute 5 and revised at minute 50 is easy to misreport.

Still check the size before you send. A crude check is characters divided by four for English text. If you are near the limit, there are two clean options:

  • Split at natural breaks such as agenda items, summarize each, and pass the partial notes to a final call that merges them.
  • Drop content that does not matter, such as long stretches of small talk before the meeting begins.

The maximum request body is 8 MB, which is not a practical limit for text. Prompts are not used for training, which matters if recordings include internal plans or client names.

Prompt details that change the output

Say who the notes are for

"Notes for an engineer who missed the meeting" gets different detail than "a one-paragraph update for the executive team". Write the reader into the system prompt.

Give the model the attendee list

Speech-to-text often garbles names. A line such as "Attendees: Priya Nair (PM), Tom Ortiz (backend), Lena Fischer (design)" lets the model repair a transcript that says "Tom Ortis" and attach tasks to the right person.

Ask for decisions and actions separately

A decision is something the group agreed on. An action is something one person will do. When both live in one list, readers skip it. Separate keys make the distinction explicit, and the model follows it consistently.

Keep the sensitive stuff in

Real meetings contain blunt language, complaints about named people, legal worries and heated disputes. The model here is tuned to answer without content refusals for lawful adult use, so it summarizes what was said rather than declining. You still own the output, so review notes before they go to people who were not in the room.

Running a whole day of meetings

A team that records six meetings a day can process them in parallel. With 8 concurrent requests, send up to eight at a time; a small worker pool with a semaphore does the job. The per-minute limit for a full key is 300 requests, which a meeting pipeline will not reach.

Node works the same way as Python. Here is the non-streaming version:

import OpenAI from "openai";
import { readFileSync } from "node:fs";

const client = new OpenAI({
  baseURL: "https://api.whisperapis.com/v1",
  apiKey: process.env.API_KEY,
});

const transcript = readFileSync("standup.txt", "utf8");

const resp = await client.chat.completions.create({
  model: "uncensored",
  messages: [
    { role: "system", content: "Summarize the meeting in five bullets, then list action items with owners." },
    { role: "user", content: transcript },
  ],
  temperature: 0.2,
});

console.log(resp.choices[0].message.content);

If you receive HTTP 429, read the error code. concurrency means too many requests at once, so lower the pool size. rate_limited means too many per minute. A 429 with a Retry-After: 5 header signals temporary overload; wait five seconds and try again.

Checking the notes before you trust them

Generated notes read confidently even when they are wrong, so build a few cheap checks into the pipeline instead of relying on a human to notice.

  • Quote check. Ask for a short supporting quote with each action item, then search the transcript for it. A quote that does not appear is a sign the item was invented.
  • Owner check. Compare every owner against your attendee list and flag names that are not on it.
  • Empty check. A meeting with no decisions is possible, but a result with all arrays empty on a 40-minute transcript usually means the input was truncated or the wrong file.
  • Length check. Notes longer than about a fifth of the transcript are not notes; tighten the prompt or set max_tokens.

Log the X-Request-Id response header next to each saved result. When someone says an action item is wrong, that identifier lets you find the request and compare it with the transcript. Keeping the original transcript beside the notes is cheap and settles most disputes quickly.

Cost and a first test

The price is $0.15 per million input tokens and $0.50 per million output tokens. A one-hour meeting with a 12,000-token transcript and an 800-token set of notes costs $0.0018 for input and $0.0004 for output, so $0.0022 in total. At that rate $1 covers about 454 such meetings. See the pricing page for top-ups, which start at $20 and can be paid by card or crypto.

To try it, sign up and open the API key page. A one-click trial of $0.50 is available once per person, valid for 7 days, no card needed. Run the Python script above on one real transcript, read the JSON, and adjust the prompt until the action items match what happened in the meeting. Then loop over the rest.

Questions and answers

Can I send audio recordings directly?

No. The API takes text only. Transcribe the recording with a speech-to-text tool first, then send the transcript.

How do I make the output format consistent?

Use JSON mode with a system prompt that names every key, and set a low temperature. Parse the result in a try block and retry once on failure.

How long a transcript fits in one request?

The context window is 100,000 tokens for prompt plus reply. That covers a full-day workshop in most cases.

Are my transcripts used for training?

No, prompts are not used for training.

What does it cost per meeting?

An hour-long meeting with a 12,000-token transcript and an 800-token summary costs about $0.0022 at $0.15 and $0.50 per million input and output tokens.

Your key is one form away

Create an account, copy the key, change the base URL. That is the whole setup.

Get API key