Skip to content
Notifications
Clear all

Step-by-step: Batch processing 100 product descriptions with custom voices.

2 Posts
2 Users
0 Reactions
3 Views
(@danag)
Estimable Member
Joined: 1 week ago
Posts: 89
Topic starter   [#16788]

Alright, so I finally got around to stress-testing Murf's API for a real-world task I had: generating voiceovers for a hundred different product descriptions. The goal was to use a mix of custom voices we'd trained and some of Murf's premium voices, all while keeping the workflow automated and the output consistent.

The main challenge wasn't the API call itself—it's pretty straightforward—but handling the batch processing, managing different voice IDs, and ensuring all the generated audio files were correctly named and stored without manual intervention. Here's the core Python script I ended up with, using `asyncio` and `aiohttp` to speed things up.

```python
import aiohttp
import asyncio
import json
from pathlib import Path

MURF_API_KEY = "your_key_here"
BASE_URL = "https://api.murf.ai/v1/speech/generate"

async def generate_speech(session, text, voice_id, output_path):
payload = {
"text": text,
"voiceId": voice_id,
"format": "mp3",
"sampleRate": 48000,
"speed": 1.0
}
headers = {
"Authorization": f"Bearer {MURF_API_KEY}",
"Content-Type": "application/json"
}

async with session.post(BASE_URL, json=payload, headers=headers) as resp:
if resp.status == 200:
data = await resp.json()
audio_url = data.get("audioUrl")
# Now download the audio file
async with session.get(audio_url) as audio_resp:
if audio_resp.status == 200:
with open(output_path, 'wb') as f:
f.write(await audio_resp.read())
print(f"Saved: {output_path}")
else:
print(f"Failed to download audio for {output_path}")
else:
print(f"API call failed for {output_path}: {resp.status}")

async def main():
# Load your batch data - e.g., a list of dicts with 'text', 'voice_id', 'filename'
with open('product_descriptions.json') as f:
descriptions = json.load(f)

async with aiohttp.ClientSession() as session:
tasks = []
for item in descriptions:
output_dir = Path("output")
output_dir.mkdir(exist_ok=True)
task = generate_speech(
session,
item["text"],
item["voice_id"],
output_dir / f"{item['filename']}.mp3"
)
tasks.append(task)
await asyncio.gather(*tasks)

if __name__ == "__main__":
asyncio.run(main())
```

A few key takeaways from running this:

1. **Rate Limiting:** Murf's API does have rate limits. I hit them initially. The fix was to add a small delay (`asyncio.sleep(0.5)`) between task creation or use a semaphore to limit concurrent requests. It's worth checking the docs for your specific plan.
2. **Voice ID Management:** Keeping a JSON file mapping product categories to specific custom voice IDs was crucial. This way, the script could just reference the right ID without any hardcoding.
3. **Error Handling:** The script above is simplified. In production, I wrapped the API calls in try/except blocks and logged errors for specific items to a separate file for retry later. A 100-item batch will almost always have one or two hiccups.
4. **Output Organization:** Naming the files with a unique product SKU or ID from the start saved a huge headache in matching audio to products later.

The whole batch finished in about 15 minutes (including the throttling I added). The quality was consistent across all files, and using our own custom voices really made the product line sound cohesive. The API is solid, but the real work is in the orchestration around it.

Happy to share more details on the error-handling or the voice mapping setup if anyone's tackling something similar. This was a fun one to piece together.

~d



   
Quote
(@finnj)
Estimable Member
Joined: 6 days ago
Posts: 57
 

Pretty slick, but I have to ask - did you price out what 100 API calls to Murf cost you? Last I checked, that's a quick way to burn through a budget for a one-off project.

There's a functional open-source TTS alternative that can run locally, like Piper. You'd trade some polish for zero marginal cost per generation. Scripting it would be similar, minus the async HTTP calls and plus a local process spawn. The voice consistency is the real trick, but for product descriptions, do you really need a bespoke, trained voice?


FOSS advocate


   
ReplyQuote