●Docs · API v1 · Early access

API reference.

One endpoint. Send an image by file or URL and get back a verdict, Likely AI or Likely real, with a confidence score and provenance signals.

REST + JSONImages by file or URLZero retention
POST /api/v1/scan200 OK
# Scan an image by URL
curl https://witness.vision/api/v1/scan \
  -H "Authorization: Bearer $WITNESS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://…/claim_photo_48213.jpg"}'

# Response
{
  "id": "6f1c2b0e…9b31",
  "verdict": "likely_ai",
  "label": "Likely AI",
  "score": 0.93,
  "confidence": 0.93,
  "face_detected": false,
  "signals": {
    "c2pa": false,
    "synthid": false,
    "exif": { "present": false }
  }
}

●Authentication

Bearer keys.

Every request needs an API key in the Authorization header. Keys start with wk_live_. Keep them server-side: never ship a key in browser or mobile code. To get a key, contact us.

Authorization: Bearer wk_live_...

●Endpoint

POST /scan

Upload a file as multipart form data, in a field named file:

curl https://witness.vision/api/v1/scan \
  -H "Authorization: Bearer $WITNESS_API_KEY" \
  -F "file=@photo.jpg"

Or send a public URL as JSON. Links to posts on social platforms work too; we resolve the media for you.

curl https://witness.vision/api/v1/scan \
  -H "Authorization: Bearer $WITNESS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/photo.jpg"}'

Accepted: JPEG, PNG, WebP and GIF up to 20 MB. Direct uploads are capped at about 4 MB by our hosting; send larger images by URL. Video and audio are coming.

●Response

What comes back.

{
  "id": "6f1c2b0e-4a8d-4b55-9d1e-2f0c8a7d9b31",
  "object": "scan",
  "modality": "image",
  "verdict": "likely_ai",
  "label": "Likely AI",
  "ai_type": "ai_generated",
  "score": 0.9731,
  "confidence": 0.9731,
  "face_detected": true,
  "explanation": "Witness found strong generation signatures.",
  "signals": {
    "c2pa": false,
    "synthid": false,
    "exif": { "present": false, "camera_make": null, "camera_model": null, "software": null }
  },
  "created_at": "2026-11-12T15:04:05.000Z"
}
FieldMeaning
verdictlikely_ai or likely_real.
labelThe same verdict as display text: Likely AI or Likely real.
ai_typeai_generated or face_swap when the verdict is Likely AI, otherwise null.
scoreAI likelihood from 0 to 1. Higher means more likely AI.
confidenceHow confident the verdict is, 0 to 1.
face_detectedWhether a face was found. Face-swap checks run when one is.
signals.c2paContent Credentials present on the file.
signals.synthidAn invisible AI watermark was found.
signals.exifCamera metadata, when present.

Results are probabilistic. Use them as one input to a decision, not the whole decision.

●Endpoint

GET /usage

Check how much of your quota is left.

curl https://witness.vision/api/v1/usage -H "Authorization: Bearer $WITNESS_API_KEY"

{
  "object": "usage",
  "key": { "name": "my-app", "prefix": "wk_live_Ab3x", "expires_at": null },
  "window": "rolling_30_days",
  "images": { "used": 42, "limit": 500, "remaining": 458 },
  "rate_limit_per_minute": 10
}

●Limits

Per key, per minute and per month.

Each key has a per-minute limit and a scan quota over a rolling 30 days. Every /scan response carries these headers:

HeaderMeaning
X-RateLimit-LimitScans allowed per minute.
X-RateLimit-RemainingScans left this minute.
X-Quota-LimitScans allowed per 30 days.
X-Quota-RemainingScans left in the window.

Failed scans do not count against your quota.

●Errors

When something goes wrong.

Errors return JSON in one shape:

{ "error": { "code": "quota_exceeded", "message": "..." } }
HTTPcodeMeaning
400missing_file · invalid_url · invalid_mediaNo file sent, a bad URL, or an image that could not be decoded.
401invalid_key · key_revoked · key_expiredThe key is missing, unknown, revoked or past its expiry.
413too_largeImage over 20 MB. Use a URL for anything over about 4 MB.
415unsupported_mediaNot JPEG, PNG, WebP or GIF, or the wrong request content type.
429rate_limited · quota_exceededLimit reached. See Retry-After and X-Quota-Remaining.
502fetch_failed · upstream_errorWe could not fetch the URL, or detection failed. Safe to retry.
503capacityThe API is temporarily at capacity.
504upstream_timeoutDetection took too long. Safe to retry.

●Examples

Python and Node.

Python

import os, requests

API = "https://witness.vision/api/v1"
headers = {"Authorization": "Bearer " + os.environ["WITNESS_API_KEY"]}

with open("photo.jpg", "rb") as f:
    r = requests.post(API + "/scan", headers=headers, files={"file": f}, timeout=60)
r.raise_for_status()
scan = r.json()
print(scan["verdict"], scan["confidence"])

Node.js

const API = "https://witness.vision/api/v1";
const res = await fetch(API + "/scan", {
  method: "POST",
  headers: {
    Authorization: "Bearer " + process.env.WITNESS_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ url: "https://example.com/photo.jpg" }),
});
if (!res.ok) throw new Error((await res.json()).error.message);
const scan = await res.json();
console.log(scan.verdict, scan.confidence);

●MCP server

Scan from your AI chat.

Connect Witness to Claude, ChatGPT, Cursor or your own agent and check images in the conversation. Tools: scan_image (by URL or base64) and check_usage.

Sign in (recommended)

Add this URL as a custom connector. Your app opens a Witness sign-in page; log in and allow the connection. Scans count toward your Witness plan, the same as on the website.

https://witness.vision/api/mcp

In Claude: Settings, Connectors, Add custom connector. In ChatGPT: Settings, Connectors, Create. In Claude Code:

claude mcp add --transport http witness https://witness.vision/api/mcp

With an API key

For agents and servers with no one to sign in. Scans count toward the key's quota.

{
  "mcpServers": {
    "witness": {
      "url": "https://witness.vision/api/mcp",
      "headers": { "Authorization": "Bearer wk_live_..." }
    }
  }
}

Images uploaded into the chat

Chat apps don’t pass uploaded files to connectors yet, so Witness can’t see them. Paste a link to the image or post instead, or upload the file at witness.vision/scan.

Connection details

OAuth 2.1 with PKCE (S256) and dynamic client registration. Discovery: /.well-known/oauth-protected-resource and /.well-known/oauth-authorization-server. Access tokens last one hour; refresh tokens rotate.