Migration guide
Move your Hume Expression Measurement workload to Behavioral Signals
Your audio pipeline and your application stay where they are. You swap the client, remap the response, and re-fit your thresholds on your own audio.
Hume EVI and TTS have an announced cutoff
Hume says EVI and TTS API access ends 13 November 2026 at 12:01 a.m. EST (12 November 2026 at 9:01 p.m. PST). Both stay fully supported until then. Hume's notice says account data is permanently deleted after 13 November 2026. Export the data you need before access ends.
This is a separate notice about EVI and TTS, not a new deadline for the legacy Expression Measurement workflow this page covers. Behavioral Signals analyzes speech and is not a replacement for either.
Every field your code reads and all 48 of Hume's prosody dimensions are mapped below. Test it on your own recordings with 30 free batch minutes. No sales call needed.
Overview
What the API returns
24 signals across eight dimensions, plus a continuous emotion intensity score. Every classified output is a probability distribution over its classes, so you set your own operating thresholds rather than inheriting ours. Batch returns one result per utterance. Streaming returns one per two-second segment or per utterance.
| Signal | Classes | Batch | Streaming |
|---|---|---|---|
emotion | happy · angry · neutral · sad | Yes | Yes |
strength (arousal) | weak · neutral · strong | Yes | Yes |
positivity (valence) | negative · neutral · positive | Yes | Yes |
hesitation | no · yes, where the "yes" probability doubles as a continuous hesitation score | Yes | Yes |
speaking_rate | slow · normal · fast | Yes | Yes |
engagement | engaged · neutral · withdrawn | Yes | Yes |
age | 18 to 22 · 23 to 30 · 31 to 45 · 46 to 65. One value per speaker in batch, per segment in streaming | Yes | Yes |
gender | male · female. One value per speaker in batch, per segment in streaming | Yes | Yes |
intensity | Continuous score from 0 to 1. No classes, no final label | Yes | Yes |
language | Detected language with its probability | Yes | No |
asr | Transcript of the utterance | Yes | No |
diarization | Speaker label, plus speaker embeddings when you ask for them | Yes | No |
features | Behavioral embeddings, returned when you ask for them | Yes | Yes (when requested) |
| Voice deepfake detection | bonafide · spoofed, speaker-agnostic, on its own endpoint | Yes | Yes |
Emotion is one signal. Here is the rest.
Not in Hume's legacy API
- Age band and gender per speaker
- Speaker-agnostic voice deepfake detection, bonafide or spoofed
- A probability on every class, on every output
Deployment
The same models run as a cloud API, in your own environment, or at the edge, in batch and in streaming. Customer-hosted and edge deployments are arranged with us directly.
Replacing Hume EVI or TTS?
Behavioral Signals does not run a conversational voice agent or generate speech, so it is not a replacement for either. If that is what you are replacing, talk to us and we will point you somewhere useful.
Hume's newer Expression APIs are a separate product. This page does not map or benchmark them. It covers the legacy batch and streaming workflow and its 48-dimension prosody output.
Quickstart
Send one recording you already sent to Hume
Every new account includes 30 minutes of behavioral batch processing, so you can run your own audio through before adding any credit. No credit card required. The SDK needs Python 3.10 or later. The REST interface is language-agnostic and covers everything the SDK does.
Step 1. Get a key
Create an account, verify your email, and issue an API key. The portal shows your client ID next to it.
Step 2. Install and set credentials
Install the SDK and export your client ID and API key.
Step 3. Send a recording and read the result
Upload one file, wait for the process to finish, and print what comes back.
Step 2. Install and set credentials
pip install behavioralsignals
export BEHAVIORALSIGNALS_CID=<your-client-id>
export BEHAVIORALSIGNALS_API_KEY=<your-api-key>Step 3. Send a recording and read the result
from behavioralsignals import Client
from behavioralsignals.utils import print_results
client = Client()
process = client.behavioral.upload_audio(file_path="call.wav")
result = client.behavioral.wait_for_result(pid=process.pid, timeout=600)
print_results(result.results)Client() reads those two environment variables. To pass credentials in code instead, use Client("your_cid", "your_api_key"). Over REST, the key goes in the X-Auth-Token header and the client ID goes in the path, replacing Hume's X-Hume-Api-Key.
The developer reference has a live console on every endpoint, so you can call the API straight from the docs page.
Quickstart
Every field your code reads has a counterpart
Submit a recording, wait for the process, read the results. Where there is no equivalent, the table says so rather than suggesting something close.
| Hume | Behavioral Signals | Note |
|---|---|---|
X-Hume-Api-Key header | X-Auth-Token header, plus client ID in the path | Both come from the portal |
POST /v0/batch/jobs with urls or files | POST /v5/clients/{cid}/processes/audio, or an S3 signed-URL submission | One recording per process |
job_id | pid | |
GET /v0/batch/jobs/{id}/predictions | GET /v5/clients/{cid}/processes/{pid}/results | Poll until complete, or use wait_for_result |
models.prosody.granularity | None in batch, which is utterance level. level in streaming | segment, utterance, or both if unset |
transcription.language hint | No equivalent | Language is detected, not hinted |
identify_speakers | The diarization task | Returned in every batch result |
grouped_predictions[].id | finalLabel of the diarization entry with the same id | SPEAKER_00, SPEAKER_01 and so on |
time.begin, time.end | startTime, endTime | Seconds, as strings |
text | The asr entry with the same id | Batch only |
confidence, speaker_confidence | No equivalent | |
emotions[{name, score}] | prediction[{label, posterior}] and finalLabel, one entry per task | intensity returns prediction[{score}] |
transcription_metadata.detected_language | finalLabel and posterior of the language entry | |
| Streaming WebSocket | gRPC StreamAudio at streaming.behavioralsignals.com:443 | 16-bit linear PCM, mono. The first message carries configuration only |
Batch, before and after
Before, with Hume
curl -X POST https://api.hume.ai/v0/batch/jobs \
-H "X-Hume-Api-Key: $HUME_API_KEY" \
-H "Content-Type: application/json" \
-d '{"urls": ["https://example.com/call.wav"],
"models": {"prosody": {"granularity": "utterance"}}}'After, over REST. The submit call returns a process ID. Pass it to the results call.
curl -X POST \
"https://api.behavioralsignals.com/v5/clients/$BEHAVIORALSIGNALS_CID/processes/audio" \
-H "X-Auth-Token: $BEHAVIORALSIGNALS_API_KEY" \
-F file=@call.wav -F name=call-0142
curl "https://api.behavioralsignals.com/v5/clients/$BEHAVIORALSIGNALS_CID/processes/$PID/results" \
-H "X-Auth-Token: $BEHAVIORALSIGNALS_API_KEY"Hume batch jobs could take a list of URLs. We take one recording per process, by file upload or S3 signed URL, so loop over your recordings. Files can be wav, mp3 or other non-proprietary audio encodings.
Reading the response
Hume nested results by speaker and segment. We return a flat results array. Each element names one task for one utterance id, with startTime, endTime, a prediction array and a finalLabel. Times and posteriors arrive as JSON strings, so cast them.
from collections import defaultdict
def num(s):
return float(s) if s not in ("", None) else None
def utterances(payload):
rows = defaultdict(dict)
for r in payload["results"]:
u = rows[r["id"]]
u["start"], u["end"] = num(r["startTime"]), num(r["endTime"])
task = r["task"]
if task == "intensity":
u["intensity"] = num(r["prediction"][0]["score"])
elif task != "features":
u[task] = r["finalLabel"]
u[task + "_p"] = {p["label"]: num(p["posterior"])
for p in r["prediction"] if p.get("label")}
return [rows[k] for k in sorted(rows, key=lambda k: rows[k]["start"])]Each row then carries keys such as u["emotion"] and u["emotion_p"]["sad"]. In batch, utterances of one second or less return only diarization, asr and language, so those rows have no behavioral keys.
With the SDK, result.model_dump() returns the same field names as the REST payload, such as startTime and finalLabel, without by_alias=True. It also drops empty fields and adds st and et, so read optional keys with .get() if you pass its output to the parser above.
Streaming
Replace the WebSocket with a gRPC stream of 16-bit linear PCM. We take raw mono PCM at any sample rate and resample to 16 kHz. Chunks of 100 to 500 ms work best.
from behavioralsignals import Client, StreamingOptions
from behavioralsignals.utils import make_audio_stream
client = Client()
audio_stream, sample_rate = make_audio_stream("call.wav", chunk_size=0.25)
options = StreamingOptions(sample_rate=sample_rate, encoding="LINEAR_PCM")
for message in client.behavioral.stream_audio(audio_stream=audio_stream,
options=options):
handle(message.results)Levels. Each result carries a level: segment for fixed two-second windows, utterance for complete speech units. You get both unless you pick one.
Short segments. Every streaming segment gets all 24 signals and intensity, however short it is.
Transcription. Streaming covers the paralinguistic outputs. Transcription and diarization are batch only, so a live product that also needs text keeps its own speech recognition running alongside.
Without the SDK. Generate a client from the proto definitions in the SDK repository.
Deepfake detection. Same calls, on client.deepfakes. It runs independently of the behavioral outputs.
Signal mapping
We have already mapped Hume's 48 prosody dimensions
Three have a class of the same meaning. 38 can be approximated from one or more outputs. Seven have no reliable proxy, and we say so rather than pointing you at the nearest-sounding label. This is a mapping by meaning, not a statistical fit between the two models, so validate it on your own audio before you rely on it.
How to read a row: "positivity = negative" means use that class's posterior, or the final label, in place of the Hume score. Where a row names two signals, combine them, usually by requiring both. Even a Direct row shares meaning, not scale, so scores and thresholds do not carry over. Re-fit them on representative audio.
| Hume dimension | Nearest Behavioral Signals signal | Rating |
|---|---|---|
| Anger | emotion = angry | Direct |
| Sadness | emotion = sad | Direct |
| Joy | emotion = happy | Direct |
| Amusement, Ecstasy, Excitement, Triumph | emotion = happy with strength = strong | Close |
| Contentment, Relief, Satisfaction | positivity = positive with strength = weak | Close |
| Calmness | emotion = neutral with strength = weak | Close |
| Admiration, Adoration, Aesthetic Appreciation, Love, Pride, Romance | positivity = positive | Close |
| Awe, Surprise (positive) | positivity = positive with strength = strong | Close |
| Fear, Horror, Surprise (negative) | positivity = negative with strength = strong. There is no fear class | Close |
| Anxiety, Distress | positivity = negative, often with hesitation = yes | Close |
| Contempt, Disgust, Envy | positivity = negative. The angry posterior is a weak proxy | Close |
| Disappointment, Empathic Pain, Guilt, Pain, Shame | emotion = sad with positivity = negative | Close |
| Boredom, Tiredness | strength = weak, engagement = withdrawn | Close |
| Concentration, Entrancement, Interest | engagement = engaged | Close |
| Awkwardness, Confusion, Doubt, Embarrassment | hesitation = yes | Close |
| Contemplation, Craving, Desire, Determination, Nostalgia, Realization, Sympathy | No direct equivalent | Gap |
How the two outputs differ
Scores and probabilities. Hume returned one score per dimension per segment, described in its own schema as a high-dimensional embedding in emotion space. Those scores did not form a distribution. We return a probability for every class of every task, and the posteriors of one task sum to 1.
Final label. Each task also returns a final label. For the behavioral tasks, it is the class with the highest probability.
Continuous values. intensity scores how intense the expressed emotion is, from 0 to 1, without naming the emotion.
Granularity. Hume offered word, sentence, utterance and conversational-turn granularity. We analyze utterances bounded by voice activity detection in batch, and fixed two-second segments or utterances in streaming. There is no word-level output. Turns can be rebuilt from diarized utterances.
Minimum length. In batch, the behavioral outputs need utterances longer than one second. Shorter ones return diarization, asr and language only. In streaming, every segment gets everything.
Taxonomy. Four emotion classes, three levels each of arousal and valence, and separate behavioral outputs including engagement, in place of 48 named dimensions.
Every value is an estimate of what is expressed in the speech signal. It is not a measurement of what the speaker feels, intends or believes.
When there is no direct equivalent
Do not force one. Pointing a rule at the nearest-sounding class swaps the signal your product relied on for a different one under the same name. Three options:
Option 1. Retire the label
Check whether the decision it fed still needs it.
Option 2. Use the close mapping
Port the rule to the mapped signals and fit its threshold on your own audio.
Option 3. Build your own taxonomy
Above the API. Train a small classifier on the per-utterance outputs and predict your own labels.
For the third option, the inputs available per utterance are the class posteriors for emotion, arousal and valence, the hesitation "yes" probability, the posteriors for speaking rate and engagement, the intensity score, and the behavioral embeddings if you request them. You build and run this layer yourself: it needs your own labeled utterances to train on and a held-out set to validate against, and it does not add labels to the API's output.
Three caveats
- A custom layer can only learn from what the payload carries. Some Hume labels drew on cues we do not model, such as non-speech vocal sounds or the words spoken. Those may not be recoverable.
- We publish no performance figure for customer-built classifiers. Your hold-out results are the measure.
- The label is yours, not the API's. It does not appear in the results payload. You own it, maintain it, and re-validate it when your audio or our major version changes.
Signal mapping
Port the rule, not the number
Your thresholds do not carry over, and that is the one part worth slowing down for. A Hume score and a Behavioral Signals posterior are different quantities. Keep the rule your product applies. Swap in the mapped signals. Fit a new threshold on a few hundred of your own labeled utterances.
Before, a threshold on one of Hume's 48 scores
if scores["Fear"] > 0.35:
flag(utterance)After, the mapped signals, with a threshold fitted on your own labels
if u["positivity_p"]["negative"] > T and u["strength"] == "strong":
flag(utterance)The flag, and whatever it triggers, belongs to your application. The API supplies the per-utterance estimates the rule reads.
Intent, churn risk, fraud, customer satisfaction and escalation are not API outputs. They are what an integration builds on top of the per-utterance estimates, in the same way that rules built on Hume scores were.
Performance and limits
Known limits, stated plainly
Domain. Results vary with the domain of the audio. Audio that differs materially from the evaluated domains should be validated before launch.
Emotion taxonomy. Four classes. A finer taxonomy is built above the API, not requested from it.
Short utterances. In batch, the behavioral outputs need utterances longer than one second. Arousal holds down to about a second of voiced speech. The others need longer to be reliable.
Speaker attributes. Age covers four bands from 18 to 65 and does not signal a speaker outside that range. Gender has two classes. In batch, both are averaged per speaker across the file.
Diarization. Errors concentrate at speaker turns and in overlapping speech.
Experimental features. Video deepfake detection and deepfake generator identification are marked experimental. Their endpoints and response schema may change.
Languages. We currently support 11 languages: English, Arabic, Russian, Chinese, French, German, Spanish, Portuguese, Japanese, Hindi, and Tamil. Our approach is language-agnostic by design. It doesn't use a separate model for each language, nor does it need to understand the words being spoken. The engine can therefore process any language today.
Performance and limits
What we do not do
Behavioral Signals is a speech analysis API. It is not a drop-in replacement for any Hume product. If your product depends on one of these, plan a separate path for it before you cut over.
Hume-compatible numbers
No Hume scores, no thresholds tuned on them, no 48-dimension taxonomy. Outputs are mapped by meaning and re-fitted.
Non-speech vocal sounds
No vocal-burst model for laughs, sighs and similar.
Text analysis
No emotional language, sentiment, toxicity or named-entity recognition from text. Valence is estimated from tone of voice, not from the words.
Face and image analysis
No facial expression, FACS or face mesh.
Conversational voice and speech generation
No EVI-style voice sessions, tools or voices, and no text-to-speech.
Hume's Expression APIs
Hume's newer product. Not mapped or benchmarked here.
Migration
Seven steps from inventory to cutover
Most of the work is mechanical. The one part that needs care is porting rules built on Hume scores, because the two APIs' numbers mean different things.
Inventory. List every Hume model, option and dimension your code reads, and every rule or threshold that uses them.
Scope the gaps. Mark anything that used vocal bursts, text emotion, toxicity, entities or face models, and every Gap-rated prosody dimension. Decide how each gets replaced or retired.
Connect. Create an account, issue a key, install the SDK, and run one recording through batch.
Swap the client. Replace the request, polling and parsing code using the field table.
Port the rules. Re-express each rule over the mapped signals, then fit its thresholds on your own labeled audio.
Move streaming. Switch live paths to gRPC with linear PCM, choose your level, and add your own transcription if the live path needs text.
Cut over. Release behind a flag, compare against your held-out results, and remove the Hume dependency.
For production-scale migrations, an optional dedicated support service gives you direct engineering contact, prioritized response and named ownership, agreed per engagement.
Migration
Validating on your own audio
Hume's legacy API is no longer available, so a side-by-side run is only possible against outputs you stored before the retirement. Either way the steps are the same.
1 · Sample
Draw a few hundred utterances from production audio that represents your callers, channels and languages, including the cases your rules exist to catch.
2 · Label
Record the outcome your product cares about for each utterance, or reuse the Hume-based decision where you trust it.
3 · Fit
For each rule, choose the mapped signals and fit the threshold on this sample.
4 · Hold out
Check the fitted rule on utterances you did not fit on, and per segment of your traffic, before switching over.
Help
Migration questions
Is Behavioral Signals a drop-in replacement?
Not as-is, but most of the change is mechanical. You swap the client and remap the response using the field and signal tables. The rules your product applies to Hume scores get re-expressed and re-fitted on your own audio.
Can I reuse my Hume score thresholds?
No. A Hume score and a Behavioral Signals posterior are different quantities. Keep the rule, swap in the mapped signals, and fit a new threshold on a few hundred of your own labeled utterances.
What if my product relies on a dimension with no equivalent?
Three options: retire the rule, use the close mapping where one exists, or build your own label above the API from the class posteriors.
Does this cover Hume's new Expression APIs?
No. This page is for the legacy Expression Measurement API: the batch and streaming client workflow and its 48-dimension prosody output. If you are comparing options, run the same recordings through your 30 free batch minutes and compare the results yourself.
We use Hume EVI or TTS. Does this apply?
Not really. This page covers speech analysis. Hume says EVI and TTS access ends 13 November 2026. We analyze speech. We do not run conversational voice agents or generate speech, so plan a separate provider for those.
Will I still get a transcript from live audio?
Keep your current speech-to-text on live paths. Our streaming adds the emotional, behavioral and speaker outputs alongside it. Batch returns transcription and diarization with every result.
What does it cost to evaluate?
Every new account starts with 30 minutes of behavioral batch processing, with no credit card required, so you can run a first sample of your own recordings. After that, you pay per minute from one prepaid balance, from $0.10 per minute for behavioral batch processing. Voice deepfake detection is priced separately. See pricing.
Help
Developer resources
Backwards compatibility covers one major version, from v5. Major updates are announced in advance with migration notes.
Start with your own audio.
Run a sample of your production recordings through the 30 free batch minutes, apply the mapping on this page, and bring the results to us.
No sales call needed. 30 minutes of batch processing on every new account.