Python SDK for the raipii PII detection and sanitization API
Project description
raipii Python SDK
Detect and sanitize PII before it reaches your LLM. Replace real data with tokens or realistic fakes. Restore original values after the model responds.
Install
pip install raipii
Requires Python 3.8+. The only dependency is requests.
Quick start
import raipii
ps = raipii.Raipii(api_key="ps_live_...")
# 1. Sanitize — strip PII before sending to your LLM
result = ps.sanitize(
"Hi, I'm John Smith — john@acme.com, SSN 392-45-7810",
mode="fake_substitute",
)
print(result.sanitized_text)
# "Hi, I'm Michael Torres — m.torres@email.net, SSN 847-23-1956"
# 2. Call your LLM with the sanitized prompt
llm_response = your_llm(result.sanitized_text)
# 3. Restore — put original values back in the response
original = ps.restore(llm_response, result.session_id)
print(original.restored_text)
# "Hi, I'm John Smith — john@acme.com, SSN 392-45-7810"
Set RAIPII_API_KEY in your environment to avoid passing the key in code:
export RAIPII_API_KEY=ps_live_...
ps = raipii.Raipii() # reads RAIPII_API_KEY automatically
Get a free API key (2M chars/month) at raipii.com.
Sanitize modes
token (default)
Replaces PII with labelled placeholder tokens. Safe, lossless, fully reversible.
result = ps.sanitize(
"Schedule a call with Jane Doe at jane@corp.com on 555-867-5309",
mode="token",
)
print(result.sanitized_text)
# "Schedule a call with [PERSON_1] at [EMAIL_1] on [PHONE_1]"
# Each token maps back to its original value on restore
restored = ps.restore(llm_response, result.session_id)
fake_substitute
Replaces PII with realistic Faker-generated values. The LLM sees natural data and produces better output. All substitutions are reversed on restore.
result = ps.sanitize(
"Write a summary for John Smith, DOB 1985-03-12, SSN 392-45-7810",
mode="fake_substitute",
)
print(result.sanitized_text)
# "Write a summary for Michael Torres, DOB 1991-07-24, SSN 847-23-1956"
restored = ps.restore(llm_response, result.session_id)
# LLM response has fake values swapped back to real ones
redact
Replaces PII with [REDACTED]. One-way — there is nothing to restore. Use when the LLM response must never reference PII at all.
result = ps.sanitize(
"Patient John Smith, MRN 00123456, DOB 1985-03-12",
mode="redact",
)
print(result.sanitized_text)
# "Patient [REDACTED], MRN [REDACTED], DOB [REDACTED]"
Detect only
Scan text for PII without modifying it. Useful for logging, compliance auditing, or deciding whether to sanitize.
result = ps.detect("My SSN is 392-45-7810 and email is john@acme.com")
print(result.pii_detected) # True
print(result.risk_level) # "HIGH"
for entity in result.entities_found:
print(f" {entity.type}: {entity.value!r} at {entity.position} ({entity.confidence:.0%} confidence)")
# US_SSN: '392-45-7810' at (10, 21) (100% confidence)
# EMAIL: 'john@acme.com' at (35, 48) (100% confidence)
Risk levels: NONE → LOW → MEDIUM → HIGH
| Risk | Triggered by |
|---|---|
HIGH |
SSN, credit card, MRN, bank account, tax ID |
MEDIUM |
Person name, email, date of birth, address |
LOW |
Any other detected entity |
NONE |
No PII found |
Detected entity types
| Type | Example | Tier |
|---|---|---|
PERSON |
John Smith | All tiers |
EMAIL |
john@acme.com | All tiers |
PHONE |
555-867-5309 | All tiers |
US_SSN |
392-45-7810 | All tiers |
CREDIT_CARD |
4111 1111 1111 1111 | All tiers |
DATE_OF_BIRTH |
1985-03-12 | All tiers |
ADDRESS |
123 Main St, Austin TX | Growth+ |
IP_ADDRESS |
192.168.1.1 | All tiers |
MEDICAL_RECORD_NUMBER |
MRN 00123456 | All tiers |
BANK_ACCOUNT |
12345678 | All tiers |
TAX_ID |
12-3456789 | All tiers |
IBAN |
GB29 NWBK 6016 1331 9268 19 | All tiers |
JWT |
eyJhbGci... | All tiers |
AWS_KEY |
AKIA... | All tiers |
Starter tier detects structured PII reliably. Growth and Business tiers add enhanced contextual detection with higher accuracy for unstructured entities such as names and addresses.
Multi-turn conversations
Keep consistent fake substitutions across all turns of a conversation. The same entity always maps to the same fake value within a session.
conv = ps.conversations.create(ttl=86400) # 24hr TTL
# Turn 1
turn1 = ps.sanitize(
"My name is John Smith. What should I know about my account?",
mode="fake_substitute",
conversation_id=conv.conversation_id,
)
llm_reply_1 = your_llm(turn1.sanitized_text)
response1 = ps.restore(llm_reply_1, turn1.session_id)
# Turn 2 — "John Smith" maps to the SAME fake name as turn 1
turn2 = ps.sanitize(
"What were you saying about John Smith earlier?",
mode="fake_substitute",
conversation_id=conv.conversation_id,
)
llm_reply_2 = your_llm(turn2.sanitized_text)
response2 = ps.restore(llm_reply_2, turn2.session_id)
Error handling
from raipii import (
AuthenticationError,
QuotaExceededError,
NotFoundError,
ValidationError,
RateLimitError,
ServiceUnavailableError,
)
try:
result = ps.sanitize(text)
except AuthenticationError:
# Invalid or missing API key
print("Check your API key at raipii.com")
except QuotaExceededError:
# Monthly character limit reached
print("Upgrade your plan at raipii.com")
except NotFoundError:
# Session expired or not found
print("Session expired — re-sanitize the original text")
except RateLimitError:
# Too many requests — SDK retries automatically, this means retries exhausted
print("Rate limit hit")
except ServiceUnavailableError:
# Detection backend temporarily unavailable — SDK retried 3 times
print("Service unavailable, try again shortly")
except ValidationError as e:
print(f"Bad request: {e}")
All exceptions inherit from raipii.RaipiiError and expose .status_code and .response.
Retry behaviour
The SDK automatically retries on 429 Too Many Requests and 503 Service Unavailable with exponential backoff:
| Attempt | Delay |
|---|---|
| 1st retry | 1s |
| 2nd retry | 2s |
| 3rd retry | 4s |
Default max_retries=3. Override:
ps = raipii.Raipii(api_key="...", max_retries=5)
ps_no_retry = raipii.Raipii(api_key="...", max_retries=0)
Client options
ps = raipii.Raipii(
api_key="ps_live_...", # or RAIPII_API_KEY env var
base_url="https://api.raipii.com", # override for testing
timeout=30, # request timeout in seconds
max_retries=3, # retries on 429/503
)
Return types
SanitizeResult
| Field | Type | Description |
|---|---|---|
session_id |
str |
Pass to restore() |
sanitized_text |
str |
Text with PII replaced |
entities_found |
list[EntityFound] |
Each detected entity |
char_count |
int |
Characters processed |
usage.chars_billed |
int |
Characters billed |
conversation_id |
str | None |
Echo of the conversation_id passed in |
RestoreResult
| Field | Type | Description |
|---|---|---|
restored_text |
str |
Text with original PII restored |
substitutions_reversed |
int |
Number of tokens replaced |
usage.chars_billed |
int |
Characters billed |
DetectResult
| Field | Type | Description |
|---|---|---|
entities_found |
list[DetectedEntity] |
Detected PII entities |
pii_detected |
bool |
True if any PII found |
risk_level |
str |
NONE / LOW / MEDIUM / HIGH |
usage.chars_billed |
int |
Characters billed |
Caveats
- Session TTL — sessions expire after
session_ttlseconds (default 1hr). Callingrestore()after expiry raisesNotFoundError. Store thesession_idand call restore promptly after getting the LLM response. redactmode has no restore —[REDACTED]tokens contain no reversible information. Callingrestore()on a redact session returns the text unchanged.- Conversation TTL — conversation sessions expire after their TTL (default 24hr). After expiry, new turns start a fresh mapping.
- Characters billed — both
sanitizeandrestorebill by character count of the input text.detectalso bills. Free tier: 2M chars/month. - Starter tier detects structured PII reliably. Upgrade to Growth for enhanced contextual detection of names and addresses in free-form text.
Get an API key
Free tier — 2M characters/month, no credit card required: raipii.com
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file raipii-0.1.0.tar.gz.
File metadata
- Download URL: raipii-0.1.0.tar.gz
- Upload date:
- Size: 9.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b346d07643b70b9934a75996a26cffccb00d4e90aaac4394ec4d97d9d987965c
|
|
| MD5 |
ef5585519c2c83d2c68dbd2fe6f9f479
|
|
| BLAKE2b-256 |
4835f059be93f80dce51efe359e2998777131f305252e9d46732a85de5da3bd7
|
File details
Details for the file raipii-0.1.0-py3-none-any.whl.
File metadata
- Download URL: raipii-0.1.0-py3-none-any.whl
- Upload date:
- Size: 9.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
62f15bbe25a0d623954dabcd0c19484112c9400cf4ad3796662c48eb8bb8c568
|
|
| MD5 |
55eebc9f34c07e33c4ea50936a215831
|
|
| BLAKE2b-256 |
0d1687bd8e196f272490979f045b2b7ae9d934e4ee5312dcc51b57b94350db0a
|