DEV Community

MikeL
MikeL

Posted on • Originally published at detectzestack.com

Find Companies Using imagesLoaded: API Guide (2026)

imagesLoaded is a small JavaScript library by David DeSandro that answers one question: have the images inside a container finished loading? That sounds too narrow to be a prospecting signal, and that narrowness is exactly why it is a good one. Nobody adds imagesLoaded on a whim. It appears on sites with image-heavy layouts that break without it, and it almost always arrives as part of a specific, dateable front-end stack: jQuery, a masonry-style grid, a WordPress theme or page builder doing the enqueueing.

This guide shows how imagesLoaded is detected from the outside and how to turn that detection into a prospect list with the DetectZeStack API. The worked example is a live site, and every response shape below comes from the live API.

What Is imagesLoaded and Why Track Sites That Use It

Browsers report an element's dimensions as zero until its images arrive. Any layout that positions items based on their rendered height — a masonry wall, a justified photo grid, an infinite-scroll feed — will compute positions from those zero heights and paint overlapping bricks unless something waits for the images first. imagesLoaded is that something. It watches a container, fires a callback when every image has loaded or errored, and lets the layout engine run against real dimensions.

As a technographic signal, that tells you three things about a site before you ever open it. First, the site is visually driven: portfolios, photography, real estate, media, ecommerce catalogs. Second, its layout is computed in JavaScript rather than in modern CSS, which dates the front end — CSS grid and aspect-ratio have made much of this machinery unnecessary. Third, someone assembled this stack deliberately, usually via a WordPress theme, a page builder, or a hand-rolled gallery, and that assembly is now a maintenance surface.

How imagesLoaded Fits Into a Front-End Stack (Masonry, Isotope, jQuery Galleries)

imagesLoaded rarely travels alone. It is the companion utility for the same author's layout libraries — Masonry, Isotope, Packery, Flickity — and for jQuery gallery and carousel plugins that need to measure images before arranging them. The canonical pattern looks like this:

// wait for images, then lay out the grid
imagesLoaded('.grid', function() {
new Masonry('.grid', { itemSelector: '.grid-item' });
});
Enter fullscreen mode Exit fullscreen mode

The other common delivery vehicle is WordPress itself. WordPress core ships imagesLoaded in wp-includes/js/imagesloaded.min.js, and themes and page-builder plugins enqueue it whenever a masonry block, filterable portfolio, or gallery widget needs it. That is worth knowing because it shapes what a detection means: a large share of imagesLoaded sites are WordPress sites, and the scan that finds the library will usually name the theme's ecosystem — Elementor, Slick, jQuery — in the same response.

Who Buys From Companies Using imagesLoaded

A company running imagesLoaded has an image-heavy site built on a JavaScript-layout stack that is, in 2026, at least one generation old. Two groups sell into exactly that profile.

Agencies and Freelancers Selling Front-End Modernization

imagesLoaded is a proxy for the whole jQuery-era gallery stack. A site loading it as a standalone file is very likely also loading jQuery, a carousel plugin, and a theme's worth of layout scripts — the exact surface an agency pitches to replace with modern CSS, a component framework, or a lighter theme. The signal also implies the site owner cares how the site looks: they picked an image-forward design and paid someone to build it once, which makes "your gallery stack is five years old and costing you Core Web Vitals" a pitch aimed at someone who already values the outcome. Because the detection usually arrives with the CMS and page builder attached, the outreach can be specific: you are not guessing that they run WordPress with Elementor, the scan said so.

SaaS Vendors Replacing Custom Gallery and Layout Code

Every imagesLoaded detection marks a site that solved image loading and layout by hand. That is the displacement target for gallery and portfolio plugins, digital asset management tools, image CDNs and optimization services, and headless media platforms. The pitch writes itself from the stack: a photography studio gluing imagesLoaded to Masonry inside a WordPress theme is maintaining code that a managed gallery product makes obsolete. And because imagesLoaded correlates with pages that ship many large images, image-optimization vendors get a second angle — these are precisely the sites where LCP improvements are visible to the owner.

How DetectZeStack Detects imagesLoaded

DetectZeStack analyzes the HTML a server returns; it does not execute JavaScript. For imagesLoaded that is enough, because the sites worth finding load it as a named file the scanner can see.

Script Signatures and CDN Fingerprints

The fingerprint keys on the script source. A script tag whose src ends in imagesloaded.js or imagesloaded.min.js resolves the site to imagesLoaded with a confidence of 100, whether the file is served from the site's own origin, from wp-includes/js/, or from a public CDN. The signature also captures the version from a ?v= or ?ver= query string when one is present:

