DEV Community

VoiceDeveloper
VoiceDeveloper

Posted on

Build and Host a Text-to-Speech SaaS with Node.js and Bluehost

Why a Text‑to‑Speech SaaS?

Voice AI is moving from novelty to necessity. From accessibility tools to interactive podcasts, developers are building apps that turn text into natural‑sounding speech in real time. The sweet spot for a SaaS product is a simple REST endpoint that accepts a string, optional voice parameters, and returns an audio file—nothing more, nothing less. In this guide we’ll:

  1. Spin up a tiny Node.js service that talks to the ElevenLabs API.
  2. Package the app for production (environment variables, error handling, logging).
  3. Deploy it on Bluehost, the affordable host that makes Node.js deployment painless.

Grab an ElevenLabs account (you’ll need an API key) and a Bluehost account using the links below, then let’s get coding.


1. Set Up the Project

# Create a fresh folder
mkdir tts-saas && cd tts-saas

# Initialize a Node project
npm init -y

# Install dependencies
npm install express axios dotenv
Enter fullscreen mode Exit fullscreen mode
  • express – lightweight web framework for our API.
  • axios – HTTP client to call ElevenLabs.
  • dotenv – keep secrets (API keys) out of source control.

Create a .env file (never commit this!) and paste your ElevenLabs API key:

ELEVENLABS_API_KEY=your_api_key_here
PORT=3000
Enter fullscreen mode Exit fullscreen mode

2. Build the TTS Endpoint

Create index.js:

require('dotenv').config();
const express = require('express');
const axios = require('axios');

const app = express();
app.use(express.json());

const ELEVEN_API = 'https://api.elevenlabs.io/v1';

// Helper to call ElevenLabs TTS
async function synthesize(text, voice = 'en_us_001') {
  const response = await axios.post(
    `${ELEVEN_API}/text-to-speech/${voice}`,
    {
      text,
      // optional: model_id, voice_settings, etc.
    },
    {
      responseType: 'arraybuffer',
      headers: {
        'xi-api-key': process.env.ELEVENLABS_API_KEY,
        'Content-Type': 'application/json',
      },
    }
  );
  return response.data; // raw audio bytes (MP3)
}

// POST /speak
app.post('/speak', async (req, res) => {
  const { text, voice } = req.body;

  if (!text) {
    return res.status(400).json({ error: 'Missing "text" in request body.' });
  }

  try {
    const audio = await synthesize(text, voice);
    res.set({
      'Content-Type': 'audio/mpeg',
      'Content-Length': audio.length,
    });
    res.send(audio);
  } catch (err) {
    console.error('ElevenLabs error:', err.response?.data || err.message);
    res.status(502).json({ error: 'Failed to generate speech.' });
  }
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`🚀 TTS SaaS listening on http://localhost:${PORT}`);
});
Enter fullscreen mode Exit fullscreen mode

Why ElevenLabs?

The ElevenLabs platform offers some of the most natural���sounding voice models on the market, with a generous free tier for testing and a straightforward API. You can swap voices by passing a different voice ID, or even create custom clones once you’ve collected enough training data.


3. Test Locally

node index.js
Enter fullscreen mode Exit fullscreen mode

In another terminal:

curl -X POST http://localhost:3000/speak \
  -H "Content-Type: application/json" \
  -d '{"text":"Hello, world! This is a Node.js TTS SaaS."}'
Enter fullscreen mode Exit fullscreen mode

You should receive an MP3 stream that you can pipe into a file:

curl -X POST http://localhost:3000/speak \
  -H "Content-Type: application/json" \
  -d '{"text":"Testing locally"}' > output.mp3
Enter fullscreen mode Exit fullscreen mode

Play output.mp3 with your favorite media player—voilà, you have a working service!


4. Prepare for Production

a. Add a Basic Logger

const morgan = require('morgan');
app.use(morgan('combined'));
Enter fullscreen mode Exit fullscreen mode

Install with npm i morgan. This gives you request logs that are useful on a live server.

b. Secure the Endpoint (optional)

If you plan to expose the API publicly, consider a simple API key header:

