Image Comparator avatar

Image Comparator

Pricing

from $2.50 / 1,000 image processeds

Go to Apify Store
Image Comparator

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.

Pricing

from $2.50 / 1,000 image processeds

Rating

5.0

(1)

Developer

Aljeandro

Aljeandro

Maintained by Community

Actor stats

1

Bookmarked

2

Total users

1

Monthly active users

a day ago

Last modified

Categories

Share

🔍 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:

InputDescription
1 source imageThe reference image you want to compare against
1 or more target imagesThe images you want to check for similarity

You get back:

OutputDescription
similarityScoreA float between 0.0 and 1.0 (higher = more similar)
isSimilartrue / 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.

EventMetricRate
Actor Startapify-actor-start$0.00005 per run (covers the first 5s of compute)
Image Processedimage-processed$2.50 per 1,000 images ($0.0025 per image)

Examples:

ScenarioTotal imagesImage CostActor Start CostTotal Cost
1 source vs 1 target2$0.005$0.00005$0.00505
1 source vs 10 targets11$0.0275$0.00005$0.02755
1 source vs 100 targets101$0.2525$0.00005$0.25255
1 source vs 1,000 targets1,001$2.5025$0.00005$2.50255

How similarity scoring works

Score rangeInterpretation
0.95 – 1.00Near-identical or the same image (possibly resized/compressed).
0.85 – 0.95Very similar — same subject, minor differences (angle, lighting).
0.70 – 0.85Related — same category or concept, but visually distinct.
Below 0.70Different images.

Performance

MetricValue
Model loadInstant — weights are pre-baked into the Docker image
Per-image embedding~20–50 ms (CPU) / ~5–10 ms (GPU)
ConcurrencyUp to 10 images downloaded in parallel
Batch processingImages 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:

  1. 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.
  2. Async image fetching — All images are downloaded concurrently using httpx with a configurable semaphore and standard browser User-Agents to bypass strict CDNs.
  3. Vectorized Math — Cosine similarity is computed simultaneously across all images using a single, blazing-fast numpy.dot() matrix instruction.
  4. Resilience — Malformed images, 404s, or 100MB+ files are automatically skipped and logged as errors without crashing your batch run.
  5. 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 ApifyClient
client = 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.85 works great for detecting near-duplicates or identical subjects from different angles. Increase it to 0.95 for 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"
}