API Documentation

Check images, videos and music 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.

1

Get your API key

Create an 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.

2

Make a request

Send an image URL to the detection endpoint.

Request
# 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"
3

Get results

Receive a detailed AI detection verdict with confidence scores.

Response
{
  "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.

Get Token
curl -X POST https://api.isitai.com/login \
  -H "Content-Type: application/json" \
  -d '{"username": "you@example.com", "password": "YOUR_API_SECRET"}'
Token Response
{
  "access_token": "eyJhbGciOiJIUzI1NiIs..."
}

Step 3: Use the token

Include the token in the Authorization header as a Bearer token on all subsequent requests.

Header
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Token lifecycle: Tokens last about 90 days. When one expires, create a new key on your API keys page or call /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

HeaderValue
Content-Typeapplication/json

JSON Body

ParameterTypeRequiredDescription
usernamestringYesYour account email address
passwordstringYesYour API secret (from the API keys page)

Response

200 OK
{
  "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). One input per request: a file or a URL, never both (400).

Headers

HeaderValue
AuthorizationBearer YOUR_ACCESS_TOKEN
acceptapplication/json

Option A: Image URL

ParameterInTypeDescription
image_urlquerystringURL of the image to analyze
cURL (URL mode)
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

ParameterInTypeDescription
filebody (multipart)fileImage file to analyze
cURL (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"

Response

200 OK
{
  "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.

ParameterInTypeDescription
filesbody (multipart)fileUp to 20 images, each up to 10 MB (repeat the field)
image_urlsquerystringUp to 20 image URLs, separated by commas
cURL (batch upload)
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"
200 OK
{
  "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).

ParameterInTypeDescription
face_swapquerybooleantrue 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. Send a file or a video_url, never both (400).

ParameterInTypeDescription
filebody (multipart)fileThe video, or use video_url
video_urlquerystringA direct link to a video file (http or https)
face_swapquerybooleanAlso run the face swap check (doubles the price)
waitqueryintegerSeconds to wait for the result, 0 to 60 (default 30). Not done by then: 202
cURL
curl -X POST "https://api.isitai.com/detect-video?wait=30" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -F "file=@/path/to/clip.mp4"

Queued

202 Accepted
{
  "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

200 OK
{
  "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.

Optional parts of a video result:

FieldTypeDescription
audioobjectOnly when the soundtrack is probably AI music (ai_music_probability over 0.5): predicted_label, ai_music_probability, music_verdict, potential_method, analyzed_seconds and chunks, as on /detect-audio. A hint, not a verdict: speech can set it off, and it never changes the video's verdict. No audio part does not mean the music is human-made
verdict_source, provenancestring, objectA video can carry Content Credentials too (MP4, MOV and AVI are read). verdict_source is "c2pa" when a trusted signed label said it is AI-generated: the label decided, and every frame has ai_probability: null (no AI score). NSFW and face swap answers are still there
metadataobjectThe unsigned metadata label, the same as for images

GET /pricing

The price of every kind of check, in credits. No token needed. audio is the music check: 1 credit per started minute, at most 5.

200 OK
{
  "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 }
  },
  "audio": {
    "credits_per_started_minute": 1,
    "max_megabytes": 20,
    "examples": { "30s": 1, "3m20s": 4 },
    ...
  },
  "notes": "Lengths are counted in whole seconds, rounded down. A failed detection does not use credits."
}

POST /detect-audio

Checks whether a music track was made with AI, with a score for every 10 seconds and separate scores for vocals and instruments. Made for music: not for speech, podcasts or voice clones. Send a file or an audio_url, never both (400). Files: MP3, WAV, FLAC, OGG, OPUS, M4A or MP4 audio (no WebM), 10 s or longer, up to 20 MB. Only the first 300 s are checked. Price: 1 credit for each started minute, at most 5, counted in whole seconds, rounded down. The answer comes back in the same request.

ParameterInTypeDescription
filebody (multipart)fileThe track, or use audio_url
audio_urlquerystringA direct link to an audio file (http or https)
cURL
curl -X POST https://api.isitai.com/detect-audio \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -F "file=@/path/to/song.mp3"

Result

200 OK
{
  "predicted_label": "AI",
  "probability": 0.92,
  "ai_music_probability": 0.92,
  "music_verdict": "pure_ai",
  "potential_method": "suno",
  "vocal_ai_probability": 0.75,
  "instrumental_ai_probability": 0.94,
  "analyzed_seconds": 200,
  "duration_seconds": 200,
  "chunks": [
    { "start_seconds": 0, "ai_music_probability": 0.88, "pure_ai_probability": 0.81 },
    { "start_seconds": 10, "ai_music_probability": 0.91, "pure_ai_probability": 0.84 },
    ...
  ],
  "quota": { "credits_used": 4, ... }
}
FieldTypeDescription
predicted_label, probabilitystring, number"AI" or "Human", and the confidence from 0 to 1
ai_music_probabilitynumberThe chance the music is AI-made, 0 to 1
music_verdictstring or null"pure_ai" (straight from an AI tool), "processed_ai" (made with AI, then edited), "human", or null when it cannot tell
potential_methodstringThe likely tool, such as "suno", "udio", "mureka" or "elevenlabs_music". Only when the label is "AI"
vocal_ai_probability, instrumental_ai_probabilitynumber or nullSeparate scores for the voice and the music, 0 to 1. null when the track could not be split: that is not a "human" score
analyzed_seconds, duration_secondsnumberHow much was checked (at most 300) and how long the track is
chunksarrayOne score per 10 s: start_seconds and ai_music_probability (0 to 1). Can be empty
quotaobjectThe same as for images: credits_used (this check), source and what is left
verdict_source, provenancestring, objectOnly for a file with Content Credentials (MP3, WAV, FLAC and M4A are read): the same as for images
metadataobjectOnly when the file's own tags name AI: the same unsigned label as for images. Never changes the verdict

Errors

StatusWhen
400A file and an audio_url together, a file that cannot be read, a format that is not supported, or a track shorter than 10 s. Not charged
403INSUFFICIENT_CREDITS (with credits_required): not enough credits for this track
413The file is over 20 MB
429RATE_LIMITED: over 120 check requests a minute for your account. Wait for Retry-After
502The music service failed. Your credits come back
503SERVICE_BUSY: try again after Retry-After. Not charged

GET /consumption/

Retrieve the total number of credits your account has used, all time (website and API together).

Note: The trailing slash is required for this endpoint.

Headers

HeaderValue
AuthorizationBearer YOUR_ACCESS_TOKEN
cURL
curl https://api.isitai.com/consumption/ \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Response

200 OK
{
  "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

HeaderValue
AuthorizationBearer YOUR_ACCESS_TOKEN
cURL
curl -X POST https://api.isitai.com/revoke-token \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Response

200 OK
{
  "message": "Token revoked successfully!"
}

Response Fields

The detection endpoint (/detect-img) returns the following fields.

FieldTypeDescription
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 What decided the verdict. "c2pa": a trusted Content Credentials label says the file was made or edited with AI. "metadata": an unsigned metadata label says ai_generated or ai_edited, or a software or encoder tag names a known AI tool (ai_tool, for example Suno in an MP3 or Sora in an MP4; only a fixed list of distinctive names counts, never free text). In both cases predicted_label is "AI" and probability is 0.99, which comes from the label, not a model score. "model" (or absent): our model decided. No label leaves the verdict to the model. Images, videos and music follow the same rules: a video decided by its label has ai_probability: null on every frame, and a song keeps its music scores, which can disagree with the label. So a real photo with one object erased by a phone's AI eraser reads "AI" when its label says so: show your users why.
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". For an AI edit, potential_method is the edit's tool as the file names it (for example "photo_assist").
metadata object Only when the file's own metadata (such as XMP or EXIF) names AI. Not signed, so anyone can add or remove it. When it says ai_generated or ai_edited, or names a known AI tool (ai_tool), it decides the verdict (verdict_source "metadata"): says ("ai_generated", "ai_edited" or "ai_tool"), generator (the tool it names, if any) and found_in (where in the file it was found)
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.

StatusMeaningCommon Cause
200SuccessRequest completed successfully
202AcceptedA video is queued or running: poll its job_id
400Bad RequestThe file could not be read, the link failed, a parameter is missing, or you sent both a file and a URL (one input per request), or the video is 60 s or longer, or the track is shorter than 10 s. Not charged
401UnauthorizedMissing, invalid, expired or revoked token
403ForbiddenQUOTA_EXHAUSTED: no plan credits or bought credits left. INSUFFICIENT_CREDITS (with credits_required): not enough credits for a face swap check, a video or a music track
404Not FoundNo video job with this job_id for your account
410GoneA video result deleted 90 days after the check
413Payload Too LargeThe file is over its limit (images 10 MB, videos 50 MB, music 20 MB)
422Unprocessable EntityA parameter is not valid, for example wait=90
429Too Many RequestsRATE_LIMITED: over 120 check requests a minute for your account (or 5 a minute on /login). Wait for Retry-After and retry
429Too Many RequestsTOO_MANY_VIDEO_JOBS: you already have 3 videos queued or running. Wait until one is done
500Internal Server ErrorUnexpected error on our side - retry or contact support
502Bad GatewayThe detection service is down for a moment. Try again shortly. Not charged
503Service BusySERVICE_BUSY: the detection service is busy. Try again after Retry-After (10 s). Not charged

Error Response Format

Error Response
{
  "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

Music Types

  • MP3, WAV and FLAC
  • OGG and OPUS
  • M4A and MP4 audio (no WebM)

Limits

  • Max image size: 10 MB
  • Videos: shorter than 60 s, up to 50 MB
  • Music: 10 s or longer, up to 20 MB; the first 300 s are checked
  • One input per request: a file or a URL, never both
  • 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

cURL
# 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"
Python
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)")
Node.js
// 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
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"
Python
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())
Node.js
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), 1 per started minute of music (at most 5). Your monthly plan credits are used first, then bought credits.

Using Claude Code or Cursor? Connect Is It AI? to your AI assistant with the same API key.

View Plans & Pricing

Start Building with Is It AI?

Every account can create an API key; checks use your credits. Create a key and start checking images, videos and music from your own apps.