New Enhanced AI detection now detects 99% of Midjourney, DALL-E, OpenAI, and Stable Diffusion images Start Building

Minno Fraud Detector API

Modern, high-performance API for detecting image manipulation and fraud in a few simple HTTP requests.

Contact Sales Generate API Key

Overview

The Minno Fraud Detector API leverages advanced algorithms and metadata analysis to identify manipulated, screenshot, or AI-generated images with high accuracy. Our service is designed to help businesses detect fraudulent receipts, documents, and images in their workflows.

Our API is RESTful and returns responses in JSON format. All requests should be made over HTTPS to ensure security of your data.

Authentication

All API requests require authentication using a bearer token. Include your API key in the Authorization header as follows:

Authorization: Bearer YOUR_API_KEY

Demo API Key

For testing purposes, you can use our demo API key:

Authorization: Bearer demo_key_123

The demo key allows for up to 2,000 requests per day.

Rate Limits

Rate limits are applied based on your subscription tier:

Tier Daily Limit Minute Limit
Free 1,000 requests (then $0.008 per call) 100 requests
Basic 10,000 requests (then $0.008 per call) 100 requests
Premium 50,000 requests (then $0.008 per call) 100 requests
Enterprise Unlimited 100 requests

Rate limit information is included in the API response headers:

  • X-RateLimit-Limit: Your daily request limit
  • X-RateLimit-Remaining: Number of requests remaining for the day
  • X-RateLimit-Reset: Unix timestamp when the rate limit will reset

If you exceed your rate limit, the API will respond with a 429 Too Many Requests status code.

Quick Start

Here's a simple example to get you started with the Minno Fraud Detector API:

# Analyze an image for fraud indicators
curl -X POST "https://api.tryminno.com/fraud-detector/api/analyze" \
  -H "Authorization: Bearer demo_key_123" \
  -F "file=@/path/to/receipt.jpg"
import requests

api_key = "demo_key_123"  # Demo API key for testing
image_path = "path/to/receipt.jpg"

url = "https://api.tryminno.com/fraud-detector/api/analyze"
headers = {"Authorization": f"Bearer {api_key}"}

with open(image_path, "rb") as image_file:
    files = {"file": image_file}
    response = requests.post(url, headers=headers, files=files)

if response.status_code == 200:
    result = response.json()
    print(f"Fraud Score: {result['normalized_score']}/100")
    print(f"Risk Level: {result['risk_level']}")
    
    if result.get('issues'):
        print("Issues detected:")
        for issue in result['issues']:
            print(f"- {issue}")
else:
    print(f"Error: {response.status_code} - {response.json()['detail']}")
// Analyze an image for fraud indicators
async function analyzeImage(imageFile) {
  const apiKey = "demo_key_123";  // Demo API key for testing
  const url = "https://api.tryminno.com/fraud-detector/api/analyze";
  
  const formData = new FormData();
  formData.append("file", imageFile);
  
  try {
    const response = await fetch(url, {
      method: "POST",
      headers: {
        "Authorization": `Bearer ${apiKey}`
      },
      body: formData
    });
    
    if (!response.ok) {
      const errorData = await response.json();
      throw new Error(`API Error: ${errorData.detail}`);
    }
    
    const result = await response.json();
    
    console.log(`Fraud Score: ${result.normalized_score}/100`);
    console.log(`Risk Level: ${result.risk_level}`);
    
    if (result.issues && result.issues.length > 0) {
      console.log("Issues detected:");
      result.issues.forEach(issue => {
        console.log(`- ${issue}`);
      });
    }
    
    return result;
  } catch (error) {
    console.error("Error analyzing image:", error);
    throw error;
  }
}

API Endpoints

The Minno Fraud Detector API provides the following endpoints for fraud detection and service status.

POST /api/analyze

Analyzes an uploaded image for potential fraud indicators. This endpoint detects manipulated, screenshot, or AI-generated images by examining metadata and image characteristics.

Request Parameters

