Skip to content

Repository files navigation

revalign-dependents

CI License: Apache 2.0 Python 3.9+ Dependencies: stdlib only

Point this at an npm package; get back the companies importing it, each row carrying a link to the exact package.json line that proves it -- the receipt no marketing-tag database (BuiltWith, Wappalyzer, ZoomInfo) can produce. A verifiable sample of the public dependency graph, not a census. Free, and it runs on the Python standard library alone.

The fact is hiding in plain sight. A public repo lists its dependencies in a public manifest, and anyone can read the line. But "it is public" is not "you have it." There is no download button, no CSV, no company name attached, and above all no proof link. The signal is scattered across tens of thousands of manifests, and the tools that claim to know a company's stack infer it from a marketing tag or a DNS record and hand you a guess with no receipt.

revalign-dependents reads the plain-sight data and exports it. Give it a package name; it enumerates the public repositories that declare that package, resolves each owner to a real organization, and hands you a spreadsheet where every row carries a proof link to the exact manifest line. Open the link and you see the dependency with your own eyes. So can a skeptical buyer. That verifiable receipt, not the row count, is the whole point; a tag-inference database cannot produce it, because the fact is in the code, not in a script tag.

The headline mode is displacement. --vs <competitor-package> returns the companies that publicly import the first package but show no public evidence of the competitor package you named. A vendor usually ships several SDKs (Clerk publishes @clerk/nextjs, @clerk/clerk-react, @clerk/backend, and more), so pass all of them as a comma-separated list to diff against the whole vendor, not one package: --vs @clerk/nextjs,@clerk/backend. For a devtool founder the clean set is a proof-backed slice of the competitor's addressable market, resolved to companies, with a receipt on every row.


The hero run

Algolia is the dominant hosted search vendor. Meilisearch is the open-source challenger that pitches itself as the Algolia alternative. So point the tool at Algolia's JavaScript client and diff against Meilisearch (install first, see Quickstart below):

export GITHUB_TOKEN=$(gh auth token)
revalign-dependents npm algoliasearch --vs meilisearch --source github-code-search --max-pages 8

(--source github-code-search pins the more complete of the two enumerators; see Two enumeration sources.)

What it printed (a real run, 2026-07-21):

enumerated 770 public dependents of 'algoliasearch' via github-code-search; excluding vendor org 'algolia'
378 survived the structural filter (dropped forks/archived/templates/tutorials/stale-unstarred/transitive)
122 company rows (122 organizations); default-branch / last-crawled view only
119 show no public evidence of 'meilisearch' = the displacement list

(These counts are a snapshot of a live, changing index on 2026-07-21; your run will differ as repos and stars change and the code-search slice shifts.)

122 organizations import Algolia's client; 119 show no public evidence of Meilisearch. The other three were excluded because they publicly import both (raycast, fireship-io, and dataspects all declare meilisearch too), which is the diff doing its job: it does not hand Meilisearch a company that is already on Meilisearch. Here are eight of the 119, each a clickable receipt. Every link below was resolving at the time of the run; open any one and you see the algoliasearch line in that org's package.json:

company domain repo stars proof (opens to the manifest)
Overleaf overleaf.com overleaf/overleaf 17955 services/web/package.json
Searchkit searchkit.co searchkit/searchkit 4858 apps/web/package.json
Apify apify.com apify/apify-mcp-server 2121 package.json
Nexxtway nexxtway.com nexxtway/react-rainbow 1793 package.json
CircleCI circleci.com circleci/circleci-docs 851 package.json
GraphCommerce graphcommerce.org graphcommerce-org/graphcommerce 360 packages/algolia-search/package.json
Supermedium supermedium.com supermedium/beatsaver-viewer 202 package.json
Uniswap Labs uniswap.org Uniswap/docs 377 archive/package.json

