Skip to main content
When your application handles API keys, tokens, or other credentials, those values can appear in LangSmith traces if they are passed as part of inputs or outputs. Use the LangSmith SDK’s built-in anonymizer to redact secrets before they are sent to the backend.
This page covers redacting secrets (API keys, tokens, credentials) from trace data via the SDK. For redacting personally identifiable information (PII) such as emails, names, or SSNs, see Prevent logging of sensitive data in traces. To redact secrets at the LLM Gateway layer, see PII and secrets redaction.

Use the SDK anonymizer

The create_anonymizer / createAnonymizer function requires:
  • Python SDK: 0.1.81 and above
  • TypeScript SDK: 0.1.33 and above
The create_anonymizer function accepts a list of regex patterns and replacement strings. Pass the resulting anonymizer to the Client constructor, and it will automatically apply to all run inputs and outputs before they reach LangSmith. The following example redacts common secret formats, including OpenAI API keys, generic bearer tokens, and sk- prefixed keys:
from langsmith.anonymizer import create_anonymizer
from langsmith import Client, traceable

# Redact common secret patterns
anonymizer = create_anonymizer([
    # OpenAI-style keys: sk-... or sk-proj-...
    {"pattern": r"sk-[A-Za-z0-9\-_]{20,}", "replace": "<REDACTED_API_KEY>"},
    # Generic bearer tokens
    {"pattern": r"Bearer\s+[A-Za-z0-9\-_\.]{20,}", "replace": "Bearer <REDACTED_TOKEN>"},
    # Anthropic keys
    {"pattern": r"sk-ant-[A-Za-z0-9\-_]{20,}", "replace": "<REDACTED_API_KEY>"},
    # Generic high-entropy strings that look like secrets (40+ hex chars)
    {"pattern": r"\b[0-9a-fA-F]{40,}\b", "replace": "<REDACTED_TOKEN>"},
])

client = Client(anonymizer=anonymizer)

@traceable(client=client)
def call_external_api(api_key: str, prompt: str) -> str:
    # The api_key value will be redacted in the trace
    return f"Response to: {prompt}"

call_external_api(
    api_key="sk-proj-AbCdEfGhIjKlMnOpQrStUvWxYz1234567890",
    prompt="What is LangSmith?",
)
The anonymizer serializes inputs and outputs to JSON, applies each regex pattern, then deserializes the result before sending to LangSmith. By default, it traverses up to 10 nesting levels deep. To change this, pass the max_depth parameter:
anonymizer = create_anonymizer(
    [{"pattern": r"sk-[A-Za-z0-9\-_]{20,}", "replace": "<REDACTED_API_KEY>"}],
    max_depth=5,
)

Use a custom function

If your redaction logic is more complex, pass a function instead of a list of patterns. The function receives a string and returns the redacted string:
import re
from langsmith.anonymizer import create_anonymizer
from langsmith import Client

# Example: redact any value that follows a known key name in JSON-like payloads
SECRET_KEYS = {"api_key", "apiKey", "token", "secret", "password", "credential"}

def redact_secret_values(text: str) -> str:
    for key in SECRET_KEYS:
        # Match patterns like: "api_key": "some-value"
        pattern = rf'("{key}"\s*:\s*)"[^"]*"'
        text = re.sub(pattern, r'\1"<REDACTED>"', text)
    return text

anonymizer = create_anonymizer(redact_secret_values)
client = Client(anonymizer=anonymizer)

Combine with LANGSMITH_HIDE_INPUTS

If your use case requires completely suppressing all inputs (for example, for zero-retention compliance), use LANGSMITH_HIDE_INPUTS=true instead. The anonymizer is skipped when LANGSMITH_HIDE_INPUTS or LANGSMITH_HIDE_OUTPUTS is set to true. For more options, including hiding all inputs and outputs, hiding metadata, function-level processors, and third-party PII libraries, see Prevent logging of sensitive data in traces.