Free, open source website technology detection. No subscription, no API key, no per-lookup credits.
Point it at any URL and it tells you what the site is built with, who they buy software from, how to contact them, what security posture they have, and which known CVEs affect their stack. Point it the other way and it tells you every site running a given technology.
Runs as a CLI, a TypeScript library, and an MCP server so Claude, Claude Code, Codex, ChatGPT and Cursor can use all of it directly.
npx opentechalyzer stripe.comCost: everything runs on your machine. No hosted service, no account, no API key, nothing to pay. The single exception is
ota reverse, which queries a public dataset on your own Google Cloud project and is free under Google's 1 TB monthly allowance.
- What you get
- Install
- Commands
- Enrichment field sets
- Use it from Claude, ChatGPT, Codex
- Library API
- Output formats
- Reverse lookup
- What it detects
- How it works
- Accuracy benchmark
- What it cannot do
- Contributing
| Capability | Command | What comes back |
|---|---|---|
| Full tech stack of a URL | ota example.com |
588 fingerprints across 55 categories, each with a confidence score and the evidence behind it |
| Bulk lookup | ota -i domains.txt -f csv -j 10 |
One row per technology per URL. Thousands of domains, no rate limit, no credits |
| Contact details | ota example.com --crawl --fields contact |
Emails, phone numbers, WhatsApp, found by crawling contact and about pages |
| Social handles | --fields social |
X, LinkedIn, Instagram, Facebook, YouTube, TikTok, GitHub, Pinterest |
| Company info | --fields company |
Name, locations, founding year, description, from structured data |
| Security posture | --fields security |
TLS certificate, issuer, expiry, plus SPF, DMARC, DKIM and CAA records |
| Software spend estimate | --fields signals |
A floor estimate in USD/month, itemised by the paid tools detected |
| Traffic rank | --fields signals |
Popularity rank via the free Tranco list |
| Locale | --fields locale |
Language, all languages served, country, currencies |
| Metadata | --fields meta |
Title, description, copyright year, schema.org types, keywords |
| Known CVEs | ota cve example.com |
CPE identifiers mapped to live CVE data from NVD |
| Subdomains | ota subdomains example.com |
Certificate transparency plus DNS probing, each result resolve-checked |
| Email verification | ota verify sales@example.com |
SMTP check without sending. safe / risky / invalid, plus catch-all and role flags |
| Change tracking | ota watch example.com |
Diff against a stored baseline. Run on a cron for buying-signal alerts |
| Reverse lookup | ota reverse --tech Shopify |
Every site running a technology, via HTTP Archive |
| Compare two sites | MCP compare_tech_stacks |
Shared / only-A / only-B |
| Markdown report | ota example.com -f markdown |
Tables grouped by category, ready to paste into a doc |
Every detection carries an auditable evidence trail. Run with --verbose and you see exactly
which header, cookie, DOM selector or npm dependency triggered each result.
npm install -g opentechalyzerOr without installing:
npx opentechalyzer example.comRequires Node 18.17 or newer. Two optional add-ons unlock more, both free and one-time:
npm i playwright && npx playwright install chromiumopentechalyzer db import && opentechalyzer db import-tranco| Add-on | Unlocks | Why bother |
|---|---|---|
| Playwright | --render |
Roughly doubles what is found. Tag managers, injected widgets and framework globals only exist after JavaScript runs |
db import |
Wider fingerprint coverage | Merges a community dataset for long-tail technologies |
db import-tranco |
trafficRank |
Popularity ranking from the free Tranco research list |
ota example.com # quick scan
ota example.com --render # + JS-injected technologies
ota example.com --crawl # + inner pages (checkout, contact, about)
ota example.com --sourcemaps # + exact npm dependencies from sourcemaps
ota example.com --fields all --verbose # everything, with evidence
ota example.com --only cms,payment,analytics # just the categories you care aboutota -i domains.txt -f csv -j 10 > stacks.csv
ota -i domains.txt --fields contact,social -f csv -j 10 > enriched.csvOne domain per line, # for comments. Failures are reported per URL on stderr, so one dead host
never aborts a batch of thousands. Bare apex domains that only serve www are retried
automatically, and the substitution is recorded as a warning.
ota subdomains example.com
ota subdomains example.com -f jsonCombines certificate transparency logs with DNS probing of common labels, then resolves every
result so dead entries are marked rather than silently included. Often surfaces internal tooling
that is not linked from anywhere: grafana., jenkins., metabase., argocd., vault.
ota verify sales@example.com
ota verify a@x.com b@y.com --dns-onlyValidates syntax, resolves MX, then opens an SMTP session and issues RCPT TO without ever sending
DATA. Nothing is delivered. Returns safe / risky / invalid / unknown plus catch-all,
role-account, disposable and free-provider flags.
Two honest caveats, both reported in the result rather than hidden. Most networks block outbound
port 25, in which case the verdict is unknown and not a false negative, so use --dns-only there.
And catch-all domains accept every address, so acceptance proves the domain works, not that the
mailbox exists; those are always reported risky, never safe.
ota cve example.com
NVD_API_KEY=... ota cve example.com # free key raises the rate limitMaps detected technologies to CPE identifiers and queries the public NVD database. Technologies without a confidently detected version are skipped rather than matched against every release ever published, because that would produce a long and meaningless list.
ota watch example.com # first run stores a baseline
ota watch example.com # later runs report what changed
ota watch --listReports technologies added, removed or version-changed since the last run. Snapshots are stored locally; nothing is uploaded. Put it on a cron for competitor buying signals, for example a competitor adding Klaviyo or dropping Shopify Plus.
ota reverse --tech Shopify --tech Klaviyo --rank 100000ota db status # what is installed
ota db import # wider fingerprint coverage
ota db import-tranco # traffic ranking| Flag | Effect |
|---|---|
-r, --render |
Headless browser render. Needs playwright |
-s, --sourcemaps |
Parse sourcemaps for exact npm dependency names |
-w, --crawl [n] |
Follow up to n internal pages, default 5 |
-F, --fields <sets> |
Enrichment sets, comma separated, or all |
-c, --certs |
Certificate transparency subdomain lookup |
--intrusive |
Include probes for /.git/HEAD and /.env |
--no-dns --no-probe --no-favicon --no-css |
Skip individual collectors |
--no-external |
Ignore any imported external database |
-f, --format <fmt> |
text json markdown csv summary |
-o, --only <cats> |
Restrict output to categories |
-m, --min <n> |
Minimum confidence, default 25 |
-v, --verbose |
Show the evidence behind every detection |
-q, --quiet |
Suppress progress output |
-t, --timeout <ms> |
Per-request timeout, default 15000 |
-j, --jobs <n> |
Concurrency for multiple URLs, default 5 |
-i, --input <file> |
Read newline-separated URLs from a file |
-A, --user-agent <ua> |
Override the User-Agent |
--categories |
List all 55 categories |
Pass with --fields, comma separated, or all.
| Set | Fields returned |
|---|---|
meta |
title, description, copyright, copyrightYear, schemaOrgTypes |
keywords |
Content keywords, declared plus frequency-derived |
company |
companyName, inferredCompanyName, about, locations, companyFounded |
contact |
email, phone, whatsapp |
social |
x, facebook, instagram, linkedin, github, youtube, tiktok, pinterest |
locale |
language, languages, ipCountry, ipCountries, currencies |
security |
certInfo (org, country, issuer, protocol, expiry, altNames), dns.spf, dns.dmarc, dns.dkim, dns.caa |
signals |
technologySpend, technologySpendMonthlyFloorUsd, spend drivers, trafficRank, trafficLevel |
Pair --fields contact,social with --crawl. Contact details and social handles live on
contact and about pages, essentially never on the homepage.
companyName comes from structured data and is trustworthy. inferredCompanyName is a best guess
from the page title and is labelled as such, because the difference matters when it feeds a CRM.
Opentechalyzer speaks MCP, so any MCP client gets all eleven tools.
Claude Code
claude mcp add opentechalyzer -- npx -y opentechalyzer-mcpClaude Desktop, in claude_desktop_config.json:
{
"mcpServers": {
"opentechalyzer": {
"command": "npx",
"args": ["-y", "opentechalyzer-mcp"]
}
}
}Codex CLI, in ~/.codex/config.toml:
[mcp_servers.opentechalyzer]
command = "npx"
args = ["-y", "opentechalyzer-mcp"]Cursor, Windsurf and Zed take the same command/args pair in their own MCP config.
These run in a browser and cannot spawn a local process, so the stdio command above will not work for them. They need a reachable HTTPS endpoint:
opentechalyzer-mcp --http --port 3000Expose it and register the resulting https://<your-url>/mcp as a connector:
cloudflared tunnel --url http://localhost:3000GET /health returns server status, handy for checking a tunnel is live.
The HTTP endpoint is unauthenticated. Anyone who discovers the URL can run scans through your machine. Keep it behind a tunnel you control, put auth in front of it, or shut it down when you are finished. Do not park it on a public IP.
| Client | Transport | Works |
|---|---|---|
| Claude Code | stdio | Yes |
| Claude Desktop | stdio | Yes |
| Codex CLI | stdio | Yes |
| Cursor / Windsurf / Zed | stdio | Yes |
| ChatGPT (Developer Mode) | HTTP | Yes, via --http and a public HTTPS URL |
| claude.ai (Custom Connectors) | HTTP | Yes, via --http and a public HTTPS URL |
| Tool | What it does |
|---|---|
detect_tech_stack |
Full stack of one URL, with confidence and evidence |
detect_tech_stack_batch |
Up to 25 URLs concurrently |
compare_tech_stacks |
Two sites diffed into shared / only-A / only-B |
tech_stack_report |
Full markdown report |
reverse_lookup |
Every site using a technology, via HTTP Archive |
find_subdomains |
Certificate transparency plus DNS discovery |
verify_email |
SMTP verification without sending |
find_vulnerabilities |
CPE mapping plus live NVD CVE lookup |
track_tech_changes |
Diff against a stored baseline |
opentechalyzer_status |
Which capabilities and datasets are available |
import_external_database |
Pull in the optional wider dataset |
Then just ask:
What's shopify.com built with?
Compare our stack against competitor.com and tell me what they have that we don't.
Here are 20 prospect domains. Which run Shopify Plus, and what's their contact email?
Find 200 Shopify stores in the top 100k using Klaviyo but not Gorgias.
Does example.com have any known CVEs?
import { analyze, analyzeMany } from 'opentechalyzer';
const result = await analyze('https://example.com', {
render: true,
crawl: 5,
sourcemaps: true,
fields: ['contact', 'social', 'security', 'signals'],
});
for (const tech of result.detections) {
console.log(tech.name, tech.version ?? '', `${tech.confidence}%`, tech.evidence[0]?.match);
}Exports: analyze, analyzeMany, detect, combineConfidence, getFingerprints,
BUILTIN_FINGERPRINTS, DATABASE_VERSION, listCategories, clearFingerprintCache,
importExternalDatabase, loadExternalDatabase, externalDatabaseStatus, externalDbPath,
EXTERNAL_DB_LICENSE_NOTICE, isRenderAvailable, formatTerminal, formatMarkdown, formatCsv,
summarise, plus all types.
| Format | Use for |
|---|---|
text |
Reading in a terminal. Grouped by category, colour-coded by confidence |
json |
Piping into jq or a script. Full evidence trail and timings included |
markdown |
Pasting into a doc or ticket. Tables per category |
csv |
Spreadsheets and CRMs. One row per technology per URL |
summary |
Compact one-line-per-category. Ideal for LLM context |
The JSON shape:
The inverse of a scan. Instead of "what does this site run?", ask "which sites run this?", the question that turns a detector into a lead-sourcing tool.
Build a prospect list. Shopify stores also running Klaviyo, inside the top 100k:
ota reverse --tech Shopify --tech Klaviyo --rank 100000Competitive displacement. Sites on a competitor's tool but not yours:
ota reverse --tech Yotpo --not-tech "Judge.me" --limit 500 -f csv > switch-targets.csvMarket sizing. How much of the top 10k is in a given category:
ota reverse --category Ecommerce --rank 10000 --limit 10000 -f json | jq '.rows | length'Agency prospecting. Old stack, no analytics, usually means somebody needs help:
ota reverse --tech WooCommerce --not-tech "Google Analytics" --rank 1000000 --limit 1000Cost-check first. Prints the SQL and byte estimate, bills nothing:
ota reverse --tech Shopify --dry-runChain it into a real scan. Reverse lookup finds candidates, a direct scan verifies them and pulls contact details. This is the full lead pipeline:
ota reverse --tech Shopify --tech Recharge --rank 100000 -f json | jq -r '.rows[].page' > leads.txt
ota -i leads.txt --crawl --fields contact,social,signals -f csv -j 10 > enriched.csv| Flag | Effect |
|---|---|
--tech <name> |
Must be present. Repeatable, ANDed |
--not-tech <name> |
Must be absent. Repeatable |
--category <name> |
Match a category instead, e.g. Ecommerce |
--rank <n> |
Only sites within the top n by popularity. Biggest lever on cost |
--client <c> |
desktop or mobile, default mobile |
--date <YYYY-MM-01> |
Crawl month, default two months back |
-l, --limit <n> |
Max sites, default 100, max 10000 |
--max-bytes <n> |
Cost ceiling, default 200 GB |
--dry-run |
Estimate and print SQL, run nothing |
Opentechalyzer itself is free and always will be. It is MIT, runs entirely on your machine, with no hosted service, no account and no API key. We run no servers, so there is nothing for us to charge for and nothing for us to pay for. Every other command, scanning, enrichment, subdomains, CVE lookup, email verification, change tracking, costs exactly nothing.
ota reverse is the single exception, and the cost is neither ours nor for hosting:
- Answering "which sites use Shopify" requires a crawl of the whole web. Nobody does that from a laptop.
- HTTP Archive already does it, monthly, across roughly 16 million pages, and publishes the results as a free public dataset on Google BigQuery. Google stores that data at no cost to you or us.
- But BigQuery charges for compute, not storage, specifically bytes scanned by a query, billed to whoever runs it. The query runs on Google's machines and Google bills your Google Cloud project. Not this repo, not GitHub, not us.
Think of ota reverse as a free SQL client. The client costs nothing; the database it talks to
meters query compute.
In practice most people pay nothing, because Google gives every account 1 TB of free query
volume per month and a well-filtered query is far smaller. A --rank 100000 lookup typically
scans a few hundred MB to a few GB, so dozens of them fit inside the free tier.
Costs only appear with broad, unfiltered queries run repeatedly. Hence the guard rails, on by default:
| Guard | What it does |
|---|---|
| Automatic dry run | Every query is estimated before it runs, and the estimate printed |
| 200 GB ceiling | Execution refused above it unless you raise --max-bytes |
Server-side maximumBytesBilled |
BigQuery itself rejects the job, rather than trusting our estimate |
--dry-run |
Prints SQL and cost, bills nothing |
--rank |
The single biggest lever on cost. Use it |
If you never want to touch BigQuery, never run ota reverse. Nothing else uses it, and no other
command will ask for cloud credentials.
gcloud auth login && gcloud config set project YOUR_PROJECTAlternatively set GOOGLE_CLOUD_PROJECT and GOOGLE_ACCESS_TOKEN. No @google-cloud/bigquery
dependency is needed; the tool uses the REST API directly.
Results come from HTTP Archive's own Wappalyzer fork, not this project's 588 fingerprints, so they can disagree with a direct scan. Coverage follows CrUX, so it skews to sites with real Chrome traffic and small or new stores may be absent. The crawl is monthly, so data lags by weeks. Treat it as a sampling frame for building a prospect list, then verify each prospect with a real scan.
588 built-in fingerprints across 55 categories, plus whatever the optional external dataset adds.
| Area | Count | Examples |
|---|---|---|
| JS libraries | 60 | jQuery, Swiper, GSAP, Three.js, D3, Chart.js, Lodash, Axios, Splide |
| Ecommerce apps | 55 | Judge.me, Yotpo, Recharge, Klaviyo, Gorgias, Rivo, PushOwl, Swym, Videowise, Loop Returns, BOGOS, Tapcart |
| Platforms | 45 | Shopify, WordPress, Wix, Squarespace, Webflow, Ghost, Drupal, Magento, VTEX, SHOPLINE, Tiendanube |
| Payments | 39 | Stripe, PayPal, Adyen, Klarna, Razorpay, GoKwik, Mercado Pago, Paystack, Flutterwave, Midtrans |
| Ecommerce platforms | 37 | BigCommerce, commercetools, SAP Commerce, Medusa, Saleor, Swell, Ecwid, OpenCart, Shopify Hydrogen |
| CMS | 28 | Contentful, Sanity, Storyblok, Strapi, Sitecore, AEM, Payload, TYPO3, Craft |
| Marketing automation | 25 | Klaviyo, HubSpot, Marketo, Braze, MoEngage, CleverTap, Attentive, Postscript, BiteSpeed |
| DevOps and self-hosted | 24 | Grafana, Jenkins, GitLab, Metabase, Kibana, Vault, Argo CD, n8n, Airflow |
| Analytics | 24 | GA4, GTM, Plausible, PostHog, Mixpanel, Amplitude, Segment, Clarity, Hotjar, Snowplow |
| Backend frameworks | 20 | Rails, Django, Laravel, Spring, ASP.NET, Phoenix, FastAPI, Express, NestJS, Statamic |
| UI frameworks | 20 | Material UI, Chakra, Ant Design, Mantine, Radix, shadcn/ui, Vuetify, Ionic |
| JS frameworks | 18 | Next.js, Nuxt, Remix, SvelteKit, Astro, Gatsby, React, Vue, Angular, Qwik |
| Advertising | 16 | Meta Pixel, Google Ads, TikTok, Pinterest, LinkedIn, Reddit, Bing UET, Criteo |
| CDN | 15 | Cloudflare, Fastly, Akamai, CloudFront, Bunny, jsDelivr, unpkg |
| Auth | 15 | Auth0, Clerk, WorkOS, Okta, Cognito, NextAuth, Better Auth, Keycloak, KwikPass |
| Shipping and logistics | 14 | Shiprocket, AfterShip, ShipStation, Sendcloud, Narvar, Delhivery, ClickPost |
| Web servers | 14 | nginx, Apache, IIS, LiteSpeed, Caddy, OpenResty, Envoy, Tomcat |
| Search | 13 | Algolia, Typesense, Meilisearch, Elasticsearch, Klevu, Coveo, Pagefind |
| Support and chat | 13 | Intercom, Zendesk, Crisp, Drift, Tawk.to, Gorgias, Freshchat, Verifast AI |
| Hosting and PaaS | 13 | Vercel, Netlify, Fly.io, Railway, Render, Heroku, Cloudflare Pages, Shopify Oxygen |
Plus databases, BaaS, APM, error tracking, feature flags, A/B testing, CAPTCHAs, bot protection,
cookie consent, fonts, media, maps, video, translation, accessibility, mail providers and DNS hosts.
Run ota --categories for the full list.
Account IDs are captured too, so you get GTM-M92FB6B, G-MFK23BV2BG or a Klaviyo public key,
not just "Google Tag Manager is present".
Nine signal sources, matched against the fingerprint database:
| Source | What it catches |
|---|---|
| Response headers | Server, framework, hosting, CDN, cache layer |
| Cookies | Framework session names, near-unique and hard to fake |
| HTML and meta | Generators, asset path conventions, inline markers |
| Rendered DOM and JS globals | Everything injected after load, needs --render |
| Runtime network requests | Beacons and APIs that never appear in the DOM |
| First-party CSS | CSS frameworks, from compiled output rather than class names |
| Sourcemaps and bundles | Exact npm dependency names and versions |
| DNS records | Mail provider, DNS host, SaaS named in SPF includes |
| Well-known probes and favicon hash | Self-hosted software with no client-side fingerprint |
Three design decisions produce the accuracy:
Confidence combines probabilistically, never by summing. Each signal has a reliability and
overall confidence is 1 - Π(1 - reliability). Repeated matches from the same source are damped,
so ten HTML regexes for one library cannot masquerade as ten independent observations. Summing
saturates at 100% as soon as you add enough weak matches, which is exactly how detectors end up
confidently wrong.
CSS frameworks come from compiled CSS, not class names. flex items-center looks like Tailwind
but any codebase can define those names. So first-party stylesheets are downloaded and matched for
--tw-* and --bs-* custom properties, which only exist if the framework generated the file.
Every detection carries its evidence. --verbose shows the exact header, cookie, selector or
dependency responsible. A result you cannot audit is a result you cannot trust.
Accuracy is measured, not asserted:
npm run benchmarkScans a fixed set of sites whose stacks were verified by hand (headers, cookies, meta[generator],
checked with curl) and scores the output against that ground truth.
site found notes
------------------------------------------------------
gitlab.com 3/3 clean
discourse.org 2/2 clean
djangoproject.com 4/4 clean
laravel.com 5/5 clean
vercel.com 4/4 clean
squarespace.com 1/1 clean
wix.com 2/2 clean
bigcommerce.com 3/3 clean
ghost.org 2/2 clean
basecamp.com 1/1 clean
------------------------------------------------------
Recall 27/27 = 100% (threshold 90%)
Hard false positives 0 (threshold 0)
It fails on a single hard false positive and only budgets misses. That asymmetry is deliberate: "we could not tell" is a usable answer, while "this Wix site runs Laravel" poisons a lead list and discredits every other row.
Running it is the fastest way to catch a regression after editing fingerprints, and it is how every
accuracy bug so far was found, including one where bulk mode silently dropped detections at -j 5
because probes were timing out. It is intentionally not part of npm test: it hits live
third-party sites, so a green CI run must never depend on someone else's deploy schedule.
Stated plainly, because a detector that overstates itself is worse than useless.
- Backend is largely invisible. A clean Go or Rails API serving JSON leaves no fingerprint. Backend is reported when it leaks through a header, cookie or error page, and stays quiet otherwise. For real backend intel, read the target's job listings.
- Reverse lookup is not our own crawl. It queries HTTP Archive, whose technology column comes from their Wappalyzer fork. Results can disagree with a direct scan, coverage is CrUX-based, and the data is a monthly snapshot.
- No company firmographics. Employee counts and revenue come from data brokers. Company name,
locations and founding year are extracted only when the site publishes them in structured data,
and are labelled
inferredwhen guessed. - Traffic rank comes from Tranco, a free research list, not a proprietary panel.
- Technology spend is a floor, not a bill. It sums entry-level list prices for the paid tools visible from outside, to separate "hobby site" from "funded company buying software". Every contributing tool is listed so you can check the arithmetic.
- Results are vantage-dependent. Geo-redirects, device targeting and logged-in state change what a site serves. This tool was written after a scan from a US IP returned a completely different Shopify store than the same URL from India. Check more than one vantage point before trusting a single scan.
- Email verification needs port 25, blocked on most cloud networks, in which case the verdict is
unknownrather than a false negative. - Catch-all domains cannot be verified. Acceptance proves the domain works, not that the mailbox
exists. Always reported
risky. - CVE results depend on version accuracy. Versionless detections are skipped rather than matched against every release. Confirm a version before acting on a CVE.
The built-in database in src/fingerprints/ is written for this project and MIT licensed. It is
not a copy of anyone else's dataset. Shopify app handles were harvested from live storefronts rather
than guessed.
The most complete open dataset available is the community-maintained Wappalyzer technologies set
(enthec/webappanalyzer), which is GPL-3.0. Vendoring
GPL-3.0 data here would force this entire project to become GPL-3.0 and stop you embedding it in
your own products, which is the exact freedom this exists to provide. So it is never redistributed:
opentechalyzer db import fetches it to your machine, at your request, and it stays under its
own licence. Everything works without it.
Adding a technology is one object in the right file under src/fingerprints/:
{
name: 'Your Technology',
categories: ['analytics'],
website: 'https://example.com',
scriptSrc: ['cdn\\.example\\.com/tracker\\.js'],
js: { yourGlobal: '' },
cookies: { '^_yt_session$': '' },
}Rules that keep accuracy up, each of which caught a real false positive:
- Prefer specific signals. A cookie name or JS global beats an HTML substring.
- Never use a bare brand name in HTML.
ecwidorshoplinematches any page mentioning the product. Use a hostname or asset path. - Anchor cookie patterns at both ends. An unanchored
_session$matched Laravel's own cookie and reported Rails at 94% on laravel.com. - Never write a status-only probe. Always require a body pattern; a 401 from
/api/proves nothing and once reported Home Assistant on Vercel and Stripe. - Set
caseSensitive: truefor identifier patterns.G-[A-Z0-9]{9,12}matchedg-recaptchauntil that existed. - Use
idfor account identifiers,versionfor versions.GTM-M92FB6Bis not a version. - Verify on real merchant sites, not the vendor's own site. Grepping "shopline" on shopline.hk proves nothing.
- Verify favicon hashes against a real instance. A guessed hash is worse than no signal.
npm test enforces unique names, valid regexes, resolvable implies/requires targets, and that
every version template has a matching capture group.
See CONTRIBUTING.md for more.
npm install
npm run build
npm test # 45 unit tests, no network
npm run benchmark # live accuracy check, needs network
npm run typecheckMIT. Use it commercially, embed it, fork it, resell it. No attribution required.
{ "url": "…", "finalUrl": "…", "status": 200, "detections": [{ "name": "Shopify", "categories": ["ecommerce", "platform"], "version": "…", "accountIds": ["GTM-M92FB6B"], "confidence": 100, "inferred": false, "evidence": [{ "source": "header", "subject": "powered-by", "match": "Shopify", "reliability": 0.88 }] }], "byCategory": { "ecommerce": [ "…" ] }, "enrichment": { "contact": {}, "social": {}, "security": {}, "signals": {}, "cpes": [] }, "crawledPages": ["…"], "meta": { "title": "…", "description": "…", "ip": "…" }, "warnings": ["…"], "timings": { "fetch": 518, "css": 282, "dns": 290, "probe": 3688, "detect": 551 }, "databaseVersion": "0.2.0", "fingerprintCount": 588, "analyzedAt": "…" }