Read the table with a clear eye. Overleaf (a SaaS product), Searchkit (which sells search UI and would obviously weigh a search backend), Apify, CircleCI, and GraphCommerce are genuine commercial displacement candidates: real companies on Algolia that Meilisearch could actually sell to. Others (a Uniswap docs site, a Docusaurus-powered docs search elsewhere in the full run) are recognizable but not the sales list; some are docs-site or framework integrations rather than a product's core search. That is exactly why the proof link matters: open it and the manifest tells you which kind you are looking at. The tool resolves a bare owner into a named company with a receipt; you decide who is a buyer.

The displacement column on every one of these rows reads no public evidence of meilisearch (crawl 2026-07-21), and each row is a link to the literal line of code. No marketing-tag database can give you that link, because there is no tag to read, only the manifest. The full 122-row run (119 displacement candidates plus the 3 excluded), with the displacement column and a proof URL on every row, is committed at examples/sample_output/.

Read the honest-yield section next. The value here is the receipt, not the row count.


Read this before you get excited about the number

The honest yield is modest, and pretending otherwise would be a rug-pull.

  • In the hero run, 770 public dependents narrowed to 122 GitHub organizations, 119 of them displacement candidates. A good fraction are real companies (Overleaf, Apify, CircleCI, Searchkit, GraphCommerce); some are docs-site or framework integrations, or class, club, and hackathon org-accounts the org-vs-user gate cannot catch, because GitHub calls them organizations too. So read the list, do not count it: the proof link on each row is what makes triaging it cheap.
  • The org-vs-user filter is the one that halves the list. Most raw dependents are personal users (student projects, one-off demos), and dropping them is the difference between a target list and a pile of noise.
  • A popular package yields more rows, and noisier ones. A widely-used SDK can have tens of thousands of dependents; you get more real companies, and much more junk to filter (and code search still caps what you can retrieve at 1000). You do more verifying, and the proof link is what keeps that cheap.

So the value is not "look how many companies." It is that every company you do surface comes with a proof link no tag-database can produce. Treat the list as a warm, verifiable starting point, not an exhaustive census.


Quickstart

Requires Python 3.9 or newer. No third-party dependencies; it runs on the standard library alone (urllib, csv, json).

Not yet published to PyPI. Install from source:

git clone https://github.com/RevAlign/revalign-dependents
cd revalign-dependents
pip install -e .        # stdlib only, so this just puts the CLI on your PATH

A GitHub token is strongly recommended. The code-search fallback enumerator, the owner-to-company enrichment, and --vs all use the GitHub API, and an authed token also lifts your rate limits:

export GITHUB_TOKEN=$(gh auth token)     # or any personal access token

Scan a package and get the company list:

revalign-dependents npm algoliasearch

Add --vs to turn it into a displacement list:

revalign-dependents npm algoliasearch --vs meilisearch

You can also run it as a module (python -m revalign_dependents npm ...). Scoped names work as-is: revalign-dependents npm @sendgrid/mail. When it finishes you have two files in ./out: open dependents.csv in a spreadsheet, put proof.md next to it, and click a few proof links to convince yourself before you use the list.

v1 is npm only. Pass any other ecosystem and it tells you so and exits; Go is a future ecosystem, not a silent no-op.


What you get back

Everything lands in the output directory (./out by default).

File What it is
dependents.csv One row per resolved company, spreadsheet-ready, with a proof_url on every row.
proof.md The same rows as a human-readable proof sheet: a clickable receipt per row, under an honest run header (package, source, known-dependents denominator, crawl date, and the "no public evidence is not not-using" disclaimer in bold).

dependents.csv columns