<script src="/wp-content/plugins/.../imagesloaded.min.js?ver=3.0.0"></script>
Enter fullscreen mode Exit fullscreen mode

That ?ver= pattern is how WordPress enqueues every bundled script, which is why imagesLoaded detections so often come with a version number attached and a WordPress detection in the same scan. The version matters for qualification: it is the version of the library the theme shipped, and an old one is a direct measure of how long the front end has gone untouched.

The blind spot to know about: a site that compiles imagesLoaded into a webpack or similar bundle never exposes the filename, so no external scanner can see it — the library's own documentation site bundles its assets and scans clean. In practice this filters your list in a useful direction: script-source detection surfaces exactly the sites loading it the old way, as a standalone file, which are the modernization prospects anyway.

API Example: Check a Single Domain for imagesLoaded

Confirm the signal by hand first. The /demo endpoint needs no API key and no signup. Here it is against wpastra.com, the site of the Astra WordPress theme:

curl -s "https://detectzestack.com/demo?url=wpastra.com" \
| jq '.technologies[] | select(.name == "imagesLoaded")'
Enter fullscreen mode Exit fullscreen mode

Which returns:

{
"name": "imagesLoaded",
"version": "3.0.0",
"categories": [
"JavaScript libraries"
],
"confidence": 100,
"description": "jQuery plugin for seeing if the images are loaded.",
"website": "https://imagesloaded.desandro.com/",
"source": "http"
}
Enter fullscreen mode Exit fullscreen mode

Three fields matter. confidence is 100 because the script source matched exactly. version is 3.0.0, captured from the ?ver= query string on the enqueue. source is http, meaning the detection came from the HTML response rather than a DNS or TLS signal. The same scan returned the rest of the story: WordPress, Elementor, Slick, jQuery, and jQuery Migrate — the full image-heavy WordPress stack this article has been describing, in one call.

For real volume, get a free API key from RapidAPI and use the authenticated endpoints. When you only need a yes or no, /check returns one directly:

curl -s "https://detectzestack.p.rapidapi.com/check?url=wpastra.com&tech=imagesLoaded" \
-H "X-RapidAPI-Key: YOUR_KEY" \
-H "X-RapidAPI-Host: detectzestack.p.rapidapi.com" | jq '.'
Enter fullscreen mode Exit fullscreen mode
{
"domain": "wpastra.com",
"technology": "imagesLoaded",
"detected": true,
"confidence": 100,
"version": "3.0.0",
"categories": ["JavaScript libraries"],
"response_ms": 431,
"cached": false
}
Enter fullscreen mode Exit fullscreen mode

The tech parameter is case insensitive, so tech=imagesloaded works and the response echoes the canonical name imagesLoaded. When the library is absent, detected is false and confidence is 0. When a site loads the file without a version query string, version comes back as an empty string; that is a normal detection, not a partial one.

API Example: Build a List of Companies Using imagesLoaded

Single checks answer "does this domain run it?" and require you to bring the domain. The next two endpoints build lists.

Using /lookup?tech=imagesLoaded with Pagination and Tier Caps

/lookup inverts the question: it returns domains already in DetectZeStack's scan index that were observed running imagesLoaded.

curl -s "https://detectzestack.p.rapidapi.com/lookup?tech=imagesLoaded&limit=50" \
-H "X-RapidAPI-Key: YOUR_KEY" \
-H "X-RapidAPI-Host: detectzestack.p.rapidapi.com" | jq '.'
Enter fullscreen mode Exit fullscreen mode
{
"technology": "imagesLoaded",
"total": 42,
"results": [
{
"domain": "wpastra.com",
"category": "JavaScript libraries",
"confidence": 100,
"version": "3.0.0",
"first_seen": "2026-07-14",
"last_seen": "2026-07-14"
}
],
"limit": 50,
"offset": 0,
"response_ms": 18
}
Enter fullscreen mode Exit fullscreen mode

first_seen and last_seen have no equivalent in a live scan: they tell you when the index first observed the library on that domain and when it was last confirmed. Page through results with limit and offset. The rows returned per request are capped by plan, and requesting more than your cap clamps to it rather than erroring:

Plan Max /lookup rows per request
Basic (free) 2
Pro ($9/mo) 50
Ultra ($29/mo) 200
Mega ($79/mo) 800

/lookup reflects what has already been scanned, so treat it as a warm start rather than a census of the web. For a niche library like imagesLoaded, the stronger play is the other direction: bring your own candidate domains and scan them.

