Guardrails
Every API key can carry its own protections: spend limits, model restrictions, an expiry date, and content scanning for prompt injection and sensitive data. All of them are optional, configured per key in your dashboard, and enforced before your request runs.
Why set guardrails
A single account often serves very different workloads — a production app, a teammate's experiment, a CI pipeline, a customer-facing chatbot. Guardrails let each key carry exactly the blast radius it deserves:
A leaked key can't drain your balance
A key with a daily spend limit and a model allowlist is worth very little to whoever finds it in a public repo.
A runaway script stops itself
An agent stuck in a loop hits its key's daily limit and stops — instead of burning through your credits overnight.
Untrusted input gets screened
If end users type into your product and that text reaches a model, content scanning catches prompt-injection attempts and accidental PII before they ever leave your key.
Spend & access limits
Four independent limits, each optional. When one is hit, the request is rejected with a specific error code before anything is billed.
Lifetime credit limit
A hard cap on the total a key may ever spend, in USD credits. Useful for one-off projects and trial integrations. Reaching it returns 429 credit_limit_exceeded.
Monthly & daily spend limits
Cap spend per calendar month or per day. The counters reset automatically. Returns 429 monthly_spend_limit_exceeded or 429 daily_spend_limit_exceeded.
Model allowlist
Restrict a key to specific models — for example, pin a production key to the exact model you tested against, or keep an experimentation key away from expensive models. A call to any other model returns 403 model_not_allowed.
Key expiry
Give a key an expiry date — for contractors, demos, or anything temporary. After it passes, the key returns 401 api_key_expired and can no longer be used.
Content scanning
Opt a key into scanning the user-supplied text of chat requests before it reaches any model. Two independent guardrails are available:
Prompt injection
Detects “ignore previous instructions”-class attacks, including common evasion techniques: base64- and hex-encoded payloads, scrambled spelling, spaced-out letters, and newline-split phrases. Scanning covers user and tool messages — your own system prompt is never second-guessed.
Sensitive info (PII)
Detects email addresses, phone numbers, SSNs, credit card numbers (Luhn-validated), and IPv4 addresses. You choose which of these patterns apply per key.
Three modes
Each guardrail is set to one of three modes, independently:
flag— observe only
The request runs untouched; the finding is recorded in your request logs. Start here to measure false positives on your real traffic before enforcing anything.
redact— sanitize and continue
Matched spans are replaced with a token before the request is forwarded — the model never sees the original text. Tokens: [PROMPT_INJECTION], [REDACTED_EMAIL], [REDACTED_PHONE], [REDACTED_SSN], [REDACTED_CARD], [REDACTED_IP].
block— reject the request
The request is rejected with 403 guardrail_blocked before any model runs — a blocked request never costs credits.
{
"error": {
"code": "guardrail_blocked",
"message": "Request blocked by this API key's content guardrail (injection).",
"type": "invalid_request_error"
}
}Knowing when a guardrail fired
When a guardrail fires in any mode, the response carries an x-apexapi-guardrails header listing which guardrail fired and in what mode, and the event appears in your request logs. Use flag mode plus this header to dry-run a policy against production traffic.
Privacy
Guardrail events never contain the matched text — we record only which kind of pattern fired and the action taken, never the offending content. That is separate from request-body storage: bodies are stored only if you enable body logging on the key, and under redact mode the stored body is the sanitized version.
Limitations
- Detection is pattern-based and deliberately conservative — it raises the bar, it is not a guarantee. Treat it as one layer of defense, not the only one.
- Scanning applies to chat completions only, and to the first 64KB of each text part.
- False positives are possible (e.g. a phone-number pattern matching a dotted version string). Always start in
flagmode. - Scanning is built to never break your traffic: if it cannot finish in time, the request proceeds unscanned rather than failing.
Recommended setups
Sensible starting points for common key types:
Customer-facing chatbot key
Daily spend limit sized to normal traffic, model allowlist pinned to your production model, prompt injection in block mode, sensitive info in redact mode. End users can't hijack your prompt or leak card numbers into model context.
Teammate or contractor key
Monthly spend limit, expiry date at the end of the engagement. Nothing to remember to revoke.
CI / automation key
Small lifetime or daily credit limit and a model allowlist with one cheap model. If the key leaks from a pipeline log, it's nearly worthless.
Compliance-sensitive workload
Sensitive info in redact mode with all patterns enabled — PII is stripped before it ever leaves your infrastructure, and the logs prove it without storing the data.
Setting up guardrails
Guardrails are configured per key in the dashboard — no code changes, and changes take effect immediately:
- Go to API Keys in your dashboard.
- Create a new key or edit an existing one.
- Use the tabs in the key dialog: Limits for spend caps, Models for the allowlist, and Scanning for content guardrails.
- Watch guardrail activity in your request logs and the
x-apexapi-guardrailsresponse header.
Spend limits are soft guardrails: they are checked before a request runs, so concurrent in-flight requests may briefly overshoot a limit before their usage is recorded. Your credit-balance ledger is the only hard stop — it can never be overspent.
Guardrail error codes
All guardrail rejections use a specific error code so your client can tell them apart from provider or billing errors. See Error Codes for the full list.
credit_limit_exceeded429The key reached its lifetime credit limit.
monthly_spend_limit_exceeded429The key reached its spend limit for the current calendar month.
daily_spend_limit_exceeded429The key reached its spend limit for the current day.
model_not_allowed403The requested model is not on this key's model allowlist.
api_key_expired401The key's expiry date has passed.
guardrail_blocked403Content scanning in block mode rejected the request before any model ran.