Column Meaning
company_name The resolved organization name (from the GitHub org profile), falling back to the owner login.
domain The company's bare hostname, cleaned from the org's website. Social and placeholder links are dropped, so this is blank rather than twitter.com.
github_owner The GitHub owner login that declares the dependency.
repo_full_name The owner/repo whose manifest declares the package.
stars The repo's star count (blank if unknown).
pushed_at ISO 8601 timestamp of the last push, so you can judge how live the adopter is.
manifest_path The path to the manifest inside the repo, usually package.json, sometimes nested like app/package.json.
package The package you scanned for.
version The declared version constraint (semver range), when the source exposes it.
is_verified true if GitHub marks the org as a verified organization.
owner_type Organization, User, or blank. The default run keeps organizations, drops classified personal users, and keeps owners it could not classify (a tokenless run, where this is blank) so their proof links survive.
confidence high for organizations; low for a user or an owner that at least names a company or domain; unknown for an owner that could not be classified at all (a tokenless run with no GitHub lookups), so the row is honestly labeled.
displacement In --vs mode: no public evidence of <pkg(s)> (crawl <date>), or blank when the owner already publicly imports one of the competitor package(s) you named, or unchecked for <pkg(s)> if the lookup itself failed. It names the exact package(s) diffed, not the vendor, so read "no public evidence of meilisearch" as exactly that, and pass every SDK a vendor ships to cover the whole vendor. Blank outside --vs. Read the safety note below.
proof_url A ready-to-click GitHub link (https://github.com/{repo}/blob/{branch}/{manifest_path}) to the exact manifest that names the package. Open it to verify the row with your own eyes.

Rows are sorted so displacement candidates come first, then organizations, then higher-star repos, so the top of the file is the part worth reading first.


How it works

  npm package name
        |
        v
  enumerate public dependents
    ecosyste.ms usage API  ---(500 / no key)-->  GitHub code search fallback
      (free, no key, rich)                        (needs token, <=1000, default branch)
        |                                                 |
        +-----------------------+-------------------------+
                                |
                                v
  structural gate   drop the vendor's OWN org (e.g. algolia's own repos),
                    forks / archived / templates / tutorials,
                    stale-and-unstarred repos, transitive deps
                                |
                                v
  resolve owner -> company    GitHub org: name, website domain, verified flag
                                |
                                v
  classify org vs. user   <--- the filter that halves the list
                                |
                                v
  (optional) --vs COMPETITOR   one code search per owner: found / no public evidence
                                |
                                v
  dependents.csv  (a proof_url on every row)   +   proof.md

Four ideas, no magic.

1. Enumerate from the public graph. Two sources, tried in order (see the next section), so the tool keeps working when one is down.

2. Filter structurally, on purpose. Most raw dependents are not company adopters. The tool drops the package vendor's own sample repos (auto-derived from the npm registry, so the algolia org's own repos never pollute an Algolia scan), forks, archived and template repos, anything whose name reads as a tutorial, demo, boilerplate, or scaffold, and, when the source can tell, transitive or lockfile-only hits in favor of declared dependencies. A repo is kept if it is starred or recently pushed; it is only dropped when both signals are weak, so a fresh low-star adopter survives.

One honesty note on directness. The ecosyste.ms source parses each manifest and marks a dependency direct or transitive, so that filter is real there. The github-code-search source only finds the package name inside a package.json; it cannot tell a dependencies entry from a devDependencies, a keyword, a script name, or a longer package name that contains yours as a substring. So a code-search row means "the name is in this manifest," and the proof link is how you see which. That is the point of the proof link.

3. Resolve the owner to a company, then classify org vs. user. Each surviving owner is resolved through the GitHub API to a name, a website reduced to a bare domain, and the verified flag. Then the decisive step: is this a GitHub Organization or a personal User? Organizations are sellable companies and are kept by default. Personal users are dropped unless you pass --include-users and they name a company or domain. This one classification roughly halves the raw count and is the difference between a real target list and a pile of student projects.

4. Optionally diff against a competitor. With --vs, one GitHub code search per owner asks whether that owner publicly declares the competitor's package. The answer becomes the displacement column, phrased as evidence, never as a verdict.


Two enumeration sources, and it fails loudly

