Redact PII from Text Using LLM Gateway

This guide will show you how to use AssemblyAI’s LLM Gateway to redact personally identifiable information (PII) from text.

Quickstart

1import requests
2import time
3import json
4import re
5
6base_url = "https://api.assemblyai.com"
7headers = {"authorization": "<YOUR_API_KEY>"}
8
9def generate_ner(transcript_text):
10 prompt = '''
11 You will be given a transcript of a conversation or text. Your task is to generate named entities from the given transcript text.
12
13 Please identify and extract the following named entities from the transcript:
14
15 1. Person names
16 2. Organization names
17 3. Email addresses
18 4. Phone numbers
19 5. Full addresses
20
21 When extracting these entities, make sure to return the exact spelling and formatting as they appear in the transcript. Do not modify or standardize the entities in any way.
22
23 Present your results in a JSON format with a single field named "named_entities". This field should contain an array of strings, where each string is a named entity you've identified. For example:
24 {
25 "named_entities": ["John Doe", "Acme Corp", "john.doe@example.com", "123-456-7890", "123 Main St, Anytown, USA 12345"]
26 }
27
28 Important: Do not include any other information, explanations, or text in your response. Your output should consist solely of the JSON object containing the named entities.
29
30 If you do not find any named entities of a particular type, simply return an empty array for the "named_entities" field.
31 '''
32
33 llm_gateway_data = {
34 "model": "claude-sonnet-4-5-20250929",
35 "messages": [
36 {"role": "user", "content": f"{prompt}\n\nTranscript: {transcript_text}"}
37 ],
38 "max_tokens": 1000,
39 "temperature": 0.0
40 }
41
42 response = requests.post(
43 "https://llm-gateway.assemblyai.com/v1/chat/completions",
44 headers=headers,
45 json=llm_gateway_data
46 )
47
48 result = response.json()["choices"][0]["message"]["content"]
49
50 try:
51 res_json = json.loads(result)
52 except:
53 res_json = {'named_entities': []}
54
55 named_entities = res_json.get('named_entities', [])
56 return named_entities
57
58# Step 1: Transcribe audio
59with open("./my-audio.mp3", "rb") as f:
60 response = requests.post(base_url + "/v2/upload", headers=headers, data=f)
61
62upload_url = response.json()["upload_url"]
63data = {"audio_url": upload_url}
64
65response = requests.post(base_url + "/v2/transcript", json=data, headers=headers)
66transcript_id = response.json()['id']
67polling_endpoint = base_url + "/v2/transcript/" + transcript_id
68
69while True:
70 transcription_result = requests.get(polling_endpoint, headers=headers).json()
71 if transcription_result['status'] == 'completed':
72 break
73 elif transcription_result['status'] == 'error':
74 raise RuntimeError(f"Transcription failed: {transcription_result['error']}")
75 else:
76 time.sleep(3)
77
78# Step 2: Split transcript into sentences and redact PII
79transcript_text = transcription_result['text']
80sentences = re.split(r'[.!?]+', transcript_text)
81redacted_transcript = ''
82
83for sentence in sentences:
84 sentence = sentence.strip()
85 if not sentence:
86 continue
87
88 generated_entities = generate_ner(sentence)
89 redacted_sentence = sentence
90
91 for entity in generated_entities:
92 redacted_sentence = redacted_sentence.replace(entity, '#' * len(entity))
93
94 redacted_transcript += redacted_sentence + '. '
95 print(redacted_sentence)
96
97print('\nFull redacted transcript:')
98print(redacted_transcript)

Get Started

Before we begin, make sure you have an AssemblyAI account and an API key. You can sign up for an account and get your API key from your dashboard.

Step-by-Step Instructions

Install the required packages:

$pip install requests

Import the required packages and set up your API client:

1import requests
2import time
3import json
4import re
5
6base_url = "https://api.assemblyai.com"
7headers = {"authorization": "<YOUR_API_KEY>"}

Define a function generate_ner that uses LLM Gateway to identify named entities (person names, organizations, emails, phone numbers, addresses) in a given text.

1def generate_ner(transcript_text):
2 prompt = '''
3 You will be given a transcript of a conversation or text. Your task is to generate named entities from the given transcript text.
4
5 Please identify and extract the following named entities from the transcript:
6
7 1. Person names
8 2. Organization names
9 3. Email addresses
10 4. Phone numbers
11 5. Full addresses
12
13 When extracting these entities, make sure to return the exact spelling and formatting as they appear in the transcript. Do not modify or standardize the entities in any way.
14
15 Present your results in a JSON format with a single field named "named_entities". This field should contain an array of strings, where each string is a named entity you've identified. For example:
16 {
17 "named_entities": ["John Doe", "Acme Corp", "john.doe@example.com", "123-456-7890", "123 Main St, Anytown, USA 12345"]
18 }
19
20 Important: Do not include any other information, explanations, or text in your response. Your output should consist solely of the JSON object containing the named entities.
21
22 If you do not find any named entities of a particular type, simply return an empty array for the "named_entities" field.
23 '''
24
25 llm_gateway_data = {
26 "model": "claude-sonnet-4-5-20250929",
27 "messages": [
28 {"role": "user", "content": f"{prompt}\n\nTranscript: {transcript_text}"}
29 ],
30 "max_tokens": 1000,
31 "temperature": 0.0
32 }
33
34 response = requests.post(
35 "https://llm-gateway.assemblyai.com/v1/chat/completions",
36 headers=headers,
37 json=llm_gateway_data
38 )
39
40 result = response.json()["choices"][0]["message"]["content"]
41
42 try:
43 res_json = json.loads(result)
44 except:
45 res_json = {'named_entities': []}
46
47 named_entities = res_json.get('named_entities', [])
48 return named_entities

Transcribe an audio file using the AssemblyAI API:

1with open("./my-audio.mp3", "rb") as f:
2 response = requests.post(base_url + "/v2/upload", headers=headers, data=f)
3
4upload_url = response.json()["upload_url"]
5data = {"audio_url": upload_url} # You can also use a URL to an audio or video file on the web
6
7response = requests.post(base_url + "/v2/transcript", json=data, headers=headers)
8transcript_id = response.json()['id']
9polling_endpoint = base_url + "/v2/transcript/" + transcript_id
10
11while True:
12 transcription_result = requests.get(polling_endpoint, headers=headers).json()
13 if transcription_result['status'] == 'completed':
14 break
15 elif transcription_result['status'] == 'error':
16 raise RuntimeError(f"Transcription failed: {transcription_result['error']}")
17 else:
18 time.sleep(3)

Split the transcript into sentences, identify named entities using generate_ner, and replace them with # characters:

1transcript_text = transcription_result['text']
2sentences = re.split(r'[.!?]+', transcript_text)
3redacted_transcript = ''
4
5for sentence in sentences:
6 sentence = sentence.strip()
7 if not sentence:
8 continue
9
10 generated_entities = generate_ner(sentence)
11 redacted_sentence = sentence
12
13 for entity in generated_entities:
14 redacted_sentence = redacted_sentence.replace(entity, '#' * len(entity))
15
16 redacted_transcript += redacted_sentence + '. '
17 print(redacted_sentence)

Print the fully redacted transcript:

1print('\nFull redacted transcript:')
2print(redacted_transcript)