I Ran 15 AI Models Through Speed Tests So You Don't Have To
I'll be honest with you — I almost lost a $4,000 client last month because of slow AI responses.
I had built a chatbot for a SaaS founder's product, and during demo week the latency was brutal. Users would type a question, sit there staring at a spinner for two seconds, then half of them bounced. The founder called me up, not happy. I had to fix it fast, and I didn't have time to guess which model was fastest.
So I did what any freelancer drowning in billable hours would do: I carved out a weekend, grabbed my credit card, and ran proper benchmarks across 15 models. I'm talking real prompts, real responses, real timing data. Took me about 14 hours total, but I saved myself from making the same expensive mistake twice. Now I'm sharing the raw data with you so you don't burn a weekend of your own.
Let me walk you through what I found, what I now use for client projects, and where every single dollar actually goes.
How I Tested Everything
Before we get into the numbers, here's my setup so you can reproduce this if you want.
| Detail | What I Used |
|---|---|
| Test Date | May 20, 2026 |
| Test Regions | US East (Ohio), Asia (Singapore) |
| Prompt | "Explain recursion in 200 words" |
| Output | ~150 tokens per run |
| Runs | 10 per model, averaged |
| Streaming | Yes, server-sent events |
| API endpoint | Global API at https://global-apis.com/v1
|
I picked that recursion prompt because it's the kind of thing clients actually ask for — clear, bounded, and around the 150-token mark. Long enough to measure sustained throughput, short enough that I could burn through 150 trials without losing my mind.
I tracked two metrics because they're the ones that actually matter when you're shipping something to a real user:
- TTFT (Time to First Token): How long until the user sees the first word appear. This is the "feels fast or feels slow" metric.
- Tokens per second: How fast the rest of the response streams out after that first token. This is the "does it keep flowing" metric.
I tested every model through Global API's unified endpoint, which is huge because normally I'd be juggling a dozen different API keys, SDK versions, and auth flows. One endpoint, one auth header, done.
The Full Speed Rankings
Here's everything in one table, fastest to slowest. All prices are per million output tokens, straight from what Global API quoted me at test time.
| Rank | Model | TTFT | Tok/s | Provider | $/M Output |
|---|---|---|---|---|---|
| 1 | Step-3.5-Flash | 120ms | 80 | StepFun | $0.15 |
| 2 | DeepSeek V4 Flash | 180ms | 60 | DeepSeek | $0.25 |
| 3 | Hunyuan-TurboS | 200ms | 55 | Tencent | $0.28 |
| 4 | Qwen3-8B | 150ms | 70 | Qwen | $0.01 |
| 5 | Qwen3-32B | 250ms | 45 | Qwen | $0.28 |
| 6 | Doubao-Seed-Lite | 220ms | 50 | ByteDance | $0.40 |
| 7 | Hunyuan-Turbo | 280ms | 42 | Tencent | $0.57 |
| 8 | GLM-4-32B | 300ms | 38 | Zhipu | $0.56 |
| 9 | Qwen3.5-27B | 350ms | 35 | Qwen | $0.19 |
| 10 | DeepSeek V4 Pro | 400ms | 30 | DeepSeek | $0.78 |
| 11 | MiniMax M2.5 | 450ms | 28 | MiniMax | $1.15 |
| 12 | GLM-5 | 500ms | 25 | Zhipu | $1.92 |
| 13 | Kimi K2.5 | 600ms | 20 | Moonshot | $3.00 |
| 14 | DeepSeek-R1 | 800ms | 15 | DeepSeek | $2.50 |
| 15 | Qwen3.5-397B | 1200ms | 10 | Qwen | $2.34 |
Quick note: reasoning models like DeepSeek-R1 and Kimi K2.5 chew up time internally before they spit out the first visible token. That 800ms TTFT on R1? Half of it is the model thinking in private. If you're going to use a reasoning model, you absolutely need to show the user a "thinking..." indicator, otherwise they'll assume your app is frozen.
Breaking It Down By What I Can Actually Afford
I'm not made of money, and neither are my clients. So I always think about this in tiers. Each tier is what I reach for depending on the project budget.
Tier 1: When The Client Won't Pay Much
If I'm building a hobby project, a prototype, or a client who's pinching pennies, this is where I live.
| Model | Tok/s | $/M |
|---|---|---|
| Qwen3-8B | 70 | $0.01 |
| Step-3.5-Flash | 80 | $0.15 |
Let me say that again. Qwen3-8B streams at 70 tokens per second and costs a penny per million output tokens. A penny. I literally spend more on coffee per client meeting than I would serving a million tokens through this thing. For categorization, tagging, simple summarization, anything where "fast" matters more than "brilliant," this thing is unbeatable. I used it on a small e-commerce site for a client who wanted auto-generated product taglines. Zero complaints, bill came out to literally nothing.
Tier 2: The Sweet Spot
This is where most of my billable work lands. Mid-budget clients who want real quality at reasonable cost.
| Model | Tok/s | $/M |
|---|---|---|
| DeepSeek V4 Flash | 60 | $0.25 |
| Hunyuan-TurboS | 55 | $0.28 |
| Qwen3-32B | 45 | $0.28 |
DeepSeek V4 Flash is my daily driver. 180ms TTFT feels instant, 60 tok/s keeps things flowing, and at $0.25/M I'm not sweating the bill when a client does 50,000 requests in a week. The output quality is genuinely close to GPT-4o for most tasks I've thrown at it — chatbot replies, draft emails, summarizing long docs. If you're a freelancer, this is your bread and butter.
Tier 3: When Quality Beats Budget
For the higher-paying engagements — legal tech clients, healthcare apps, anyone where getting it wrong costs real money.
| Model | Tok/s | $/M |
|---|---|---|
| Doubao-Seed-Lite | 50 | $0.40 |
| GLM-4-32B | 38 | $0.56 |
| Hunyuan-Turbo | 42 | $0.57 |
| DeepSeek V4 Pro | 30 | $0.78 |
DeepSeek V4 Pro is the upgrade pick. Slower — 30 tok/s, 400ms TTFT — but the reasoning is noticeably sharper. I run it for the second pass when a client wants a more "considered" answer.
Tier 4: The Premium Stuff
Only when absolutely necessary.
| Model | Tok/s | $/M |
|---|---|---|
| MiniMax M2.5 | 28 | $1.15 |
| GLM-5 | 25 | $1.92 |
| Kimi K2.5 | 20 | $3.00 |
These are the models I reach for when a client is paying me to build something where mistakes aren't tolerated. Kimi K2.5 at $3.00/M hurts a little, but for a contract review tool? Worth it.
The Geography Problem Nobody Talks About
Here's something that bit me on an Asia-based client project: server location matters more than you'd think.
| Model | US East TTFT | Asia TTFT | Difference |
|---|---|---|---|
| DeepSeek V4 Flash | 180ms | 150ms | -30ms |
| Qwen3-32B | 250ms | 210ms | -40ms |
| GLM-5 | 500ms | 420ms | -80ms |
| Kimi K2.5 | 600ms | 480ms | -120ms |
Asian models like Qwen, GLM, and Kimi have servers physically closer to Singapore, so users there get a free 16-20% latency boost. If your client is in APAC, that's a real UX win you can deliver without changing a single line of code — just swap the model.
DeepSeek is the only provider that felt evenly distributed globally. Their infrastructure clearly has decent coverage everywhere. Good to know if you've got clients on multiple continents.
What Speed Actually Means To Users
I learned this the hard way during that botched demo I mentioned. Here's the rough mental model I use now when picking models:
| TTFT Range | What Users Think |
|---|---|
| Under 200ms | "Wow, this is instant." |
| 200-400ms | "Fast enough." |
| 400-800ms | "Why is this taking so long..." |
| 800ms+ | closes tab |
Anything over 800ms and you're hemorrhaging conversions. For interactive chat, I won't ship anything with TTFT above 400ms unless the client specifically asked for higher-quality output and accepted the tradeoff. DeepSeek V4 Flash at 180ms is comfortably in the "wow" zone.
My Actual Production Setup (Code)
Here's how I'm using this in a current client project — a writing assistant for a content agency. I route simple requests to Qwen3-8B and complex requests to DeepSeek V4 Flash. One endpoint, two models, smart routing.
python
import os
import time
import requests
BASE_URL = "https://global-apis.com/v1"
API_KEY = os.environ["GLOBAL_API_KEY"]
def chat(model: str, prompt: str, stream: bool = True):
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
}
payload = {
"model": model,
"messages": [{"role": "user", "content": prompt}],
"stream": stream,
"max_tokens": 200,
}
start = time.perf_counter()
first_token_time = None
token_count = 0
with requests.post(
f"{BASE_URL}/chat/completions",
headers=headers,
json=payload,
stream=True,
) as r:
r.raise_for_status()
for line in r.iter_lines():
if not line:
continue
chunk = line.decode("utf-8").removeprefix("data: ")
if chunk == "[DONE]":
break
if first_token_time is None and '"content"' in chunk:
first_token_time = time.perf_counter() - start
token_count += 1
total_time = time.perf_counter() - start
return {
"ttft_ms": round(first_token_time
Top comments (0)