Spoken confirmations ("Your order is ready for pickup") are easy to add, and easy to get wrong by shipping an API key in front-end code. Here is a small pattern that keeps the key on your server.
I work on Speakvora, a speech API. It is in early access (free allowance first, then pay as you go), and this post is mainly about the pattern; the same shape works with any text-to-speech API that returns an audio link.
The idea
The browser never talks to the speech API. It asks your server to speak a sentence; your server calls the API with the key and sends back only the audio URL.
1. Server route (Node 18+, Express)
import express from "express";
const app = express(); app.use(express.json());
app.post("/api/speak", async (req, res) => {
const text = String(req.body.text || "").slice(0, 500);
const r = await fetch("https://speakvora.com/api/v1/speak", {
method: "POST",
headers: { "x-api-key": process.env.SPEAKVORA_KEY, "content-type": "application/json" },
body: JSON.stringify({ text, voice: "af_heart", lang: "en" }),
});
const j = await r.json();
res.status(r.status).json(r.ok ? { url: j.url } : { error: j.error });
});
app.listen(3000);
2. Browser
async function say(text) {
const r = await fetch("/api/speak", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ text }) });
const j = await r.json();
if (!r.ok) return console.error(j.error);
new Audio(j.url).play(); // must follow a user gesture such as a click
}
document.querySelector("#go").onclick = () => say("Welcome back.");
3. The errors worth handling
-
402 free_allowance_used: the free allowance is spent. -
429 rate_limited: waitretry_after_secondsand retry. -
404 not_in_library: that voice only plays pre-recorded library lines; pick a live voice.
Things I would do next
- Show a loading state. Phrases already in the library come back almost instantly; a brand-new sentence is generated and takes longer.
- Keep the returned URL and reuse it rather than asking for the same sentence again.
- Put a length limit and a rate limit on your own
/api/speakroute, because it spends your quota.
Full version with the Python and cURL variants: https://speakvora.com/guides/add-speech-to-a-web-app?utm_source=devto&utm_medium=article&utm_campaign=tutorial-add-speech
Free key (no card): https://speakvora.com/dashboard. Questions: support@eyeguide.ai
Top comments (0)