Puter

Speak with a Speechify voice from puter.ai.txt2speech() — the key stays server-side in your Puter instance.

Overview

Puter is an open-source internet OS with a serverless SDK, puter.js, that gives web apps auth, storage, and AI from a single script tag. Its text-to-speech call, puter.ai.txt2speech(), includes a native Speechify provider: you configure your Speechify API key once on the Puter instance, and every page served against that instance can pass provider: 'speechify' — the key never reaches the client.

SpeechifyAI Agents — our own voice-agent platform

SpeechifyAI ships its own voice-agents platform powered by Simba 3.2, which ranks #1 on the Artificial Analysis TTS leaderboard as of July 2026. See SpeechifyAI Agents if you want an end-to-end voice-agent stack instead of speech synthesis in a web app.

Prerequisites

  • Speechify API key (platform.speechify.ai/api-keys)
  • A Puter instance running a build that includes the Speechify TTS provider (Node 24+, npm install && npm start)
  • A Puter account on that instance (txt2speech prompts sign-in on the first call; the first boot prints admin credentials)

Enable the provider on your instance

Create config.json in the Puter repo root (or point PUTER_CONFIG_PATH at a config file) with your key under providers.speechify:

1{
2 "providers": {
3 "speechify": { "apiKey": "your Speechify API key" }
4 }
5}

The instance registers the provider at boot — without this entry, provider: 'speechify' is not available, so restart after adding it. The key stays in the instance’s server-side config; pages calling puter.ai.txt2speech() never see it, and synthesis is billed to the Speechify account the key belongs to.

Use the Speechify provider

puter.js is JavaScript-only, so there are no Python or cURL variants of this integration — the whole point is calling TTS straight from the page.

1<html>
2<body>
3 <script src="https://js.puter.com/v2/"></script>
4 <button id="play">Speak!</button>
5 <script>
6 document.getElementById('play').addEventListener('click', async () => {
7 const audio = await puter.ai.txt2speech(
8 "Hello from Speechify Simba 3.2, running inside Puter.",
9 { provider: 'speechify' }
10 );
11 audio.play();
12 });
13 </script>
14</body>
15</html>

txt2speech resolves to an HTMLAudioElement, ready to play(). With no other options, the Speechify provider defaults to the simba-3.2 model and the geffen_32 voice.

puter.js targets the instance that serves the page. For a standalone page against your own instance, open it with the puter.api_origin query parameter (e.g. ?puter.api_origin=http://api.puter.localhost:4100) — puter.js reads it at load time.

Options

OptionTypeDefaultNotes
providerString'aws-polly'Set to 'speechify' to route through Speechify
modelString'simba-3.2'Also accepts 'simba-english', 'simba-multilingual'
voiceString'geffen_32'Simba 3.2 voices: 'geffen_32', 'dominic_32', 'harper_32', 'hugh_32', 'imogen_32'
output_formatString'mp3''mp3', 'wav', 'ogg', 'aac'
test_modeBooleanfalseReturns sample audio without hitting the Speechify API

Input text is limited to 3,000 characters per call (a Puter-side limit). Pick model and voice together: simba-3.2 voices carry the _32 suffix, while simba-english and simba-multilingual accept the broader catalog — see Models for the compatibility matrix.

1const audio = await puter.ai.txt2speech(
2 "Same sentence, different voice.",
3 { provider: 'speechify', model: 'simba-3.2', voice: 'dominic_32' }
4);
5audio.play();

Test without hitting the API

Pass test_mode: true (or the testMode positional argument) while developing:

1const audio = await puter.ai.txt2speech(
2 "This returns sample audio and costs nothing.",
3 { provider: 'speechify', test_mode: true }
4);
5audio.play();

Troubleshooting

SymptomLikely causeFix
Unknown or unsupported provider errorThe instance has no Speechify API key configured (the provider isn’t registered without one), or the build predates the Speechify driverSet providers.speechify.apiKey in config.json and restart; run a current build
voice is not available errorVoice/model mismatch (e.g. a _32 voice on simba-english)Keep model and voice from the same family — see the options table above
No audio plays on clickBrowser autoplay policyCall audio.play() from a user-gesture handler, as in the examples
Text rejected as too longOver Puter’s 3,000-character capSplit the text and synthesize per chunk

Resources