Detect AI-Generated Images
The Copyleaks AI Image Detection API is a powerful tool to determine if a given image was generated or partially generated by an AI. The API is synchronous, meaning you get the results in the same API call.
This guide will walk you through the process of submitting an image for AI detection and understanding the results.
🚀 Get Started
Section titled “🚀 Get Started”-
Before you begin
Section titled “Before you begin”Before you start, ensure you have the following:
- An active Copyleaks account. If you don’t have one, sign up for free.
- You can find your API key on the API Dashboard.
-
Installation
Section titled “Installation”Choose your preferred method for making API calls.
You can interact with the API using any standard HTTP client.
For a quicker setup, we provide a Postman collection. See our Postman guide for instructions.
Terminal window sudo apt-get install curlDownload it from curl.se
Terminal window brew install curlTerminal window pip install copyleaksTerminal window npm install plagiarism-checker -
To perform a scan, we first need to generate an access token. For that, we will use the login endpoint. The API key can be found on the Copyleaks API Dashboard.
Upon successful authentication, you will receive a token that must be attached to subsequent API calls via the Authorization: Bearer
<TOKEN>header. This token remains valid for 48 hours.POST https://id.copyleaks.com/v3/account/login/apiHeadersContent-Type: application/jsonBody{"key": "00000000-0000-0000-0000-000000000000"}Terminal window export COPYLEAKS_API_KEY="your-api-key-here"curl --request POST \--url https://id.copyleaks.com/v3/account/login/api \--header 'Accept: application/json' \--header 'Content-Type: application/json' \--data "{\"email\": \"${COPYLEAKS_EMAIL}\",\"key\": \"${COPYLEAKS_API_KEY}\"}"from copyleaks.copyleaks import CopyleaksAPI_KEY = "your-api-key-here"# Login to Copyleaksauth_token = Copyleaks.login(EMAIL_ADDRESS, API_KEY)print("Logged successfully!\nToken:", auth_token)const { Copyleaks } = require("plagiarism-checker");const API_KEY = "your-api-key-here";const copyleaks = new Copyleaks();// Login functionfunction loginToCopyleaks() {return copyleaks.loginAsync(EMAIL_ADDRESS, API_KEY).then((loginResult) => {console.log("Login successful!");console.log("Access Token:", loginResult.access_token);return loginResult;},(err) => {console.error('Login failed:', err);throw err;});}loginToCopyleaks();import com.copyleaks.sdk.api.Copyleaks;String API_KEY = "00000000-0000-0000-0000-000000000000";// Login to Copyleakstry {String authToken = Copyleaks.login(EMAIL_ADDRESS, API_KEY);System.out.println("Logged successfully!\nToken: " + authToken);} catch (CommandException e) {System.out.println("Failed to login: " + e.getMessage());System.exit(1);}Response
{"access_token": "<ACCESS_TOKEN>",".issued": "2025-07-31T10:19:40.0690015Z",".expires": "2025-08-02T10:19:40.0690016Z"} -
Submit for Analysis
Section titled “Submit for Analysis”Use the AI Image Detector Endpoint to send an image for analysis. We suggest you to provide a unique
scanIdfor each submission.Image Requirements
Section titled “Image Requirements”- Minimum 512×512px, maximum 16 megapixels, less than 32MB
- Supported formats:
PNG,JPG,JPEG,BMP,WebP,HEIC/HEIF
POST https://api.copyleaks.com/v1/ai-image-detector/my-image-scan-1/checkHeadersAuthorization: Bearer <YOUR_AUTH_TOKEN>Content-Type: application/jsonBody{"base64": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJ...","filename": "test-image.png","sandbox": true,"model": "ai-image-1-ultra"}Terminal window curl -X POST "https://api.copyleaks.com/v1/ai-image-detector/my-image-scan-1/check" \-H "Authorization: Bearer <YOUR_AUTH_TOKEN>" \-H "Content-Type: application/json" \-d '{"base64": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJ...","filename": "test-image.png","sandbox": true,"model": "ai-image-1-ultra"}'import base64import requests# Read and encode the imagewith open('test-image.png', 'rb') as image_file:base64_image = base64.b64encode(image_file.read()).decode('utf-8')# Prepare the requesturl = 'https://api.copyleaks.com/v1/ai-image-detector/my-image-scan-1/check'headers = {'Authorization': 'Bearer YOUR_LOGIN_TOKEN','Content-Type': 'application/json'}payload = {'base64': base64_image,'filename': 'test-image.png','sandbox': True,'model': 'ai-image-1-ultra'}# Send the requestresponse = requests.post(url, json=payload, headers=headers)result = response.json()print(f"AI Detection Summary: {result['summary']}")const imageFile = document.getElementById('fileInput').files[0];const reader = new FileReader();reader.onload = async function(e) {const base64Data = e.target.result.split(',')[1];const response = await fetch('https://api.copyleaks.com/v1/ai-image-detector/my-image-scan-1/check', {method: 'POST',headers: {'Authorization': 'Bearer YOUR_LOGIN_TOKEN','Content-Type': 'application/json'},body: JSON.stringify({base64: base64Data,filename: imageFile.name,sandbox: true,model: 'ai-image-1-ultra'})});const result = await response.json();console.log('AI Detection Result:', result);};reader.readAsDataURL(imageFile);import java.io.IOException;import java.net.URI;import java.net.http.HttpClient;import java.net.http.HttpRequest;import java.net.http.HttpResponse;import java.nio.file.Files;import java.nio.file.Paths;import java.util.Base64;public class AiImageDetection {public static void main(String[] args) throws IOException, InterruptedException {String authToken = "YOUR_LOGIN_TOKEN";String imagePath = "path/to/your/test-image.png";String scanId = "my-java-scan-1";byte[] imageBytes = Files.readAllBytes(Paths.get(imagePath));String base64Image = Base64.getEncoder().encodeToString(imageBytes);// Using a simple String.format for the JSON payload.// For more complex scenarios, a JSON library like Gson or Jackson is recommended.String jsonPayload = String.format("{\"base64\": \"%s\", \"filename\": \"test-image.png\", \"sandbox\": true, \"model\": \"ai-image-1-ultra\"}",base64Image);HttpClient client = HttpClient.newHttpClient();HttpRequest request = HttpRequest.newBuilder().uri(URI.create("https://api.copyleaks.com/v1/ai-image-detector/" + scanId + "/check")).header("Authorization", "Bearer " + authToken).header("Content-Type", "application/json").POST(HttpRequest.BodyPublishers.ofString(jsonPayload)).build();HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());System.out.println("Status Code: " + response.statusCode());System.out.println("Response Body: " + response.body());}} -
Interpreting The Response
Section titled “Interpreting The Response”The API response contains:
- A
summaryobject with the overall percentage of AI vs. human pixels - A
resultobject with a Run-Length Encoded (RLE) mask imageInfowith the image dimensions and metadata (when available)scannedDocumentwith scan details including credits used
Understanding the RLE Mask
Section titled “Understanding the RLE Mask”Run-Length Encoding (RLE) is a compression method used to represent the AI-detected regions of the image efficiently. It provides an array of
startspositions andlengthsfor each run of AI-detected pixels in a flattened 1D version of the image.You can decode this RLE data to create a binary mask. Below are implementations in different languages:
def decode_mask(rle_data, image_width, image_height):"""Decode RLE mask data into a binary mask array.Args:rle_data (dict): Dictionary with 'starts' and 'lengths' arraysimage_width (int): Width of the image in pixelsimage_height (int): Height of the image in pixelsReturns:list: A 1D array where 1 represents AI-detected pixels"""total_pixels = image_width * image_heightmask = [0] * total_pixelsstarts = rle_data.get('starts', [])lengths = rle_data.get('lengths', [])for i in range(len(starts)):start = starts[i]length = lengths[i]for j in range(length):position = start + jif position < total_pixels:mask[position] = 1return mask# Example usage:# result = response.json()# binary_mask = decode_mask(# result['result'],# result['imageInfo']['shape']['width'],# result['imageInfo']['shape']['height']# )function decodeMask(rleData, imageWidth, imageHeight) {const totalPixels = imageWidth * imageHeight;const mask = new Array(totalPixels).fill(0);const starts = rleData.starts || [];const lengths = rleData.lengths || [];for (let i = 0; i < starts.length; i++) {const start = starts[i];const length = lengths[i];for (let j = 0; j < length; j++) {const position = start + j;if (position < totalPixels) {mask[position] = 1;}}}return mask;}// Example usage:// const { result, imageInfo } = await response.json();// const binaryMask = decodeMask(result, imageInfo.shape.width, imageInfo.shape.height);/*** Decodes RLE mask data into a binary mask array** @param rleMask The RLE mask with starts and lengths arrays* @param width Image width in pixels* @param height Image height in pixels* @return Binary mask where true represents AI-detected pixels*/public static boolean[] decodeMask(RleMask rleMask, int width, int height) {int totalPixels = width * height;boolean[] mask = new boolean[totalPixels];if (rleMask == null || rleMask.starts() == null || rleMask.lengths() == null) {return mask;}for (int i = 0; i < rleMask.starts().length; i++) {int start = rleMask.starts()[i];int length = rleMask.lengths()[i];for (int j = 0; j < length; j++) {int position = start + j;if (position < totalPixels) {mask[position] = true;}}}return mask;}// Example usage:// Response contains: { "result": { "starts": [0, 512...], "lengths": [256, 512...] }, "imageInfo": {...} }// boolean[] binaryMask = decodeMask(// new RleMask(result.getJSONObject("result").getJSONArray("starts"), result.getJSONObject("result").getJSONArray("lengths")),// result.getJSONObject("imageInfo").getJSONObject("shape").getInt("width"),// result.getJSONObject("imageInfo").getJSONObject("shape").getInt("height")// );The resulting binary mask is an array where a
1(ortruein Java) represents an AI-detected pixel. You can use this mask to create a visual overlay on the original image.Creating a Visual Overlay
Section titled “Creating a Visual Overlay”After decoding the RLE data, you can use the resulting mask to draw a semi-transparent overlay on the original image. Here are some examples of how to achieve this:
# Requires: pip install Pillowfrom PIL import Imageimport numpy as npdef apply_overlay(image_path, mask_array, output_path):"""Apply a red (1) and green (0) overlay to the image and save the result.Args:image_path (str): Path to the original imagemask_array (np.ndarray): 2D numpy array with 1 (red) and 0 (green)output_path (str): Path to save the output image"""height, width = mask_array.shapeoriginal_img = Image.open(image_path).convert('RGBA')overlay = Image.new('RGBA', (width, height), (0, 0, 0, 0))overlay_pixels = overlay.load()for y in range(height):for x in range(width):if mask_array[y, x] == 1:overlay_pixels[x, y] = (255, 0, 0, 120) # Red, semi-transparentelse:overlay_pixels[x, y] = (0, 255, 0, 120) # Green, semi-transparentresult_img = Image.alpha_composite(original_img, overlay)result_img.save(output_path)# Usage example:width = result['imageInfo']['shape']['width']height = result['imageInfo']['shape']['height']mask_array = np.array(binary_mask, dtype=np.uint8).reshape((height, width))apply_overlay('test-image.png', mask_array, 'output-with-overlay.png')// Assumes 'decodeMask' function from above is available/*** Creates a canvas with the original image and an overlay showing AI vs human regions* @param {HTMLImageElement} imageElement - The image element to overlay* @param {Object} rleData - The RLE mask data with starts and lengths arrays* @returns {HTMLCanvasElement} Canvas with the original image and overlay*/function createOverlay(imageElement, rleData) {const canvas = document.createElement('canvas');const ctx = canvas.getContext('2d');const width = imageElement.width;const height = imageElement.height;canvas.width = width;canvas.height = height;// Draw original imagectx.drawImage(imageElement, 0, 0);// Get the binary maskconst binaryMask = decodeMask(rleData, width, height);// Create an ImageData object to manipulate pixels directlyconst imageData = ctx.getImageData(0, 0, width, height);const data = imageData.data;// Apply overlay for each pixelfor (let i = 0; i < binaryMask.length; i++) {const pixelIndex = i * 4; // RGBA data has 4 values per pixelif (binaryMask[i] === 1) {// AI-generated area (red overlay)data[pixelIndex] = data[pixelIndex] * 0.7 + 255 * 0.3; // Rdata[pixelIndex + 1] = data[pixelIndex + 1] * 0.7; // Gdata[pixelIndex + 2] = data[pixelIndex + 2] * 0.7; // Bdata[pixelIndex + 3] = 255; // A} else {// Human-generated area (green overlay)data[pixelIndex] = data[pixelIndex] * 0.7; // Rdata[pixelIndex + 1] = data[pixelIndex + 1] * 0.7 + 255 * 0.3; // Gdata[pixelIndex + 2] = data[pixelIndex + 2] * 0.7; // Bdata[pixelIndex + 3] = 255; // A}}// Put the modified image data back on the canvasctx.putImageData(imageData, 0, 0);return canvas;}// Example usage:function displayImageWithOverlay(imagePath, apiResult) {// Create image elementconst img = new Image();img.crossOrigin = "Anonymous";// When image loads, create and display the overlayimg.onload = () => {// Get image dimensions from the API resultconst width = apiResult.imageInfo.shape.width;const height = apiResult.imageInfo.shape.height;// Create the overlay canvasconst canvas = createOverlay(img, apiResult.result);// Add to page and optionally downloaddocument.body.appendChild(canvas);// Optionally: convert to a downloadable imagecanvas.toBlob(blob => {const link = document.createElement('a');link.download = 'overlay-image.png';link.href = URL.createObjectURL(blob);link.textContent = 'Download Image with Overlay';document.body.appendChild(link);});};// Set the image source to load itimg.src = imagePath;}import java.awt.AlphaComposite;import java.awt.Color;import java.awt.Graphics2D;import java.awt.image.BufferedImage;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.File;import java.io.IOException;import java.nio.file.Files;import java.nio.file.Paths;import javax.imageio.ImageIO;// First, create a record for the RLE data:// For Java 16+:// public record RleMask(int[] starts, int[] lengths) {}// For earlier Java versions:public static class RleMask {private final int[] starts;private final int[] lengths;public RleMask(int[] starts, int[] lengths) {this.starts = starts;this.lengths = lengths;}public int[] starts() { return starts; }public int[] lengths() { return lengths; }}/*** Class to handle applying AI detection overlays to images*/public class ImageOverlay {/*** Applies red (AI) and green (human) overlays to the image based on mask data** @param imagePath Path to the original image file* @param maskArray 2D boolean array where true represents AI-detected pixels* @param outputPath Path to save the output image* @return The overlaid image as a BufferedImage*/public static BufferedImage applyOverlay(String imagePath, boolean[][] maskArray, String outputPath)throws IOException {// Read the original imageBufferedImage original = ImageIO.read(new File(imagePath));int width = original.getWidth();int height = original.getHeight();// Create overlay image with transparent backgroundBufferedImage overlay = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);Graphics2D g = overlay.createGraphics();// Apply overlay for each pixelfor (int y = 0; y < height; y++) {for (int x = 0; x < width; x++) {if (y < maskArray.length && x < maskArray[0].length) {if (maskArray[y][x]) {// AI-detected area - red overlayg.setColor(new Color(255, 0, 0, 120)); // Red with 47% opacity} else {// Human-created area - green overlayg.setColor(new Color(0, 255, 0, 120)); // Green with 47% opacity}g.fillRect(x, y, 1, 1);}}}g.dispose();// Combine original and overlayBufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);Graphics2D g2 = result.createGraphics();g2.drawImage(original, 0, 0, null);g2.drawImage(overlay, 0, 0, null);g2.dispose();// Save the resulting imageImageIO.write(result, "PNG", new File(outputPath));return result;}/*** Convert 1D mask array to 2D for easier processing** @param mask 1D boolean array representing the mask* @param width Image width* @param height Image height* @return 2D boolean array*/public static boolean[][] convertTo2DMask(boolean[] mask, int width, int height) {boolean[][] result = new boolean[height][width];for (int i = 0; i < mask.length; i++) {int y = i / width;int x = i % width;if (y < height && x < width) {result[y][x] = mask[i];}}return result;}}// Example usage:public static void main(String[] args) throws IOException {// Step 1: Get the API result with RLE data (simplified example)int[] starts = {0, 512, 1536, 2560};int[] lengths = {256, 512, 768, 1024};RleMask rleMask = new RleMask(starts, lengths);// Step 2: Decode the RLE maskint width = 1024;int height = 768;boolean[] binaryMask = decodeMask(rleMask, width, height);// Step 3: Convert to 2D array for easier processingboolean[][] mask2D = ImageOverlay.convertTo2DMask(binaryMask, width, height);// Step 4: Apply overlay and saveString imagePath = "path/to/your/image.jpg";String outputPath = "output-with-overlay.png";ImageOverlay.applyOverlay(imagePath, mask2D, outputPath);System.out.println("Overlay applied and saved to " + outputPath);}For a complete breakdown of all fields in the response, see the AI Image Detection Response documentation.
- A
-
🎉Congratulations!
Section titled “🎉Congratulations!”You have successfully submitted an image for AI detection. You can now use the JSON response in your application to take further action based on the findings.