Parameter Type Required Description
file File Required The image file to analyze. Supported formats: PNG, JPG, JPEG, HEIC, BMP, TIFF. Maximum size: 100MB.

Headers

Header Required Description
Authorization Required Bearer token authentication. Format: Bearer YOUR_API_KEY
Content-Type Required Must be multipart/form-data for file uploads

Example Request

curl -X POST "https://api.tryminno.com/fraud-detector/api/analyze" \
  -H "Authorization: Bearer demo_key_123" \
  -F "file=@receipt.jpg"
import requests

api_key = "demo_key_123"  # Demo API key for testing
image_path = "receipt.jpg"

url = "https://api.tryminno.com/fraud-detector/api/analyze"
headers = {"Authorization": f"Bearer {api_key}"}

with open(image_path, "rb") as image_file:
    files = {"file": image_file}
    response = requests.post(url, headers=headers, files=files)

result = response.json()
print(result)
const apiKey = "demo_key_123";  // Demo API key for testing
const file = document.querySelector('input[type="file"]').files[0];
const formData = new FormData();
formData.append("file", file);

fetch("https://api.tryminno.com/fraud-detector/api/analyze", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${apiKey}`
  },
  body: formData
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error("Error:", error));

Successful Response (200 OK)

{
  "request_id": "6ab48cfe-a9ba-49a4-b7f1-e328b7c55271",
  "normalized_score": 85,
  "risk_level": "HIGH",
  "risk_description": "Almost certainly not an authentic photo",
  "categories": {
    "ai_generated": {
      "score": 30,
      "max": 30,
      "details": "Image contains AI generation markers"
    },
    "file_format": {
      "score": 15,
      "max": 15,
      "details": "Format not typical for camera photos"
    },
    "device_info": {
      "score": 20,
      "max": 20,
      "details": "Missing or suspicious device information"
    },
    "camera_data": {
      "score": 15,
      "max": 15,
      "details": "Limited camera settings data"
    },
    "location_data": {
      "score": 10,
      "max": 10,
      "details": "Missing location information"
    },
    "metadata_consistency": {
      "score": 0,
      "max": 10,
      "details": ""
    }
  },
  "issues": [
    "AI-generated image detected",
    "Missing device information",
    "Incomplete camera data (0/7 fields)",
    "Missing GPS data",
    "Non-camera image format: PNG"
  ],
  "metadata_summary": {
    "file_format": "PNG",
    "device": "Unknown",
    "camera_data_fields": [],
    "has_gps": false,
    "ai_markers_found": true
  },
  "location": {
    "latitude": 37.7749,
    "longitude": -122.4194,
    "timestamp": "2025:03:15 12:34:56"
  },
  "detailed_analysis": {
    "ai_detection": {
      "detected": true,
      "markers": ["CBOR:ActionsDigitalSourceType: trainedAlgorithmicMedia"],
      "score_impact": 30,
      "explanation": "AI generation markers were found in the image metadata"
    },
    "device_analysis": {
      "make": null,
      "model": null,
      "has_device_info": false,
      "is_expected_device": false,
      "score_impact": 20,
      "explanation": "No device information found"
    },
    "file_format_analysis": {
      "format": "PNG",
      "is_camera_format": false,
      "expected_formats": ["HEIC", "JPG", "JPEG", "CR2", "NEF", "DNG", "ARW", "RAF"],
      "score_impact": 15,
      "explanation": "Non-camera format: PNG"
    },
    "camera_data_analysis": {
      "fields_found": [],
      "fields_expected": ["FNumber", "ExposureTime", "ISO", "FocalLength", "ApertureValue", "ShutterSpeedValue", "LensModel"],
      "completeness": "0/7",
      "score_impact": 15,
      "explanation": "Camera data completeness: 0/7 fields present"
    },
    "location_analysis": {
      "has_gps": false,
      "gps_fields_found": [],
      "is_apple_device": false,
      "score_impact": 10,
      "explanation": "No GPS data"
    },
    "metadata_consistency": {
      "is_consistent": true,
      "device_specific_fields_found": 0,
      "device_specific_fields_expected": 0,
      "score_impact": 0,
      "explanation": "Metadata is consistent with claimed device"
    }
  },
  "filename": "receipt.jpg",
  "image_hash": "1e7ba8a3b81cac3e9f52ef1d0fbc589d5bf110c2fc477e7ccf1f3bed7b01440d",
  "timestamp": "2024-04-01T14:32:28.481192",
  "analysis_time_ms": 243,
  "api_version": "1.0.0",
  "api_tier": "basic",
  "rate_limit": {
    "tier": "basic",
    "daily_limit": 10000,
    "requests_used": 12,
    "requests_remaining": 9988,
    "reset_at": "2024-04-01 23:59:59 UTC"
  }
}
GET /api/health

Health check endpoint to verify if the API is running properly. Use this endpoint to monitor the service status.

Example Request

curl "https://api.tryminno.com/fraud-detector/api/health"

Response (200 OK)

{
  "status": "healthy",
  "timestamp": "2024-04-01T15:30:45.123456"
}
GET /api/version

Returns information about the current API version.

Example Request

curl "https://api.tryminno.com/fraud-detector/api/version"

Response (200 OK)

{
  "version": "1.0.0",
  "name": "Minno API",
  "description": "Modern, high-performance API for detecting fraudulent and AI-generated images"
}

Response Structure

The response from the /api/analyze endpoint provides comprehensive information about fraud indicators detected in the image. Here's what the fields mean:

Field Description
request_id Unique identifier for the analysis request
normalized_score Overall fraud score from 0-100, with higher values indicating more suspicious content
risk_level Categorical assessment: LOW, LOW_MEDIUM, MEDIUM, MEDIUM_HIGH, or HIGH
risk_description Human-readable description of the risk assessment
categories Detailed breakdown of six fraud detection categories with individual scores and details
issues List of specific issues or suspicious elements detected in the image
metadata_summary Summary of key metadata points from the image
location GPS coordinates (latitude, longitude) and timestamp if available
detailed_analysis Comprehensive breakdown of all fraud indicators with explanations and impact scores
rate_limit Information about your current API usage and limits

Categories

The analysis examines six key categories to determine if an image is potentially fraudulent:

Category Max Score Description
ai_generated 30 Checks for AI generation markers in image metadata
file_format 15 Assesses if the file format matches expected formats for authentic photos
device_info 20 Evaluates presence and consistency of device information
camera_data 15 Checks for camera-specific metadata like aperture, exposure, and focal length
location_data 10 Assesses presence and validity of GPS information
metadata_consistency 10 Evaluates internal consistency of metadata fields

Detailed Analysis

The detailed_analysis field provides in-depth information about each aspect of the fraud detection analysis. It includes six sub-sections:

Section Description Key Data Points
ai_detection Analysis of AI generation markers Detection status, specific markers found, score impact, explanation
device_analysis Information about the device used to create the image Make/model, validity assessment, score impact, explanation
file_format_analysis Analysis of file type and its typical usage Format details, compatibility with camera formats, score impact, explanation
camera_data_analysis Evaluation of camera-specific metadata Found/expected fields, completeness ratio, score impact, explanation
location_analysis Assessment of GPS and location metadata GPS status, found fields, device context, score impact, explanation
metadata_consistency Checks for internal consistency of metadata Consistency assessment, expected/found device-specific fields, score impact, explanation

Each section contains a score_impact field showing how much that aspect contributed to the overall fraud score, and an explanation field with a human-readable description of the findings.

Error Handling

The API uses standard HTTP status codes to indicate the success or failure of a request.

Status Code Description
200 OK The request was successful
400 Bad Request The request was invalid (e.g., unsupported file format, missing parameters)
401 Unauthorized Authentication failed (invalid or missing API key)
429 Too Many Requests Rate limit exceeded
500 Internal Server Error An error occurred on the server

Error Response Format

Error responses include a detail field explaining what went wrong:

{
  "detail": "Invalid file format. Only PNG, JPG, JPEG, HEIC, BMP, and TIFF are supported."
}

Rate Limit Error Example

{
  "detail": "Daily limit exceeded for your API key tier (basic). Limit: 10000 requests per day.",
  "limit": 10000,
  "current_usage": 10000,
  "reset_at": "2024-04-01 23:59:59 UTC"
}

Code Examples

Here's a complete example showing how to integrate the Minno Fraud Detector API with error handling:

import requests
import os
import json
from datetime import datetime

def analyze_receipt(image_path, api_key="demo_key_123"):
    """
    Analyze a receipt image for fraud indicators using the Minno API.
    
    Args:
        image_path: Path to the image file
        api_key: Your Minno API key (defaults to demo key)
        
    Returns:
        Analysis results or error message
    """
    url = "https://api.tryminno.com/fraud-detector/api/analyze"
    headers = {"Authorization": f"Bearer {api_key}"}
    
    # Validate file exists
    if not os.path.exists(image_path):
        return {"error": f"File not found: {image_path}"}
    
    # Validate file type
    valid_extensions = ['.png', '.jpg', '.jpeg', '.heic', '.bmp', '.tiff']
    ext = os.path.splitext(image_path)[1].lower()
    if ext not in valid_extensions:
        return {"error": f"Unsupported file type: {ext}. Must be one of: {', '.join(valid_extensions)}"}
    
    try:
        with open(image_path, "rb") as image_file:
            files = {"file": image_file}
            response = requests.post(url, headers=headers, files=files)
        
        # Check for errors
        if response.status_code == 200:
            return response.json()
        elif response.status_code == 401:
            return {"error": "Authentication failed. Check your API key."}
        elif response.status_code == 429:
            rate_data = response.json()
            return {
                "error": "Rate limit exceeded",
                "details": rate_data.get("detail", ""),
                "reset_at": rate_data.get("reset_at", "")
            }
        else:
            return {
                "error": f"API error: {response.status_code}",
                "details": response.json().get("detail", "Unknown error")
            }
    except requests.RequestException as e:
        return {"error": f"Request failed: {str(e)}"}
    except Exception as e:
        return {"error": f"Error: {str(e)}"}

def print_analysis_results(results):
    """Print the analysis results in a readable format."""
    if "error" in results:
        print(f"❌ Error: {results['error']}")
        if "details" in results:
            print(f"   {results['details']}")
        return
    
    # Print summary
    print(f"📊 Fraud Analysis Results")
    print(f"-------------------------")
    print(f"Score: {results['normalized_score']}/100")
    
    risk_level = results['risk_level']
    if risk_level == "LOW":
        risk_icon = "✅"
    elif risk_level in ["LOW_MEDIUM", "MEDIUM"]:
        risk_icon = "⚠️"
    else:
        risk_icon = "🚨"
    
    print(f"Risk: {risk_icon} {risk_level} - {results['risk_description']}")
    
    # Print issues
    if results.get('issues'):
        print("\nIssues Detected:")
        for issue in results['issues']:
            print(f"❗ {issue}")
    else:
        print("\nNo issues detected.")
    
    # Print category breakdown
    print("\nCategory Breakdown:")
    for category, data in results['categories'].items():
        if data['score'] > 0:
            print(f"- {category.replace('_', ' ').title()}: {data['score']}/{data['max']}")
            print(f"  {data['details']}")

# Example usage
if __name__ == "__main__":
    API_KEY = "YOUR_API_KEY"
    image_path = "receipt.jpg"
    
    results = analyze_receipt(image_path, API_KEY)
    print_analysis_results(results)
/**
 * Analyzes an image for fraud indicators using the Minno API
 * 
 * @param {File} imageFile - The image file to analyze
 * @param {string} apiKey - Your Minno API key
 * @returns {Promise} - Resolves with analysis results or rejects with error
 */
async function analyzeReceiptImage(imageFile, apiKey) {
  // Validate file type
  const validTypes = ['image/png', 'image/jpeg', 'image/jpg', 'image/heic', 'image/bmp', 'image/tiff'];
  if (!validTypes.includes(imageFile.type)) {
    throw new Error(`Unsupported file type: ${imageFile.type}. Supported types: ${validTypes.join(', ')}`);
  }
  
  // Validate file size (max 100MB)
  const MAX_SIZE = 100 * 1024 * 1024; // 100MB
  if (imageFile.size > MAX_SIZE) {
    throw new Error(`File too large: ${(imageFile.size / 1024 / 1024).toFixed(2)}MB. Maximum size is 100MB.`);
  }
  
  const url = "https://api.tryminno.com/fraud-detector/api/analyze";
  const formData = new FormData();
  formData.append("file", imageFile);
  
  try {
    const response = await fetch(url, {
      method: "POST",
      headers: {
        "Authorization": `Bearer ${apiKey}`
      },
      body: formData
    });
    
    // Parse response JSON
    const data = await response.json();
    
    // Handle errors
    if (!response.ok) {
      const error = new Error(data.detail || "Unknown API error");
      error.status = response.status;
      error.data = data;
      
      // Add rate limit info if available
      if (response.status === 429) {
        error.rateLimit = {
          limit: data.limit,
          reset: data.reset_at
        };
      }
      
      throw error;
    }
    
    return data;
  } catch (error) {
    if (error.status) {
      // This is an API error we've already processed
      throw error;
    } else {
      // Network or other error
      throw new Error(`Request failed: ${error.message}`);
    }
  }
}

/**
 * Renders the fraud analysis results in the UI
 * 
 * @param {Object} results - The analysis results from the API
 * @param {HTMLElement} container - The container to render results into
 */
function displayAnalysisResults(results, container) {
  // Risk level display
  let riskColor, riskIcon;
  switch(results.risk_level) {
    case "LOW":
      riskColor = "#10B981"; // Green
      riskIcon = "✅";
      break;
    case "LOW_MEDIUM":
    case "MEDIUM":
      riskColor = "#FBBF24"; // Yellow
      riskIcon = "⚠️";
      break;
    default:
      riskColor = "#EF4444"; // Red
      riskIcon = "🚨";
  }
  
  // Create HTML content
  const html = `
    
${results.normalized_score}
Fraud Score

${riskIcon} ${results.risk_level} RISK

${results.risk_description}

Issues Detected:

${results.issues.length > 0 ? `
    ${results.issues.map(issue => `
  • ${issue}
  • `).join('')}
` : '

No issues detected

' }

Category Breakdown:

${Object.entries(results.categories) .filter(([_, data]) => data.score > 0) .map(([category, data]) => `
${category.replace('_', ' ')} ${data.score}/${data.max}
${data.details}
`).join('') }
`; // Set HTML content container.innerHTML = html; } // Example usage document.getElementById('analyzeButton').addEventListener('click', async () => { const fileInput = document.getElementById('imageInput'); const resultsContainer = document.getElementById('resultsContainer'); const loadingIndicator = document.getElementById('loadingIndicator'); if (!fileInput.files || fileInput.files.length === 0) { alert('Please select an image to analyze'); return; } const API_KEY = "demo_key_123"; // Demo API key for testing try { // Show loading state loadingIndicator.style.display = 'block'; resultsContainer.innerHTML = ''; // Analyze the image const results = await analyzeReceiptImage(fileInput.files[0], API_KEY); // Display results displayAnalysisResults(results, resultsContainer); } catch (error) { resultsContainer.innerHTML = `

Error

${error.message}

${error.rateLimit ? `

Rate limit exceeded. Resets at: ${error.rateLimit.reset}

` : ''}
`; } finally { // Hide loading indicator loadingIndicator.style.display = 'none'; } });