
We are going to build an earnings-call transcript analyzer that reads raw text and returns structured financial metrics, management sentiment, and risk flags. It is useful for equity analysts and portfolio managers who need to process dozens of transcripts without reading every word. Because Oxlo.ai charges a flat rate per request instead of per token, feeding the model a ten-thousand-word transcript costs the same as a one-sentence prompt, which makes this workflow economical at scale.
What you'll need
- Python 3.10 or newer installed locally.
- The OpenAI SDK. Install it with
pip install openai. - An Oxlo.ai API key from https://portal.oxlo.ai. The free tier includes 60 requests per day and more than sixteen models, which is plenty for prototyping.
- A sample earnings call transcript. We will embed a short excerpt in the script so you can run the demo immediately.
I also recommend setting your API key as an environment variable rather than hard-coding it. If you want to test multiple models, Oxlo.ai lets you swap the model string without changing the base URL or authentication.
Step 1: Configure the Oxlo.ai client
Oxlo.ai exposes a fully OpenAI-compatible endpoint, so the only changes from a standard OpenAI setup are the base_url and the model identifier. I use Llama 3.3 70B here because it follows structured instructions reliably and runs without cold starts. Create a file named analyzer.py and add the client setup below.
from openai import OpenAI
import os
client = OpenAI(
base_url="https://api.oxlo.ai/v1",
api_key=os.environ.get("OXLO_API_KEY"),
)
Step 2: Define the analyst system prompt
The system prompt is the only training the agent gets. It must enforce a strict JSON schema so we can feed the output directly into a spreadsheet or database. I keep the categories aligned with what a research associate would highlight: revenue growth, margin direction, guidance changes, tone, red flags, and supporting quotes. Keeping the prompt explicit reduces the chance of hallucinated fields. If you later want to track additional metrics, such as capital expenditure or buyback commentary, you only need to add a key to this prompt and rerun.
SYSTEM_PROMPT = """You are a senior equity research analyst.
Read the earnings call transcript below and produce a single JSON object with these exact keys:
- revenue_growth: string describing year-over-year or quarter-over-quarter trends
- margin_trend: string noting gross or operating margin direction
- guidance_sentiment: one of "raised", "maintained", "lowered", or "no_comment"
- management_tone: one of "bullish", "neutral", or "cautious"
- red_flags: list of strings describing risks, warnings, or negative disclosures
- key_quotes: list of up to three verbatim short quotes that support your assessment
Return only valid JSON. Do not add markdown formatting or explanation outside the JSON."""
Step 3: Build the extraction function
This function takes a transcript string, sends it to Oxlo.ai, and parses the JSON response. I set the temperature low to reduce hallucination and enable JSON mode so the model stays in schema. If you are batch-processing filings, you can swap the model to qwen-3-32b or kimi-k2.6 without changing any other code. The flat per-request pricing on Oxlo.ai means that switching to a larger context window or a stronger reasoning model does not suddenly multiply your cost. That is a major advantage over token-based providers when you are processing long documents.
import json
def analyze_transcript(transcript: str) -> dict:
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": transcript},
],
response_format={"type": "json_object"},
temperature=0.2,
)
content = response.choices[0].message.content
return json.loads(content)
Step 4: Add a validation wrapper
LLMs can occasionally return malformed JSON or omit a key when the input is noisy. A thin guard layer catches parsing errors and returns a safe fallback dict so the pipeline does not crash during overnight batch jobs. This pattern also lets you log failures for later inspection without losing the whole run. In production, you might retry once with a shorter transcript chunk before giving up.
def safe_analyze(transcript: str) -> dict:
try:
return analyze_transcript(transcript)
except Exception as e:
return {
"error": str(e),
"revenue_growth": "unknown",
"margin_trend": "unknown",
"guidance_sentiment": "no_comment",
"management_tone": "neutral",
"red_flags": ["analysis_failed"],
"key_quotes": [],
}
Run it
Now we feed the agent a realistic excerpt from a fictitious earnings call and print the structured result. Save the full script and run python analyzer.py. The transcript below contains mixed signals, raised guidance, and a regional risk, which tests whether the agent can track nuance across multiple speakers.
if __name__ == "__main__":
transcript = """
Operator: Good afternoon. Welcome to the Q3 2024 earnings call for Example Corp.
CEO: Thank you. Revenue grew 12 percent year over year, driven by enterprise adoption.
CFO: Operating margin compressed 150 basis points due to cloud migration costs.
CEO: We are raising full year guidance by 2 percent. Demand remains robust, though we remain watchful on macro headwinds in Europe.
Analyst: Can you quantify the Europe exposure?
CFO: Roughly 18 percent of revenue. We have seen two large customers pause expansions.
"""
result = safe_analyze(transcript)
print(json.dumps(result, indent=2))
Example output:
{
"revenue_growth": "12% year over year, driven by enterprise adoption",
"margin_trend": "Operating margin compressed 150 basis points due to cloud migration costs",
"guidance_sentiment": "raised",
"management_tone": "cautious",
"red_flags": [
"Operating margin compression of 150 bps",
"Two large European customers paused expansions",
"18% revenue exposure to Europe with macro headwinds"
],
"key_quotes": [
"We are raising full year guidance by 2 percent.",
"We remain watchful on macro headwinds in Europe.",
"Roughly 18 percent of revenue. We have seen two large customers pause expansions."
]
}
Next steps
From here, you can extend the agent by wiring it into a cron job that fetches new transcripts via an SEC RSS feed or a vendor API. You could also swap in deepseek-v3.2 or kimi-k2.6 on Oxlo.ai if you need deeper reasoning for complex financial footnotes, still under the same flat per-request cost. Another natural next step is to add a second pass that compares the current quarter's JSON output against the previous quarter's to auto-generate a delta report. Check the latest model list and pricing at https://oxlo.ai/pricing to scale the workload.
