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.

Mapping reviewed 6 October 2026 · Covers Hume's legacy Expression Measurement API, not Hume's newer Expression APIs

Hume EVI and TTS have an announced cutoff

Read Hume's official notice

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.

SignalClassesBatchStreaming
emotionhappy · angry · neutral · sadYesYes
strength (arousal)weak · neutral · strongYesYes
positivity (valence)negative · neutral · positiveYesYes
hesitationno · yes, where the "yes" probability doubles as a continuous hesitation scoreYesYes
speaking_rateslow · normal · fastYesYes
engagementengaged · neutral · withdrawnYesYes
age18 to 22 · 23 to 30 · 31 to 45 · 46 to 65. One value per speaker in batch, per segment in streamingYesYes
gendermale · female. One value per speaker in batch, per segment in streamingYesYes
intensityContinuous score from 0 to 1. No classes, no final labelYesYes
languageDetected language with its probabilityYesNo
asrTranscript of the utteranceYesNo
diarizationSpeaker label, plus speaker embeddings when you ask for themYesNo
featuresBehavioral embeddings, returned when you ask for themYesYes (when requested)
Voice deepfake detectionbonafide · spoofed, speaker-agnostic, on its own endpointYesYes

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.

HumeBehavioral SignalsNote
X-Hume-Api-Key headerX-Auth-Token header, plus client ID in the pathBoth come from the portal
POST /v0/batch/jobs with urls or filesPOST /v5/clients/{cid}/processes/audio, or an S3 signed-URL submissionOne recording per process
job_idpid
GET /v0/batch/jobs/{id}/predictionsGET /v5/clients/{cid}/processes/{pid}/resultsPoll until complete, or use wait_for_result
models.prosody.granularityNone in batch, which is utterance level. level in streamingsegment, utterance, or both if unset
transcription.language hintNo equivalentLanguage is detected, not hinted
identify_speakersThe diarization taskReturned in every batch result
grouped_predictions[].idfinalLabel of the diarization entry with the same idSPEAKER_00, SPEAKER_01 and so on
time.begin, time.endstartTime, endTimeSeconds, as strings
textThe asr entry with the same idBatch only
confidence, speaker_confidenceNo equivalent
emotions[{name, score}]prediction[{label, posterior}] and finalLabel, one entry per taskintensity returns prediction[{score}]
transcription_metadata.detected_languagefinalLabel and posterior of the language entry
Streaming WebSocketgRPC StreamAudio at streaming.behavioralsignals.com:44316-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.

3 Direct38 Close7 Gap

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 dimensionNearest Behavioral Signals signalRating
Angeremotion = angryDirect
Sadnessemotion = sadDirect
Joyemotion = happyDirect
Amusement, Ecstasy, Excitement, Triumphemotion = happy with strength = strongClose
Contentment, Relief, Satisfactionpositivity = positive with strength = weakClose
Calmnessemotion = neutral with strength = weakClose
Admiration, Adoration, Aesthetic Appreciation, Love, Pride, Romancepositivity = positiveClose
Awe, Surprise (positive)positivity = positive with strength = strongClose
Fear, Horror, Surprise (negative)positivity = negative with strength = strong. There is no fear classClose
Anxiety, Distresspositivity = negative, often with hesitation = yesClose
Contempt, Disgust, Envypositivity = negative. The angry posterior is a weak proxyClose
Disappointment, Empathic Pain, Guilt, Pain, Shameemotion = sad with positivity = negativeClose
Boredom, Tirednessstrength = weak, engagement = withdrawnClose
Concentration, Entrancement, Interestengagement = engagedClose
Awkwardness, Confusion, Doubt, Embarrassmenthesitation = yesClose
Contemplation, Craving, Desire, Determination, Nostalgia, Realization, SympathyNo direct equivalentGap
Direct a class of the same meaning Close approximated from one or more outputs Gap no reliable proxy in the payload

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.

Talk to us about mapping your implementation.

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.

  1. Inventory. List every Hume model, option and dimension your code reads, and every rule or threshold that uses them.

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

  3. Connect. Create an account, issue a key, install the SDK, and run one recording through batch.

  4. Swap the client. Replace the request, polling and parsing code using the field table.

  5. Port the rules. Re-express each rule over the mapped signals, then fit its thresholds on your own labeled audio.

  6. Move streaming. Switch live paths to gRPC with linear PCM, choose your level, and add your own transcription if the live path needs text.

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

© 2026 Behavioral Signals. All rights reserved.