Image Comparator
Pricing
from $2.50 / 1,000 image processeds
Image Comparator
Compare a source image against multiple targets using deep-learning model to determine visual similarity. Ideal for e-commerce matching, copyright detection, and image deduplication. Accepts both URLs and Base64 encoded images to provide highly accurate similarity scoring.
🔍 Image Comparator — AI-Powered Visual Similarity
Instantly compare images and get a similarity score + match verdict. Works with public URLs and base64-encoded images — no preprocessing needed.
What it does
This actor uses a state-of-the-art deep learning vision model to measure how visually similar two images are. It converts each image into a high-dimensional embedding vector, then computes the cosine similarity between them.
You provide:
| Input | Description |
|---|---|
| 1 source image | The reference image you want to compare against |
| 1 or more target images | The images you want to check for similarity |
You get back:
| Output | Description |
|---|---|
| similarityScore | A float between 0.0 and 1.0 (higher = more similar) |
| isSimilar | true / false — whether the score meets your threshold |
Use cases
- Duplicate detection — Find near-duplicate product images across catalogs.
- Brand monitoring — Check if scraped images match your reference assets.
- Content moderation — Flag re-uploaded or slightly altered images.
- Visual QA — Verify screenshots or rendered pages against golden references.
- E-commerce matching — Match supplier images to your existing product photos.
💰 Pricing
This actor uses pay-per-event pricing based on the total number of images processed, plus a minimal base charge for the actor start.
| Event | Metric | Rate |
|---|---|---|
| Actor Start | apify-actor-start | $0.00005 per run (covers the first 5s of compute) |
| Image Processed | image-processed | $2.50 per 1,000 images ($0.0025 per image) |
Examples:
| Scenario | Total images | Image Cost | Actor Start Cost | Total Cost |
|---|---|---|---|---|
| 1 source vs 1 target | 2 | $0.005 | $0.00005 | $0.00505 |
| 1 source vs 10 targets | 11 | $0.0275 | $0.00005 | $0.02755 |
| 1 source vs 100 targets | 101 | $0.2525 | $0.00005 | $0.25255 |
| 1 source vs 1,000 targets | 1,001 | $2.5025 | $0.00005 | $2.50255 |
How similarity scoring works
| Score range | Interpretation |
|---|---|
| 0.95 – 1.00 | Near-identical or the same image (possibly resized/compressed). |
| 0.85 – 0.95 | Very similar — same subject, minor differences (angle, lighting). |
| 0.70 – 0.85 | Related — same category or concept, but visually distinct. |
| Below 0.70 | Different images. |
Performance
| Metric | Value |
|---|---|
| Model load | Instant — weights are pre-baked into the Docker image |
| Per-image embedding | ~20–50 ms (CPU) / ~5–10 ms (GPU) |
| Concurrency | Up to 10 images downloaded in parallel |
| Batch processing | Images are embedded in batches of 16 for throughput |
Expected Execution Times (1-to-1 comparison)
On standard Apify CPU instances, you can expect the following benchmarks for a single source vs. target comparison:
- ~0.3 to 0.5 seconds ("Hot" Start) — If the exact same URLs/Base64 strings have been run before, the actor instantly grabs the embeddings from the local cache.
- ~1 to 2 seconds ("Cold" Start) — The lightweight ONNX model loads instantly from disk (< 50MB), downloads the images, and computes embeddings via vectorized numpy arrays. No more 15+ second HuggingFace model download delays!
💡 Note on Batches: If you run large batches (e.g., 1 source vs 100 targets), the sub-second model loading time is a flat fee, and the remaining 100 images will process rapidly in memory.
The actor is designed for speed:
- Pure ONNX Runtime — The massive PyTorch dependency has been stripped out. The AI model runs on a highly optimized ONNX graph, cutting RAM usage to < 500MB and starting instantly.
- Async image fetching — All images are downloaded concurrently using
httpxwith a configurable semaphore and standard browser User-Agents to bypass strict CDNs. - Vectorized Math — Cosine similarity is computed simultaneously across all images using a single, blazing-fast
numpy.dot()matrix instruction. - Resilience — Malformed images, 404s, or 100MB+ files are automatically skipped and logged as errors without crashing your batch run.
- Smart Embedding Cache — Automatically saves computed embeddings in an Apify Key-Value Store (
embedding-cache). If you run the actor again with the same URLs or base64 images, it instantly reuses the cached data—skipping the download and inference steps entirely to save massive amounts of processing time.
Limitations
- Image format — Supports JPEG, PNG, WebP, BMP, TIFF. Animated GIFs use the first frame only.
- Image size — Very large images (>20 MP) may slow down processing. The model internally resizes to 224×224 px.
- Pixel-level diff — This actor measures semantic similarity (does it look like the same thing?), not pixel-exact differences. Two photos of the same cat from different angles will score high; a red square and a blue square will score low, even if they're the same shape.
- Timeout — Image downloads time out after 30 seconds per image.
Integrations & API usage
Call this actor from any language via the Apify API:
curl -X POST "https://api.apify.com/v2/acts/<YOUR_ACTOR_ID>/runs?token=<YOUR_TOKEN>" \-H "Content-Type: application/json" \-d '{"sourceImage": "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg","targetImages": ["https://upload.wikimedia.org/wikipedia/commons/thumb/4/4d/Cat_November_2010-1a.jpg/1200px-Cat_November_2010-1a.jpg"]}'
Python
from apify_client import ApifyClientclient = ApifyClient("<YOUR_TOKEN>")run = client.actor("<YOUR_ACTOR_ID>").call(run_input={"sourceImage": "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg","targetImages": ["https://upload.wikimedia.org/wikipedia/commons/thumb/4/4d/Cat_November_2010-1a.jpg/1200px-Cat_November_2010-1a.jpg","https://upload.wikimedia.org/wikipedia/commons/thumb/2/26/YellowLabradorLooking_new.jpg/1200px-YellowLabradorLooking_new.jpg",],})for item in client.dataset(run["defaultDatasetId"]).iterate_items():for result in item["results"]:print(f"Target #{result['targetIndex']}: "f"score={result['similarityScore']:.4f} "f"match={result['isSimilar']}")
JavaScript / Node.js
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_TOKEN>' });const run = await client.actor('<YOUR_ACTOR_ID>').call({sourceImage: 'https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg',targetImages: ['https://upload.wikimedia.org/wikipedia/commons/thumb/4/4d/Cat_November_2010-1a.jpg/1200px-Cat_November_2010-1a.jpg','https://upload.wikimedia.org/wikipedia/commons/thumb/2/26/YellowLabradorLooking_new.jpg/1200px-YellowLabradorLooking_new.jpg',],});const { items } = await client.dataset(run.defaultDatasetId).listItems();items[0].results.forEach((r) => {console.log(`Target #${r.targetIndex}: score=${r.similarityScore} match=${r.isSimilar}`);});
Support & Feedback
Found a bug or have a feature request? Open an issue on the actor's GitHub repository or reach out via the Apify community.
📋 Input Description
Detailed reference for every input field accepted by this actor.
🖼️ Source Image
sourceImage
Optional
The reference image to compare all target images against. This is the "baseline" — every target will be scored based on how similar it is to this image.
Accepts two formats:
- Public URL — Any directly accessible image URL (e.g.,
https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg) - Base64-encoded string — With or without the
data:image/...;base64,prefix
💡 Pro tip: For fastest results, use URLs pointing to images hosted on a CDN or fast server. Base64 input avoids the network download but increases the payload size.
⚠️ Note: If left empty, a sample image will be used for demonstration purposes. This is useful for testing the actor before integrating it into your pipeline.
Type: string
Default:
https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg
Example (URL):
https://upload.wikimedia.org/wikipedia/commons/thumb/6/68/Orange_tabby_cat_sitting_on_fallen_leaves-Hisashi-01A.jpg/1200px-Orange_tabby_cat_sitting_on_fallen_leaves-Hisashi-01A.jpg
Example (base64 with data-URI):
data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD...
Example (raw base64):
/9j/4AAQSkZJRgABAQAAAQABAAD...
🎯 Target Images
targetImages
Optional
A list of one or more images to compare against the source image. Each item in the array is scored independently — you get one result object per target.
Each array element accepts the same two formats as sourceImage:
- Public URL
- Base64-encoded string (with or without data-URI prefix)
You can freely mix URLs and base64 strings in the same array.
💡 Pro tip: For 1-to-1 comparison, pass a single-element array. For 1-to-N batch comparison, pass as many targets as you need — they are all processed in a single run with batched inference for maximum speed.
⚠️ Pricing note: You are charged based on the total number of images processed (1 source + N targets). See the Pricing section above.
⚠️ Blank entries: Any blank or empty strings in the array are automatically filtered out. If all entries are blank, the default sample images will be used.
Type: array of string
Default:
["https://upload.wikimedia.org/wikipedia/commons/thumb/4/4d/Cat_November_2010-1a.jpg/1200px-Cat_November_2010-1a.jpg","https://upload.wikimedia.org/wikipedia/commons/thumb/2/26/YellowLabradorLooking_new.jpg/1200px-YellowLabradorLooking_new.jpg"]
Example (URLs only):
["https://upload.wikimedia.org/wikipedia/commons/thumb/b/bb/Kittyply_edit1.jpg/1200px-Kittyply_edit1.jpg","https://upload.wikimedia.org/wikipedia/commons/thumb/0/0c/GoldenGateBridge-001.jpg/1200px-GoldenGateBridge-001.jpg","https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/Camponotus_flavomarginatus_ant.jpg/1200px-Camponotus_flavomarginatus_ant.jpg"]
Example (mixed URLs + base64):
["https://upload.wikimedia.org/wikipedia/commons/thumb/b/bb/Kittyply_edit1.jpg/1200px-Kittyply_edit1.jpg","data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...","/9j/4AAQSkZJRgABAQAAAQABAAD..."]
🎚️ Threshold
threshold
Optional
The similarity score cutoff (between -1.0 and 1.0) that determines whether isSimilar is true or false. Any target image that scores equal to or higher than this value will be marked as a match.
💡 Pro tip: A threshold of
0.85works great for detecting near-duplicates or identical subjects from different angles. Increase it to0.95for strict exact-duplicate detection.
Type: number
Default: 0.85
📥 Input Example
Full JSON input with all fields:
{"sourceImage": "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg","targetImages": ["https://upload.wikimedia.org/wikipedia/commons/thumb/4/4d/Cat_November_2010-1a.jpg/1200px-Cat_November_2010-1a.jpg","https://upload.wikimedia.org/wikipedia/commons/thumb/2/26/YellowLabradorLooking_new.jpg/1200px-YellowLabradorLooking_new.jpg","data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD..."]}
Minimal input (uses all defaults)
{}
1-to-1 comparison
{"sourceImage": "https://upload.wikimedia.org/wikipedia/commons/thumb/6/68/Orange_tabby_cat_sitting_on_fallen_leaves-Hisashi-01A.jpg/1200px-Orange_tabby_cat_sitting_on_fallen_leaves-Hisashi-01A.jpg","targetImages": ["https://upload.wikimedia.org/wikipedia/commons/thumb/b/bb/Kittyply_edit1.jpg/1200px-Kittyply_edit1.jpg"]}
1-to-N comparison with custom threshold
{"sourceImage": "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg","targetImages": ["https://upload.wikimedia.org/wikipedia/commons/thumb/4/4d/Cat_November_2010-1a.jpg/1200px-Cat_November_2010-1a.jpg","https://upload.wikimedia.org/wikipedia/commons/thumb/b/bb/Kittyply_edit1.jpg/1200px-Kittyply_edit1.jpg","https://upload.wikimedia.org/wikipedia/commons/thumb/2/26/YellowLabradorLooking_new.jpg/1200px-YellowLabradorLooking_new.jpg","https://upload.wikimedia.org/wikipedia/commons/thumb/0/0c/GoldenGateBridge-001.jpg/1200px-GoldenGateBridge-001.jpg","https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/Camponotus_flavomarginatus_ant.jpg/1200px-Camponotus_flavomarginatus_ant.jpg"]}
Pure base64 input (no URLs)
{"sourceImage": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...","targetImages": ["/9j/4AAQSkZJRgABAQAAAQABAAD..."]}
📤 Output Description
The actor pushes a single JSON object to the default dataset. Below is the detailed schema of every output field.
sourceImage
Required
Truncated echo of your source image input (max 120 characters). Useful for identifying which source was used when reviewing results.
Type: string
Example:
https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg
totalTargets
Required
The number of target images that were compared against the source.
Type: integer
Example:
3
totalImagesProcessed
Required
The total number of images processed in this run (1 source + N targets). This is the number used for billing.
Type: integer
Example:
4
results
Required
Array of comparison results — one entry per target image.
Type: array of object
Each object in the array contains:
results[].targetIndex
Required
Zero-based index matching the position in your targetImages input array. Use this to map results back to your original input list.
Type: integer
Example: 0
results[].targetImage
Required
Truncated echo of the target image input (max 120 characters). For base64 inputs, this will be the first 120 characters of the encoded string.
Type: string
Example:
https://upload.wikimedia.org/wikipedia/commons/thumb/4/4d/Cat_November_2010-1a.jpg/1200px-Cat_November_2010-1a.jpg
results[].similarityScore
Required
Cosine similarity between the source and this target image. Ranges from 0.0 (completely different) to 1.0 (identical). This is the raw AI confidence score.
Type: number
Minimum: 0.0
Maximum: 1.0
Example: 0.943218
results[].isSimilar
Required
Boolean verdict: true if similarityScore >= 0.85 (our internal similarity threshold), otherwise false. This is the quick yes/no answer — use similarityScore for more nuanced decisions.
Type: boolean
Example: true
error
Optional (only present when the run fails)
Human-readable error message describing what went wrong. Present instead of the normal output fields when the actor encounters an unrecoverable error.
Type: string
Example:
HTTP error fetching image: 404 – https://upload.wikimedia.org/wikipedia/commons/nonexistent-image.jpg
📤 Output Example
Successful run — 1 source vs 2 targets
{"sourceImage": "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg","totalTargets": 2,"totalImagesProcessed": 3,"results": [{"targetIndex": 0,"targetImage": "https://upload.wikimedia.org/wikipedia/commons/thumb/4/4d/Cat_November_2010-1a.jpg/1200px-Cat_November_2010-1…","similarityScore": 0.943218,"isSimilar": true},{"targetIndex": 1,"targetImage": "https://upload.wikimedia.org/wikipedia/commons/thumb/2/26/YellowLabradorLooking_new.jpg/1200px-YellowLabradorL…","similarityScore": 0.612745,"isSimilar": false}]}
Successful run — 1 source vs 1 target (exact duplicate)
{"sourceImage": "https://upload.wikimedia.org/wikipedia/commons/thumb/6/68/Orange_tabby_cat_sitting_on_fallen_leaves-Hisashi-01…","totalTargets": 1,"totalImagesProcessed": 2,"results": [{"targetIndex": 0,"targetImage": "https://upload.wikimedia.org/wikipedia/commons/thumb/b/bb/Kittyply_edit1.jpg/1200px-Kittyply_edit1.jpg","similarityScore": 0.998714,"isSimilar": true}]}
Failed run — bad URL
{"error": "HTTP error fetching image: 404 – https://upload.wikimedia.org/wikipedia/commons/nonexistent-image.jpg"}