const CLIENT_KEY = process.env.CLIENT_KEY; // set in .env

app.use((req, res, next) => {
  const key = req.headers['x-client-key'];
  if (!key || key !== CLIENT_KEY) {
    return res.status(401).json({ error: 'Unauthorized' });
  }
  next();
});
Enter fullscreen mode Exit fullscreen mode

Add CLIENT_KEY=some_secret to .env.

c. Add a Health Check

app.get('/health', (req, res) => res.json({ status: 'ok' }));
Enter fullscreen mode Exit fullscreen mode

5. Deploy to Bluehost

Bluehost isn’t just for WordPress sites; its shared hosting plans now include Node.js support via the cPanel “Setup Node.js App” wizard. The process is quick, inexpensive, and perfect for a lightweight SaaS like this.

Step‑by‑Step Deployment

  1. Create a Bluehost account – use the affiliate link: https://bluehost.sjv.io/5k0d52. Their entry‑level plan gives you 50 GB SSD storage and unlimited bandwidth, more than enough for a modest TTS API.

  2. Log into cPanel and look for “Setup Node.js App” under the Software section.

  3. Click Create Application:

    • Node version – choose the latest LTS (e.g., 20.x).
    • Application mode – Production.
    • Application root – ~/tts-saas.
    • Application URL – set to something like https://yourdomain.com.
    • Startup file – index.js.
  4. Upload your code. The easiest way is to zip the project (excluding node_modules) and use the File Manager to upload and extract it into the application root.

  5. Install dependencies from the terminal provided in the Node.js app interface:

   cd ~/tts-saas
   npm install
Enter fullscreen mode Exit fullscreen mode
  1. Add environment variables in the same cPanel panel:

    • ELEVENLABS_API_KEY – your ElevenLabs key.
    • PORT – leave blank; Bluehost will inject the correct port.
    • (Optional) CLIENT_KEY if you secured the endpoint.
  2. Start the app with the “Run NPM Start” button or by typing:

   npm start
Enter fullscreen mode Exit fullscreen mode

The wizard automatically creates a reverse proxy so your endpoint is reachable at https://yourdomain.com/speak.

  1. Test the live service:
   curl -X POST https://yourdomain.com/speak \
     -H "Content-Type: application/json" \
     -d '{"text":"Hello from Bluehost!"}' > live.mp3
Enter fullscreen mode Exit fullscreen mode

If you hear the audio, congratulations—you’ve just turned a few lines of Node.js into a fully hosted text‑to‑speech SaaS.

Why Bluehost?

  • Affordability – starts at a few dollars a month, ideal for early‑stage projects.
  • Simplicity – the cPanel UI handles Node.js setup, environment variables, and process management without needing a full VPS.
  • Scalability – when traffic grows, you can upgrade to a VPS or dedicated server with a single click.

6. Next Steps & Enhancements

Feature How to Implement
Voice cloning Use ElevenLabs’ “Create Voice” endpoint to upload a few minutes of a speaker’s audio, then call the new voice ID in your /speak request.
Rate limiting Add a middleware like express-rate-limit to protect your API from abuse.
Caching Store recently generated MP3s in a Redis cache (or even a simple file cache) to avoid duplicate API calls.
Billing Integrate Stripe or Paddle to charge per‑minute usage, turning your demo into a revenue stream.
Web UI Build a tiny React front‑end that posts text and streams back audio for quick testing.

All of these can be added without changing the core architecture we just built.


7. Wrap‑Up

You now have:

  • A Node.js microservice that leverages the high‑quality voices of ElevenLabs.
  • A production‑ready deployment on Bluehost, the budget‑friendly host that makes Node.js apps feel like a one‑click experience.
  • A solid foundation to iterate, monetize, and scale your own voice AI product.

Ready to give your users a voice?

👉 Try ElevenLabs today: https://try.elevenlabs.io/kr07zfuqn1bp

👉 Deploy instantly with Bluehost: https://bluehost.sjv.io/5k0d52

Happy coding, and may your SaaS speak loudly!

Top comments (0)