InícioBlog → Voz para texto em Go: código só com a biblioteca padrão

Voz para texto em Go: código só com a biblioteca padrão

Publicado em 2026-08-20 · 5 min de leitura

Go não precisa de SDK nem de pacotes de terceiros para enviar áudio: uma requisição multipart sai em vinte linhas de biblioteca padrão. Abaixo, a função completa, tratamento de erros sensato e um pool de workers para varrer um acervo de gravações.

A função de envio

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"os"
	"time"
)

func transcribe(path, key string) (string, error) {
	f, err := os.Open(path)
	if err != nil {
		return "", err
	}
	defer f.Close()

	var body bytes.Buffer
	w := multipart.NewWriter(&body)
	part, _ := w.CreateFormFile("file", f.Name())
	if _, err := io.Copy(part, f); err != nil {
		return "", err
	}
	w.WriteField("model", "whisper-1")
	w.Close()

	req, _ := http.NewRequest("POST", "https://voicesscribe.com/v1/audio/transcriptions", &body)
	req.Header.Set("Authorization", "Bearer "+key)
	req.Header.Set("Content-Type", w.FormDataContentType())

	client := &http.Client{Timeout: 3 * time.Minute}
	resp, err := client.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	var out struct {
		Text  string
		Error struct{ Message string }
	}
	json.NewDecoder(resp.Body).Decode(&out)
	if resp.StatusCode != 200 {
		return "", fmt.Errorf("status %d: %s", resp.StatusCode, out.Error.Message)
	}
	return out.Text, nil
}

Repare no tempo limite do cliente: o padrão é não haver nenhum, então uma requisição sobre gravação longa pode ficar pendurada para sempre.

Tratando erros

As respostas de erro chegam no formato conhecido — objeto error com campo message. Separe as falhas em dois grupos: permanentes (400, 401, 413) e temporárias (429, 502, queda de rede). Repetir o primeiro grupo é inútil; o segundo merece retentativa com pausa crescente.

func withRetry(path, key string) (string, error) {
	delay := 2 * time.Second
	var last error
	for i := 0; i < 4; i++ {
		text, err := transcribe(path, key)
		if err == nil {
			return text, nil
		}
		last = err
		if !temporary(err) {
			break
		}
		time.Sleep(delay)
		delay *= 2
	}
	return "", last
}

Pool de workers para acervos

É aqui que Go brilha: um pool limitado sai em poucas linhas, e o limite protege você das rejeições por excesso de requisições.

files := make(chan string)
var wg sync.WaitGroup

for i := 0; i < 4; i++ {
	wg.Add(1)
	go func() {
		defer wg.Done()
		for path := range files {
			text, err := withRetry(path, key)
			if err != nil {
				log.Println(path, err)
				continue
			}
			os.WriteFile(path+".txt", []byte(text), 0o644)
		}
	}()
}

Dimensione o pool pelo limite de requisições por segundo do seu plano — subir cem goroutines só produz cem rejeições.

Notas para produção

Teste com as suas próprias gravações. O cadastro leva um minuto e os minutos gratuitos bastam para avaliar a qualidade.

Obter uma chave de API grátis

Perguntas frequentes

Existe SDK para Go?

Não é necessário. Uma requisição multipart com a biblioteca padrão cobre toda a superfície usada aqui.

Por que envios longos são cortados?

http.Client não tem tempo limite padrão, mas proxies e balanceadores têm. Defina o seu, na casa de dois a três minutos.

Como enviar idioma ou prompt?

Como campos extras do formulário: language e prompt ao lado do campo model.

Quantas goroutines devo usar?

Tantas quantas o limite de taxa do seu plano permitir. Ultrapassar devolve 429 e obriga a refazer o trabalho.

Leitura relacionada