Modern, high-performance API for detecting image manipulation and fraud in a few simple HTTP requests.
Contact Sales Generate API KeyThe 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.
https://api.tryminno.com/fraud-detector
Our API is RESTful and returns responses in JSON format. All requests should be made over HTTPS to ensure security of your data.
All API requests require authentication using a bearer token. Include your API key in the Authorization header as follows:
Authorization: Bearer YOUR_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 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 limitX-RateLimit-Remaining
: Number of requests remaining for the dayX-RateLimit-Reset
: Unix timestamp when the rate limit will resetIf you exceed your rate limit, the API will respond with a 429 Too Many Requests status code.
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;
}
}
The Minno Fraud Detector API provides the following endpoints for fraud detection and service status.
Analyzes an uploaded image for potential fraud indicators. This endpoint detects manipulated, screenshot, or AI-generated images by examining metadata and image characteristics.
Parameter | Type | Required | Description |
---|---|---|---|
file | File | Required | The image file to analyze. Supported formats: PNG, JPG, JPEG, HEIC, BMP, TIFF. Maximum size: 100MB. |
Header | Required | Description |
---|---|---|
Authorization | Required | Bearer token authentication. Format: Bearer YOUR_API_KEY |
Content-Type | Required | Must be multipart/form-data for file uploads |
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));
{ "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" } }
Health check endpoint to verify if the API is running properly. Use this endpoint to monitor the service status.
curl "https://api.tryminno.com/fraud-detector/api/health"
{ "status": "healthy", "timestamp": "2024-04-01T15:30:45.123456" }
Returns information about the current API version.
curl "https://api.tryminno.com/fraud-detector/api/version"
{ "version": "1.0.0", "name": "Minno API", "description": "Modern, high-performance API for detecting fraudulent and AI-generated images" }
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 |
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 |
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.
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 responses include a detail field explaining what went wrong:
{ "detail": "Invalid file format. Only PNG, JPG, JPEG, HEIC, BMP, and TIFF are supported." }
{ "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" }
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 = `
`;
} finally {
// Hide loading indicator
loadingIndicator.style.display = 'none';
}
});