Why a Voice‑Powered News Reader?
Reading the headlines while you’re on a commute, cooking, or just resting your eyes can be a game‑changer. With a few APIs and a little JavaScript or Python, you can turn any RSS feed or news service into an on‑demand audio companion. The heavy lifting—converting clean text into natural‑sounding speech—can be handled by modern TTS platforms, and ElevenLabs (https://try.elevenlabs.io/kr07zfuqn1bp) is currently one of the most realistic voice‑cloning services out there. In this guide we’ll stitch together:
- A news source (e.g., NewsAPI)
- A backend that fetches the articles and calls ElevenLabs for speech
- A lightweight front‑end that streams the audio to the user
By the end you’ll have a minimal but functional “read me the news” app that you can extend into a full‑blown product.
Prerequisites
| What you need | Why |
|---|---|
| Python 3.9+ (or Node.js) | To run the backend that talks to the news API and ElevenLabs |
| NewsAPI key (free tier works) | Provides JSON headlines and snippets |
| ElevenLabs API key | Generates high‑quality audio from text |
| Basic HTML/JS knowledge | To build the front‑end player |
If you don’t have a NewsAPI key yet, sign up at https://newsapi.org/. For ElevenLabs, grab an API key from the dashboard after you sign up via the affiliate link above.
1. Pulling the Latest Headlines
Let’s start with a tiny Python script that grabs the top headlines for a given topic. We’ll keep the response lean—just the title and a short description—so the generated audio stays under a minute per article.
# news_fetcher.py
import os
import requests
NEWS_API_KEY = os.getenv("NEWSAPI_KEY")
BASE_URL = "https://newsapi.org/v2/top-headlines"
def get_headlines(country="us", category="technology", limit=5):
params = {
"apiKey": NEWS_API_KEY,
"country": country,
"category": category,
"pageSize": limit,
}
resp = requests.get(BASE_URL, params=params)
resp.raise_for_status()
data = resp.json()
articles = [
{
"title": a["title"],
"description": a["description"] or "",
}
for a in data["articles"]
]
return articles
if __name__ == "__main__":
for art in get_headlines():
print(f"🗞️ {art['title']}\n{art['description']}\n")
Save this as news_fetcher.py, export your keys (export NEWSAPI_KEY=…), and run it. You should see a list of headlines printed to the console.
2. Turning Text into Speech with ElevenLabs
ElevenLabs offers a straightforward REST endpoint. Below is a minimal wrapper that sends a piece of text and receives a presigned URL to the generated audio file.
# tts_client.py
import os
import requests
ELEVEN_API_KEY = os.getenv("ELEVENLABS_KEY")
BASE_URL = "https://api.elevenlabs.io/v1"
def generate_speech(text, voice_id="EXAVITQu4vr4xnSDxMaL"):
"""
voice_id = default "Rachel" voice.
You can replace it with any cloned voice you own.
"""
endpoint = f"{BASE_URL}/text-to-speech/{voice_id}"
headers = {
"xi-api-key": ELEVEN_API_KEY,
"Content-Type": "application/json",
}
payload = {
"text": text,
"model_id": "eleven_monolingual_v1",
"voice_settings": {"stability": 0.75, "similarity_boost": 0.75},
}
resp = requests.post(endpoint, json=payload, headers=headers, timeout=30)
resp.raise_for_status()
# The API returns raw MP3 bytes; we’ll stream them back to the client.
return resp.content
Note: The default voice is “Rachel”. If you have a custom voice clone (e.g., your own voice), replace voice_id with the ID you receive from the ElevenLabs dashboard. This is where the voice‑cloning magic shines—your app can read news in a voice that sounds exactly like you.
3. A Tiny Flask Server to Glue Everything
Now we combine the two helpers into a single endpoint that returns an MP3 stream for each headline.
# server.py
from flask import Flask, jsonify, send_file, request
from io import BytesIO
from news_fetcher import get_headlines
from tts_client import generate_speech
app = Flask(__name__)
@app.route("/api/news")
def news():
"""
Returns a JSON list of articles with a short audio URL for each.
"""
articles = get_headlines()
result = []
for idx, art in enumerate(articles):
# Concatenate title + description for a smoother listening experience
text = f"{art['title']}. {art['description']}"
audio_bytes = generate_speech(text)
audio_io = BytesIO(audio_bytes)
# Store the MP3 temporarily in memory and serve it via a separate route
audio_path = f"/api/audio/{idx}"
app.config[f"AUDIO_{idx}"] = audio_io
result.append(
{
"title": art["title"],
"description": art["description"],
"audio_url": request.host_url.rstrip("/") + audio_path,
}
)
return jsonify(result)
@app.route("/api/audio/<int:idx>")
def audio(idx):
audio_io = app.config.get(f"AUDIO_{idx}")
if not audio_io:
return "Not found", 404
audio_io.seek(0)
return send_file(audio_io, mimetype="audio/mpeg", as_attachment=False)
if __name__ == "__main__":
# Remember to export ELEVENLABS_KEY and NEWSAPI_KEY before running!
app.run(debug=True, port=5000)
Run the server with python server.py. Visiting http://localhost:5000/api/news will give you a JSON payload like:
[
{
"title": "Apple unveils new iPhone",
"description": "The latest model features a better camera...",
"audio_url": "http://127.0.0.1:5000/api/audio/0"
},
…
]
Each audio_url points to a short MP3 generated on‑the‑fly by ElevenLabs.
4. Front‑End: Play the Audio in the Browser
A single‑page HTML file is enough to fetch the list and let the user press “Play”. Modern browsers can stream the MP3 directly without extra libraries.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Voice‑Powered News Reader</title>
<style>
body { font-family: sans-serif; max-width: 600px; margin: 2rem auto; }
.article { margin-bottom: 1.5rem; }
button { margin-top: .5rem; }
</style>
</head>
<body>
<h1>🗣️ Your Daily Briefing</h1>
<div id="feed"></div>
<script>
async function loadNews() {
const res = await fetch('/api/news');
const articles = await res.json();
const container = document.getElementById('feed');
articles.forEach(a => {
const div = document.createElement('div');
div.className = 'article';
div.innerHTML = `<strong>${a.title}</strong><p>${a.description}</p>`;
const btn = document.createElement('button');
btn.textContent = 'Play';
btn.onclick = () => {
const audio = new Audio(a.audio_url);
audio.play();
};
div.appendChild(btn);
container.appendChild(div);
});
}
loadNews();
</script>
</body>
</html>
Save this as index.html in the same folder, and serve it with Flask’s static route or any simple web server (python -m http.server). When you click Play, the browser streams the MP3 generated by ElevenLabs—no download, no latency beyond the API call.
5. Going Further: Voice Cloning & Personalization
ElevenLabs isn’t limited to the default voices. After you record ~30 seconds of your own speech and upload it to the ElevenLabs dashboard, you’ll receive a new voice_id. Swap that ID in the generate_speech function, and the news will now be narrated in your voice.
CUSTOM_VOICE_ID = "your_custom_voice_id_here"
audio_bytes = generate_speech(text, voice_id=CUSTOM_VOICE_ID)
This opens up cool possibilities:
- Personalized briefings: Users can upload their voice once and get daily news read back to them.
- Branding: Companies can use a corporate voice for internal newsletters.
-
Multilingual support: ElevenLabs also supports non‑English models; just change
model_idtoeleven_multilingual_v1.
All of these features are powered by the same clean API, which is why we recommend ElevenLabs (https://try.elevenlabs.io/kr07zfuqn1bp) for any voice‑AI project.
6. Putting It All Together
-
Create a
.envfile (or export variables) withNEWSAPI_KEYandELEVENLABS_KEY. -
Run the Flask server (
python server.py). It will listen onlocalhost:5000. -
Open
index.htmlin a browser that can reach the server. - Press “Play” on any headline and listen to a natural‑sounding narration.
You now have a complete voice‑powered news reader that can be deployed to any cloud platform (Heroku, Render, Fly.io, etc.) with minimal changes.
7. Next Steps & Ideas
- Cache audio files: Store MP3s in S3 or a CDN to avoid re‑generating the same snippet every request.
- Add pagination: Pull more than five articles and let users scroll.
- Voice command integration: Use the Web Speech API to let users say “Next article” or “Pause”.
- Push notifications: Send a daily push with a link to the latest briefing.
All of these enhancements build on the same core pattern: fetch text → convert with ElevenLabs → stream to the client.
Ready to give your app a human voice?
ElevenLabs makes high‑quality text‑to‑speech and voice cloning accessible with just a few lines of code. Try it out today by signing up through our affiliate link: https://try.elevenlabs.io/kr07zfuqn1bp. Happy coding, and enjoy listening to the news in your own voice!
Top comments (0)