The tool never invents a zero. If it cannot enumerate a package, it says so and exits non-zero.

Source What it is Trade-offs
ecosyste.ms usage API The reverse-dependency graph. Free, no key, rich per-repo metadata inline (stars, last push, fork flag, default branch, manifest path). A best-effort community service with no SLA. It intermittently returns internal server error for a given package (real, observed on live packages, and not size-correlated). Retry with backoff does not cure a package that is persistently failing; when it fails, the tool falls back automatically.
GitHub code search "<pkg>" in:file filename:package.json. A genuine fallback enumerator, not just a freshness add-on. Returns the owner type (org vs. user) inline. Needs a token, is capped at 1000 results, sees default branches only, and is rate-limited to roughly ten searches a minute. A partial net, but it works when ecosyste.ms is down.

If ecosyste.ms is unavailable for a package and no token is set for the fallback, the tool refuses to report zero and tells you to set GITHUB_TOKEN. That refusal is deliberate: a single failed source is not evidence that a package has no dependents, and "zero" from a broken API is the most dangerous number this tool could print.


Displacement mode (--vs)

--vs <competitor-package> runs a second pass: for each company importing your target package, it asks the public graph whether that owner declares the competitor's package anywhere on any default branch. The rows with no match are flagged as displacement candidates and sorted to the top. For a devtool founder that set is a proof-backed slice of the addressable market, resolved to named organizations, with a proof link on every row.

Diff the whole vendor, not one package. --vs takes one package or a comma-separated list, and a hit on any of them counts as evidence. A vendor ships several SDKs, so a customer on @clerk/clerk-react is a real Clerk customer even if they never touch @clerk/nextjs. Diffing against a single package would put that customer on the displacement list by mistake, which the exact buyer would catch instantly. Pass every SDK the vendor publishes: --vs @clerk/nextjs,@clerk/clerk-react,@clerk/backend. The displacement column names the exact packages it checked, never the vendor, so you always know what was actually diffed.

"No public evidence" is not "not using." This is a hard rule, not a hedge. The index is public, default-branch-only, capped, and lagging. A company can use the competitor in a private repo, on a feature branch, behind an env var, or in a repo the crawl has not reached. The displacement column therefore says "no public evidence of X (crawl date)" and never "does not use X," and every row is stamped with the crawl date. Telling a vendor's happy customer to switch off a competitor they in fact rely on is the single worst thing this tool could help you do, so the phrasing is built to keep you from doing it. Verify every row through its proof_url before you act on it.


Limitations (read these before you trust a row)

This is a strong, verifiable sample, not a census. Know what it cannot see:

  • A proof link reflects the crawl, not this instant. It points at the manifest as the source last saw it. If a repo is deleted, made private, or renamed after that, the link can 404. The github-code-search source reads GitHub's live index and its links are current; the ecosyste.ms source can lag. Either way, verify a row through its proof_url before you act on it. A link that 404s is a signal (the repo moved), never proof the dependency was fake.
  • Public and default-branch only. Usage in a private repo, on a feature branch, or behind a build step is invisible. This is why --vs says "no public evidence," never "not using."
  • The enumerators are capped and best-effort. GitHub code search returns at most 1000 results and sees only default branches; ecosyste.ms serves partial page ranges and intermittently errors for a package. For a package with tens of thousands of dependents you are seeing a slice, and the tool tells you the slice size in its header.
  • Forks are dropped. A real adopter that exists only as a fork of someone else's repo is missed in v1. Fork-to-parent attribution is the highest-value open contribution; see CONTRIBUTING.md.
  • Org-vs-user is a heuristic. GitHub "organizations" include companies, but also clubs, university classes, and hackathon teams. The gate removes personal users, not every non-company org. The proof link plus a glance at the domain is how you finish the triage.

What this is, and where the paid version picks up

