LobeLOBE

Learn

What is a good TTFB?

For an API endpoint, a good time-to-first-byte is roughly under 50ms on loopback, under 100ms on a LAN, and under 200–500ms over the internet depending on distance. The widely quoted “800ms is good” figure comes from web-page field data (redirects + connection setup + server time for full page loads) and is far too lenient a bar for a single API call.

Budgets by environment

TTFB = network round trip + your server’s processing time before the first response byte. So a sensible budget is the network floor for where you’re running, plus a defensible allowance for app work:

  • Loopback (localhost): network cost is ~0, so TTFB is almost pure server time. ≤50ms is healthy; anything beyond it is your code, your query, or your framework — not the network.
  • LAN (device → dev machine): add ~1–5ms of network. ≤100ms is a reasonable ceiling.
  • Remote (over the internet): add 20–150ms of round trip depending on geography. ≤200ms is excellent, ≤500ms acceptable for non-critical endpoints; beyond that users feel it.

Why the 800ms web guidance doesn’t apply to APIs

Google’s web-vitals guidance calls page TTFB under 800ms “good” — but that number is measured from real-user page navigations and includes redirect chains, DNS, TCP, and TLS setup on cold connections across the whole world’s networks. An API call on a warm keep-alive connection has none of those costs. If your JSON endpoint takes 700ms to first byte on localhost, it’s not “good” — it’s hiding a slow query.

Judge the p95, not the average

A route with a 60ms average can still be delivering 900ms to one request in twenty. Percentiles catch what averages hide: p50 tells you the typical experience, p95 tells you the bad day. Budgets are most useful applied to p95 — “p95 TTFB ≤ 200ms” is a commitment; “average 90ms” is a hope. And if the distribution has two clusters (some requests 40ms, some 600ms), you don’t have one slow endpoint — you have two code paths, which is a different problem entirely.

What’s inside a high TTFB

TTFB is opaque from the outside — it doesn’t say whether the time went to the database, rendering, or an upstream call. The standard way to open it up is the Server-Timing response header: one middleware line makes your app report db;dur=190, render;dur=40 alongside each response, and any client that reads the header can split the TTFB bar into named segments. Local profilers like Lobe render that split automatically, so “280ms TTFB” becomes “190ms of it is one query.”

Measure it instead of guessing: Lobe is a free, local HTTP profiler — one command captures every request’s DNS, TCP, TLS, TTFB, and download time, judged against grounded baselines. No agent, no account. brew install kpwithcode/lobe/lobe or cargo install lobe-cli. How it works →