Python tutorial
Detect emotion from audio in Python
Start with a saved result you can hear and inspect. Then send your own English recording through the oruk SDK and read its selected emotion scores. The file API scores 15 emotion labels; the Python client needs no GPU or model download.
Updated · Python 3.10+ · SDK 0.2.15
Read a saved result first
These commands download a small response reader and an existing analysis result. They make no inference request and need no key or installed package.
curl --fail --location -o inspect-emotions.py https://oruk.ai/examples/inspect-emotions.py
curl --fail --location -o response.json https://oruk.ai/samples/conversations/02-grocery-prices.oruk.json
curl --fail --location -o attribution.txt https://oruk.ai/examples/call-review-attribution.txt
python3 inspect-emotions.py response.jsonanalysis response contains model-generated annotations. It illustrates the format; it is not a new accuracy measurement.Read the saved model transcript
I'm sorry. The price of food has gone up so much. I mean... It really has. What what happened? I don't know. Like, honestly, I don't know what happened.
- 0.185–4.325 sdisappointed 0.868
- 4.325–6.065 shappy 0.780neutral 0.756
- 6.065–7.805 sangry 0.903frustrated 0.849disgusted 0.847
- 8.805–14.125 sdisgusted 0.845frustrated 0.827angry 0.815
Inspect the JSON excerpt
{
"id": "speech_a852195bbb04415d8b506d94d5a09148",
"task": "analysis",
"model": "oruk-resonance",
"duration": 14.45,
"segments": [
{
"start": 0.185,
"end": 4.325,
"emotions": [
{
"label": "disappointed",
"score": 0.8679338097572327
}
]
}
]
}Cost estimates in this saved JSON are historical reference values. Use your current plan and account usage to check billing.
Open Yap 1K, The Agentic Data Company (2026), CC BY 4.0. Oruk excerpted and downmixed the audio. Attribution and scope · Complete saved response · Replay each passage in Python
The second interval contains both happy and neutral. This is a multilabel output, so the labels are not mutually exclusive. A label missing from the array was not selected; its absence does not give you its raw score.
Analyze your own recording
Create a virtual environment, then install the versioned SDK. Create a key in the developer portal and set ORUK_API_KEY in your shell. Keep it out of browser code and source control.
python3 -m venv .venv
# macOS / Linux. On Windows: .venv\Scripts\activate
source .venv/bin/activate
python -m pip install https://oruk.ai/sdk/oruk-0.2.15-py3-none-any.whl- 1. Install the SDK. Create a Python 3.10+ virtual environment, then run python -m pip install https://oruk.ai/sdk/oruk-0.2.15-py3-none-any.whl.
- 2. Send an English recording. Set ORUK_API_KEY, then call client.emotions with model="oruk-resonance" and a supported local audio file.
- 3. Keep and inspect the response. Save the complete JSON after success. Read selected labels from emotions, timed results from segments, and measured duration from usage.audio_seconds.
Save the following as emotion-example.py, place your recording beside it, and run python emotion-example.py. The endpoint accepts WAV, FLAC, MP3, M4A, OGG, and WebM files up to 30 MB and 60 minutes. This request uses your plan minutes.
import json
import os
from pathlib import Path
from oruk import Oruk
# Set ORUK_API_KEY in your shell before running this file.
with Oruk(api_key=os.environ["ORUK_API_KEY"], max_retries=0) as client:
result = client.emotions("recording.wav", model="oruk-resonance")
# Write only after a successful response. Keep the full result for inspection.
Path("emotions.json").write_text(
json.dumps(result, ensure_ascii=False, indent=2), encoding="utf-8"
)
print("Audio seconds:", result["usage"]["audio_seconds"])
for emotion in result["emotions"]:
print(emotion["label"], emotion["score"])Run python inspect-emotions.py emotions.json to inspect the saved result. For a command-line wrapper with task and model options, download analyze-file.py. This short example disables automatic retries so a failed response does not silently resend audio.
Use requests instead of the SDK
Run python -m pip install requests, then save and run the Python code below. This sends the same multipart request and prints the complete response.
import json
import os
import requests
with open("recording.wav", "rb") as audio:
response = requests.post(
"https://speech-api.oruk.ai/v1/audio/emotions",
headers={"Authorization": f"Bearer {os.environ['ORUK_API_KEY']}"},
files={"file": ("recording.wav", audio, "audio/wav")},
data={"model": "oruk-resonance"},
timeout=(10, 120),
)
response.raise_for_status()
print(json.dumps(response.json(), ensure_ascii=False, indent=2))Add a transcript and speaker turns
Use client.analyze when you need words and speaking styles alongside emotion. With diarize=True, Resonance returns speaker-labeled turns. The labels identify speakers within this recording; they do not identify people across recordings.
import json
import os
from pathlib import Path
from oruk import Oruk
with Oruk(api_key=os.environ["ORUK_API_KEY"], max_retries=0) as client:
result = client.analyze(
"recording.wav", model="oruk-resonance", diarize=True
)
Path("analysis.json").write_text(
json.dumps(result, ensure_ascii=False, indent=2), encoding="utf-8"
)
for segment in result["segments"]:
print(segment["start"], segment["end"], segment.get("speaker"))
print("Words:", segment.get("text"))
print("Emotion scores:", segment["emotions"])
print("Speaking styles:", segment["styles"])Keep each segment’s start and end times with its scores. Do not assign a recording-level score to every word, or fill an empty style array with a guessed label. For an existing Whisper pipeline, follow the Whisper plus emotion example.
When something goes wrong
- Missing key or HTTP 401
- Set
ORUK_API_KEYin the same shell that runs Python. Confirm the key is active in your account. - HTTP 400 or an unsupported file
- Check the error code, current model ID, file size, duration, and whether the file contains decodable English speech. Use
oruk-resonancefor diarization. - HTTP 429, server failure, or a timeout
- Check your plan, rate limits, and the service status. An interrupted response does not tell you whether the server processed the recording. Inspect the request ID and usage before retrying.
- A low-scoring label or no style labels
- Emotion has a highest-score fallback; speaking styles do not. Preserve that distinction instead of treating every returned label as high confidence. See the score reference.
Questions before you build
- Can I try the example without an API key?
- Yes. The first example reads a previously published response locally and needs only Python. The live analyzer also works without an account. Sending your own recording through the Python API requires an API key.
- Do I need a GPU?
- No. The SDK sends the recording to the oruk API. Python handles the request and response; it does not download or run the speech model.
- What does a returned score mean?
- It is a model score for vocal expression. The endpoint returns labels that pass model-specific thresholds, or the highest-scoring emotion when none passes. A returned label can therefore have a low score. Scores are not calibrated probabilities of a speaker’s feelings.
- What accuracy should I expect?
- The published July 2026 benchmark describes a historical seven-class checkpoint, not the accuracy of the current 15-label endpoint on your recordings. Test the endpoint on representative audio from speakers excluded from your development set, and review errors by label and recording condition.
- What does the API cost?
- Plans start at $9/month for 250 audio minutes. One audio minute uses one plan minute for emotion or unified analysis. Standard self-serve trials last 7 days and require a card. Check the pricing page for allowances and overage rates.
For live audio, use the separate realtime phrase-emotion guide. Read the capabilities and evaluation guide before setting application rules.