How to automatically redact PII from audio and video files with Python
In this tutorial, we’ll learn how to automatically redact Personal Identifiable Information (PII) from audio and video files in 5 minutes using Python and AssemblyAI.



A health tech team ships an ambient scribe. They redact patient names, dates of birth and phone numbers out of every transcript before it hits their analytics warehouse. Clean pipeline, documented policy, security review passed. Then someone asks where the original recordings live — and the answer is a retention bucket full of WAV files in which a nurse clearly says "Maria Delgado, date of birth March fourth, 1961."
The transcript was redacted. The PHI was not. That's the single most common gap in medical transcription pipelines, and it exists because redaction is usually thought of as a text problem when the source material is audio.
This is a practical guide to redacting protected health information from both surfaces. What redaction actually does to your data, which entity types to configure and which to leave alone, working code for text and audio redaction, what it costs, and the scope mistake that quietly breaks clinical usefulness.
What redaction does, and what it isn't
Three terms get used interchangeably and shouldn't be.
Redaction removes or replaces the sensitive content. The name is gone from the transcript and the spoken name is gone from the audio. It's not recoverable from the output.
Masking obscures part of the value while keeping its shape — the last four digits of a number, for example. Useful for reconciliation, not a privacy control on its own.
De-identification is a legal standard about whether a dataset can be linked back to an individual, which depends on the whole dataset, not one field. Redacting names from transcripts is a step toward it, not a substitute for it. A transcript with names removed but a rare diagnosis, a specific date and a small geography intact may still identify someone.
Redaction is the mechanism this post covers. Whether your resulting dataset meets a de-identification standard is a determination your privacy counsel makes, not a checkbox in an API.
The two surfaces you have to cover
Transcript text
The obvious one. Entities get detected in the transcript and replaced. Costs +$0.08/hr on top of transcription.
The audio file itself
The one teams miss. Audio redaction returns a version of the recording with the spoken PHI removed, so the artifact you retain no longer contains a voice reading out identifiers. Costs +$0.05/hr.
If you retain recordings at all — for quality review, for model evaluation, because a contract requires it — you need both. If you truly delete audio immediately after transcription, text redaction alone may be defensible. Most teams believe they're in the second category and are actually in the first.
Which entities are detected
Entity detection covers 50+ types, which is considerably broader than the identifier list most teams write down from memory. The categories that matter most for clinical audio:
Direct identifiers — person names, dates of birth, phone numbers, email addresses, addresses and locations, medical record and account numbers, national ID numbers, insurance and payment details.
Quasi-identifiers — occupation, employer, specific dates, age, organization names. Individually harmless, collectively identifying. These are the ones that decide whether a dataset is genuinely de-identified.
Clinical content — medical conditions, medications, procedures. Detectable as entities, and where the scope decision below gets interesting.
You configure which policies apply per request, so this isn't all-or-nothing. Choose deliberately rather than enabling everything.
Implementation: text and audio in one request
Install the SDK and set your key:
pip install assemblyaiThen transcribe with redaction on both surfaces. Note speech_models is plural for pre-recorded audio, and domain: "medical-v1" turns on Medical Mode so the clinical vocabulary is accurate before anything gets redacted:
import assemblyai as aai
aai.settings.api_key = "YOUR_API_KEY"
config = aai.TranscriptionConfig(
speech_models=["universal-3-5-pro"],
domain="medical-v1",
speaker_labels=True,
redact_pii=True,
redact_pii_audio=True,
redact_pii_sub=aai.PIISubstitutionPolicy.hash,
redact_pii_policies=[
aai.PIIRedactionPolicy.person_name,
aai.PIIRedactionPolicy.date_of_birth,
aai.PIIRedactionPolicy.phone_number,
aai.PIIRedactionPolicy.email_address,
aai.PIIRedactionPolicy.location,
aai.PIIRedactionPolicy.medical_process,
aai.PIIRedactionPolicy.us_social_security_number,
],
)
transcriber = aai.Transcriber(config=config)
transcript = transcriber.transcribe("./encounters/enc-40118.wav")
if transcript.status == "error":
raise RuntimeError(f"Transcription failed: {transcript.error}")
print(transcript.text)
Getting the redacted audio back
Audio redaction produces a separate artifact you retrieve after the transcript completes:
redacted_audio = transcript.get_redacted_audio_url()
print(redacted_audio)
# Download it, then delete your original.
import requests
response = requests.get(redacted_audio, timeout=120)
response.raise_for_status()
with open("./retained/enc-40118-redacted.wav", "wb") as f:
f.write(response.content)
That last comment is the whole point. Fetching the redacted audio and leaving the original in place accomplishes nothing. Make deletion of the source a step in the same job, not a cleanup task somebody remembers later.
Substitution method
Two supported approaches, and the choice affects what you can do downstream:
Hash replaces the entity with a fixed marker. Simplest, and the right default when the redacted transcript is going to analytics.
Entity name replaces the value with its type — [PERSON_NAME], [DATE_OF_BIRTH]. Keeps the sentence readable and tells a human reader what was removed, which matters if a clinician will ever look at the redacted version.
No pseudonym mode — redact_pii_sub accepts hash and entity_name only, so linking two mentions of the same person has to happen before redaction.
Full parameter reference is in the docs, and the feature page is at PII redaction.
The scope trap: don't redact the clinical content out of a clinical transcript
Medical conditions, medications and procedures are all detectable entities, and enabling those policies feels responsible. On a clinical transcript it's usually a mistake, because you've just removed the information the transcript exists to capture. A scribe pipeline that redacts medication names produces a note with no medication list.
The resolution is that different consumers need different versions of the same encounter, and you should be explicit about which is which:
In practice that means the redaction configuration is a property of the destination, not of the encounter. Build it that way from the start; retrofitting per-destination policies onto a single pipeline is painful.
Accuracy is a prerequisite for redaction, not a separate concern
This connection gets missed. Redaction operates on detected entities, and entity detection operates on the transcript. If the transcript mangles a name, the redactor may not recognize it as a name — and an unrecognized entity is an unredacted entity.
Which means transcription quality is a privacy control. Universal-3.5 Pro with Medical Mode records a 3.2% Missed Entity Rate — the lowest across the providers on our benchmarks page. Against the base model without Medical Mode, the domain delivers roughly 20% fewer missed medical entities and 87% fewer entity errors. The argument for why entity-level accuracy is the right measure is in WER vs MER for medical transcription.
Two more accuracy levers worth knowing. Contextual prompting — passing a patient's prior-visit note alongside the audio — cut missed medical terms by 31% in an internal healthcare test. And the base model code-switches natively across 18 languages with no configuration, while Medical Mode itself covers English, Spanish, German and French; keep those two facts separate when you scope multilingual coverage.
What it costs
Fully redacted clinical transcription on both surfaces is $0.49/hr — about twelve cents for a 15-minute encounter. That's a rounding error against the cost of an incident, which is the correct comparison. Current rates live on the pricing page.
Production considerations
Validate that redaction happened
Don't assume. Add an assertion step that scans the redacted transcript for patterns you know shouldn't survive — phone number shapes, the patient name from your own database, date formats — and fails the job loudly rather than writing to the warehouse. Cheap to build, and it catches configuration drift when someone edits the policy list.
Handle the two-artifact lifecycle
Audio redaction gives you a second file, which means a window in which both the original and the redacted version exist. Make that window as short as you can, own the deletion of the original in code, and log it.
Retry with idempotency
Transcription jobs fail for boring reasons. Retries should be keyed on your encounter ID so a retry doesn't create a second set of PHI-bearing artifacts you then forget about.
Streaming is a different problem
If you're transcribing live with Universal-3.5 Pro Realtime over wss://streaming.assemblyai.com/v3/ws, the partial-transcript nature of streaming means redaction is best applied when you persist, not on the wire. Treat the live view as ephemeral and redact at the storage boundary.
Where the BAA fits
Redaction is a technical control, and it doesn't stand in for the contract. AssemblyAI signs a Business Associate Addendum (BAA) for customers processing PHI, acting as a business associate under HIPAA, and maintains SOC 2 Type 2. Customer audio isn't used to train models and isn't shared with third parties. Details are in the BAA FAQ and on the BAA page. Get the BAA in place before you send the first production file, not after the security review asks about it.
Related builds: the end-to-end medical scribe architecture shows where redaction sits in a full pipeline, the ambient scribe evaluation guide covers vendor selection, and telehealth speech to text covers the virtual care variants. Broader healthcare deployment options are on solutions/medical and medical transcription use cases.
Conclusion
Redaction today is a filter you run at the edge of your pipeline, which means every system upstream of it holds unredacted PHI for some window. The direction worth building toward is narrowing that window until it's effectively zero — redaction applied at the moment of transcription, per destination, with the unredacted form never persisted at all. That's an architectural stance more than a feature request, and the teams that adopt it early will find their security reviews get shorter rather than longer as they scale. The pipelines that treat redaction as a final cleanup step are the ones that end up with a retention bucket nobody wants to open.
Frequently asked questions
Does AssemblyAI automatically redact patient PII from medical transcripts?
It's opt-in, not automatic — you enable it per request and choose which policies apply. Transcript text redaction is +$0.08/hr and audio redaction is +$0.05/hr, both driven by entity detection covering 50+ types. Enabling it deliberately is the right design, because a clinical note pipeline needs the unredacted transcript while your analytics warehouse does not.
What's the difference between redaction and masking?
Redaction removes or replaces the sensitive content so it isn't recoverable from the output. Masking obscures part of a value while keeping its shape — useful for reconciliation, not sufficient as a privacy control. Neither is the same as legal de-identification, which is a judgment about the whole dataset rather than a per-field operation.
Can you redact PHI from the audio file, not just the transcript?
Yes, and you should if you retain recordings. Audio redaction returns a version of the file with the spoken identifiers removed, at +$0.05/hr, retrieved after the transcript completes. Redacting only the transcript leaves a recording in which someone says the patient's name out loud.
What happens if the transcript has an error where PHI should be?
A misrecognized entity may not be detected as an entity, and an undetected entity doesn't get redacted — which makes transcription accuracy a privacy control, not just a quality one. Running Medical Mode (3.2% Missed Entity Rate, the lowest across benchmarked providers) reduces that class of failure. Add a validation step that scans redacted output for identifier patterns before it's written anywhere.
How much does PHI redaction add to transcription cost?
$0.08/hr for transcript text and $0.05/hr for audio. On top of $0.36/hr for pre-recorded transcription with Medical Mode, fully redacted clinical transcription on both surfaces is $0.49/hr — roughly twelve cents for a 15-minute encounter.
How does AssemblyAI handle HIPAA and PHI?
AssemblyAI signs a Business Associate Addendum (BAA) for customers processing PHI, acting as a business associate under HIPAA. Alongside the BAA there's PHI redaction across audio and transcripts, entity detection covering 50+ types, and SOC 2 Type 2. Customer audio isn't used for model training. See the BAA FAQ and the BAA page.
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.



