Skip to content
ScaledPapers

Blog

TTS latency monitoring: slow or down?

Updated on by Nurbol Sakenov

A text-to-speech API fails in two ways. The obvious one is down: requests time out or come back 5xx. The quieter one is slow: the API still answers 200, but the first audio arrives a second late and your voice agent sounds like it froze. Callers hang up either way.

The provider's status page tells you what the provider has confirmed. A check from outside tells you what your requests are getting right now.

What "down" looks like for a TTS API

For ElevenLabs, Deepgram, OpenAI's speech endpoint, or any other voice API, "down" usually shows up as one of these:

  • Connection or TLS failures to the API host
  • 5xx responses from the synthesis endpoint
  • 429 responses when your own traffic is normal
  • 401 or 403 after a key rotation. That one is on your side, but production breaks the same way.
  • A 200 with an empty body or an error in the JSON

A ping of the provider's marketing site catches none of these. The marketing site and the API usually run on different hosts. You need a check against the API itself.

What "slow" looks like

Speech latency has more than one clock:

  • Time to first byte: when the response headers arrive. Easy to measure, but on a streaming endpoint headers can arrive well before any audio.
  • Time to first audio: when the first playable chunk arrives. This is what a caller hears.
  • Total synthesis time: when the whole clip is done. Matters for batch jobs, less for streaming agents.

A ScaledPapers check measures duration: from the start of the request until the response body has been read, up to the first 64 KB. A clip of one or two words fits well inside that, so a short synthesis request gives you connect time plus the whole tiny clip. That is close to time to first audio, and it moves when the provider gets slow.

Look past the average. A 300 ms average can hide one request in twenty that takes 2.5 s or longer, and that is the one a caller notices.

Two checks that cover most of it

You do not need to synthesize audio every minute to know the API is reachable. Two API checks do most of the work.

1. Reachability check (frequent, no synthesis cost). Method GET against a lightweight authenticated endpoint, such as the provider's voices or models list. Put your key in the header your provider expects under Request headers. Keep Expected status code at 200 and add a Body contains or JSONPath equals assertion, so an empty or error body counts as a failure.

2. Synthesis canary (less often, real audio). Method POST to the synthesis endpoint, a Content-Type: application/json request header, and a body with very short text such as OK. Expect 200. The response is audio, so assert on the status code only. Body assertions cannot match binary audio.

Run the canary every 15 minutes, or every 5 if voice is the core of your product. Do the math before you pick: OK every 5 minutes is about 17,000 characters a month per region, and every 15 minutes is about 6,000. Check how your provider bills very short requests. Use a separate key with a spending cap, not a low rate limit. A tight rate limit can produce the same 429s you are watching for.

The key is stored with the check. A separate key means you can rotate it without touching production.

The same two-check setup works for an LLM API. See monitor the OpenAI API.

Regions

The free plan runs each check from one region. Pro runs it from all five: San Francisco, Ashburn, Montreal, Frankfurt, and Singapore.

From one region you know whether the API answers from there. From five, a failure in one city while four succeed usually means routing near that city, not a provider outage. That is the difference between paging someone and writing a note. More on that in why we ping from different regions.

What to alert on

Failures in a row, not one failure. Under Advanced, the Confirmation period sets how many failed checks in a row count as down: 1, 2, 3, or 5. The default is 3. On a canary that runs every 15 minutes, 3 failures is half an hour, so set it to 2. On a 5-minute reachability check, keep 3.

Slow as a failure. There is no latency alert. The lever you have is Request timeout: 5, 10, 15, or 30 seconds. A 5-second timeout on the canary turns any synthesis slower than that into a failed check, and confirmation still has to be met before anyone is paged. Pick a timeout your agent cannot live past, not your normal latency.

Read the status code before you blame the provider. A 401 or 403 fails the check the same way an outage does. Open the samples table on the check before you escalate. If the code is 401, it is your key.

