Find Companies Using Modernizr: Detection API Guide

September 23, 2026 · 10 min read

Modernizr is one of those libraries nobody adds to a new project anymore and nobody gets around to removing from an old one. That makes it a useful signal. A site still serving modernizr.js has a front end that was set up in the era when feature detection had to be done in JavaScript, and it has probably not been rebuilt since. Python.org, NPR, and Clojure.org all still load it today.

This guide covers how to spot Modernizr on a single site by hand, exactly how DetectZeStack detects it (and where that detection has gaps), and how to turn a domain list into a list of companies using Modernizr with a few API calls. Every API response and script path quoted below came from a real request made while writing this post.

What Modernizr Is and Why It Still Shows Up on Production Sites

Modernizr is a small JavaScript library that tests which HTML5 and CSS3 features the current browser supports. It records the answers in two places: a global Modernizr object (Modernizr.flexbox, Modernizr.svg, and so on), and a set of CSS classes on the <html> element, such as flexbox or no-flexbox. Stylesheets and scripts then branch on those results.

Feature Detection vs. User-Agent Sniffing, and Why Modernizr Mattered

Before feature detection, the usual way to decide whether a browser could handle something was to read its user-agent string and guess. That broke every time a browser shipped a new version or lied about its identity. Modernizr made the better approach easy: ask the browser directly whether a feature works, then load a polyfill or a fallback style only if it does not. It shipped as part of the HTML5 Boilerplate starter template, which is a large part of why it ended up on so many sites built in the early 2010s.

What a Modernizr Install Tells You About a Site's Front-End Age

Today's evergreen browsers support nearly everything Modernizr was built to test, so a fresh project rarely needs it. When you find it on a production site, it usually means one of three things:

The file itself often says how old it is. The script python.org serves at /static/js/libs/modernizr.js opens with this banner:

