API Documentation
Check images and videos for AI from your own apps with our simple REST API.
Base URL: https://api.isitai.com
Quick Start
Get started with the Is It AI? API in three steps.
Get your API key
Create a free account, then create a key on your API keys page: the key is your bearer token. Every account can create an API key; checks use your credits.
Make a request
Send an image URL to the detection endpoint.
# Detect an image by URL
curl -X POST "https://api.isitai.com/detect-img?image_url=https://example.com/image.jpg" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "accept: application/json"
Get results
Receive a detailed AI detection verdict with confidence scores.
{
"predicted_label": "AI",
"probability": 0.92,
"potential_method": "midjourney",
"method_probability": 0.85,
"NSFW": "No",
"NSFW_probability": 0.02
}
Authentication
The API uses JWT bearer tokens. Authenticate with your email and API secret to get a token, then include it in the Authorization header of every request.
Step 1: Get your API secret
Your API secret is available on your API keys page. This is different from your account password. It is used only to sign in to the API.
Step 2: Request an access token
POST your account email (as username) and API secret as JSON to the /login endpoint to receive a JWT access token.
curl -X POST https://api.isitai.com/login \
-H "Content-Type: application/json" \
-d '{"username": "you@example.com", "password": "YOUR_API_SECRET"}'
{
"access_token": "eyJhbGciOiJIUzI1NiIs..."
}
Step 3: Use the token
Include the token in the Authorization header as a Bearer token on all subsequent requests.
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
/login again. You can also revoke a token early using the /revoke-token endpoint. /login allows 5 requests a minute.
Endpoint Reference
All endpoints use the base URL https://api.isitai.com.
POST /login
Authenticate and receive a JWT access token.
Headers
| Header | Value |
|---|---|
Content-Type | application/json |
JSON Body
| Parameter | Type | Required | Description |
|---|---|---|---|
username | string | Yes | Your account email address |
password | string | Yes | Your API secret (from the API keys page) |
Response
{
"access_token": "eyJhbGciOiJIUzI1NiIs..."
}
POST /detect-img
Analyze an image for AI-generated content. Supports two modes: image URL (query parameter) or file upload (multipart form).
Headers
| Header | Value |
|---|---|
Authorization | Bearer YOUR_ACCESS_TOKEN |
accept | application/json |
Option A: Image URL
| Parameter | In | Type | Description |
|---|---|---|---|
image_url | query | string | URL of the image to analyze |
curl -X POST "https://api.isitai.com/detect-img?image_url=https://example.com/photo.jpg" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "accept: application/json"
Option B: File Upload
| Parameter | In | Type | Description |
|---|---|---|---|
file | body (multipart) | file | Image file to analyze |
curl -X POST https://api.isitai.com/detect-img \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "accept: application/json" \
-F "file=@/path/to/image.jpg"
Response
{
"predicted_label": "AI",
"probability": 0.92,
"potential_method": "midjourney",
"method_probability": 0.85,
"NSFW": "No",
"NSFW_probability": 0.02
}
POST /detect-img/batch
Check up to 20 images in one request, 1 credit each. Upload them as files, or send image_urls as a comma-separated list. If one image cannot be read or downloaded, the whole request fails with 400 and nothing is charged. A failed image, or one you have no credits left for, is not charged. Match the results to your images by index.
| Parameter | In | Type | Description |
|---|---|---|---|
files | body (multipart) | file | Up to 20 images, each up to 10 MB (repeat the field) |
image_urls | query | string | Up to 20 image URLs, separated by commas |
curl -X POST https://api.isitai.com/detect-img/batch \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-F "files=@/path/to/first.jpg" \
-F "files=@/path/to/second.png"
{
"results": [
{ "index": 0, "success": true, "predicted_label": "AI", "probability": 0.92, ... },
{ "index": 1, "success": false, "error": "Monthly quota exhausted and no credits available", "code": "QUOTA_EXHAUSTED" }
],
"quota": { "monthly_limit": 200, "monthly_used": 200, "monthly_remaining": 0, "credits_remaining": 0 }
}
Face swap option (face_swap=true)
Adds the face swap check to an image check: did someone swap or fake a real person's face? The check uses 2 credits instead of 1. Only useful for images with people. Works the same on /detect-img/batch (2 credits per image).
| Parameter | In | Type | Description |
|---|---|---|---|
face_swap | query | boolean | true adds face_swap ("Yes" or "No") and face_swap_probability (0 to 1) to the answer. Default false. |
POST /detect-video
Checks a video shorter than 60 s (up to 50 MB; MP4, MOV, WebM, MKV or AVI) one frame every 2 seconds. Videos are analyzed one at a time, so the check goes into a queue: the answer is the result, or 202 with a job_id to poll. Price: 6 credits for each started 10 s (12 with face_swap=true), counted in whole seconds, rounded down. You can have 3 videos queued or running at a time; a fourth gets 429 TOO_MANY_VIDEO_JOBS.
| Parameter | In | Type | Description |
|---|---|---|---|
file | body (multipart) | file | The video, or use video_url |
video_url | query | string | A direct link to a video file (http or https) |
face_swap | query | boolean | Also run the face swap check (doubles the price) |
wait | query | integer | Seconds to wait for the result, 0 to 60 (default 30). Not done by then: 202 |
curl -X POST "https://api.isitai.com/detect-video?wait=30" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-F "file=@/path/to/clip.mp4"
Queued
{
"job_id": 48213,
"status": "queued",
"position": 2,
"duration_seconds": 23,
"poll_url": "/detect-video/48213",
"quota": { "credits_used": 18, ... }
}
The credits are taken when the job is queued and come back if it fails. Save the job_id at once.
GET /detect-video/{job_id}
Where a video check stands: 202 while it is queued or running (with its place in line), 200 with the result, 410 if the result was deleted (90 days after the check), or the stored error of a failed job (its credits were refunded). Poll every few seconds. You can read only your own jobs: any other job_id gets 404.
Result
{
"predicted_label": "AI",
"probability": 0.95,
"potential_method": "sora",
"NSFW": "No",
"frames_checked": 12,
"frames": [
{ "time_seconds": 0.0, "ai_probability": 0.08, "NSFW_probability": 0.01 },
{ "time_seconds": 2.0, "ai_probability": 0.95, "NSFW_probability": 0.01 },
...
],
"job_id": 48213,
"quota": { "credits_used": 18, ... }
}
The highest frame decides each verdict: one AI frame is enough to call the video AI-generated. With face_swap=true, the answer also has face_swap and face_swap_probability, and so does each frame.
GET /pricing
The price of every kind of check, in credits. No token needed.
{
"image": { "credits": 1, "with_face_swap": 2 },
"video": {
"credits_per_started_10_seconds": 6,
"face_swap_factor": 2,
"max_seconds": 60,
"max_megabytes": 50,
"examples": { "10s": 6, "30s": 18, "59s": 36, "30s_with_face_swap": 36 }
},
"notes": "Lengths are counted in whole seconds, rounded down. A failed detection does not use credits."
}
GET /consumption/
Retrieve the total number of credits your account has used, all time (website and API together).
Headers
| Header | Value |
|---|---|
Authorization | Bearer YOUR_ACCESS_TOKEN |
curl https://api.isitai.com/consumption/ \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Response
{
"username": "you@example.com",
"consumption": 142
}
POST /revoke-token
Revoke the token you send. After revocation, you will need a new key or a new token from /login.
Headers
| Header | Value |
|---|---|
Authorization | Bearer YOUR_ACCESS_TOKEN |
curl -X POST https://api.isitai.com/revoke-token \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Response
{
"message": "Token revoked successfully!"
}
Response Fields
The detection endpoint (/detect-img) returns the following fields.
| Field | Type | Description |
|---|---|---|
predicted_label |
string | "AI" or "Human" - the detection verdict |
probability |
number | Confidence score from 0 to 1 (e.g. 0.92 = 92% confidence) |
potential_method |
string | Most likely AI generator (e.g. "midjourney", "stable_diffusion", "dalle"). Only when the label is "AI" and a generator was named. |
method_probability |
number | Confidence score for the generator (0 to 1). Only with potential_method. |
NSFW |
string | "Yes" or "No" - whether the image contains NSFW content |
NSFW_probability |
number | Confidence score for the NSFW classification (0 to 1) |
face_swap, face_swap_probability |
string, number | With face_swap=true only: "Yes" or "No", and its chance from 0 to 1 |
verdict_source |
string | Only for a file with Content Credentials: "c2pa" when a trusted signed label said the image is AI-generated (the label decided; probability is 0.99), else "model" |
provenance |
object | Only for a file with Content Credentials: c2pa (always true), trusted, signed_by, generator, source_type, ai_generated, ai_edited. No label never means "human-made". |
quota |
object | credits_used (this check), source ("subscription", "credit" or "mixed": plan credits are used first, then bought credits), monthly_limit, monthly_used, monthly_remaining and credits_remaining (bought credits left) |
Error Handling
The API uses standard HTTP status codes. Errors include a JSON body with a message.
| Status | Meaning | Common Cause |
|---|---|---|
200 | Success | Request completed successfully |
202 | Accepted | A video is queued or running: poll its job_id |
400 | Bad Request | The file could not be read, the link failed or a parameter is missing, or the video is 60 s or longer. Not charged |
401 | Unauthorized | Missing, invalid, expired or revoked token |
403 | Forbidden | QUOTA_EXHAUSTED: no plan credits or bought credits left. INSUFFICIENT_CREDITS (with credits_required): not enough credits for a face swap check or a video |
404 | Not Found | No video job with this job_id for your account |
410 | Gone | A video result deleted 90 days after the check |
413 | Payload Too Large | The file is over its limit (images 10 MB, videos 50 MB) |
422 | Unprocessable Entity | A parameter is not valid, for example wait=90 |
429 | Too Many Requests | RATE_LIMITED: over 120 check requests a minute for your account (or 5 a minute on /login). Wait for Retry-After and retry |
429 | Too Many Requests | TOO_MANY_VIDEO_JOBS: you already have 3 videos queued or running. Wait until one is done |
500 | Internal Server Error | Unexpected error on our side - retry or contact support |
502 | Bad Gateway | The detection service is down for a moment. Try again shortly. Not charged |
503 | Service Busy | SERVICE_BUSY: the detection service is busy. Try again after Retry-After (10 s). Not charged |
Error Response Format
{
"detail": "Monthly quota exhausted and no credits available",
"code": "QUOTA_EXHAUSTED"
}
Every error has a detail message. Some also have a code you can check in your app, such as QUOTA_EXHAUSTED, RATE_LIMITED or SERVICE_BUSY.
Supported Formats & Limits
Image Types
- JPEG / JPG
- PNG
- WebP
- GIF (first frame)
- AVIF, BMP and TIFF
Video Types
- MP4 and MOV
- WebM and MKV
- AVI
Limits
- Max image size: 10 MB
- Videos: shorter than 60 s, up to 50 MB
- Batch: up to 20 images per request
- Up to 3 videos queued or running at a time
- Up to 120 check requests a minute per account
- Credits from your plan first, then bought credits
Best Practices
- Use images at least 256×256 px for best accuracy
- Avoid heavily compressed or cropped images
- Re-authenticate when you receive a 401 response
Code Examples
Full authentication + detection flow in popular languages.
Detect by Image URL
# 1. Get access token
TOKEN=$(curl -s -X POST https://api.isitai.com/login \
-H "Content-Type: application/json" \
-d '{"username": "you@example.com", "password": "YOUR_API_SECRET"}' \
| jq -r '.access_token')
# 2. Detect image
curl -X POST "https://api.isitai.com/detect-img?image_url=https://example.com/photo.jpg" \
-H "Authorization: Bearer $TOKEN" \
-H "accept: application/json"
import requests
# 1. Authenticate
auth = requests.post("https://api.isitai.com/login", json={
"username": "you@example.com",
"password": "YOUR_API_SECRET"
})
token = auth.json()["access_token"]
# 2. Detect by URL
response = requests.post(
"https://api.isitai.com/detect-img",
params={"image_url": "https://example.com/photo.jpg"},
headers={
"Authorization": f"Bearer {token}",
"accept": "application/json"
}
)
result = response.json()
print(f"Verdict: {result['predicted_label']} ({result['probability']:.0%} confidence)")
// 1. Authenticate
const authRes = await fetch("https://api.isitai.com/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username: "you@example.com", password: "YOUR_API_SECRET" })
});
const { access_token } = await authRes.json();
// 2. Detect by URL
const imageUrl = encodeURIComponent("https://example.com/photo.jpg");
const res = await fetch(
`https://api.isitai.com/detect-img?image_url=${imageUrl}`,
{
method: "POST",
headers: {
"Authorization": `Bearer ${access_token}`,
"accept": "application/json"
}
}
);
const result = await res.json();
console.log(`Verdict: ${result.predicted_label} (${(result.probability * 100).toFixed(0)}% confidence)`);
Detect by File Upload
curl -X POST https://api.isitai.com/detect-img \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "accept: application/json" \
-F "file=@/path/to/image.jpg"
import requests
token = "YOUR_ACCESS_TOKEN"
with open("image.jpg", "rb") as f:
response = requests.post(
"https://api.isitai.com/detect-img",
headers={
"Authorization": f"Bearer {token}",
"accept": "application/json"
},
files={"file": f}
)
print(response.json())
import { readFileSync } from "fs";
const token = "YOUR_ACCESS_TOKEN";
const formData = new FormData();
formData.append("file", new Blob([readFileSync("image.jpg")]), "image.jpg");
const res = await fetch("https://api.isitai.com/detect-img", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"accept": "application/json"
},
body: formData
});
console.log(await res.json());
API Pricing
Every account can create an API key; checks use your credits, the same as on the website: 1 per image (2 with the face swap check), 6 per started 10 s of video (12 with the face swap check). Your monthly plan credits are used first, then bought credits.
View Plans & PricingStart Building with Is It AI?
Every account can create an API key; checks use your credits. Create a key and start checking images and videos from your own apps.