This is the free, top-of-funnel scanner. It answers "who publicly imports this, resolved to companies, with a receipt" and stops there. On purpose, it does not do:

  • contact enrichment (names, emails, titles); you get an organization and its domain, not a person to email,
  • firmographics (headcount, revenue, funding, industry tags),
  • exhaustive enumeration beyond the public, capped, best-effort sources; this is a strong sample, not a census,
  • freshness or intent events (a dependency added last week, a version bump, a competitor removed); this is a snapshot, not a stream,
  • any private-repo signal; public, default-branch graph only.

Those live behind RevAlign's paid wrap, layered on top of this list.

On the moat, plainly: the technique is not one. Reading a public dependency graph is well understood, and other companies (Reo.dev, for one) sell the enriched outcome with better contact data. The edge here is not the detection; it is the open-source-as-marketing flywheel plus the paid layer that turns this proof-backed list into a working pipeline. We are open-sourcing the honest, verifiable core and selling the wiring, and we are not pretending the algorithm is defensible.


CLI reference

revalign-dependents [ecosystem] PACKAGE [options]
Argument / option Meaning Default
ecosystem Package ecosystem. v1 supports npm. npm
PACKAGE Package name, e.g. algoliasearch or @clerk/nextjs. required
--vs COMPETITOR_PKG Displacement mode: flag companies with no public evidence of this package. off
--token GitHub token (else $GITHUB_TOKEN / $GH_TOKEN). Try --token $(gh auth token). none
--min-stars Keep a repo if stars >= this OR pushed recently. 2
--stale-months "Recently" means pushed within this many months. 18
--max-pages Max pages to enumerate per source. 10
--source Enumerator: auto (ecosyste.ms, then code-search fallback), ecosyste.ms (only), or github-code-search (force it; more complete when ecosyste.ms serves only a partial page range for a package). auto
--include-users Also keep personal users that name a company or domain (default: orgs only). off
--only-displacement With --vs, output only clean displacement candidates. off
--max-vs-checks Cap the number of owners checked in --vs mode. 200
--out Output directory. ./out

Environment: set GITHUB_TOKEN (or GH_TOKEN). Without a token the tool still tries ecosyste.ms, but owner enrichment, the code-search fallback, and --vs will not work, and it warns you.


Install notes

  • Requires Python 3.9 or newer.
  • Standard library only. urllib, csv, json, concurrent.futures; no third-party packages, no vendor SDK. pip install -e . and you are done.
  • Not yet published to PyPI. Install from source for now.
  • A GitHub token is not strictly required to hit ecosyste.ms, but almost everything useful (company resolution, the fallback enumerator, --vs) needs one. Set it.

Roadmap

  • Go modules as the second ecosystem (go.mod imports), same interface.
  • Better owner-to-company resolution for orgs whose GitHub profile has no website set.
  • Publish to PyPI once the interface settles.

Contributions toward any of these are welcome. See CONTRIBUTING.md.


License and credits

revalign-dependents is Apache-2.0 (see LICENSE). It depends only on the Python standard library, so the whole tool is permissively licensed and dependency-free.

Data comes from public sources: the ecosyste.ms usage API (a best-effort community service; please be a polite caller and identify yourself with a contact address), the GitHub REST and code-search APIs, and the npm registry. Respect their terms and rate limits. This tool only reads what a company already publishes about itself.

Built by RevAlign (https://revalign.io). Questions, ideas, or a package you want profiled: jordan@revalign.io. We open-sourced this because "a company probably uses this tool" is worth a lot less than a link to the line of code that proves it, and no tag-database will give you that link.

Sibling project: revalign-overhead finds any object in one satellite tile (what is there now); revalign-dependents finds every company importing a package, with a proof link (who is using what).

About

Find the companies publicly importing an npm package, resolved to organizations, each row with a proof link to the exact manifest line. --vs = displacement diff. $0, stdlib-only. A verifiable sample, not a census.

Topics

Resources

Code of conduct

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages