Find Companies Using Node.js: Technographic API Guide
Node.js powers a huge share of modern backends, from solo-founder SaaS products to the API tier of large engineering organizations. A company that ships a Node.js backend is telling you something specific: they have JavaScript engineers, they likely run a single-language stack across front end and back end, and they make deliberate runtime choices. That makes “built with Node.js” one of the more useful buying signals you can detect from the outside—if you can detect it reliably.
That “if” is the whole problem. Node.js is genuinely harder to fingerprint than a CMS, because it is a runtime, not a page generator. This guide explains exactly how Node.js detection works, why the X-Powered-By and Express headers matter so much, how to verify a single domain by hand, and how to turn a raw domain list into a Node.js-confirmed lead list with the DetectZeStack API.
Why Find Companies Using Node.js?
Technographic data—what technology a company runs—is a stronger qualifier than firmographics alone for many products. A confirmed Node.js backend tells you several things at once:
- They employ JavaScript/TypeScript engineers. Node.js on the server usually means the same language runs end to end. If you sell developer tooling, observability, CI/CD, API gateways, or hiring services aimed at JS teams, that is your qualifier.
- They run their own application tier. Unlike a company on a fully hosted page builder, a Node.js shop deploys and operates real backend code—processes, package dependencies, runtime versions to keep patched.
- They made a deliberate stack choice. Seeing Express, Next.js, Nuxt.js, or Koa confirmed tells you the company has a real production architecture, and which corner of the Node ecosystem they live in.
The same pipeline applies to any detectable technology. We have companion guides for finding companies using Nginx and finding companies using Stripe—the filter changes, the workflow stays the same.
How Node.js Is Detected on a Website
Detecting Node.js from the outside is an exercise in reading indirect evidence. The runtime does not introduce itself; the framework sitting on top of it usually does.
The X-Powered-By and Express Header Signals
The single most reliable Node.js signal is the X-Powered-By response header set by Express, the dominant Node.js web framework. By default Express adds:
X-Powered-By: Express
Express runs only on Node.js, so this header is a transitive confirmation of the runtime. DetectZeStack fingerprints Express from exactly this header and then adds Node.js by implication—Express in the technology list always brings Node.js with it. You can spot the raw header yourself with a single command:
$ curl -sI https://example.com | grep -i "^x-powered-by"
X-Powered-By: Express
A handful of other frameworks emit their own telltale headers (for example, some Sails.js and Koa setups), but Express is by far the most common header-level signal you will encounter in the wild.
Why Node.js Is Harder to Detect Than a CMS
A CMS makes detection easy by stamping fingerprints into every page it serves. WordPress leaves a meta generator tag, wp-content/ asset paths, and predictable theme files—see our guide on checking if a website uses WordPress. Node.js leaves none of that, because it does not generate markup on its own. It serves whatever the application code emits, which means there is no default HTML signature to match.
So detection leans on two indirect routes:
- Server headers — primarily
X-Powered-By: Express, which directly implies Node.js. - Client-side framework fingerprints — a Next.js or Nuxt.js build leaves unmistakable markers in the page HTML (script paths, hydration data, build IDs), and both frameworks imply a Node.js runtime. So does Gatsby, Koa, Meteor, and a long list of other Node frameworks. Even when the server header is missing, a framework fingerprint can still confirm Node.js underneath.
The honest limitation: if a site sets none of these—a bare Express app with app.disable('x-powered-by'), served as a pure JSON API behind a proxy that strips headers—there may be no external signal at all. Absence of a Node.js detection is not proof the company does not run Node.js; it is an unknown. We come back to how to handle those cleanly below.
| Signal | Example | What It Tells You |
|---|---|---|
| X-Powered-By header | X-Powered-By: Express | Express confirmed → Node.js implied |
| Next.js build markers | /_next/static/… | Next.js confirmed → Node.js implied |
| Nuxt.js markers | __NUXT__ / /_nuxt/ | Nuxt.js confirmed → Node.js implied |
| No Node signal | (headers stripped) | Unknown — not a confirmed miss |
Manual Detection with curl Headers
For a single domain, header inspection takes one command. Fetch just the response headers and read them:
$ curl -sI https://example.com
HTTP/2 200
content-type: text/html; charset=utf-8
x-powered-by: Express
cache-control: public, max-age=0
etag: W/"1f9-..."
date: Sat, 21 Jun 2026 14:02:11 GMT
The x-powered-by: Express line is your confirmation. If you do not see it, check the page source for a framework marker before concluding anything—a quick way is to grep the HTML for Next.js or Nuxt.js fingerprints:
$ curl -s https://example.com | grep -o "/_next/static" | head -1
/_next/static
This works fine for one or two domains. It does not scale to a prospect list of hundreds, it does not resolve framework implications for you, and it makes you eyeball every response. That is what the API is for.
Building a Node.js Lead List at Scale with the DetectZeStack API
The DetectZeStack API runs the full detection pass—HTTP headers and body fingerprints, DNS records, and TLS certificates—in a single call and returns structured JSON you can filter in a script. Header-matched technologies like Express come back at confidence 100, and every implied technology (Node.js included) rides along in the same technologies array.
Single-Domain Check with /demo
You can try it right now against the public demo endpoint, no API key required. The demo is IP-rate-limited, so use it for spot checks rather than bulk scans:
$ curl -s "https://detectzestack.com/demo?url=example.com" \
| jq '.technologies[] | select(.name == "Express" or .name == "Node.js")'
{
"name": "Express",
"categories": ["Web frameworks", "Web servers"],
"confidence": 100,
"description": "Express is a web application framework for Node.js.",
"website": "https://expressjs.com",
"icon": "Express.svg",
"source": "http",
"version": "",
"cpe": ""
}
{
"name": "Node.js",
"categories": ["Programming languages"],
"confidence": 100,
"description": "Node.js is an open-source, cross-platform JavaScript runtime environment.",
"website": "https://nodejs.org",
"icon": "Node.js.svg",
"source": "http",
"version": "",
"cpe": ""
}
Two entries come back from one header. Express is matched directly off X-Powered-By and categorized as a web framework and web server; Node.js is added by implication under Programming languages. Both carry source: "http" because the evidence came from the HTTP response. The version fields are empty here—Express does not advertise its version in the default header, and Node.js is inferred rather than read off a version string.
Single-Domain Check with /analyze
When you want the full stack for a domain (not just a filtered slice), call /analyze with your API key. The complete response is shaped like this:
$ curl -s "https://detectzestack.p.rapidapi.com/analyze?url=example.com" \
-H "X-RapidAPI-Key: YOUR_KEY" \
-H "X-RapidAPI-Host: detectzestack.p.rapidapi.com"
{
"url": "https://example.com",
"domain": "example.com",
"technologies": [
{
"name": "Express",
"categories": ["Web frameworks", "Web servers"],
"confidence": 100,
"description": "Express is a web application framework for Node.js.",
"website": "https://expressjs.com",
"icon": "Express.svg",
"source": "http",
"version": "",
"cpe": ""
},
{
"name": "Node.js",
"categories": ["Programming languages"],
"confidence": 100,
"description": "Node.js is an open-source, cross-platform JavaScript runtime environment.",
"website": "https://nodejs.org",
"icon": "Node.js.svg",
"source": "http",
"version": "",
"cpe": ""
}
],
"categories": {
"Web frameworks": ["Express"],
"Web servers": ["Express"],
"Programming languages": ["Node.js"]
},
"meta": { "status_code": 200, "tech_count": 2, "scan_depth": "full" },
"cached": false,
"response_ms": 1842
}
The top-level categories map groups every detection so you can pull all programming languages with .categories["Programming languages"] without iterating the array. The meta object carries the HTTP status_code, the tech_count, and the scan_depth; response_ms and cached sit at the top level.
One field to watch for list building is meta.scan_depth. A value of "full" means the HTTP fetch succeeded and header and body detection ran. A value of "partial" means the site blocked or timed out the HTTP request and only DNS and TLS layers completed—in that case an absent Express or Node.js entry tells you nothing, and the domain belongs in a retry queue rather than your rejects file.
Batch Scanning a Prospect List with /analyze/batch
For list building, POST /analyze/batch accepts up to 10 URLs per request and analyzes them concurrently. Each entry in the response carries either a full analysis result or an error for domains that could not be fetched:
$ 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": ["example.com", "nextjs.org", "wordpress.org"]}'
The response wraps one result object per URL, each with the same shape as a single /analyze response:
{
"results": [
{ "url": "example.com", "result": { "...full analysis..." : "" } },
{ "url": "nextjs.org", "result": { "...full analysis..." : "" } },
{ "url": "wordpress.org", "result": { "...full analysis..." : "" } }
],
"total_ms": 2341,
"successful": 3,
"failed": 0
}
Because each result matches the single-domain shape, the filtering logic is identical whether you scan one domain or a thousand. For a deeper treatment of batch throughput, retries, and a production Python scanner, see how to batch scan 1,000 websites.
From Domain List to Node.js-Confirmed Leads (Full Walkthrough)
Here is a complete, copy-pasteable pipeline using nothing but bash, curl, and jq. It reads domains.txt (one domain per line), sends batches of 10 to /analyze/batch, and appends every domain where Node.js is detected (directly or by implication) to nodejs_leads.csv, recording which framework triggered the match:
#!/usr/bin/env bash
# find-nodejs.sh — filter a domain list down to Node.js-confirmed leads
KEY="YOUR_KEY"
HOST="detectzestack.p.rapidapi.com"
echo "domain,node_framework,tech_count" > nodejs_leads.csv
# Process domains.txt in batches of 10 (the /analyze/batch maximum)
xargs -n 10 < domains.txt | while read -r batch; do
urls=$(printf '%s\n' $batch | jq -R . | jq -s '{urls: .}')
curl -s -X POST "https://$HOST/analyze/batch" \
-H "X-RapidAPI-Key: $KEY" \
-H "X-RapidAPI-Host: $HOST" \
-H "Content-Type: application/json" \
-d "$urls" |
jq -r '.results[]
| select(.result != null)
| . as $r
| select([$r.result.technologies[].name] | index("Node.js"))
| ([$r.result.technologies[].name]
| map(select(. == "Express" or . == "Next.js" or . == "Nuxt.js" or . == "Koa"))
| (.[0] // "Node.js")) as $framework
| [$r.result.domain, $framework, ($r.result.meta.tech_count | tostring)]
| @csv' >> nodejs_leads.csv
done
wc -l nodejs_leads.csv
A 1,000-domain list becomes 100 batch calls. The index("Node.js") guard keeps a domain whenever Node.js appears—whether it was matched off the Express header or implied by a Next.js or Nuxt.js fingerprint—and the $framework expression records which technology drove the match, so your lead list segments cleanly into Express APIs versus Next.js front ends. Domains that failed to resolve appear with an error field instead of a result, and select(.result != null) skips them.
Filtering and Enriching Your Node.js Lead List
A raw “runs Node.js” flag is a starting point, not a finished lead. The framework that triggered the match is where the real segmentation lives, because each one implies a different buyer:
| Framework Detected | Implies | Likely Profile |
|---|---|---|
| Express | Node.js | Custom backend / REST or JSON API tier |
| Next.js | Node.js, React | Modern full-stack React team, often product-led SaaS |
| Nuxt.js | Node.js, Vue.js | Vue-based full-stack team |
| Koa | Node.js | Smaller, opinionated backend team |
Because frameworks like Next.js also imply their front-end library (React or Vue.js), a single scan often hands you the full stack in one shot. If you want to segment by the front-end layer specifically, our guide on detecting the JavaScript framework of a website covers reading those fingerprints, and how to detect a Next.js website goes deep on the Next.js markers in particular.
Two practical enrichment moves once the list is built:
- Cross-reference the proxy layer. A Node.js app fronted by Nginx is a different operational profile than one served directly. Pair this scan with Nginx detection to find teams running a full self-managed web tier.
- Score by stack richness. The
meta.tech_countfield is a rough proxy for how built-out a company's front end is. A Node.js site with 20+ detected technologies is a more mature engineering org than one with three. Feeding these signals into a scoring model is covered in our lead enrichment pipeline guide.
Get Started
The free tier includes 100 requests per month with no credit card—enough to validate the pipeline on a sample of your prospect list before scaling up. Sign-up is instant through RapidAPI:
- Get a key at rapidapi.com/mlugoapx/api/detectzestack.
- Spot-check a domain you know:
curl -s "https://detectzestack.com/demo?url=yourdomain.com" | jq '.technologies[].name' - Run the batch script above against your first 100 domains.
Finding companies built with Node.js comes down to reading indirect evidence well: the X-Powered-By: Express header, the framework fingerprints that imply the runtime, and honest handling of the domains where nothing leaks. A single /analyze call answers the one-domain question; /analyze/batch turns a raw domain list into a Node.js-confirmed lead list with the triggering framework attached; and meta.scan_depth tells you which negatives are real and which are unknowns worth a retry. Swap the jq filter and the same pipeline segments by CMS, payment processor, or web server instead.
Related Reading
- How to Find Companies Using Nginx — The reverse-proxy layer that often fronts a Node.js app
- How to Detect the JavaScript Framework of a Website — Reading the front-end fingerprints that imply Node.js
- How to Detect a Next.js Website — The Next.js build markers in depth
- How to Batch Scan 1,000 Websites for Tech Stack Data — Deep dive on /analyze/batch throughput, retries, and a Python scanner
- Find Companies Using Stripe: Technographic Prospecting — The same batch workflow filtered for the payment stack
- Lead Enrichment Pipeline with Tech Detection — Turning raw detections into scored, routable leads
- Find Companies Using jsDelivr — The same batch workflow filtered for a public library-delivery CDN
Try DetectZeStack Free
100 requests per month, no credit card required. Header, DNS, and TLS detection included on every plan.
Get Your Free API Key