DEV Community

MikeL
MikeL

Posted on Originally published at detectzestack.com

Find Companies Using Modernizr: Detection API Guide

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 site's template or theme dates from the HTML5 Boilerplate era and has been maintained rather than rebuilt.
  • A CMS theme or agency starter kit bundled it by default, and it has stayed in the page ever since.
  • Some CSS still branches on Modernizr classes, so removing it is not a one-line change.

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-...
Enter fullscreen mode Exit fullscreen mode

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[^"]*'
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
});
})();
Enter fullscreen mode Exit fullscreen mode

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 type What it looks for Usable from an HTTP fetch?
js Modernizr._version No — needs a JavaScript runtime
scriptSrc a path containing /modernizr and ending in .js</td> <td>Yes</td> </tr> </tbody></table> <p>DetectZeStack fetches the HTML and parses it. It does not run a headless browser or execute page JavaScript, so <strong>every Modernizr detection from the API comes from a script tag path</strong>. The match is case-insensitive and works with self-hosted files, CDN files, custom builds like <code>modernizr.custom.js</code>, and minified builds.</p> <h3> <a name="script-source-filename-matching-and-version-extraction-from-the-path" href="#script-source-filename-matching-and-version-extraction-from-the-path" class="anchor"> </a> Script Source Filename Matching and Version Extraction from the Path </h3> <p>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:</p> <table><thead> <tr> <th>Script path</th> <th>Detected?</th> <th>Version reported</th> </tr> </thead><tbody> <tr> <td>/js/modernizr.js</td> <td>yes</td> <td>empty</td> </tr> <tr> <td>/js/modernizr.2.8.3.min.js</td> <td>yes</td> <td>2.8.3. (note the trailing dot)</td> </tr> <tr> <td>/js/vendor/modernizr-2.8.3.min.js</td> <td>yes</td> <td>empty — the hyphen is not parsed</td> </tr> <tr> <td>cdnjs .../ajax/libs/modernizr/2.8.3/modernizr.min.js</td> <td>yes</td> <td>empty — the earlier /modernizr directory matches first</td> </tr> <tr> <td>.../modernizr.custom.js?ver=2.6.2</td> <td>yes</td> <td>empty — query strings are ignored for this rule</td> </tr> <tr> <td>/media/1089/modernizr.js</td> <td>yes</td> <td>1089 — a media folder ID, not a version</td> </tr> </tbody></table> <p>Two takeaways. First, an empty version is normal. Of the 135 domains where DetectZeStack&#39;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 <code>/modernizr</code> as a version, and some CMSs put files in numbered media folders. Clearmatch.com serves <code>/media/1089/modernizr.js</code>, and the API reports <code>&quot;version&quot;: &quot;1089&quot;</code>. Slick on the same site reports <code>1166</code> for the same reason. If you filter by version, drop anything that does not look like <code>2.x</code> or <code>3.x</code>, and strip trailing dots.</p> <h3> <a name="limits-renamed-or-bundled-builds-without-a-modernizr-filename" href="#limits-renamed-or-bundled-builds-without-a-modernizr-filename" class="anchor"> </a> Limits: Renamed or Bundled Builds Without a modernizr Filename </h3> <p>If the path has no <code>modernizr</code> in it, there is nothing to match. A Modernizr build compiled into <code>/assets/app.4f2c1a.js</code> by webpack, or saved as <code>feature-detects.js</code>, 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&#39;s Modernizr list leans toward exactly the legacy sites a prospecting list is after. Even so, read a missing entry as &quot;no matching script tag,&quot; not as proof the library is absent.</p> <h2> <a name="api-example-detect-modernizr-with-a-curl-request" href="#api-example-detect-modernizr-with-a-curl-request" class="anchor"> </a> API Example: Detect Modernizr with a curl Request </h2> <h3> <a name="single-domain-check-with-get-analyze" href="#single-domain-check-with-get-analyze" class="anchor"> </a> Single Domain Check with GET /analyze </h3> <p>For production use, call <code>/analyze</code> with your RapidAPI key and pull out the Modernizr entry with <code>jq</code>:<br> </p> <div class="highlight"><pre class="highlight shell"><code>curl <span class="nt">-s</span> <span class="s2">"https://detectzestack.p.rapidapi.com/analyze?url=https://www.python.org"</span> <span class="se">\</span> <span class="nt">-H</span> <span class="s2">"x-rapidapi-key: </span><span class="nv">$RAPIDAPI_KEY</span><span class="s2">"</span> <span class="se">\</span> <span class="nt">-H</span> <span class="s2">"x-rapidapi-host: detectzestack.p.rapidapi.com"</span> <span class="se">\</span> | jq <span class="s1">'.technologies[] | select(.name == "Modernizr")'</span> </code></pre></div> <p></p> <p>If Modernizr is not detected, <code>jq</code> prints nothing and exits cleanly, which makes this easy to drop into a shell loop. If you only need a yes or no, <code>/check</code> returns a flat object with <code>detected</code>, <code>confidence</code>, and <code>version</code>:<br> </p> <div class="highlight"><pre class="highlight shell"><code>curl <span class="nt">-s</span> <span class="s2">"https://detectzestack.p.rapidapi.com/check?tech=Modernizr&amp;url=https://www.python.org"</span> <span class="se">\</span> <span class="nt">-H</span> <span class="s2">"x-rapidapi-key: </span><span class="nv">$RAPIDAPI_KEY</span><span class="s2">"</span> <span class="se">\</span> <span class="nt">-H</span> <span class="s2">"x-rapidapi-host: detectzestack.p.rapidapi.com"</span> </code></pre></div> <p></p> <h3> <a name="reading-the-technologies-array-name-version-categories-javascript-libraries" href="#reading-the-technologies-array-name-version-categories-javascript-libraries" class="anchor"> </a> Reading the technologies Array: name, version, categories (JavaScript libraries) </h3> <p>Here is the live response for python.org, trimmed to the JavaScript libraries, with the <code>categories</code> map and the <code>meta</code> block left intact:<br> </p> <div class="highlight"><pre class="highlight json"><code><span class="p">{</span><span class="w"> </span><span class="nl">"url"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://www.python.org"</span><span class="p">,</span><span class="w"> </span><span class="nl">"domain"</span><span class="p">:</span><span class="w"> </span><span class="s2">"www.python.org"</span><span class="p">,</span><span class="w"> </span><span class="nl">"technologies"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Modernizr"</span><span class="p">,</span><span class="w"> </span><span class="nl">"categories"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"JavaScript libraries"</span><span class="p">],</span><span class="w"> </span><span class="nl">"confidence"</span><span class="p">:</span><span class="w"> </span><span class="mi">100</span><span class="p">,</span><span class="w"> </span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Modernizr is a JavaScript library that detects the features available in a user's browser."</span><span class="p">,</span><span class="w"> </span><span class="nl">"website"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://modernizr.com"</span><span class="p">,</span><span class="w"> </span><span class="nl">"icon"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Modernizr.svg"</span><span class="p">,</span><span class="w"> </span><span class="nl">"source"</span><span class="p">:</span><span class="w"> </span><span class="s2">"http"</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"jQuery"</span><span class="p">,</span><span class="w"> </span><span class="nl">"version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1.8.2"</span><span class="p">,</span><span class="w"> </span><span class="nl">"categories"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"JavaScript libraries"</span><span class="p">],</span><span class="w"> </span><span class="nl">"confidence"</span><span class="p">:</span><span class="w"> </span><span class="mi">100</span><span class="p">,</span><span class="w"> </span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"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."</span><span class="p">,</span><span class="w"> </span><span class="nl">"website"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://jquery.com"</span><span class="p">,</span><span class="w"> </span><span class="nl">"icon"</span><span class="p">:</span><span class="w"> </span><span class="s2">"jQuery.svg"</span><span class="p">,</span><span class="w"> </span><span class="nl">"cpe"</span><span class="p">:</span><span class="w"> </span><span class="s2">"cpe:2.3🅰️jquery:jquery:*:*:*:*:*:*:*:*"</span><span class="p">,</span><span class="w"> </span><span class="nl">"source"</span><span class="p">:</span><span class="w"> </span><span class="s2">"http"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">],</span><span class="w"> </span><span class="nl">"categories"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"JavaScript libraries"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"jQuery"</span><span class="p">,</span><span class="w"> </span><span class="s2">"jQuery UI"</span><span class="p">,</span><span class="w"> </span><span class="s2">"Modernizr"</span><span class="p">]</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="nl">"meta"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"status_code"</span><span class="p">:</span><span class="w"> </span><span class="mi">200</span><span class="p">,</span><span class="w"> </span><span class="nl">"tech_count"</span><span class="p">:</span><span class="w"> </span><span class="mi">10</span><span class="p">,</span><span class="w"> </span><span class="nl">"scan_depth"</span><span class="p">:</span><span class="w"> </span><span class="s2">"full"</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="nl">"cached"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"> </span><span class="nl">"response_ms"</span><span class="p">:</span><span class="w"> </span><span class="mi">555</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre></div> <p></p> <p>What to take from it:</p> <ul> <li><code>name</code> is exactly <code>Modernizr</code>, with a capital M. Filter on that string.</li> <li><code>categories</code> is <code>[&quot;JavaScript libraries&quot;]</code>, so Modernizr also appears in the top-level <code>categories</code> map under that key.</li> <li>The <code>version</code> field is missing. It is omitted when empty, because the path <code>/static/js/libs/modernizr.js</code> has no version in it, even though the file&#39;s banner says 2.6.2. In <code>jq</code>, use <code>.version // &quot;&quot;</code> to handle both cases.</li> <li><code>source</code> is <code>http</code>: the detection came from the fetched HTML.</li> <li>jQuery 1.8.2 shows up in the same response. Modernizr and old jQuery together are the usual legacy pairing.</li> </ul> <h3> <a name="try-it-without-a-key-using-get-demo" href="#try-it-without-a-key-using-get-demo" class="anchor"> </a> Try It Without a Key Using GET /demo </h3> <p>The public <code>/demo</code> 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:<br> </p> <div class="highlight"><pre class="highlight shell"><code>curl <span class="nt">-s</span> <span class="s2">"https://detectzestack.com/demo?url=https://www.python.org"</span> | python3 <span class="nt">-m</span> json.tool </code></pre></div> <p></p> <p>Try it on <code>https://clojure.org</code> or <code>https://www.npr.org</code> too. Both returned a <code>Modernizr</code> entry with no version when we checked.</p> <h2> <a name="building-a-list-of-companies-using-modernizr-at-scale" href="#building-a-list-of-companies-using-modernizr-at-scale" class="anchor"> </a> Building a List of Companies Using Modernizr at Scale </h2> <p>There are two ways to build the list: start from the index, or start from your own domains. Most teams do both.</p> <p>To start from the index, use <code>/lookup</code>. It returns domains where Modernizr has already been detected, with <code>first_seen</code> and <code>last_seen</code> timestamps. How many results you get per page depends on your plan:<br> </p> <div class="highlight"><pre class="highlight shell"><code>curl <span class="nt">-s</span> <span class="s2">"https://detectzestack.p.rapidapi.com/lookup?tech=Modernizr&amp;limit=50"</span> <span class="se">\</span> <span class="nt">-H</span> <span class="s2">"x-rapidapi-key: </span><span class="nv">$RAPIDAPI_KEY</span><span class="s2">"</span> <span class="se">\</span> <span class="nt">-H</span> <span class="s2">"x-rapidapi-host: detectzestack.p.rapidapi.com"</span> <span class="se">\</span> | jq <span class="nt">-r</span> <span class="s1">'.results[].domain'</span> </code></pre></div> <p></p> <h3> <a name="scanning-a-prospect-list-with-post-analyzebatch" href="#scanning-a-prospect-list-with-post-analyzebatch" class="anchor"> </a> Scanning a Prospect List with POST /analyze/batch </h3> <p>To check your own list, such as a CRM export or a conference attendee list, send up to 10 URLs per request to <code>/analyze/batch</code>. Each URL counts as one request against your quota:<br> </p> <div class="highlight"><pre class="highlight shell"><code>curl <span class="nt">-s</span> <span class="nt">-X</span> POST <span class="s2">"https://detectzestack.p.rapidapi.com/analyze/batch"</span> <span class="se">\</span> <span class="nt">-H</span> <span class="s2">"x-rapidapi-key: </span><span class="nv">$RAPIDAPI_KEY</span><span class="s2">"</span> <span class="se">\</span> <span class="nt">-H</span> <span class="s2">"x-rapidapi-host: detectzestack.p.rapidapi.com"</span> <span class="se">\</span> <span class="nt">-H</span> <span class="s2">"Content-Type: application/json"</span> <span class="se">\</span> <span class="nt">-d</span> <span class="s1">'{"urls": ["https://www.python.org", "https://www.npr.org", "https://clojure.org"]}'</span> <span class="se">\</span> | jq <span class="nt">-c</span> <span class="s1">'.results[] | {url: .url, modernizr: ([.result.technologies[]? | select(.name == "Modernizr")] | length &gt; 0)}'</span> </code></pre></div> <p></p> <p>In a batch response, each item nests its analysis under <code>.result</code> (or has an <code>error</code> string instead). The top level also carries <code>total_ms</code>, <code>successful</code>, and <code>failed</code>. With those three domains, based on the single-domain scans above, the filter prints:<br> </p> <div class="highlight"><pre class="highlight json"><code><span class="p">{</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"https://www.python.org"</span><span class="p">,</span><span class="nl">"modernizr"</span><span class="p">:</span><span class="kc">true</span><span class="p">}</span><span class="w"> </span><span class="p">{</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"https://www.npr.org"</span><span class="p">,</span><span class="nl">"modernizr"</span><span class="p">:</span><span class="kc">true</span><span class="p">}</span><span class="w"> </span><span class="p">{</span><span class="nl">"url"</span><span class="p">:</span><span class="s2">"https://clojure.org"</span><span class="p">,</span><span class="nl">"modernizr"</span><span class="p">:</span><span class="kc">true</span><span class="p">}</span><span class="w"> </span></code></pre></div> <p></p> <p>For lists longer than 10, send them in chunks. <a href="https://detectzestack.com/blog/batch-scan-1000-websites">Batch Scan 1,000 Websites for Tech Stack</a> covers concurrency, retries, and quota planning.</p> <h3> <a name="comparing-competitor-front-ends-with-post-compare" href="#comparing-competitor-front-ends-with-post-compare" class="anchor"> </a> Comparing Competitor Front Ends with POST /compare </h3> <p>If you want to see which competitors still run Modernizr, not scan a whole list, <code>/compare</code> takes 2 to 10 URLs and returns each domain&#39;s technologies, a <code>unique</code> list per domain, and a top-level <code>shared</code> list:<br> </p> <div class="highlight"><pre class="highlight shell"><code>curl <span class="nt">-s</span> <span class="nt">-X</span> POST <span class="s2">"https://detectzestack.p.rapidapi.com/compare"</span> <span class="se">\</span> <span class="nt">-H</span> <span class="s2">"x-rapidapi-key: </span><span class="nv">$RAPIDAPI_KEY</span><span class="s2">"</span> <span class="se">\</span> <span class="nt">-H</span> <span class="s2">"x-rapidapi-host: detectzestack.p.rapidapi.com"</span> <span class="se">\</span> <span class="nt">-H</span> <span class="s2">"Content-Type: application/json"</span> <span class="se">\</span> <span class="nt">-d</span> <span class="s1">'{"urls": ["https://www.python.org", "https://clojure.org"]}'</span> <span class="se">\</span> | jq <span class="s1">'{shared: .shared, unique: [.domains[] | {domain, unique}]}'</span> </code></pre></div> <p></p> <p>If <code>Modernizr</code> appears in <code>shared</code>, every site you compared loads it. If it appears in only one domain&#39;s <code>unique</code> list, that competitor is the one still carrying it.</p> <h3> <a name="filtering-results-by-version-and-exporting-to-a-crm" href="#filtering-results-by-version-and-exporting-to-a-crm" class="anchor"> </a> Filtering Results by Version and Exporting to a CRM </h3> <p>This script reads domains from <code>domains.txt</code>, 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 <code>1089</code> are dropped and trailing dots are stripped:<br> </p> <div class="highlight"><pre class="highlight python"><code><span class="kn">import</span> <span class="n">csv</span><span class="p">,</span> <span class="n">json</span><span class="p">,</span> <span class="n">os</span><span class="p">,</span> <span class="n">re</span><span class="p">,</span> <span class="n">urllib</span><span class="p">.</span><span class="n">request</span> <span class="n">KEY</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">environ</span><span class="p">[</span><span class="sh">"</span><span class="s">RAPIDAPI_KEY</span><span class="sh">"</span><span class="p">]</span> <span class="n">URL</span> <span class="o">=</span> <span class="sh">"</span><span class="s">https://detectzestack.p.rapidapi.com/analyze/batch</span><span class="sh">"</span> <span class="k">def</span> <span class="nf">clean</span><span class="p">(</span><span class="n">v</span><span class="p">):</span> <span class="n">v</span> <span class="o">=</span> <span class="p">(</span><span class="n">v</span> <span class="ow">or</span> <span class="sh">""</span><span class="p">).</span><span class="nf">rstrip</span><span class="p">(</span><span class="sh">"</span><span class="s">.</span><span class="sh">"</span><span class="p">)</span> <span class="k">return</span> <span class="n">v</span> <span class="k">if</span> <span class="n">re</span><span class="p">.</span><span class="nf">match</span><span class="p">(</span><span class="sa">r</span><span class="sh">"</span><span class="s">^[23]\.\d+</span><span class="sh">"</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span> <span class="k">else</span> <span class="sh">""</span> <span class="n">domains</span> <span class="o">=</span> <span class="p">[</span><span class="n">d</span><span class="p">.</span><span class="nf">strip</span><span class="p">()</span> <span class="k">for</span> <span class="n">d</span> <span class="ow">in</span> <span class="nf">open</span><span class="p">(</span><span class="sh">"</span><span class="s">domains.txt</span><span class="sh">"</span><span class="p">)</span> <span class="k">if</span> <span class="n">d</span><span class="p">.</span><span class="nf">strip</span><span class="p">()]</span> <span class="n">rows</span> <span class="o">=</span> <span class="p">[]</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nf">len</span><span class="p">(</span><span class="n">domains</span><span class="p">),</span> <span class="mi">10</span><span class="p">):</span> <span class="n">chunk</span> <span class="o">=</span> <span class="p">[</span><span class="sh">"</span><span class="s">https://</span><span class="sh">"</span> <span class="o">+</span> <span class="n">d</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">d</span><span class="p">.</span><span class="nf">startswith</span><span class="p">(</span><span class="sh">"</span><span class="s">http</span><span class="sh">"</span><span class="p">)</span> <span class="k">else</span> <span class="n">d</span> <span class="k">for</span> <span class="n">d</span> <span class="ow">in</span> <span class="n">domains</span><span class="p">[</span><span class="n">i</span><span class="p">:</span><span class="n">i</span><span class="o">+</span><span class="mi">10</span><span class="p">]]</span> <span class="n">req</span> <span class="o">=</span> <span class="n">urllib</span><span class="p">.</span><span class="n">request</span><span class="p">.</span><span class="nc">Request</span><span class="p">(</span><span class="n">URL</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="n">json</span><span class="p">.</span><span class="nf">dumps</span><span class="p">({</span><span class="sh">"</span><span class="s">urls</span><span class="sh">"</span><span class="p">:</span> <span class="n">chunk</span><span class="p">}).</span><span class="nf">encode</span><span class="p">(),</span> <span class="n">headers</span><span class="o">=</span><span class="p">{</span> <span class="sh">"</span><span class="s">x-rapidapi-key</span><span class="sh">"</span><span class="p">:</span> <span class="n">KEY</span><span class="p">,</span> <span class="sh">"</span><span class="s">x-rapidapi-host</span><span class="sh">"</span><span class="p">:</span> <span class="sh">"</span><span class="s">detectzestack.p.rapidapi.com</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">Content-Type</span><span class="sh">"</span><span class="p">:</span> <span class="sh">"</span><span class="s">application/json</span><span class="sh">"</span><span class="p">,</span> <span class="p">})</span> <span class="k">with</span> <span class="n">urllib</span><span class="p">.</span><span class="n">request</span><span class="p">.</span><span class="nf">urlopen</span><span class="p">(</span><span class="n">req</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mi">120</span><span class="p">)</span> <span class="k">as</span> <span class="n">resp</span><span class="p">:</span> <span class="n">data</span> <span class="o">=</span> <span class="n">json</span><span class="p">.</span><span class="nf">load</span><span class="p">(</span><span class="n">resp</span><span class="p">)</span> <span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">data</span><span class="p">[</span><span class="sh">"</span><span class="s">results</span><span class="sh">"</span><span class="p">]:</span> <span class="n">techs</span> <span class="o">=</span> <span class="p">{</span><span class="n">t</span><span class="p">[</span><span class="sh">"</span><span class="s">name</span><span class="sh">"</span><span class="p">]:</span> <span class="n">t</span> <span class="k">for</span> <span class="n">t</span> <span class="ow">in</span> <span class="p">(</span><span class="n">item</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">result</span><span class="sh">"</span><span class="p">)</span> <span class="ow">or</span> <span class="p">{}).</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">technologies</span><span class="sh">"</span><span class="p">,</span> <span class="p">[])}</span> <span class="k">if</span> <span class="sh">"</span><span class="s">Modernizr</span><span class="sh">"</span> <span class="ow">in</span> <span class="n">techs</span><span class="p">:</span> <span class="n">rows</span><span class="p">.</span><span class="nf">append</span><span class="p">({</span> <span class="sh">"</span><span class="s">url</span><span class="sh">"</span><span class="p">:</span> <span class="n">item</span><span class="p">[</span><span class="sh">"</span><span class="s">url</span><span class="sh">"</span><span class="p">],</span> <span class="sh">"</span><span class="s">modernizr_version</span><span class="sh">"</span><span class="p">:</span> <span class="nf">clean</span><span class="p">(</span><span class="n">techs</span><span class="p">[</span><span class="sh">"</span><span class="s">Modernizr</span><span class="sh">"</span><span class="p">].</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">version</span><span class="sh">"</span><span class="p">)),</span> <span class="sh">"</span><span class="s">jquery_version</span><span class="sh">"</span><span class="p">:</span> <span class="n">techs</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">jQuery</span><span class="sh">"</span><span class="p">,</span> <span class="p">{}).</span><span class="nf">get</span><span class="p">(</span><span class="sh">"</span><span class="s">version</span><span class="sh">"</span><span class="p">,</span> <span class="sh">""</span><span class="p">),</span> <span class="sh">"</span><span class="s">tech_count</span><span class="sh">"</span><span class="p">:</span> <span class="nf">len</span><span class="p">(</span><span class="n">techs</span><span class="p">),</span> <span class="p">})</span> <span class="k">with</span> <span class="nf">open</span><span class="p">(</span><span class="sh">"</span><span class="s">modernizr_prospects.csv</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">w</span><span class="sh">"</span><span class="p">,</span> <span class="n">newline</span><span class="o">=</span><span class="sh">""</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span> <span class="n">w</span> <span class="o">=</span> <span class="n">csv</span><span class="p">.</span><span class="nc">DictWriter</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">fieldnames</span><span class="o">=</span><span class="p">[</span><span class="sh">"</span><span class="s">url</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">modernizr_version</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">jquery_version</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">tech_count</span><span class="sh">"</span><span class="p">])</span> <span class="n">w</span><span class="p">.</span><span class="nf">writeheader</span><span class="p">()</span> <span class="n">w</span><span class="p">.</span><span class="nf">writerows</span><span class="p">(</span><span class="n">rows</span><span class="p">)</span> <span class="nf">print</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="si">{</span><span class="nf">len</span><span class="p">(</span><span class="n">rows</span><span class="p">)</span><span class="si">}</span><span class="s"> of </span><span class="si">{</span><span class="nf">len</span><span class="p">(</span><span class="n">domains</span><span class="p">)</span><span class="si">}</span><span class="s"> domains load Modernizr</span><span class="sh">"</span><span class="p">)</span> </code></pre></div> <p></p> <p>The CSV imports straight into HubSpot, Salesforce, or a spreadsheet. For a full enrichment flow, see <a href="https://detectzestack.com/blog/lead-enrichment-pipeline-with-tech-detection">Build a Lead Enrichment Pipeline with Tech Detection</a>.</p> <h2> <a name="modernizr-alongside-jquery-bootstrap-and-other-legacy-libraries" href="#modernizr-alongside-jquery-bootstrap-and-other-legacy-libraries" class="anchor"> </a> Modernizr Alongside jQuery, Bootstrap, and Other Legacy Libraries </h2> <p>Because <code>/analyze</code> 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:</p> <ul> <li><strong>python.org:</strong> Modernizr, jQuery 1.8.2, jQuery UI 1.12.1, Google Hosted Libraries, Nginx, Varnish, and Fastly.</li> <li><strong>clojure.org:</strong> Modernizr, jQuery 1.11.1, and Google Hosted Libraries, on Webflow behind Amazon CloudFront.</li> <li><strong>clearmatch.com:</strong> Modernizr, jQuery 2.1.4, Slick, Stellar.js, and ZURB Foundation.</li> <li><strong>poynter.org:</strong> Modernizr on WordPress, with Bootstrap 5.3.3, jQuery Migrate 3.4.1, Slick 1.6.0, and Magnific Popup. Here Modernizr sits next to a current Bootstrap, which shows a theme that was updated one piece at a time instead of rebuilt.</li> </ul> <p>The combinations are worth filtering on. Modernizr with jQuery 1.x is the strongest &quot;not touched in a decade&quot; signal. Modernizr with <a href="https://detectzestack.com/blog/find-companies-using-google-hosted-libraries">Google Hosted Libraries</a> or <a href="https://detectzestack.com/blog/find-companies-using-jquery-cdn">the jQuery CDN</a> means dependencies still come from public CDNs. Modernizr with jQuery carousel plugins or <a href="https://detectzestack.com/blog/find-companies-using-imagesloaded">imagesLoaded</a> points to an off-the-shelf theme. For the neighbouring libraries, see <a href="https://detectzestack.com/blog/how-to-detect-jquery">How to Detect jQuery</a> and <a href="https://detectzestack.com/blog/how-to-detect-bootstrap">How to Detect Bootstrap</a>.</p> <h2> <a name="faq" href="#faq" class="anchor"> </a> FAQ </h2> <h3> <a name="how-do-i-check-if-a-website-uses-modernizr" href="#how-do-i-check-if-a-website-uses-modernizr" class="anchor"> </a> How do I check if a website uses Modernizr? </h3> <p>In DevTools, run <code>typeof window.Modernizr</code>. It returns <code>&quot;object&quot;</code> when the library is loaded, and <code>Modernizr._version</code> usually gives the version. Without a browser, grep the HTML for a script path containing <code>modernizr</code>. Through the API, call <code>/demo</code> or <code>/analyze</code> and look for <code>name == &quot;Modernizr&quot;</code> in <code>technologies[]</code>.</p> <h3> <a name="how-do-i-find-a-list-of-companies-using-modernizr" href="#how-do-i-find-a-list-of-companies-using-modernizr" class="anchor"> </a> How do I find a list of companies using Modernizr? </h3> <p>Use <code>/lookup?tech=Modernizr</code> to get domains already in the index, then check your own prospect list 10 URLs at a time with <code>POST /analyze/batch</code>.</p> <h3> <a name="why-is-the-modernizr-version-empty" href="#why-is-the-modernizr-version-empty" class="anchor"> </a> Why is the Modernizr version empty? </h3> <p>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.</p> <h3> <a name="can-the-api-detect-a-bundled-modernizr-build" href="#can-the-api-detect-a-bundled-modernizr-build" class="anchor"> </a> Can the API detect a bundled Modernizr build? </h3> <p>No. Without a script path containing <code>modernizr</code>, there is nothing for an HTTP scan to match. The DevTools console check will still find it.</p> <h2> <a name="conclusion-get-a-free-api-key-and-start-scanning" href="#conclusion-get-a-free-api-key-and-start-scanning" class="anchor"> </a> Conclusion: Get a Free API Key and Start Scanning </h2> <p>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.</p> <p>To build your list: one <code>/demo</code> call to check the response shape, <code>/lookup</code> for domains already in the index, <code>/analyze/batch</code> 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.</p> <h3> <a name="related-reading" href="#related-reading" class="anchor"> </a> Related Reading </h3> <ul> <li><a href="https://detectzestack.com/blog/how-to-detect-jquery">How to Detect jQuery on Any Website</a> — the library that shows up next to Modernizr most often, with version parsing</li> <li><a href="https://detectzestack.com/blog/find-companies-using-jquery-cdn">Find Companies Using jQuery CDN (code.jquery.com)</a> — the delivery-host signal for jQuery-era front ends</li> <li><a href="https://detectzestack.com/blog/find-companies-using-imagesloaded">Find Companies Using imagesLoaded</a> — another jQuery-era library used as a prospecting signal</li> <li><a href="https://detectzestack.com/blog/how-to-detect-bootstrap">How to Detect if a Website Uses Bootstrap</a> — CSS framework detection and version parsing</li> <li><a href="https://detectzestack.com/blog/detect-javascript-framework-website">Detect the JavaScript Framework a Website Uses</a> — React, Vue, Angular, and what bundling does to detection</li> <li><a href="https://detectzestack.com/blog/batch-scan-1000-websites">Batch Scan 1,000 Websites for Tech Stack</a> — concurrency, retries, and quota planning for large lists</li> <li><a href="https://detectzestack.com/blog/lead-enrichment-pipeline-with-tech-detection">Build a Lead Enrichment Pipeline with Tech Detection</a> — turning scan results into CRM-ready leads</li> </ul>

Top comments (0)