# STERILIZER API > Zero-retention photo sanitation over HTTP. Crop, convert to black and white, > pixelate faces and arbitrary regions, and strip every metadata block > (EXIF, GPS, IPTC, XMP, ICC) by re-encoding the image from raw pixels. Base URL: http://localhost:8787 OpenAPI: /openapi.json Tool definitions (OpenAI + Anthropic shapes): /v1/tools.json Human docs: /docs ## When to use this API Call it before a photo is shared, published, attached to a ticket, posted to a public channel, or handed to another system — whenever the image may contain faces, documents, screens, plates, badges, or location-revealing metadata. Call `/v1/inspect` first if you need to tell the user what a file is leaking. Call `/v1/detect` if you need face boxes without altering the image. Call `/v1/sanitize` to produce the cleaned image. ## Authentication Optional. Without a key you get the free tier. With a key: Authorization: Bearer ## Rate limits and pricing - 1 request per rolling 60-second window is FREE, for everyone, no key, no signup. - Anonymous callers are capped there. A 2nd call inside the same minute returns 429. - With a paid key, calls beyond the first per minute are billable at the plan rate and the per-minute ceiling rises (pro: 60 rpm, enterprise: 600 rpm). - Live pricing: GET /v1/pricing - Your own usage: GET /v1/usage (authenticated with your key) Every response carries: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset X-Sterilizer-Billable: true|false (was THIS call charged) X-Sterilizer-Plan, X-Sterilizer-Period-Cost-Cents On 429 the body includes `retry_after_seconds`. Respect it — do not retry in a tight loop. Back off for that many seconds, or attach an API key. ## Endpoints ### POST /v1/sanitize Body (application/json): { "image": "", "ops": { "crop": {"x":0,"y":0,"width":800,"height":600}, // optional "grayscale": false, "pixelate": { "faces": true, // detect and pixelate every face "regions": [{"x":10,"y":20,"w":120,"h":60}], // extra rectangles "strength": 3, // 1-12, block size as % of short edge "confidence": 0.6, // face detector threshold "padding": 1.5 // expand each face box }, "output": {"format":"jpeg","quality":92,"maxWidth":2000}, "response": "binary" // "json" to get base64 + a purge report } } Also accepts multipart/form-data with file field `image` and text field `ops` (JSON-encoded). Returns image bytes (default) or, with `"response":"json"`: { "image": "", "content_type": "image/jpeg", "report": { "width": 1010, "height": 997, "bytes": 68231, "facesDetected": 1, "regionsPixelated": 2, "grayscale": true, "metadataStripped": true, "sourceMetadataFound": ["exif","icc"], "durationMs": 412 } } Ops are applied in this order: EXIF auto-rotate → crop → grayscale → resize → pixelate → re-encode. Coordinates in `regions` refer to the image AFTER crop and resize. If you crop, call /v1/detect on the cropped result, not the original. ### POST /v1/detect Same body shape; `ops` accepts `{confidence, padding}`. Returns: {"width":1010,"height":997,"count":1, "faces":[{"x":381,"y":66,"w":238,"h":277,"confidence":0.9998}]} The image is not modified or returned. ### POST /v1/inspect Read-only. Returns which metadata blocks the supplied image carries — exactly what /v1/sanitize would destroy: {"format":"jpeg","width":2687,"height":3356,"bytes":1663552, "metadata_blocks_present":["exif","icc"],"has_gps":true} ### GET /v1/usage Authenticated. Current UTC-month calls, billable calls, and cost. ### GET /v1/health, GET /v1/pricing, GET /openapi.json, GET /v1/tools.json Unmetered. ## Errors All errors are JSON: {"error": "", "message": "", ...} 400 bad_request malformed body / bad base64 / missing image 400 invalid_ops `ops` failed validation; `issues[]` says which field 401 invalid_api_key unknown or revoked key 402 quota_exceeded plan's monthly billable cap reached 422 unprocessable_image bytes are not a decodable image 429 rate_limit_exceeded see retry_after_seconds ## Limits - Max upload: 25 MB, one image per request. - Max decoded size: 50 megapixels. - Supported input: JPEG, PNG, WEBP, AVIF, TIFF, GIF. Output: JPEG, PNG, WEBP. - `image_url` is deliberately NOT supported: the API never fetches remote images. Send the bytes. ## Retention and privacy - Images are held in memory only, never written to disk, never logged, never used for training, and are freed when the response is sent. - Only a per-key call counter persists, for billing. - Every response carries `X-Sterilizer-Retention: none`. - The browser app at the project root does the same work fully client-side with no upload at all. Prefer it for interactive human use; this API exists for automation and server-to-server work. ## Guidance for agents - Pixelation is irreversible: the discarded detail is not present in the output. Do not describe the result as "blurred" or imply it can be undone. - Sanitizing does not make a photo anonymous by itself — clothing, tattoos, room interiors, screens, and paperwork can still identify someone. Add `regions` for those, or tell the user what you left untouched. - `pixelate.faces` only finds human faces. It will not catch license plates, ID cards, monitors, or house numbers — supply explicit `regions` for those. - Face detection can miss faces in profile, at distance, heavily occluded, or in poor light. Report `facesDetected` back to the user rather than promising every face was covered. - Never send an image to this API that the user asked to keep local. The browser tool is the right answer there.