Scaling Up with POST /analyze/batch for Your Own Domain Lists

Batch accepts up to 10 URLs per request and scans them concurrently. Feed it a directory of photography studios, a list of agencies' portfolio clients, or an export from any lead database:

curl -s -X POST "https://detectzestack.p.rapidapi.com/analyze/batch" \
-H "X-RapidAPI-Key: YOUR_KEY" \
-H "X-RapidAPI-Host: detectzestack.p.rapidapi.com" \
-H "Content-Type: application/json" \
-d '{"urls": ["wpastra.com", "thisiscolossal.com", "example.com"]}' \
| jq -r '.results[]
| select(.result.technologies[]?.name == "imagesLoaded")
| .result.domain'
Enter fullscreen mode Exit fullscreen mode

The response wraps one result per URL with a per-item error field, so a single unreachable domain does not fail the batch, and the top level reports total_ms, successful, and failed counts. Each URL in a batch counts as one request against your monthly quota — a 10-URL batch costs 10 requests. Batching saves round trips, not quota.

Turning Detections Into a Prospect List

Putting it together: read domains from a file, scan them in batches of 10, keep the imagesLoaded sites, and record the stack context that makes the outreach specific.

import csv
import requests

API = "https://detectzestack.p.rapidapi.com/analyze/batch"
HEADERS = {
"X-RapidAPI-Key": "YOUR_KEY",
"X-RapidAPI-Host": "detectzestack.p.rapidapi.com",
"Content-Type": "application/json",
}
BATCH_SIZE = 10  # API maximum per request

def scan(domains):
"""Scan up to 10 domains, yielding (domain, technologies, status_code)."""
resp = requests.post(API, headers=HEADERS, json={"urls": domains}, timeout=60)
resp.raise_for_status()
for item in resp.json()["results"]:
result = item.get("result")
if not result:
print(f"  skipped {item['url']}: {item.get('error', 'no result')}")
continue
techs = result.get("technologies", [])
status = result.get("meta", {}).get("status_code")
yield result["domain"], techs, status

def main():
with open("domains.txt") as f:
domains = [line.strip() for line in f if line.strip()]

prospects = []
for i in range(0, len(domains), BATCH_SIZE):
chunk = domains[i:i + BATCH_SIZE]
print(f"Scanning {i + 1}-{i + len(chunk)} of {len(domains)}...")
for domain, techs, status in scan(chunk):
if status != 200:
# The page never loaded, so script tags could not be seen.
print(f"  {domain}: status {status}, inconclusive")
continue
names = {t["name"] for t in techs}
if "imagesLoaded" in names:
il = next(t for t in techs if t["name"] == "imagesLoaded")
prospects.append({
"domain": domain,
"imagesloaded_version": il.get("version", ""),
"wordpress": "yes" if "WordPress" in names else "no",
"jquery": "yes" if "jQuery" in names else "no",
"stack": ", ".join(sorted(names)),
})
print(f"  {domain}: imagesLoaded {il.get('version') or '(no version)'}")

with open("imagesloaded_prospects.csv", "w", newline="") as f:
writer = csv.DictWriter(
f,
fieldnames=["domain", "imagesloaded_version", "wordpress", "jquery", "stack"],
)
writer.writeheader()
writer.writerows(prospects)

print(f"\n{len(prospects)} imagesLoaded sites out of {len(domains)} domains.")

if __name__ == "__main__":
main()
Enter fullscreen mode Exit fullscreen mode

Two details in that script carry most of the value. The status != 200 guard treats blocked or failed fetches as inconclusive instead of negative — a site behind aggressive bot protection returns no HTML, so the library will be absent from results even if the site runs it, and silently dropping those rows corrupts the list. And the script records the version and the surrounding stack, because "you are running imagesLoaded 3.x inside an Elementor theme with jQuery Migrate" is an opener that proves you looked, where "I noticed you have a website" is not.

Results are cached for 24 hours by default. A cache hit comes back with "cached": true, which is worth knowing when you rescan a list: the second pass measures the cache, not the site.

Get Started

imagesLoaded is a narrow library with a broad implication: an image-heavy site whose layout still depends on jQuery-era JavaScript, usually inside a WordPress theme that names itself in the same scan. One API call finds the library, its version, and the entire stack around it — which is the research a modernization pitch or a gallery-SaaS displacement pitch actually needs.

Start with /demo to confirm the signal on a site you know. Move to /check for booleans, /analyze/batch for your own lists, and /lookup for a warm start from the index.

Related Reading

Top comments (0)