DEV Community

Cover image for Audio Price Cuts: I Normalized Three Billing Units First
张洲诚(Zack.ZHANG)
张洲诚(Zack.ZHANG)

Posted on

Audio Price Cuts: I Normalized Three Billing Units First

For two days my feed has been full of speech model price cuts: Alibaba Cloud's model platform Bailian shipped a batch of new audio models, and the coverage quotes reductions up to 95%. I was sitting on narration work and interview audio to transcribe, so I opened the price sheet. What I got out of it was confusion, and the cause turned out to be measurement rather than pricing.

The three rows I was comparing were never in the same unit. One model charges per 10,000 characters of text. One charges per second of audio. The new batch charges per million tokens. Deciding that 0.00022 beats 1.5 because it looks smaller is not analysis. On top of that, nobody writing about the 95% said which unit it moved in.

So instead of comparing the sheet, I compared bills. Same script into two generations of synthesis, same recording into three generations of transcription, then the usage each call actually recorded, converted to one unit.

Setup, in case you want to repeat it

The Bailian CLI skill package lets your coding agent run these commands for you; npm install -g bailian-cli works in a terminal if you prefer that. Synthesis and transcription need a key, and you can sign one here.

bl speech synthesize --text-file text-zh.txt --model qwen-audio-3.1-tts-flash --voice longanhuan_v3.1 --format wav --out new.wav --output json
bl speech recognize --url new.wav --model qwen-audio-3.1-asr-flash --output json
Enter fullscreen mode Exit fullscreen mode

--voice is mandatory, and the command that lists voices for a model comes back empty for the new ones, so you finish on the official voice page reading IDs like longanhuan_v3.1. Voices do not carry across models. My first attempt left that flag out and returned Missing required flag: --voice.

The catalog itself is the cheapest thing to check, because that query needs no login and burns no quota:

bl model list --capability TTS --page-size 60 --output json
bl model list --capability ASR --page-size 60 --output json
Enter fullscreen mode Exit fullscreen mode

Nineteen families came back on the synthesis side with two units in the same list, the older cosyvoice and qwen3-tts entries priced per 10,000 characters and the new 3.1 batch per million tokens. Eighteen came back on the transcription side and it is messier: fun-asr, paraformer and qwen3-asr all bill per second of audio, and only 3.1-ASR bills per token.

One price I got wrong at first, so check your own extraction. Asking for a version returns a family view, and the first item in it is not necessarily the model you named. I read 0.8 off items[0], which belonged to cosyvoice-v3.5-flash, when the model I was comparing against lists 1 CNY per 10,000 characters. My headline ratio ran at 5.15x until I matched on items[].model and corrected it to 6.44x. Family names fail outright, and there is no fuzzy matching behind that either.

What the measurements said

My script is 133 characters of Chinese: 120 characters plus 13 punctuation marks. Billing recorded 253 characters, because Chinese counts as 2 and punctuation as 1. An English line of 240 characters recorded 240, no multiplier. That single detail halves or doubles any estimate you build from a word count.

Three billing units survive on one price sheet

Same script, same 24.88 seconds of output audio, two models:

Synthesis run That one call CNY per 10,000 characters CNY per minute of audio
qwen-audio-3.1-tts-flash 0.0039315 0.1554 0.0095
cosyvoice-v3-flash 0.0253 1.0000 0.0610

6.44x for identical work. Same recording through three transcription models: qwen-audio-3.1-asr-flash billed 0.0004996 CNY, qwen3-asr-flash 0.00528, fun-asr 0.00506, a 10.57x gap on that one call, or 0.0012 versus 0.0132 CNY per minute of audio.

Those ratios are the useful output, more than the absolute prices, because both sides were measured on the same file. This catalog quotes in Chinese yuan and I have left it that way rather than invent a conversion.

Neither number was free to get. I have no ffmpeg on this machine, and the wav header the CLI writes is not trustworthy: Python's wave module reads it as 44739.24 seconds of audio where the real figure is 24.88. Both generations carry the same corrupted header. The duration had to come back out of the file size, 1194284 bytes minus a 44 byte header over 24000 samples at 2 bytes each. The English line came out at 698924 bytes and 14.56 seconds by the same arithmetic.

The wait time that never reaches the price sheet

Same script, same output format. qwen-audio-3.1-tts-flash returned in 2.2 seconds and cosyvoice-v3-flash took 17.0, a gap of 7.7x sitting right next to the 6.44x gap in money. On the transcription side the same recording went through three models in 0.9, 0.9 and 1.7 seconds.

Nobody prints a latency column on a discount announcement. It is still the same bill. Ten thousand short clips run one after another differ by roughly 41 hours of waiting between those two synthesis models. The cheaper generation is also the faster one here, and I checked both numbers twice. As the batch grows, this term stops being a rounding note and competes with unit price for attention.

Two places a naive comparison breaks

Token counts do not travel. The same 24.88 seconds of audio registered 368 input tokens on one transcription model and 622 on another, a density gap of 1.76x (14.72 versus 25.92 tokens per second). "Billed per token, therefore cheaper" is a broken inference; only money back in minutes compares.

