Every qualitative thesis, market research project and NGO evaluation produces the same pile of work: hours of recorded interviews and focus groups that have to become text before anyone can analyse them. Typing an interview by hand takes roughly four hours per hour of audio. That’s the rule of thumb transcribers have used for decades, and it’s why researchers pay someone else to do it.
AI has changed the economics on the supply side, not the demand side. Open-source speech recognition now produces a decent first draft of an Indonesian or English interview in minutes. What clients still can’t get from a free app is a transcript they can trust and cite: correct speaker labels, timestamps, names spelled right, no invented sentences, and a person who checked it against the audio.
That’s the service: run the audio through a local pipeline, fix the draft by ear, deliver a clean Word file with speaker labels. You sell your review time. The machine does the typing.
In this guide:
- what established transcription companies charge, and how to price against them
- a Python script that transcribes with faster-whisper, labels speakers with pyannote, and highlights every word the model wasn’t sure about in a .docx
- costs, realistic hours and earnings
- the risks: privacy law, research confidentiality, misrepresenting AI work, and Whisper’s habit of making things up
What the market pays (October 2026)
Published starting prices (checked October 2026; both vendors adjust the final price at checkout, so treat these as floors):
| Service | Base price | Extras |
|---|---|---|
| Scribie | from $0.80 per audio minute | +$0.50/min advanced precision verbatim, +$0.50/min noisy or accented audio, +$1.25/min priority turnaround; legal plan $1.30/min |
| TranscribeMe | from $0.79 per audio minute | Verbatim, extra review, rush and non-English languages cost more |
So a one-hour interview costs a client around $48 at the cheapest tier, and more if it’s noisy, verbatim or rushed. Those companies are mainly built for English. Indonesian audio, especially interviews that switch between Bahasa Indonesia, English and regional languages, is where a local transcriber has a real edge.
Your pricing. These are my suggested numbers, not a survey. Search “jasa transkrip” on Fastwork, Instagram and Facebook groups in your area and adjust before you quote:
| Client | Audio | Suggested price |
|---|---|---|
| Thesis student (S1/S2), Indonesian | Clean 1-on-1 interview | Rp 6,000–8,000 per audio minute |
| Same, but noisy / phone recording / 3+ speakers | FGD, cafe recording | Rp 10,000–15,000 per audio minute |
| International client via Fiverr/Upwork | Indonesian or Malay audio | $0.50–0.70 per audio minute |
| Rush (24h) | Any | +50% |
At Rp 7,000 per minute a one-hour interview is Rp 420,000. That’s a lot cheaper than $48 and still pays well for 1.5–2 hours of review work.
What you need
| Item | Cost | Notes |
|---|---|---|
| Google Colab (free tier) | $0 | T4 GPU, but availability and session length aren’t guaranteed |
| Colab Pay As You Go (optional) | $9.99 for 100 compute units | For reliable GPU time once you have paying work. A T4 uses roughly 1–2 units per hour; check the resources panel |
| Or your own PC with an NVIDIA GPU | $0 extra | Best for confidential audio (see Risks) |
| Hugging Face account + access token | $0 | huggingface.co/settings/tokens |
faster-whisper 1.2.x, pyannote.audio 4.0.x, python-docx | $0 | Whisper weights are MIT; pyannote community-1 is CC-BY-4.0 (commercial use OK with attribution) |
ffmpeg | $0 | Already installed on Colab |
| Closed headphones | ~Rp 200,000–400,000 | You will listen to a lot of mumbling |
| Fiverr / Upwork account (optional) | Fiverr keeps 20% of each order; Upwork charges a variable 0–15% freelancer fee per contract | Local clients via WhatsApp pay you directly |
No API bills. Everything runs locally or on Colab, so the marginal cost per hour of audio is close to zero.
Step 1: Get the models ready
- Create a Hugging Face account and a read access token.
- Open
pyannote/speaker-diarization-community-1while logged in and accept the user conditions. Without this the download fails with a 401/403 error. - Local install (Linux/WSL, NVIDIA GPU):
sudo apt install ffmpeg
python3 -m venv .venv && source .venv/bin/activate
pip install faster-whisper pyannote.audio python-docx
export HF_TOKEN="hf_your_token_here"
On Colab, set the runtime to GPU (Runtime → Change runtime type → T4 GPU), save your token under the key icon (Secrets) as HF_TOKEN, and run:
!pip install -q faster-whisper pyannote.audio python-docx
import os
from google.colab import userdata
os.environ["HF_TOKEN"] = userdata.get("HF_TOKEN")
If pip upgrades torch during the install and Colab asks you to restart the session, restart it and run the userdata cell again.
pyannote.audio 4.x needs a recent PyTorch (2.8+) and normally decodes audio through torchcodec, which is picky about the installed ffmpeg version. The script below avoids that dependency: it converts everything to a 16 kHz mono WAV with ffmpeg, then hands pyannote the audio as an in-memory waveform, which pyannote accepts without torchcodec. You may still see a torchcodec warning on import; it’s harmless here.
For a local GPU install, faster-whisper (via CTranslate2) also needs NVIDIA’s CUDA 12 cuBLAS and cuDNN 9 libraries. Colab already has them. If you get a libcudnn or libcublas error on your own PC, install them per the faster-whisper README, or run with --cpu.
Step 2: Quote from the audio length, not the file size
Before you accept a job, get the exact duration of every file:
for f in *.m4a *.mp3 *.wav; do
[ -e "$f" ] || continue
d=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$f")
awk -v f="$f" -v d="$d" 'BEGIN { printf "%s\t%.1f min\n", f, d / 60 }'
done
Multiply by your per-minute rate and send the quote. Ask three questions in the same message:
- Clean verbatim or full verbatim? Clean drops “eh”, “hmm”, false starts and repetitions. Full keeps them. Full verbatim takes noticeably longer to check, so price it higher.
- Speaker names or roles? e.g. “Pewawancara / Narasumber 1”. Many ethics protocols require pseudonyms, so let them choose.
- Timestamps how often? Per speaker turn (what the script does) is enough for most research.
Get their answers in writing before you start. That’s your protection if they dispute the delivery later.
Step 3: The pipeline script
Save this as transcribe.py. It converts the audio to 16 kHz mono WAV, transcribes it with word-level timestamps and confidence scores, works out who spoke when, matches each word to a speaker, and writes a Word file. Words the model was unsure about are highlighted yellow. Whole turns containing a phrase Whisper is known to invent are highlighted turquoise.
import argparse, os, subprocess, tempfile
from docx import Document
from docx.enum.text import WD_COLOR_INDEX
from docx.shared import Pt
# Phrases Whisper tends to invent over silence or music (YouTube subtitle leftovers)
HALLUCINATIONS = [
"terima kasih telah menonton", "terima kasih sudah menonton",
"jangan lupa subscribe", "sampai jumpa di video", "thanks for watching",
"subtitle by", "subtitles by",
]
def to_wav(src, dst):
subprocess.run(["ffmpeg", "-y", "-loglevel", "error", "-i", src,
"-ac", "1", "-ar", "16000", dst], check=True)
def transcribe(wav, model_name, lang, device):
from faster_whisper import WhisperModel
compute = "float16" if device == "cuda" else "int8"
model = WhisperModel(model_name, device=device, compute_type=compute)
segments, info = model.transcribe(
wav, language=lang, beam_size=5, vad_filter=True,
word_timestamps=True, condition_on_previous_text=False)
words = []
for seg in segments:
for w in seg.words or []:
words.append({"start": w.start, "end": w.end,
"text": w.word, "prob": w.probability})
return words, info.duration
def load_wav(wav):
# read the 16 kHz mono 16-bit WAV ourselves, so pyannote doesn't need torchcodec
import wave
import numpy as np
import torch
with wave.open(wav, "rb") as f:
sr = f.getframerate()
pcm = np.frombuffer(f.readframes(f.getnframes()), dtype=np.int16)
return {"waveform": torch.from_numpy(pcm.astype(np.float32) / 32768.0).unsqueeze(0),
"sample_rate": sr}
def diarize(wav, n_speakers, device):
import torch
from pyannote.audio import Pipeline
pipe = Pipeline.from_pretrained("pyannote/speaker-diarization-community-1",
token=os.environ["HF_TOKEN"])
pipe.to(torch.device(device))
kwargs = {"num_speakers": n_speakers} if n_speakers else {}
out = pipe(load_wav(wav), **kwargs)
return [(turn.start, turn.end, spk) for turn, spk in out.speaker_diarization]
def speaker_for(word, turns):
if not turns:
return "SPEAKER_00"
best, best_overlap = None, 0.0
for start, end, spk in turns:
overlap = min(end, word["end"]) - max(start, word["start"])
if overlap > best_overlap:
best, best_overlap = spk, overlap
if best is None: # word sits in a gap between turns: use the nearest one
mid = (word["start"] + word["end"]) / 2
best = min(turns, key=lambda t: min(abs(t[0] - mid), abs(t[1] - mid)))[2]
return best
def group_blocks(words, turns, max_gap=2.0):
blocks = []
for w in words:
spk = speaker_for(w, turns)
last = blocks[-1] if blocks else None
if last and last["spk"] == spk and w["start"] - last["end"] < max_gap:
last["words"].append(w)
last["end"] = w["end"]
else:
blocks.append({"spk": spk, "start": w["start"], "end": w["end"], "words": [w]})
return blocks
def ts(sec):
sec = int(sec)
return f"{sec // 3600:02d}:{sec % 3600 // 60:02d}:{sec % 60:02d}"
def write_docx(blocks, names, path, threshold):
doc = Document()
doc.styles["Normal"].font.name = "Calibri"
doc.styles["Normal"].font.size = Pt(11)
order = []
for b in blocks:
if b["spk"] not in order:
order.append(b["spk"])
label = {spk: names[i] if i < len(names) else f"Speaker {i + 1}"
for i, spk in enumerate(order)}
flagged = total = 0
for b in blocks:
p = doc.add_paragraph()
stamp, who = ts(b["start"]), label[b["spk"]]
p.add_run(f"[{stamp}] {who}: ").bold = True
text = "".join(w["text"] for w in b["words"]).lower()
suspicious = any(h in text for h in HALLUCINATIONS)
for i, w in enumerate(b["words"]):
run = p.add_run(w["text"].lstrip() if i == 0 else w["text"])
total += 1
if suspicious:
run.font.highlight_color = WD_COLOR_INDEX.TURQUOISE
elif w["prob"] < threshold:
run.font.highlight_color = WD_COLOR_INDEX.YELLOW
flagged += 1
doc.save(path)
return flagged, total
def main():
ap = argparse.ArgumentParser(description="Audio -> speaker-labelled draft .docx")
ap.add_argument("audio")
ap.add_argument("--speakers", type=int, default=0, help="exact speaker count, if known")
ap.add_argument("--names", default="", help="comma-separated, in order of first appearance")
ap.add_argument("--lang", default="id")
ap.add_argument("--model", default="large-v3")
ap.add_argument("--threshold", type=float, default=0.5, help="flag words below this probability")
ap.add_argument("--cpu", action="store_true")
a = ap.parse_args()
device = "cpu" if a.cpu else "cuda"
base = os.path.splitext(os.path.basename(a.audio))[0]
with tempfile.TemporaryDirectory() as tmp:
wav = os.path.join(tmp, "audio.wav")
to_wav(a.audio, wav)
words, duration = transcribe(wav, a.model, a.lang, device)
turns = diarize(wav, a.speakers, device)
blocks = group_blocks(words, turns)
names = [n.strip() for n in a.names.split(",") if n.strip()]
out = f"{base}_draft.docx"
flagged, total = write_docx(blocks, names, out, a.threshold)
pct = 100 * flagged / max(total, 1)
print(f"{out}: {duration / 60:.1f} audio min, {len(blocks)} turns, "
f"{total} words, {flagged} flagged ({pct:.1f}%)")
if __name__ == "__main__":
main()
A few choices worth knowing about:
vad_filter=Trueskips silence before transcription. Most Whisper hallucinations happen on silence, so this removes a lot of them.condition_on_previous_text=Falsestops one bad sentence from dragging the next ones off course. Without it, Whisper sometimes repeats the same line over and over.--speakers: if you know it’s a 1-on-1 interview, pass--speakers 2. Telling pyannote the exact number gives noticeably cleaner labels than letting it guess.- The 0.5 threshold is a starting point. If almost nothing gets flagged but you still find errors, raise it to 0.6–0.7.
Step 4: Run it
python transcribe.py wawancara_01.m4a --speakers 2 --names "Pewawancara,Narasumber"
On Colab, upload transcribe.py and the audio with the file panel, then run the same command with ! in front. Output:
wawancara_01_draft.docx: 58.3 audio min, 214 turns, 7412 words, 389 flagged (5.2%)
(Example output format; your numbers will differ.) Whisper large-v3 on a T4 runs several times faster than real time, so an hour of audio is minutes of waiting, not hours. Time your first few files so you know your own numbers.
No GPU? Add --cpu --model large-v3-turbo. It works, but expect it to take a large fraction of the audio length on a laptop.
Use the flagged percentage to quote. In my rule of thumb, under about 5% means clean audio you can review fast. Above roughly 10% means noise, accents or crosstalk, and you should charge the difficult-audio rate. Scribie does something similar, with a +$0.50/min surcharge for noisy or accented audio. If a client sends a sample, run it before you quote.
Step 5: The human pass (this is the product)
Open the .docx next to the audio in VLC or oTranscribe (otranscribe.com, free, runs in the browser, and has keyboard shortcuts for pause and rewind). Then:
- Jump to every yellow word and turquoise turn. Listen, fix, remove the highlight. Turquoise turns are often whole invented sentences; delete them if nobody said them.
- Check speaker changes. Diarization makes the most mistakes on short replies (“iya”, “betul”) and when people talk over each other. Fix the label if it’s wrong.
- Fix names and jargon. Whisper doesn’t know your client’s village names, company names or field terms. Ask the client for a glossary upfront and use Find & Replace.
- Mark what you can’t hear as
[tidak jelas 00:14:32]/[inaudible 00:14:32]instead of guessing. Researchers prefer an honest gap to a confident wrong word. - Listen through once at 1.5x with the transcript in front of you. Low-confidence highlighting misses some errors where the model was confidently wrong.
- Remove all highlights (Select all → Highlight → No color) and save as
wawancara_01_final.docx.
For clean 1-on-1 audio, budget 1.5–2 hours of review per audio hour. For a noisy 6-person focus group, 3+ hours. Still faster than typing from scratch at four hours, but not “click and done”.
Step 6: Find clients today
- Thesis students. Campus Facebook groups, Telegram groups for S2/S3 students, and Twitter/X accounts like menfess bases. Post one before/after screenshot (with fake or your own audio, never client audio) and your per-minute price. Peak demand is around thesis deadlines.
- Fastwork, Sribu, Projects.co.id. (Sribulancer was folded into Sribu.com in 2023.) Create a “Transkrip wawancara penelitian” listing with your per-minute rates.
- Fiverr / Upwork. Target “Indonesian transcription” and “Malay transcription”. Mention speaker labels, timestamps and human review in the gig title.
- Market research agencies and NGOs. They run focus groups every month. Email the research manager with your rates and a free 10-minute sample offer. One agency client can be worth more than 20 students.
- Podcasters and journalists. Lower priority. They often accept AI-only output, so they’re less willing to pay for review.
Do one free 10-minute sample per serious lead. It costs you 20 minutes and closes more deals than any portfolio.
Cost and earnings breakdown
Fixed costs: $0 to start (Colab free tier). $9.99 for Colab compute units once you need reliable GPUs. Headphones if you don’t own decent ones.
Variable cost per audio hour: effectively $0 on your own GPU. On paid Colab, a few compute units at most.
Example month (my estimate, part-time):
| Hours of audio | Price | Gross | Your time | |
|---|---|---|---|---|
| Local students, clean audio | 8 h | Rp 7,000/min | Rp 3,360,000 | ~14 h |
| One agency FGD batch, noisy | 4 h | Rp 12,000/min | Rp 2,880,000 | ~13 h |
| Fiverr, Indonesian audio | 3 h | $0.60/min, minus 20% fee | ~$86 | ~6 h |
That’s roughly Rp 7.5 million for about 33 hours of work if you have that many clients. You won’t in month one. Realistically, the first month is a handful of student orders while you collect reviews. The work is not passive. The AI removes the typing, but review time scales with every audio hour you sell. Your hourly rate goes up as your listening and editing get faster, not because the bot runs at night.
Risks
Privacy law. Interview recordings are personal data, sometimes sensitive (health, religion, politics). In Indonesia, the Personal Data Protection Law (UU PDP, Law No. 27 of 2022) has been fully in force since October 2024. You’re processing data on the client’s behalf, so act like it: process only for the job, delete audio and drafts after delivery (say “deleted 7 days after delivery” in your offer and do it), and never reuse client audio as samples.
“Runs locally” has to be true. Colab is Google’s cloud. If you promise “audio never leaves my machine”, run the script on your own PC. For sensitive projects (medical, legal, minors, whistleblowers), either use local hardware or decline.
Research ethics. Many studies require transcribers to sign a confidentiality agreement. Keep a one-page NDA ready. Researchers who have one to show their ethics committee choose you over someone who doesn’t.
Misrepresenting AI work. Plenty of transcription gigs are AI output sold as “100% human typed”. If a client finds out, expect a refund demand, a dispute and a bad review. On Fiverr that can mean an order cancellation. Describe it honestly: “AI-assisted draft, every line reviewed by a human against the audio.”
Platform rules. On Fiverr and Upwork, keep communication and payment on the platform. Moving clients to WhatsApp transfers before the first order is a common cause of account warnings and suspensions.
Academic misconduct. Transcription is a normal, accepted research service. Writing the student’s coding, analysis or thesis chapters is not. Universities treat it as misconduct, and it’s the kind of request you’ll get once students trust you. Decline it.
Chargebacks and disputes. Your protection is the written agreement on verbatim type, speaker labels and deadline, plus the free sample the client approved. Bill local clients 50% upfront for big batches.
Reality check
What doesn’t work:
- Selling the raw AI draft. Free tools already produce that. If you skip the review, you’re competing with free, and the first invented sentence a client finds in a quote ends the relationship.
- Competing on price alone. At Rp 2,000 per minute you’ll get orders and lose money on your time. Compete on accuracy, speaker labels and turnaround.
- Phone-speaker recordings and big FGDs at the standard rate. Diarization falls apart with 6+ people talking over each other, and Whisper struggles with heavy Javanese, Sundanese or Minang mixed into Indonesian. Quote these at the difficult rate, or by the hour.
- Expecting passive income. This is a fast service business. The pipeline saves you about half the hours, not all of them.
Common mistakes:
- Not asking clean vs full verbatim upfront, then redoing the whole file
- Letting pyannote guess the speaker count on a 2-person interview (pass
--speakers 2) - Trusting un-highlighted text. Whisper can be confidently wrong, so do the full listen-through
- Leaving the
--langdefault on English interviews (--lang en), or forcing one language on code-switched audio without checking the English parts - Forgetting to accept the pyannote model conditions on Hugging Face, then spending an hour on a 403 error
- Posting real client transcripts as portfolio samples
Model names change. If a newer Whisper model or pyannote pipeline ships, test it on the same three files you already reviewed and compare flagged percentages and diarization errors before switching.
Final checklist
- Hugging Face token created, pyannote community-1 conditions accepted
-
transcribe.pytested on three recordings of your own: clean 1-on-1, phone call, noisy group - Your own review time measured per audio hour, so you know your real hourly rate
- Price list (clean / difficult / rush) and verbatim definitions written down
- One-page NDA and a data deletion policy ready
- Listings live: one campus group post, Fastwork, and a Fiverr gig
Start today: record a 10-minute conversation with a friend in Indonesian, run it through the script, review it, and screenshot the before (highlighted draft) and after (clean transcript). That’s your portfolio sample, and your first listing can go live tonight.