npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

raipii

v0.1.0

Published

Node.js SDK for the raipii PII detection and sanitization API

Readme

raipii Node.js 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.

npm version Node 18+


Install

npm install raipii
# or
yarn add raipii
# or
pnpm add raipii

Requires Node 18+. Zero runtime dependencies — uses native fetch.


Quick start

import { Raipii } from "raipii";

const ps = new Raipii({ apiKey: "ps_live_..." });

// 1. Sanitize — strip PII before sending to your LLM
const result = await ps.sanitize(
  "Hi, I'm John Smith — [email protected], SSN 392-45-7810",
  { mode: "fake_substitute" },
);
console.log(result.sanitizedText);
// "Hi, I'm Michael Torres — [email protected], SSN 847-23-1956"

// 2. Call your LLM with the sanitized prompt
const llmResponse = await yourLLM(result.sanitizedText);

// 3. Restore — put original values back in the response
const original = await ps.restore(llmResponse, result.sessionId);
console.log(original.restoredText);
// "Hi, I'm John Smith — [email protected], SSN 392-45-7810"

Set RAIPII_API_KEY in your environment to avoid passing the key in code:

export RAIPII_API_KEY=ps_live_...
const ps = new 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.

const result = await ps.sanitize(
  "Schedule a call with Jane Doe at [email protected] on 555-867-5309",
  { mode: "token" },
);
console.log(result.sanitizedText);
// "Schedule a call with [PERSON_1] at [EMAIL_1] on [PHONE_1]"

const restored = await ps.restore(llmResponse, result.sessionId);

fake_substitute

Replaces PII with realistic values. The LLM sees natural data and produces better output. All substitutions are reversed on restore.

const result = await ps.sanitize(
  "Write a summary for John Smith, DOB 1985-03-12, SSN 392-45-7810",
  { mode: "fake_substitute" },
);
console.log(result.sanitizedText);
// "Write a summary for Michael Torres, DOB 1991-07-24, SSN 847-23-1956"

const restored = await ps.restore(llmResponse, result.sessionId);
// LLM response has fake values swapped back to real ones

redact

Replaces PII with [REDACTED]. One-way — there is nothing to restore.

const result = await ps.sanitize(
  "Patient John Smith, MRN 00123456, DOB 1985-03-12",
  { mode: "redact" },
);
console.log(result.sanitizedText);
// "Patient [REDACTED], MRN [REDACTED], DOB [REDACTED]"

Detect only

Scan text for PII without modifying it.

const result = await ps.detect("My SSN is 392-45-7810 and email is [email protected]");

console.log(result.piiDetected);  // true
console.log(result.riskLevel);    // "HIGH"

for (const entity of result.entitiesFound) {
  console.log(`${entity.type}: ${entity.value} (${Math.round(entity.confidence * 100)}%)`);
}
// US_SSN: 392-45-7810 (100%)
// EMAIL: [email protected] (100%)

Risk levels: NONELOWMEDIUMHIGH

| 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 | [email protected] | 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.

const conv = await ps.conversations.create({ ttl: 86400 }); // 24hr TTL

// Turn 1
const turn1 = await ps.sanitize(
  "My name is John Smith. What should I know about my account?",
  { mode: "fake_substitute", conversationId: conv.conversationId },
);
const llmReply1 = await yourLLM(turn1.sanitizedText);
const response1 = await ps.restore(llmReply1, turn1.sessionId);

// Turn 2 — "John Smith" maps to the SAME fake name as turn 1
const turn2 = await ps.sanitize(
  "What were you saying about John Smith earlier?",
  { mode: "fake_substitute", conversationId: conv.conversationId },
);
const llmReply2 = await yourLLM(turn2.sanitizedText);
const response2 = await ps.restore(llmReply2, turn2.sessionId);

Error handling

import {
  AuthenticationError,
  QuotaExceededError,
  NotFoundError,
  ValidationError,
  RateLimitError,
  ServiceUnavailableError,
} from "raipii";

try {
  const result = await ps.sanitize(text);
} catch (err) {
  if (err instanceof AuthenticationError) {
    // Invalid or missing API key
    console.error("Check your API key at raipii.com");
  } else if (err instanceof QuotaExceededError) {
    // Monthly character limit reached
    console.error("Upgrade your plan at raipii.com");
  } else if (err instanceof NotFoundError) {
    // Session expired or not found
    console.error("Session expired — re-sanitize the original text");
  } else if (err instanceof RateLimitError) {
    // SDK retries automatically — this means retries were exhausted
    console.error("Rate limit hit");
  } else if (err instanceof ServiceUnavailableError) {
    // SDK retried 3 times and failed
    console.error("Service unavailable, try again shortly");
  } else if (err instanceof ValidationError) {
    console.error("Bad request:", err.message);
  }
}

All error classes extend RaipiiError which exposes .statusCode 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 maxRetries: 3. Override:

const ps = new Raipii({ apiKey: "...", maxRetries: 5 });
const psNoRetry = new Raipii({ apiKey: "...", maxRetries: 0 });

Client options

const ps = new Raipii({
  apiKey: "ps_live_...",              // or RAIPII_API_KEY env var
  baseUrl: "https://api.raipii.com", // override for testing/proxies
  timeoutMs: 30_000,                 // request timeout in ms
  maxRetries: 3,                     // retries on 429/503
});

TypeScript types

All methods are fully typed. Key interfaces:

interface SanitizeResult {
  sessionId: string;
  sanitizedText: string;
  entitiesFound: EntityFound[];
  charCount: number;
  usage: { charsBilled: number };
  conversationId: string | null;
}

interface RestoreResult {
  restoredText: string;
  substitutionsReversed: number;
  usage: { charsBilled: number };
}

interface DetectResult {
  entitiesFound: DetectedEntity[];
  piiDetected: boolean;
  riskLevel: "NONE" | "LOW" | "MEDIUM" | "HIGH";
  usage: { charsBilled: number };
}

interface Conversation {
  conversationId: string;
  expiresAt: string;
}

Caveats

  • Session TTL — sessions expire after sessionTtl seconds (default 1hr). Calling restore() after expiry throws NotFoundError. Call restore promptly after getting the LLM response.
  • redact mode has no restore[REDACTED] tokens contain no reversible information. Calling restore() 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 billedsanitize, restore, and detect all bill by character count of the input text. 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.

ESM and CJS

The package ships both ESM and CommonJS builds:

// ESM (TypeScript / bundlers / Node with "type": "module")
import { Raipii } from "raipii";

// CJS (require)
const { Raipii } = require("raipii");

Get an API key

Free tier — 2M characters/month, no credit card required: raipii.com