/* Modernizr 2.6.2 (Custom Build) | MIT & BSD
 * Build: http://modernizr.com/download/#-fontface-generatedcontent-input-inputtypes-geolocation-svg-touch-shiv-cssclasses-...

Modernizr 2.6.2 is a 2.x build from the early 2010s. That one comment tells you roughly when this front end was put together.

Who Looks for Companies Using Modernizr

Agencies Selling Front-End Modernization and Redesigns

A Modernizr hit is one of the cleaner "this site is due for a rebuild" signals you can get from outside. Paired with an old jQuery version in the same response, it gives an agency a concrete opening: here is what your site loads, here is when it dates from, here is what a modern build would remove.

Sales Teams Qualifying Leads by Legacy JavaScript Stack

If you sell performance tooling, front-end migration services, accessibility audits, or a headless CMS, a legacy JavaScript stack is a qualification filter. Modernizr works well as one because it is almost never added to new builds, so its presence tracks front-end age more reliably than jQuery does.

Competitive and Market Researchers

Researchers use library detection to measure how fast a segment modernizes: what share of a vertical still ships Modernizr, which of those also run jQuery 1.x, and how that changes quarter over quarter.

How to Detect Modernizr Manually

Check the Page Source for a modernizr.js Script Tag

The fastest check needs no browser. Fetch the HTML and grep for the filename. Use --compressed, because some servers (python.org is one) return gzip even to a plain curl, and grepping compressed bytes finds nothing:

curl -s --compressed https://www.python.org/ | grep -io '[^"]*modernizr[^"]*'

That prints /static/js/libs/modernizr.js. The same command against NPR prints a path ending in /lib/modernizr/modernizr.custom.js, and against Clojure.org prints /js/modernizr.js. To read the version, fetch the script and look at its first line:

curl -s --compressed https://www.python.org/static/js/libs/modernizr.js | head -c 200

Check the Browser Console for the Modernizr Global and Modernizr._version

In DevTools, the global object settles the question and usually gives the version too:

(function () {
  var m = window.Modernizr;
  console.log({
    loaded: typeof m === 'object' || typeof m === 'function',
    version: m && m._version,
    htmlClasses: document.documentElement.className.split(/\s+/).length
  });
})();

This check also finds Modernizr when it is bundled into an application file, because it runs inside the page instead of reading the HTML. That matters later: it is the one case the API cannot see.

Look for Feature Classes Added to the html Element

Modernizr adds a class for each test it runs. Inspect the <html> element in the Elements panel (not view-source, since the classes are added at runtime) and you will see a long list like js flexbox canvas svg no-touchevents. The raw HTML often carries class="no-js", which Modernizr swaps for js when it runs. Treat no-js as a hint only. It is an HTML5 Boilerplate convention that many sites keep without loading Modernizr at all.

How DetectZeStack Detects Modernizr

DetectZeStack's HTTP detection uses the open-source Wappalyzer fingerprint set. Its Modernizr entry declares two signals, and only one of them can fire in an HTTP scan:

Signal typeWhat it looks forUsable from an HTTP fetch?
jsModernizr._versionNo — needs a JavaScript runtime
scriptSrca <script src> path containing /modernizr and ending in .jsYes

DetectZeStack fetches the HTML and parses it. It does not run a headless browser or execute page JavaScript, so every Modernizr detection from the API comes from a script tag path. The match is case-insensitive and works with self-hosted files, CDN files, custom builds like modernizr.custom.js, and minified builds.

Script Source Filename Matching and Version Extraction from the Path

The version comes from the URL only, and the pattern is narrower than you might expect. We ran these paths through the same fingerprint library the API uses:

Script pathDetected?Version reported
/js/modernizr.jsyesempty
/js/modernizr.2.8.3.min.jsyes2.8.3. (note the trailing dot)
/js/vendor/modernizr-2.8.3.min.jsyesempty — the hyphen is not parsed
cdnjs .../ajax/libs/modernizr/2.8.3/modernizr.min.jsyesempty — the earlier /modernizr directory matches first
.../modernizr.custom.js?ver=2.6.2yesempty — query strings are ignored for this rule
/media/1089/modernizr.jsyes1089 — a media folder ID, not a version

Two takeaways. First, an empty version is normal. Of the 135 domains where DetectZeStack's index had recorded Modernizr as of this writing, 134 had no version. Second, the one that did have a version had a wrong one. The rule treats any numeric directory right before /modernizr as a version, and some CMSs put files in numbered media folders. Clearmatch.com serves /media/1089/modernizr.js, and the API reports "version": "1089". Slick on the same site reports 1166 for the same reason. If you filter by version, drop anything that does not look like 2.x or 3.x, and strip trailing dots.

Limits: Renamed or Bundled Builds Without a modernizr Filename

If the path has no modernizr in it, there is nothing to match. A Modernizr build compiled into /assets/app.4f2c1a.js by webpack, or saved as feature-detects.js, is invisible to an HTTP scan. We confirmed both paths produce no detection. The sites that bundle Modernizr are usually the ones with a newer build pipeline, so the API's Modernizr list leans toward exactly the legacy sites a prospecting list is after. Even so, read a missing entry as "no matching script tag," not as proof the library is absent.

API Example: Detect Modernizr with a curl Request

Single Domain Check with GET /analyze

For production use, call /analyze with your RapidAPI key and pull out the Modernizr entry with jq:

curl -s "https://detectzestack.p.rapidapi.com/analyze?url=https://www.python.org" \
  -H "x-rapidapi-key: $RAPIDAPI_KEY" \
  -H "x-rapidapi-host: detectzestack.p.rapidapi.com" \
  | jq '.technologies[] | select(.name == "Modernizr")'

If Modernizr is not detected, jq prints nothing and exits cleanly, which makes this easy to drop into a shell loop. If you only need a yes or no, /check returns a flat object with detected, confidence, and version:

curl -s "https://detectzestack.p.rapidapi.com/check?tech=Modernizr&url=https://www.python.org" \
  -H "x-rapidapi-key: $RAPIDAPI_KEY" \
  -H "x-rapidapi-host: detectzestack.p.rapidapi.com"

Reading the technologies Array: name, version, categories (JavaScript libraries)

Here is the live response for python.org, trimmed to the JavaScript libraries, with the categories map and the meta block left intact:

{
  "url": "https://www.python.org",
  "domain": "www.python.org",
  "technologies": [
    {
      "name": "Modernizr",
      "categories": ["JavaScript libraries"],
      "confidence": 100,
      "description": "Modernizr is a JavaScript library that detects the features available in a user's browser.",
      "website": "https://modernizr.com",
      "icon": "Modernizr.svg",
      "source": "http"
    },
    {
      "name": "jQuery",
      "version": "1.8.2",
      "categories": ["JavaScript libraries"],
      "confidence": 100,
      "description": "jQuery is a JavaScript library which is a free, open-source software designed to simplify HTML DOM tree traversal and manipulation, as well as event handling, CSS animation, and Ajax.",
      "website": "https://jquery.com",
      "icon": "jQuery.svg",
      "cpe": "cpe:2.3:a:jquery:jquery:*:*:*:*:*:*:*:*",
      "source": "http"
    }
  ],
  "categories": {
    "JavaScript libraries": ["jQuery", "jQuery UI", "Modernizr"]
  },
  "meta": { "status_code": 200, "tech_count": 10, "scan_depth": "full" },
  "cached": false,
  "response_ms": 555
}

What to take from it:

Try It Without a Key Using GET /demo

The public /demo endpoint runs the same detection with no API key. It is rate-limited by IP, but it is enough to check the response shape before you write any code:

curl -s "https://detectzestack.com/demo?url=https://www.python.org" | python3 -m json.tool

Try it on https://clojure.org or https://www.npr.org too. Both returned a Modernizr entry with no version when we checked.

Building a List of Companies Using Modernizr at Scale

There are two ways to build the list: start from the index, or start from your own domains. Most teams do both.

To start from the index, use /lookup. It returns domains where Modernizr has already been detected, with first_seen and last_seen timestamps. How many results you get per page depends on your plan:

curl -s "https://detectzestack.p.rapidapi.com/lookup?tech=Modernizr&limit=50" \
  -H "x-rapidapi-key: $RAPIDAPI_KEY" \
  -H "x-rapidapi-host: detectzestack.p.rapidapi.com" \
  | jq -r '.results[].domain'

Scanning a Prospect List with POST /analyze/batch

To check your own list, such as a CRM export or a conference attendee list, send up to 10 URLs per request to /analyze/batch. Each URL counts as one request against your quota:

curl -s -X POST "https://detectzestack.p.rapidapi.com/analyze/batch" \
  -H "x-rapidapi-key: $RAPIDAPI_KEY" \
  -H "x-rapidapi-host: detectzestack.p.rapidapi.com" \
  -H "Content-Type: application/json" \
  -d '{"urls": ["https://www.python.org", "https://www.npr.org", "https://clojure.org"]}' \
  | jq -c '.results[] | {url: .url, modernizr: ([.result.technologies[]? | select(.name == "Modernizr")] | length > 0)}'

In a batch response, each item nests its analysis under .result (or has an error string instead). The top level also carries total_ms, successful, and failed. With those three domains, based on the single-domain scans above, the filter prints:

{"url":"https://www.python.org","modernizr":true}
{"url":"https://www.npr.org","modernizr":true}
{"url":"https://clojure.org","modernizr":true}

For lists longer than 10, send them in chunks. Batch Scan 1,000 Websites for Tech Stack covers concurrency, retries, and quota planning.

Comparing Competitor Front Ends with POST /compare

If you want to see which competitors still run Modernizr, not scan a whole list, /compare takes 2 to 10 URLs and returns each domain's technologies, a unique list per domain, and a top-level shared list:

curl -s -X POST "https://detectzestack.p.rapidapi.com/compare" \
  -H "x-rapidapi-key: $RAPIDAPI_KEY" \
  -H "x-rapidapi-host: detectzestack.p.rapidapi.com" \
  -H "Content-Type: application/json" \
  -d '{"urls": ["https://www.python.org", "https://clojure.org"]}' \
  | jq '{shared: .shared, unique: [.domains[] | {domain, unique}]}'

If Modernizr appears in shared, every site you compared loads it. If it appears in only one domain's unique list, that competitor is the one still carrying it.

Filtering Results by Version and Exporting to a CRM

This script reads domains from domains.txt, scans them in batches of 10, and writes a CSV with the Modernizr version and the jQuery version from the same scan. It applies the version check from earlier, so media-folder IDs like 1089 are dropped and trailing dots are stripped:

import csv, json, os, re, urllib.request

KEY = os.environ["RAPIDAPI_KEY"]
URL = "https://detectzestack.p.rapidapi.com/analyze/batch"

def clean(v):
    v = (v or "").rstrip(".")
    return v if re.match(r"^[23]\.\d+", v) else ""

domains = [d.strip() for d in open("domains.txt") if d.strip()]
rows = []
for i in range(0, len(domains), 10):
    chunk = ["https://" + d if not d.startswith("http") else d for d in domains[i:i+10]]
    req = urllib.request.Request(URL, data=json.dumps({"urls": chunk}).encode(), headers={
        "x-rapidapi-key": KEY,
        "x-rapidapi-host": "detectzestack.p.rapidapi.com",
        "Content-Type": "application/json",
    })
    with urllib.request.urlopen(req, timeout=120) as resp:
        data = json.load(resp)
    for item in data["results"]:
        techs = {t["name"]: t for t in (item.get("result") or {}).get("technologies", [])}
        if "Modernizr" in techs:
            rows.append({
                "url": item["url"],
                "modernizr_version": clean(techs["Modernizr"].get("version")),
                "jquery_version": techs.get("jQuery", {}).get("version", ""),
                "tech_count": len(techs),
            })

with open("modernizr_prospects.csv", "w", newline="") as f:
    w = csv.DictWriter(f, fieldnames=["url", "modernizr_version", "jquery_version", "tech_count"])
    w.writeheader()
    w.writerows(rows)
print(f"{len(rows)} of {len(domains)} domains load Modernizr")

The CSV imports straight into HubSpot, Salesforce, or a spreadsheet. For a full enrichment flow, see Build a Lead Enrichment Pipeline with Tech Detection.

Modernizr Alongside jQuery, Bootstrap, and Other Legacy Libraries

Because /analyze returns the whole stack in one response, you get the libraries next to Modernizr for free, and they often tell you more than Modernizr does alone. From the live scans for this post:

The combinations are worth filtering on. Modernizr with jQuery 1.x is the strongest "not touched in a decade" signal. Modernizr with Google Hosted Libraries or the jQuery CDN means dependencies still come from public CDNs. Modernizr with jQuery carousel plugins or imagesLoaded points to an off-the-shelf theme. For the neighbouring libraries, see How to Detect jQuery and How to Detect Bootstrap.

FAQ

How do I check if a website uses Modernizr?

In DevTools, run typeof window.Modernizr. It returns "object" when the library is loaded, and Modernizr._version usually gives the version. Without a browser, grep the HTML for a script path containing modernizr. Through the API, call /demo or /analyze and look for name == "Modernizr" in technologies[].

How do I find a list of companies using Modernizr?

Use /lookup?tech=Modernizr to get domains already in the index, then check your own prospect list 10 URLs at a time with POST /analyze/batch.

Why is the Modernizr version empty?

The version is read from the script URL only, and most paths do not carry one in a form the rule can parse. Fetch the script and read the banner comment for the real version.

Can the API detect a bundled Modernizr build?

No. Without a script path containing modernizr, there is nothing for an HTTP scan to match. The DevTools console check will still find it.

Conclusion: Get a Free API Key and Start Scanning

Modernizr is a good legacy-stack signal because almost no one adds it to a new build. DetectZeStack detects it from the script tag path, which covers the self-hosted, CDN, and custom-build filenames most legacy sites use. It will miss bundled builds, and the version field is usually empty (and sometimes wrong), so check the banner comment when the exact version matters.

To build your list: one /demo call to check the response shape, /lookup for domains already in the index, /analyze/batch for your own list, and the script above to export it to your CRM. Each scan also returns the jQuery version, CMS, CDN, and hosting, so you get the whole stack from the same request.

Related Reading

Find Every Site Still Running Modernizr

One HTTP request returns every framework, library, CDN, CMS, and analytics tag on a page, with versions where the URL shows them. 100 requests per month free. No credit card.

Get your free API key

Get API updates and tech detection tips

Join the mailing list. No spam, unsubscribe anytime.