Latency trend. On the check page, the Test duration (avg) metric follows the time range you pick, back to the start of your plan's history: 14 days on Free, 90 on Pro. The region filter splits it by city. The samples table shows the duration of each run. Look at it when the provider ships a new model or moves regions. If you want percentiles, GET /v1/endpoints/{id}/history returns latency_ms for every sample, and you can compute p50 and p95 yourself.

Tell your users before they tell you

If your product speaks, your customers notice a TTS outage quickly. Link the canary to a component on your status page, for example "Voice responses". Under When the check is down, pick Create an incident automatically. The default is Don't create an incident. When the check is confirmed down, the incident opens on its own. If you want a person to approve it before it goes public, pick Ask me to review first instead. Then post one line:

Voice replies are delayed because of an upstream provider issue. Text replies still work.

Quick checks when something feels off

  • Is the provider's site reachable at all? See elevenlabs.io or deepgram.com from 16 locations. That page checks the provider's website, not its API, so it backs up your own check and does not replace it. Read the provider's status page too.
  • Is the problem your webhook or callback, not the synthesis? Replay the payload in the webhook tester.

FAQ

Can I monitor a TTS API without paying for synthesis every minute? Yes. Check a free authenticated endpoint, such as the voices or models list, often. Run a tiny synthesis request less often.

What is a good latency target for a voice agent? It depends on the provider and model. Watch the duration of your canary for a week from the regions your users are in. Then set the request timeout above the slowest runs you can accept.

Should I monitor from one region? One region tells you whether the API answers from there. It cannot tell a provider outage from a network problem near the probe. That takes several regions.


Watch your voice API from outside. The free plan includes 5 checks from one region. Pro checks from all five. See pricing.

Validation report

Validation summary

Status validated

Date: 2026-11-03

What was checked

Every claim about ScaledPapers checks was compared with the console and engine source, the plan migrations, and the live region list.

Result

  • Plans: Free is 5 checks, a 5-minute minimum interval, 1 region, and 14 days of results. Pro is 25 checks, 60 seconds, 5 regions, and 90 days. Source: billing.write_workspace_plan in supabase/migrations/0049_resume_checks_on_pro.sql, matching /pricing/.
  • Regions: San Francisco (us-west-1), Ashburn (us-east-1), Montreal (ca-central-1), Frankfurt (eu-central-1), and Singapore (ap-southeast-1). Source: live list_regions on 2026-11-03.
  • Duration: the HTTP probe starts its timer before the request and stops it after reading the response body, capped at 64 KB (MAX_READ_BYTES = 65536 in data_pipeline/engine/workers/checkers/http.py). A one- or two-word clip is well under that cap.
  • Request options: GET, POST, and the other methods. Request headers, a request body (disabled for GET and HEAD), Expected status code, and the Body contains and JSONPath equals assertions. Source: app/src/CheckForm.tsx.
  • Confirmation period: 1, 2, 3, or 5 failures, default 3 (CONFIRMATIONS in CheckForm.tsx, confirmation_failures default in tables.py). Request timeout: 5, 10, 15, or 30 seconds (TIMEOUTS).
  • No latency alerts: HTTP checks have no latency threshold. The post only uses the request timeout as the limit for slow responses.
  • Duration metric: Test duration (avg) is an average (app/src/pages/EndpointCheck.tsx). Time ranges are capped at the plan's retention (rangesForRetention, checkChart.ts). The console shows no percentiles. GET /v1/endpoints/{id}/history returns latency_ms for each sample (data_pipeline/engine/api/routes/endpoints.py).
  • Incident option: under When the check is down, the choices are Don't create an incident (default), Create an incident automatically, and Ask me to review first (INCIDENT_MODE_CHOICES in CheckForm.tsx).
  • Links: /is-it-down/elevenlabs.io and /is-it-down/deepgram.com probe the provider's website (probe_url in public/is-it-down/seed-targets.json), and the post says so. /tools/webhook-tester/, /docs/checks/, /pricing/, and /blog/why-we-ping-from-different-regions/ exist.

Not checked

  • Provider API endpoints, auth headers, and per-character billing. The post keeps these general on purpose.
  • The character counts assume one 2-character request per run and a 30-day month.

This is a source check. No live synthesis request was sent to a TTS provider.