The same three models logged that file as 25, 24 and 23 seconds, under three different field names: duration for 3.1, seconds plus an audio_tokens breakdown for qwen3-asr-flash, and duration alone for fun-asr. Per-second billing handles silence and rounding its own way, so a long recording does not bill at wall-clock duration times rate.

And on the new synthesis model, 94.93% of the cost was on the output side: 133 input tokens at 1.5 CNY per million against 311 output tokens at 12 CNY per million, which is 18.7x. Shortening my script from 133 characters to 100 changes almost nothing. Shortening the audio changes everything. Within one family there is also a 7.5x jump waiting: streaming 3.1-ASR input lists 6 CNY per million tokens, the non-streaming version of the same model 0.8.

Checking your own bill: one command out of four

Prerequisite: every command here is tagged [Console] in bl's help, so it needs bl auth login --console, not just the API key.

bl usage stats --model qwen-audio-3.1-tts-flash --days 1 --output json
bl usage stats --days 1 --type Audio --output json
bl monitor metrics --metric model_usage --model qwen-audio-3.1-tts-flash --days 1 --output json
Enter fullscreen mode Exit fullscreen mode

Empty items, modelsCalled: 0, empty series. Account-level views have numbers but blend every call of the day into one line. The subtraction trick fails too: --days 1 is a sliding window, and input_tokens fell from 470537 to 443114 while I sent no text calls.

The audit log is what works: request_id, timestamp and raw usage per call.

bl log audit list --hours 1 --model qwen-audio-3.1-tts-flash --output json
bl usage free --model qwen-audio-3.1-tts-flash --output json
Enter fullscreen mode Exit fullscreen mode

Every number above came out of the first one. The second showed 1346 tokens used against my four synthesis calls of 444 + 444 + 229 + 229, and the extra pair was a helper script of mine that died on a typo after the billable call had already gone out. Rerunning it synthesized the same text twice, with no refund and no trace but that delta. A billable call is not rolled back when your script crashes downstream, so the syntax check happens before the request leaves.

The one command that works also argues with itself. The Usage line of bl log audit list --help lists only [--hours <n>], while the --start-time description on that same page says it overrides --days. Pass --days and you get Unknown flag "--days" back. The error is the truth and the copy is stale, so when you write a wrapper, or hand this to an agent, code against the error message and not the help text.

Quota lookups run on naming too, and the failure is silent rather than loud:

bl usage free --model qwen-audio-3.1-tts --output json
bl usage free --model qwen-audio-3.1-tts-flash --output json
bl usage free --model paraformer-v2 --output json
Enter fullscreen mode Exit fullscreen mode

The family name returns total=0. The version name returns 1,000,000 tokens with 998654 left and an expiry of 2026-12-21, and the transcription model of the same batch returns 1,000,000 with 999556 left. paraformer-v2 reports 36000 seconds and shows its expiry as 2099-01-01, and cosyvoice-v3.5-flash returns total=None, so the older generation is not on this surface at all. A legitimate looking zero is the worst answer a query gives.

Almost all of the new model's cost sits on the output side

What I would tell you to do

Each of the two 3.1 models carries 1,000,000 free tokens until 2026-12-21. The terms are per model, 90 days, Beijing region only, no reissue after expiry, no automatic fallback when used up. The "permanently valid" version in circulation is wrong. My whole test batch cost 0 CNY inside that allowance.

Two leftovers from the same runs are worth more than the ratios if you automate on top of this. Synthesis is deterministic: the same script and the same voice produced two wav files with identical MD5 sums, 7a8e577b9515ca954f4d18afd1841439. A --seed flag exists in the range 0 to 65535, but it is not a precondition for reproduction here, so a retry loop costs money without adding noise. Second, --vocabulary works on fun-asr, where bl speech recognize --help says it takes effect only on Qwen-Audio-3.0-ASR-Flash models. I built a sentence around two invented place names and ran the same recording twice, once bare and once with the hot word table {"蟠越":5,"鳻溪":5}. The first name came back wrong without the table and right in both places with it, punctuation corrected along with it; the second name came back wrong both times at the same weight of 5. Test the flag the docs exclude rather than assuming the exclusion, and do not assume a higher weight is safer either.

Quality did not stay out of it entirely. Both qwen-audio-3.1-asr-flash and qwen3-asr-flash rendered one word of my script as the wrong homophone, and I left the raw output as returned instead of fixing it by hand. Dialect, background noise and multiple speakers per recording I did not touch at all.

Before you act on any price news, here or anywhere else: find the billing unit on both sides, convert to something your work is actually measured in (money per minute of delivered audio is the one that survives), and pull per-call usage instead of trusting an aggregate. Add the wait time to the same row, because a batch job pays for it and no price sheet lists it. That is the whole method, and it took me half a day including the mistakes.

If you want to walk through it yourself: install the skill package and sign a key.

Which of the three units bites you most often, characters, seconds or tokens? Put your case in the comments and I will run the useful ones next.

Top comments (0)