Neural Documentation
Transform documents into narrated audio with the Narrator API. Our pipeline handles document extraction, script generation with character detection, and high-fidelity text-to-speech synthesis with 100+ unique voices.
API Capabilities
PDF, DOCX, PPTX, images to markdown/JSON
AI-powered character detection & dialogue tagging
100+ voices, streaming & batch, multi-character
Authentication
REQUIREDAll API requests require authentication via the x-api-key header.
-H "x-api-key: YOUR_API_KEY"
Additional Headers
Some endpoints (script formatting, AI models) require an OpenRouter API key:
x-openrouter-api-key: sk-or-v1-xxxxxxxxxxxxx
List Voices
GET /tts/voicesGet a list of all available TTS voices. Returns voice aliases that can be used with synthesis endpoints.
curl -X GET "https://api.narrator.ai/api/v1/tts/voices" \ -H "x-api-key: YOUR_API_KEY"
{
"voices": [
"Thundervox",
"Wonderstruck",
"Chronicler",
"Serenity",
"Storyteller",
// ... 100+ more voices
],
"count": 107
}
Popular Voice Categories
Synthesize Speech
POST /tts/synthesizeConvert text to speech and receive a complete WAV audio file. Best for non-real-time generation (podcasts, articles, audiobooks).
| Parameter | Type | Description |
|---|---|---|
| text * | string | Text to convert to speech. Max 10,000 chars. Markdown auto-converted to plain text. |
| voice | string | Voice alias (e.g., "Thundervox", "Chronicler"). Defaults to "Thundervox". |
| temperature | float | Randomness/creativity (0.2-1.2, default: 0.8). Lower = more consistent. |
| top_k | int | Limits sampling to K most likely tokens (default: 250). |
| cfg_alpha | float | Voice guidance strength (0.5-3.0, default: 1.5). Higher = stronger voice character. |
| seed | int | Random seed for reproducible output. |
import requests url = "https://api.narrator.ai/api/v1/tts/synthesize" payload = { "text": "Welcome to the future of audio narration.", "voice": "Chronicler", "temperature": 0.8 } headers = { "x-api-key": "YOUR_API_KEY", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) # Save audio file (WAV format, 24kHz, PCM Float32) with open("speech.wav", "wb") as f: f.write(response.content)
curl -X POST "https://api.narrator.ai/api/v1/tts/synthesize" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"text": "Hello world!", "voice": "Thundervox"}' \ --output speech.wav
Real-time Streaming
WEBSOCKET /tts/streamUltra-low latency streaming via WebSocket. Audio chunks are received as they're generated - perfect for real-time playback.
1. Connect
Connect with voice and api_key as query parameters.
2. Send Text
Send text as plain string. Server handles chunking automatically.
3. Receive Audio
Get base64-encoded PCM Float32 chunks for immediate playback.
// Connect to WebSocket with voice and API key const ws = new WebSocket( 'wss://api.narrator.ai/api/v1/tts/stream?voice=Chronicler&api_key=YOUR_API_KEY' ); const audioContext = new AudioContext(); let scheduledTime = 0; ws.onmessage = async (event) => { const msg = JSON.parse(event.data); if (msg.status === 'ready') { // Connection ready - send text to synthesize ws.send("Hello! This is real-time streaming synthesis."); } else if (msg.type === 'audio') { // Decode base64 to raw bytes const audioBytes = Uint8Array.from(atob(msg.data), c => c.charCodeAt(0)); const audioFloat32 = new Float32Array(audioBytes.buffer); // Create AudioBuffer for playback const audioBuffer = audioContext.createBuffer(1, audioFloat32.length, msg.sample_rate); audioBuffer.getChannelData(0).set(audioFloat32); // Schedule for seamless playback const source = audioContext.createBufferSource(); source.buffer = audioBuffer; source.connect(audioContext.destination); source.start(audioContext.currentTime + scheduledTime); scheduledTime += audioBuffer.duration; } else if (msg.type === 'finalComplete') { console.log('Synthesis complete!'); scheduledTime = 0; // Reset for next request } };
Message Types
Script Narration
WEBSOCKET /tts/script/streamStream multi-character narration with different voices per character. Perfect for stories, dialogues, and audiobook production.
const ws = new WebSocket( 'wss://api.narrator.ai/api/v1/tts/script/stream?api_key=YOUR_API_KEY' ); ws.onmessage = (event) => { const msg = JSON.parse(event.data); if (msg.status === 'ready') { // Send script with character-voice mappings ws.send(JSON.stringify({ "content": `[NARRATOR] Once upon a time, in a distant land... [PRINCESS] "I wish for adventure," she sighed. [NARRATOR] said the princess. [KNIGHT] "Then adventure you shall have!" [NARRATOR] replied the brave knight.`, "voices": { "NARRATOR": "Chronicler", "PRINCESS": "Serenity", "KNIGHT": "Thundervox" }, "default_voice": "Chronicler", "filename": "chapter1" })); } else if (msg.type === 'audio') { console.log(`Audio from ${msg.character} (${msg.section}/${msg.total_sections})`); playAudioChunk(msg.data, msg.sample_rate); } else if (msg.type === 'sectionComplete') { console.log(`Completed: ${msg.character}`); } else if (msg.type === 'finalComplete') { console.log('All narration complete!'); } };
| Request Field | Type | Description |
|---|---|---|
| content * | string | Script with [CHARACTER] delimiters for each speaking part. |
| voices * | object | Mapping of character names to voice aliases. |
| default_voice | string | Fallback voice for unmapped characters. |
| filename | string | Optional script filename for tracking. |
Extract Document Content
POST /extractionExtract content from documents (PDF, DOCX, PPTX, images) into markdown or JSON format for further processing.
Supported File Types
curl -X POST "https://api.narrator.ai/api/v1/extraction" \ -H "x-api-key: YOUR_API_KEY" \ -F "[email protected]" \ -F "to_formats=md" \ -F "to_formats=json"
import requests url = "https://api.narrator.ai/api/v1/extraction" with open("document.pdf", "rb") as f: files = {"file": ("document.pdf", f, "application/pdf")} data = {"to_formats": ["md"]} headers = {"x-api-key": "YOUR_API_KEY"} response = requests.post(url, files=files, data=data, headers=headers) result = response.json() # Access extracted markdown content markdown_content = result["document"]["md_content"] print(markdown_content)
{
"document": {
"filename": "document.pdf",
"md_content": "# Chapter 1\n\nOnce upon a time...",
"json_content": {...}
},
"status": "completed",
"processing_time": 2.5
}
Format Script
POST /script/format-jsonAI-powered script formatting. Automatically detects characters, extracts dialogue, and creates narration-ready scripts with [CHARACTER] tags.
Note: This endpoint
requires an OpenRouter API key for AI processing. Pass it via the x-openrouter-api-key header.
import requests url = "https://api.narrator.ai/api/v1/script/format-json" payload = { "text": """Once upon a time, there was a princess. "I would like a cup of coffee," said the princess. "I'll have the same," replied the prince with a smile.""", "model": "gpt-4o-mini" } headers = { "x-api-key": "YOUR_API_KEY", "x-openrouter-api-key": "sk-or-v1-xxxxxxxxxxxxx", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) result = response.json() # Use the formatted script print(result["formatted_text"]) # Output: # [NARRATOR] Once upon a time, there was a princess. # [PRINCESS] "I would like a cup of coffee," # [NARRATOR] said the princess. # [PRINCE] "I'll have the same," # [NARRATOR] replied the prince with a smile.
{
"filename": "input.txt",
"success": true,
"script": {
"title": "Untitled",
"lines": [
{"character": "NARRATOR", "text": "Once upon a time...", "line_number": 1},
{"character": "PRINCESS", "text": "\"I would like...\"", "line_number": 2}
],
"characters": ["NARRATOR", "PRINCESS", "PRINCE"]
},
"formatted_text": "[NARRATOR] Once upon a time...",
"processing_time": 3.2,
"model_used": "gpt-4o-mini"
}
Complete Workflow Example
TUTORIALEnd-to-end example: Upload a document, extract content, format it as a script with character detection, and synthesize multi-voice narration.
import requests import asyncio import websockets import json import base64 BASE_URL = "https://api.narrator.ai/api/v1" API_KEY = "YOUR_API_KEY" OPENROUTER_KEY = "sk-or-v1-xxxxxxxxxxxxx" headers = { "x-api-key": API_KEY, "x-openrouter-api-key": OPENROUTER_KEY, } # Step 1: Extract content from document with open("story.pdf", "rb") as f: extract_resp = requests.post( f"{BASE_URL}/extraction", files={"file": f}, data={"to_formats": ["md"]}, headers={"x-api-key": API_KEY} ) extracted_text = extract_resp.json()["document"]["md_content"] print(f"Extracted {len(extracted_text)} characters") # Step 2: Format as script with character detection script_resp = requests.post( f"{BASE_URL}/script/format-json", json={"text": extracted_text, "model": "gpt-4o-mini"}, headers={**headers, "Content-Type": "application/json"} ) script_data = script_resp.json() formatted_script = script_data["formatted_text"] characters = script_data["script"]["characters"] print(f"Detected characters: {characters}") # Step 3: Create voice mapping for each character voice_mapping = { "NARRATOR": "Chronicler", "PRINCESS": "Serenity", "PRINCE": "Thundervox", "WITCH": "Mysterion", } # Step 4: Stream multi-character narration async def stream_narration(): uri = f"wss://api.narrator.ai/api/v1/tts/script/stream?api_key={API_KEY}" audio_chunks = [] async with websockets.connect(uri) as ws: # Wait for ready ready = json.loads(await ws.recv()) print(f"Connected: {ready}") # Send script with voice mappings await ws.send(json.dumps({ "content": formatted_script, "voices": voice_mapping, "default_voice": "Chronicler", "filename": "my_story" })) # Collect audio chunks while True: msg = json.loads(await ws.recv()) if msg["type"] == "audio": audio_chunks.append(base64.b64decode(msg["data"])) print(f" {msg['character']} ({msg['section']}/{msg['total_sections']})") elif msg["type"] == "finalComplete": print(f"Complete! {msg['total_sections']} sections") break # Save combined audio (would need WAV header in production) with open("narration.raw", "wb") as f: f.write(b"".join(audio_chunks)) print("Saved narration.raw") asyncio.run(stream_narration())
Workflow Summary
PDF, DOCX, etc.
/extraction
/script/format-json
/tts/script/stream
FastRTC Integration
GUIDEBuild real-time voice assistants by combining FastRTC with the Narrator API. FastRTC handles WebRTC/WebSocket streaming with automatic voice activity detection and pause detection, while Narrator provides high-quality TTS for responses.
What is FastRTC?
FastRTC is a Python library that transforms functions into real-time audio/video streams. It handles the complexity of WebRTC connections, voice activity detection, and turn-taking automatically.
# Install FastRTC with voice activity detection pip install "fastrtc[vad]" # Additional dependencies for this example pip install websockets numpy
import asyncio import json import base64 import numpy as np import websockets from fastapi import FastAPI from fastrtc import ReplyOnPause, Stream API_KEY = "YOUR_API_KEY" NARRATOR_WS_URL = "wss://api.narrator.ai/api/v1/tts/stream" async def synthesize_with_narrator(text: str, voice: str = "Chronicler"): """Stream TTS audio from Narrator API.""" uri = f"{NARRATOR_WS_URL}?voice={voice}&api_key={API_KEY}" async with websockets.connect(uri) as ws: # Wait for ready signal ready = json.loads(await ws.recv()) if ready.get("status") != "ready": return # Send text for synthesis await ws.send(text) # Receive and yield audio chunks while True: msg = json.loads(await ws.recv()) if msg.get("type") == "audio": # Decode base64 audio to numpy array audio_bytes = base64.b64decode(msg["data"]) audio_array = np.frombuffer(audio_bytes, dtype=np.float32) sample_rate = msg.get("sample_rate", 24000) yield (sample_rate, audio_array) elif msg.get("type") == "finalComplete": break def voice_assistant(audio: tuple[int, np.ndarray]): """ Handler triggered when user stops speaking. Receives audio, processes it, and yields TTS response. """ sample_rate, audio_array = audio # Step 1: Transcribe user audio (use your preferred STT) # user_text = transcribe(audio_array, sample_rate) user_text = "Hello, tell me about the weather." # Placeholder # Step 2: Generate response (use your preferred LLM) # response_text = generate_response(user_text) response_text = "The weather today is sunny with a high of 72 degrees." # Step 3: Stream TTS response from Narrator API async def stream_response(): async for chunk in synthesize_with_narrator(response_text): yield chunk # Run async generator in sync context loop = asyncio.new_event_loop() gen = stream_response() try: while True: yield loop.run_until_complete(gen.__anext__()) except StopAsyncIteration: pass finally: loop.close() # Create the FastRTC stream stream = Stream( handler=ReplyOnPause(voice_assistant), modality="audio", mode="send-receive", ) # Mount on FastAPI app = FastAPI() stream.mount(app) # Run with: uvicorn voice_assistant:app --host 0.0.0.0 --port 8000 # Test with: stream.ui.launch() for built-in UI
Key Concepts
Triggers your handler when the user stops speaking. Handles voice activity detection automatically.
Core class that manages WebRTC connections and routes audio between client and your handler.
tuple[int, np.ndarray] - sample rate and
audio samples as numpy array.
Use .mount(app) for
FastAPI or .ui.launch() for testing.
Voice Assistant Architecture
FastRTC captures audio via WebRTC
ReplyOnPause triggers handler
STT, generate response
Stream audio back to user
Tip: For production,
consider using fastrtc[vad, stt] for built-in
speech-to-text, or integrate with services like Groq Whisper or Deepgram for transcription.