Aug 20, 2025  ·  3 min read

Turn any Doc into a WAV file with the Docs API and Cloud Text-to-Speech.

Written in Aug 2025. APIs may have changed since.

Hear What You Wrote

A short Python script that reads a Google Doc and turns it into a WAV file.

Useful for audio versions of blog posts, rough voiceovers for video scripts, or listening back to copy to check its tone. You pass in a Doc URL. You get back a link to the audio.

It uses the Google Docs API to pull the text and Cloud Text-to-Speech with a Chirp 3 HD voice to read it.

This is sample code for learning. Write your own for production.

Four Steps

01

Authenticate

Load a service account key from Secret Manager.

02

Read

Fetch the Doc with the Docs API and flatten it to plain text.

03

Synthesize

Send the text to the long-audio TTS endpoint.

04

Store

TTS writes the WAV straight to a Cloud Storage bucket.

Before You Code

You need a Google Cloud project with billing, Python 3.11+, the gcloud CLI, and a Cloud Storage bucket.

pip install google-api-python-client google-auth google-cloud-secret-manager google-cloud-texttospeech
  1. Enable APIs: Google Docs, Cloud Text-to-Speech, Secret Manager.
  2. Create a service account in IAM & Admin, then create a JSON key for it. Treat the key like a password.
  3. Store the key in Secret Manager, e.g. as doc-to-speech-credentials.
  4. Share the Doc with the service account's client_email as a Viewer.

Docs Client

GoogleDocsService pulls the key from Secret Manager and builds a read-only Docs client.

import os, json
import google.auth
from google.cloud import secretmanager
from google.oauth2 import service_account
from googleapiclient.discovery import build

class GoogleDocsService:
    def __init__(self):
        self.credentials, self.project_id = google.auth.default()
        self.scopes = ['https://www.googleapis.com/auth/documents.readonly']
        self._docs = None

    def get_secret(self, secret_id: str) -> dict:
        client = secretmanager.SecretManagerServiceClient()
        name = f"projects/{self.project_id}/secrets/{secret_id}/versions/latest"
        data = client.access_secret_version(request={"name": name}).payload.data
        return json.loads(data.decode("UTF-8"))

    @property
    def docs(self):
        if not self._docs:
            info = self.get_secret(os.environ['SERVICE_ACCOUNT_SECRET_ID'])
            creds = service_account.Credentials.from_service_account_info(
                info, scopes=self.scopes)
            self._docs = build('docs', 'v1', credentials=creds)
        return self._docs

GoogleDoc takes a URL or ID, fetches the document, and walks its JSON to collect every textRun. Formatting is dropped. Only the words remain.

class GoogleDoc:
    def __init__(self, service: GoogleDocsService, file_id_or_uri: str):
        self.service = service
        self.file_id_or_uri = file_id_or_uri

    @property
    def id(self) -> str:
        if '/d/' in self.file_id_or_uri:
            return self.file_id_or_uri.split('/d/')[1].split('/')[0]
        return self.file_id_or_uri

    @property
    def text(self) -> str:
        doc = self.service.docs.documents().get(documentId=self.id).execute()
        out = ""
        for block in doc.get('body', {}).get('content', []):
            for elem in block.get('paragraph', {}).get('elements', []):
                out += elem.get('textRun', {}).get('content', '')
        return out

Text to Speech

Add this method to GoogleDoc. synthesize_long_audio handles long inputs and writes the result directly to Cloud Storage, so there's no audio to stream back.

from uuid import uuid4
from google.cloud import texttospeech

def doc_tts(self, bucket: str, project_id: str) -> str:
    blob = f'chirp-audio/{self.id}_{uuid4().hex}.wav'
    client = texttospeech.TextToSpeechLongAudioSynthesizeClient()

    request = texttospeech.SynthesizeLongAudioRequest(
        parent=f"projects/{project_id}/locations/us-central1",
        input=texttospeech.SynthesisInput(text=self.text),
        voice=texttospeech.VoiceSelectionParams(
            language_code="en-US", name="en-US-Chirp3-HD-Charon"),
        audio_config=texttospeech.AudioConfig(
            audio_encoding=texttospeech.AudioEncoding.LINEAR16),
        output_gcs_uri=f'gs://{bucket}/{blob}',
    )

    client.synthesize_long_audio(request=request).result(timeout=600)
    return f'https://storage.cloud.google.com/{bucket}/{blob}'

Browse other voices in the Cloud TTS voice list.

Run It

if __name__ == '__main__':
    os.environ.setdefault('SERVICE_ACCOUNT_SECRET_ID', 'doc-to-speech-credentials')
    service = GoogleDocsService()
    doc = GoogleDoc(service, input("Google Doc URL or ID: "))
    print(doc.doc_tts('your-gcs-bucket-name', service.project_id))
  1. Outside Google Cloud, run gcloud auth application-default login first.
  2. Run python google_doc.py and paste a Doc URL.
  3. Open the printed link while signed in to an account that can read the bucket.

What I'd Do Differently

  • Skip the key file. Downloaded keys are a liability. Share the Doc with the identity the script already runs as and use those default credentials.
  • Don't make the bucket public. Use signed URLs if you need to share the audio.
  • Text extraction is shallow. It reads top-level paragraphs only. Tables, headers, and footnotes are skipped.
  • Check voice support. Confirm the voice you pick works with the long-audio endpoint in your region.

Next steps: wrap it in a Cloud Run service, trigger it when a Doc changes, or add more languages and voices.