API Reference
Forensic AI image detection over a single REST endpoint. Bearer-token auth, multipart upload, JSON response with per-detector breakdown. Sub-350ms typical latency.
Quickstart
Three steps from zero to your first detection:
- Create a free account at pixelprism.ai
- Generate an API key from your dashboard — keys start with
dsk_live_ - POST an image to
/api/detectwith your key in theAuthorizationheader
SHELLcurl -X POST https://pixelprism.ai/api/detect \
-H "Authorization: Bearer dsk_live_YOUR_KEY" \
-F "file=@/path/to/image.jpg"
You'll receive a JSON response within a few hundred milliseconds:
JSON{
"verdict": "AI_GENERATED",
"confidence": 0.9234,
"latency_ms": 312,
"signals": ["fft_artifact", "vit_artifact", "clip_artifact", "exif_artifact"],
"detector_scores": {
"fft": 0.92, "vit": 0.99, "vit2": 0.88, "vit3": 0.91,
"dire": 0.76, "clip": 0.95, "srm": 0.82,
"exif": 1.0, "face": 0.04
},
"meta_classifier_used": true
}
Authentication
Authenticate every request with an API key in the Authorization header using the Bearer scheme:
HTTPAuthorization: Bearer dsk_live_8f77305a8f2d4aa2ab947fe1cc409333…
API keys are issued from your dashboard and shown once at creation — store them in a secure place (environment variable, secret manager). We store only a hashed prefix; if you lose the full key, you'll need to generate a new one.
Key prefixes
| Prefix | Environment |
|---|---|
dsk_live_ | Production keys (real billing) |
dsk_test_ | Reserved for future test-mode keys (not yet supported — see changelog) |
Base URL
All API calls use HTTPS over the production base URL:
https://pixelprism.ai
HTTP requests are redirected to HTTPS. Plaintext is not supported.
POST /api/detect
Request
Send a multipart/form-data request with one form field named file:
| Field | Type | Required | Description |
|---|---|---|---|
file | file | yes | The image to analyze. JPEG, PNG, WebP, GIF, BMP. Max 25 MB. |
Headers
| Header | Required | Value |
|---|---|---|
Authorization | yes | Bearer dsk_live_… |
Content-Type | yes (set by client) | multipart/form-data; boundary=… |
Response
HTTP 200 with a JSON body — see Response shape below for the full schema.
GET /api/health
JSON{
"status": "ok",
"version": "0.8.0",
"meta_classifier_loaded": true,
"feature_order": ["fft", "vit", "vit2", "vit3", "dire", "clip", "srm", "exif", "face"]
}
Returns 503 with {"status":"backend_unreachable"} if the GPU detection backend is offline.
Response shape
| Field | Type | Description |
|---|---|---|
verdict | string | One of "AI_GENERATED" or "LIKELY_REAL" |
confidence | number 0–1 | P(AI-generated). The verdict flips at the 0.5 threshold. A confidence of 0.13 with verdict LIKELY_REAL means 87% confidence the image is real. |
latency_ms | integer | End-to-end inference latency on our backend |
signals | array of strings | Forensic flags raised — see Signals |
detector_scores | object | Per-detector P(AI) scores in [0,1] — see below |
skipped_detectors | array of strings | Detectors that returned -1 (e.g., face detector when no face found) |
meta_classifier_used | boolean | true when the trained meta-classifier produced the verdict; false if a weighted-fallback was used (rare, only during model load) |
Detector scores
Each detector outputs a probability in [0, 1] that the image is AI-generated. The meta-classifier combines all 9 detector outputs into the final confidence. Useful for explaining a verdict, debugging false positives, or targeting your own thresholds.
| Detector | Catches | Hardware · Latency |
|---|---|---|
fft | Frequency-domain artifacts (GAN/diffusion radial spectrum) | CPU · ~5ms |
vit | SDXL-class semantic artifacts (Organika fine-tune) | GPU · ~50ms |
vit2 | Mixed-generator artifacts (umm-maybe AI-image-detector) | GPU · ~50ms |
vit3 | Custom fine-tune on diverse training corpus | GPU · ~50ms |
dire | Diffusion-model reconstruction error (SD/DALL-E/Midjourney/FLUX) | GPU · ~200ms |
clip | CLIP-space Mahalanobis distance from real-photo reference | GPU · ~30ms |
srm | Camera sensor noise pattern absence (SRM filter bank) | CPU · ~10ms |
exif | EXIF / C2PA provenance metadata check | CPU · <1ms |
face | Face deepfake / face-swap (Xception). Returns a score only when a face is detected; otherwise omitted from response. | GPU · ~40ms |
Signals
Each detector raises a corresponding {name}_artifact signal when its score crosses a per-detector threshold. The signals array is a quick human-readable summary of which forensic systems flagged the image:
fft_artifact— frequency-domain anomalyvit_artifact,vit2_artifact,vit3_artifact— semantic / classifier hitsdire_artifact— diffusion reconstruction error above thresholdclip_artifact— out-of-distribution from real-photo referencesrm_artifact— sensor noise absenceexif_artifact— provenance signal (no EXIF, AI-tagged C2PA, etc.)face_artifact— face deepfake detected
Error responses
Errors return appropriate HTTP status codes with a JSON body of the form {"detail": "…"}:
| Status | When | How to handle |
|---|---|---|
400 | Malformed request (missing file, invalid format) | Fix the request |
401 | Missing or invalid Authorization header | Check your API key is correct and not revoked |
403 | API key revoked or account suspended | Generate a new key or contact support |
413 | Image larger than 25 MB | Resize before upload |
415 | Unsupported image format | Convert to JPEG/PNG/WebP |
429 | Quota exceeded (Free / Enterprise hard cap) or per-IP rate limit | Upgrade your plan or back off |
500 | Internal error | Retry with exponential backoff |
502 | Detection backend unreachable | Retry; check /api/health |
Rate limits & quotas
Per-tier monthly quota
| Tier | Monthly images | Detectors active | Overage |
|---|---|---|---|
| Free | 50 | 3 (FFT + ViT + EXIF) | none — hard cap at 50 |
| Starter | 2,000 | 5 | $0.001 / image |
| Pro | 25,000 | All 9 + meta-classifier | $0.0005 / image |
| Enterprise | Custom | All + custom fine-tunes | Volume pricing |
Concurrent request rate
Soft limit of 20 concurrent requests per API key. Beyond that, requests are queued briefly. Sustained bursts above 100 RPS may receive 429 responses; in that case back off using Retry-After.
Overage billing
Once a paid tier exceeds its monthly quota, additional requests are billed as metered overage through Stripe. Overage charges appear on your next invoice as a separate line item:
Detection overage: 247 imgs × $0.0005 = $0.1235
Free and Enterprise tiers do not overage — Free hard-caps at 50 and Enterprise is custom-contracted.
Webhooks
Webhook delivery (per-detection, retried with exponential backoff, signed with an HMAC) is a Pro-tier feature currently in development. When ready, you'll be able to register a callback URL from your dashboard and receive a POST for every detection your account performs.
UsageRecord table on Enterprise on-prem deployments. We'll announce in the changelog when webhooks go live.
Code samples
The same operation in four languages. All examples expect DEEPSCAN_API_KEY to be set in your environment.
SHELL# Single image
curl -X POST https://pixelprism.ai/api/detect \
-H "Authorization: Bearer $DEEPSCAN_API_KEY" \
-F "file=@/path/to/image.jpg"
# Pretty-print + extract verdict using jq
curl -s -X POST https://pixelprism.ai/api/detect \
-H "Authorization: Bearer $DEEPSCAN_API_KEY" \
-F "file=@image.jpg" | jq '{verdict, confidence, signals}'
# Loop a folder with concurrency-2 (xargs)
ls *.jpg | xargs -P 2 -I {} curl -s -X POST https://pixelprism.ai/api/detect \
-H "Authorization: Bearer $DEEPSCAN_API_KEY" \
-F "file=@{}"
PYTHON 3.10+import os
import httpx
API_KEY = os.environ["DEEPSCAN_API_KEY"]
BASE = "https://pixelprism.ai"
def detect(image_path: str) -> dict:
"""POST an image to /api/detect and return the parsed JSON response."""
with open(image_path, "rb") as f:
files = {"file": (os.path.basename(image_path), f.read(), "image/jpeg")}
r = httpx.post(
f"{BASE}/api/detect",
headers={"Authorization": f"Bearer {API_KEY}"},
files=files,
timeout=120.0,
)
r.raise_for_status()
return r.json()
if __name__ == "__main__":
result = detect("my-image.jpg")
print(f"verdict={result['verdict']}, P(AI)={result['confidence']:.3f}")
print("signals:", result['signals'])
NODE 18+import fs from "fs/promises";
const API_KEY = process.env.DEEPSCAN_API_KEY;
const BASE = "https://pixelprism.ai";
async function detect(imagePath) {
const bytes = await fs.readFile(imagePath);
const form = new FormData();
form.append("file", new Blob([bytes], { type: "image/jpeg" }), imagePath.split("/").pop());
const r = await fetch(`${BASE}/api/detect`, {
method: "POST",
headers: { Authorization: `Bearer ${API_KEY}` },
body: form,
});
if (!r.ok) throw new Error(`${r.status} ${await r.text()}`);
return r.json();
}
const result = await detect("my-image.jpg");
console.log(`verdict=${result.verdict}, P(AI)=${result.confidence.toFixed(3)}`);
console.log("signals:", result.signals);
BROWSER JS// Browser code MUST go through your own backend proxy — never embed
// API keys in client-side JS where users can read them.
// Below: client posts to YOUR /detect endpoint; YOUR backend forwards
// to DeepScan with the secret key.
async function detectFromBrowser(file) {
const form = new FormData();
form.append("file", file);
const r = await fetch("/your-backend/detect", {
method: "POST",
body: form,
credentials: "same-origin",
});
return r.json();
}
// Wire up to a file input
document.querySelector("#file").addEventListener("change", async (e) => {
const file = e.target.files[0];
if (!file) return;
const result = await detectFromBrowser(file);
console.log("verdict:", result.verdict, "P(AI):", result.confidence);
});
Best practices
- Never trust a single detector. Use the meta-classifier
verdict+confidenceas your primary signal. The per-detector scores are explanatory, not authoritative. - Use the verdict field, not custom thresholds, unless you have a reason. The 0.5 threshold has been calibrated against a held-out canary set. If you need higher precision (fewer false positives), consider acting only on
confidence ≥ 0.85; for higher recall,≥ 0.30. Document whatever choice you make. - Resize aggressively before upload. Detection accuracy plateaus past ~1024 pixels on the long edge. We auto-downscale on our side, but the network round-trip is faster if you've shrunk the image first.
- Keep originals when verdicts matter. If you're going to act on a result (warn a user, reject content), keep the original image bytes for at least 30 days so you can re-check after our next detector retraining.
- Handle 429s with exponential backoff. Start at 1s, double on each retry, cap at 60s.
- Cache verdicts on idempotent inputs. The same image bytes produce the same verdict (until our model retrains). A SHA-256 → verdict cache in your application avoids re-billing.
Changelog
| Version | Date | Notes |
|---|---|---|
| 0.8.0 | 2026-05-01 | 9-detector ensemble live (added vit3); meta-classifier retrained on diverse corpus including DALL-E 3, Imagen 3, Nano Banana, FLUX, SD 3.5, Recraft, Grok, Ideogram |
| 0.7.0 | 2026-04-26 | Added vit2; meta-classifier fusion launched |
| 0.6.0 | 2026-04-23 | EXIF / C2PA provenance check added |
| 0.5.0 | 2026-04-15 | Initial 7-detector ensemble (FFT + ViT + DIRE + CLIP + SRM + Face + EXIF) |
Support
Email: support@pixelprism.ai
Status: /api/health (live)
Bug reports: include the request timestamp + a sample image, and the request body if available.
SLA on response time: best-effort within 24 business hours for Free / Starter; same-day for Pro; contractual for Enterprise.