Using Speech Revolutions with Django
Integrate Speech Revolutions into a Django app: a view that submits a file for transcription, a model that stores each job's status and progress, and a webhook view that verifies the signature and records the result. The API key stays in Django settings or the server environment and never reaches the browser.
SpeechRevolutions() reads SPEECHREVOLUTIONS_API_KEY from the environment. Views run on the server, so the client and key are never sent to templates or JavaScript.
Install
pip install django speechrevolutions
export SPEECHREVOLUTIONS_API_KEY=stt_...A model to store status and progress
Store the Speech Revolutions job_id, a status, and a 0–100 progress value. Store the download URL and transcript when the job completes.
from django.db import models
class TranscriptionJob(models.Model):
class Status(models.TextChoices):
PROCESSING = "processing"
COMPLETED = "completed"
FAILED = "failed"
job_id = models.CharField(max_length=64, unique=True, db_index=True)
status = models.CharField(
max_length=16, choices=Status.choices, default=Status.PROCESSING
)
percent = models.FloatField(default=0.0) # overall 0-100
download_url = models.URLField(blank=True, default="")
text = models.TextField(blank=True, default="")
reason = models.CharField(max_length=255, blank=True, default="")
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)A view that submits
Read the uploaded file, call submit() to get a job ID without blocking the request, and create the row. Pass a callback_url so Speech Revolutions notifies you when the job finishes. The webhook view below records the result.
from django.http import JsonResponse
from django.shortcuts import get_object_or_404
from django.views.decorators.http import require_POST
from speechrevolutions import SpeechRevolutions
from .models import TranscriptionJob
client = SpeechRevolutions() # reads SPEECHREVOLUTIONS_API_KEY
@require_POST
def start_transcription(request):
upload = request.FILES["file"]
# submit() returns a job id without holding the request open.
# callback_url must be publicly reachable: a localhost / 127.0.0.1 URL is
# refused with HTTP 403. In local development, use a tunnel's public URL.
job_id = client.submit(
upload.read(),
speaker_labels=True,
callback_url=request.build_absolute_uri("/webhooks/stt/"),
)
TranscriptionJob.objects.create(job_id=job_id)
return JsonResponse({"job_id": job_id, "status": "processing"})
def job_progress(request, job_id):
# Return 404 for an unknown, mistyped, or expired id
# (get() would raise DoesNotExist and return a 500).
job = get_object_or_404(TranscriptionJob, job_id=job_id)
return JsonResponse(
{"status": job.status, "percent": job.percent, "text": job.text}
)submit() returns immediately and doesn't report progress. To update percent between submit and completion, run the blocking transcribe() with on_progress and on_upload_progress callbacks in a background worker (Celery, RQ, or a thread) that writes each update to the row. See Live progress for web apps for how to weight the callbacks into one progress bar.
Webhook view with signature verification
Speech Revolutions POSTs a signed JSON body to your callback_url when the job completes or fails permanently. The signature is an HMAC-SHA256 of the raw body, sent in the X-SR-Signature: sha256=<hex> header. Verify it against request.body (the exact bytes) and exempt the view from CSRF, because the request is a server-to-server POST, not a browser form. Copy your signing secret from the console (API Keys → Webhook signing secret) into SPEECHREVOLUTIONS_WEBHOOK_SECRET. The webhooks guide covers the payload, retries, and secret rotation.
import hashlib
import hmac
import json
from django.conf import settings
from django.http import HttpResponse, JsonResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST
from .models import TranscriptionJob
def verify_signature(raw_body: bytes, signature_header: str) -> bool:
expected = "sha256=" + hmac.new(
settings.SPEECHREVOLUTIONS_WEBHOOK_SECRET.encode(), raw_body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature_header or "")
@csrf_exempt
@require_POST
def stt_webhook(request):
raw = request.body # verify against the exact bytes received
if not verify_signature(raw, request.headers.get("X-SR-Signature", "")):
return HttpResponse(status=401)
event = json.loads(raw)
# {job_id, status: "completed" | "failed", download_url?, step?, reason?}
try:
job = TranscriptionJob.objects.get(job_id=event["job_id"])
except TranscriptionJob.DoesNotExist:
return HttpResponse(status=404)
if event["status"] == "completed":
job.status = TranscriptionJob.Status.COMPLETED
job.percent = 100.0
job.download_url = event.get("download_url", "")
# Fetch + parse the transcript, then store the text:
# from speechrevolutions import SpeechRevolutions
# job.text = SpeechRevolutions().get_transcript(job.job_id).text
else:
job.status = TranscriptionJob.Status.FAILED
job.reason = event.get("reason", "")
job.save()
return JsonResponse({"ok": True}) # a 2xx acks delivery; 5xx is retriedURLs
from django.urls import path
from transcripts import views
from transcripts.webhooks import stt_webhook
urlpatterns = [
path("transcribe/", views.start_transcription),
path("jobs/<str:job_id>/progress/", views.job_progress),
path("webhooks/stt/", stt_webhook),
]submit() creates the job through the upload flow and returns its ID. You fetch status and the transcript later through the jobs endpoints. See the Python SDK for the full API.