Python 음성 인식(STT): 오디오를 타임스탬프와 함께 텍스트로

Python에서 Requests로 음성을 텍스트로 변환하세요. 오디오 URL을 보내고 Job을 폴링한 뒤 전사문과 단어별 타임스탬프를 읽는 Sume STT 1.0 스크립트입니다.

읽는 시간 5분Sume
전체 글

내 컴퓨터에서 모델을 돌리지 않고 Python으로 음성을 텍스트로 변환하려면, requests로 호스팅된 음성 인식(STT) API를 호출하세요. 오디오 파일의 URL을 POST하고, Job이 끝날 때까지 폴링한 뒤, JSON 결과에서 전사문과 타임스탬프를 읽으면 됩니다. Sume STT 1.0에서는 POST /v1/stt-1.0/transcribe를 보내고, terminal이 true가 될 때까지 GET /v1/jobs/{id}/status를 호출한 다음, GET /v1/jobs/{id}/result에서 text, words, segments를 읽습니다.

Sume는 TypeScript SDK(@sume-com/sdk)를 문서화하고 있으며, Python에서는 HTTP API를 직접 호출합니다. 여기서는 Requests의 빠른 시작에 나온 대로 Requests를 씁니다. STT 관련 사실은 Sume API 레퍼런스의 STT 1.0 스키마와 Job과 결과 (영문)에서 가져왔으며, 모두 2026-09-27에 확인했습니다. Sume API 레퍼런스는 API 레퍼런스 문서의 바탕이 되는 OpenAPI 문서입니다. 제출·폴링 루프, 타임아웃, 재시도 키는 Python 텍스트 음성 변환(TTS) API와 같은 방식으로 동작하므로, 이 글에서는 전사에만 해당하는 내용을 다룹니다.

Python에서 오디오 파일은 어떻게 보내나요?

파일이 아니라 링크를 보내세요. 본문의 필수 필드는 공개 HTTPS URL인 audio_url 하나뿐이고 스키마에는 파일 바이트를 담을 필드가 없으므로, 녹음 파일은 자체 스토리지 같은 공개 HTTPS 주소에 이미 올라가 있어야 합니다. API 레퍼런스는 Sume 미디어 URL을 권장합니다. 허용되는 오디오 형식은 나와 있지 않으며, 레퍼런스 예시의 audio_url은 .m4a와 .wav 파일을 가리킵니다.

  • language_code는 en이나 ko 같은 선택적 힌트입니다. 생략하면 언어를 자동으로 감지합니다.
  • duration_seconds(1–600)는 사용량 예약의 크기를 정합니다. 생략하면 Sume가 1분 기준으로 예약합니다.
  • segmentation: {"mode": "sentence"}를 넣으면 결과에 문장 구간이 추가됩니다.

전체 스크립트는 어떻게 생겼나요?

제출하고, terminal이 true가 될 때까지 폴링한 뒤, 감지된 언어와 전사문, 각 단어의 시작 시각을 출력합니다.

import os, time, requests

AUTH = {"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"}
body = {"audio_url": "https://example.com/audio/interview.m4a",
        "segmentation": {"mode": "sentence"}}
r = requests.post("https://api.sume.com/v1/stt-1.0/transcribe", json=body,
                  headers={**AUTH, "Idempotency-Key": "interview-001"}, timeout=30)
r.raise_for_status()
job = r.json()["data"]
while True:
    s = requests.get(job["status_url"], headers=AUTH, timeout=30)
    s.raise_for_status()
    status = s.json()["data"]
    if status["terminal"]:
        break
    time.sleep(status["next_poll_after_seconds"] or 2)
if status["sume_status"] != "completed":
    raise RuntimeError(f"STT job ended as {status['sume_status']}")
res = requests.get(job["result_url"], headers=AUTH, timeout=30)
res.raise_for_status()
result = res.json()["data"]["result"]
print(result.get("language_code"), result["text"])
for w in result["words"]:
    if w.get("type") != "spacing":
        print(f"{w['start']:7.2f}s  {w['word']}")

단어나 문장마다 타임스탬프를 받으려면 어떻게 하나요?

단어별 타임스탬프에는 플래그가 필요 없습니다. STT 결과에는 항상 words가 있으며, 항목은 start 순으로 정렬되고 시간은 오디오 시작부터의 초 단위입니다. 항목에는 word나 spacing 같은 type이 붙을 수 있으므로 스크립트는 spacing을 건너뜁니다. 문장 타임스탬프를 받으려면 요청에 segmentation을 넣어야 합니다.

Sume API 레퍼런스의 STT 1.0 스키마 기준, 2026-09-27 확인.
필요한 것본문에 추가할 항목결과에서 읽을 곳
단어별 타임스탬프없음result["words"]: word, start, end
문장 타임스탬프"segmentation": {"mode": "sentence"}result["segments"]: index, text, start, end, duration_seconds
감지된 언어language_code 생략result["language_code"]와 result["language_probability"](있는 경우)
전체 전사문없음result["text"]

언어를 지정해야 하나요?

지정하지 않아도 됩니다. language_code를 생략하면 STT 1.0이 언어를 감지하며, 있는 경우 결과에 language_code와 감지 신뢰도인 language_probability가 담깁니다. 언어를 이미 알고 있다면 한국어 녹음에는 ko처럼 힌트로 보내세요. 언어 감지만 따로 다루는 글은 오디오에서 언어 감지하기이고, 전사문 다음 단계는 오디오 파일을 영어로 번역하기에서 이어집니다.

한도는 어떻게 되고, 비용은 얼마인가요?

API 요금에 나온 STT 1.0 요금은 오디오 분당 $0.01이며, 기본적으로 5.5% 에이전트 수수료가 더해집니다. 잔액이 예상 금액을 감당하지 못하면 제출은 402 insufficient_credits로 실패하고 Job은 시작되지 않습니다.

  • 요청당 오디오는 문서에 명시된 최대치인 10분까지입니다. duration_seconds의 상한이 600입니다. 더 긴 파일은 긴 오디오 파일 전사하기에 나온 것처럼 여러 부분으로 나눠 전사하고, 각 부분의 시작 시각을 그 부분의 타임스탬프에 더하세요.
  • 화자 라벨은 없습니다. 현재 코드에서 STT 1.0은 화자 분리(diarize)를 끈 채 실행되며, diarize나 tag_audio_events를 보내는 요청은 거부됩니다.
  • 마이크 스트리밍은 지원하지 않습니다. Job은 URL에 있는 파일을 읽으며, 현재 Developer API에는 SSE나 WebSocket 전송 방식이 없습니다.
  • words는 최대 20,000개 항목까지입니다. 600초 분량의 전사문은 이 상한에 한참 못 미치며, 상한에 걸린 결과는 words_truncated로 그 사실을 알립니다.

출처

관련 글

개발자 카테고리의 다른 글

개발자 글 전체 보기

작성자 Sume