# linkpeek Comparison Notes Keep linkpeek's positioning narrow and evidence-based. ## Positioning Use linkpeek when you need: - server-side or edge-runtime link preview extraction from a known URL - Open Graph, Twitter Card, JSON-LD, favicon, canonical, media, and oEmbed discovery - a small TypeScript package with one runtime dependency - safe defaults for untrusted preview URLs Do not position linkpeek as: - a full article extraction framework - a provider-specific scraper rule ecosystem - a text-to-first-URL parser - a fetched oEmbed client - the fastest metadata scraper ## Tradeoffs | Package | Good fit | Tradeoff vs linkpeek | | --- | --- | --- | | `link-preview-js` | Extracting a preview from a URL or from the first URL in text | Broader text-input API; less focused on edge-runtime and safe-by-default URL fetching | | `open-graph-scraper` | Node Open Graph/Twitter Card scraping with broader scraper options | Node-oriented and broader than a small preview-card utility | | `metascraper` | Rule-based metadata extraction with provider-specific packages | More powerful framework; usually more setup and dependency surface | | `unfurl.js` | Rich Node metadata with fetched oEmbed support | Richer nested output; not focused on small edge-runtime preview extraction | | `url-metadata` | Broad page metadata extraction | Broader output shape than preview cards | ## Measured install footprint (2026-08-26) Pinned published versions installed into clean directories on Darwin 25.6 arm64 with Node.js 24.15.0 and npm 11.12.1: | Package | Version | Published | Installed packages | `node_modules` | | --- | --- | --- | ---: | ---: | | linkpeek | 2.1.4 | 2026-08-26 | 7 | 1.2 MB | | unfurl.js | 6.4.0 | 2024-02-13 | 16 | 3.0 MB | | link-preview-js | 5.0.0 | 2026-07-31 | 18 | 7.2 MB | | open-graph-scraper | 6.12.0 | 2026-06-26 | 27 | 10.3 MB | | url-metadata | 5.10.0 | 2026-08-02 | 30 | 9.8 MB | | metascraper + 7 rules | 5.56.2 | 2026-08-17 | 129 | 76.2 MB | Reproduce the table with the exact package set: ```bash measure_install() ( label="$1" shift probe_dir="$(mktemp -d)" cd "$probe_dir" npm init -y >/dev/null npm install --ignore-scripts --no-fund --no-audit "$@" >/dev/null printf '%s: ' "$label" node -e "const l=require('./package-lock.json');process.stdout.write(String(Object.keys(l.packages).filter(Boolean).length))" du -sk node_modules | awk '{print " packages, " $1 " KiB"}' ) measure_install linkpeek linkpeek@2.1.4 measure_install unfurl.js unfurl.js@6.4.0 measure_install link-preview-js link-preview-js@5.0.0 measure_install open-graph-scraper open-graph-scraper@6.12.0 measure_install url-metadata url-metadata@5.10.0 measure_install metascraper \ metascraper@5.56.2 metascraper-author@5.56.2 \ metascraper-date@5.56.2 metascraper-description@5.56.2 \ metascraper-image@5.56.2 metascraper-publisher@5.56.2 \ metascraper-title@5.56.2 metascraper-url@5.56.2 ``` ## Published package artifact The published linkpeek 2.1.4 tarball is 30,592 bytes with 8 files. Its ESM entry is 30,171 bytes raw and 8,144 bytes at gzip level 9. This excludes the external `htmlparser2` dependency and is not a full application-bundle measurement. Reproduce directly from the npm registry artifact: ```bash artifact_dir="$(mktemp -d)" npm pack linkpeek@2.1.4 --pack-destination "$artifact_dir" mkdir "$artifact_dir/unpacked" tar -xzf "$artifact_dir/linkpeek-2.1.4.tgz" -C "$artifact_dir/unpacked" wc -c "$artifact_dir/unpacked/package/dist/index.js" gzip -9 -c "$artifact_dir/unpacked/package/dist/index.js" | wc -c ``` ## Same-corpus speed benchmark (2026-08-26) Generated by the dedicated harness in [benchmarks/competitive](../benchmarks/competitive) (identical URLs served from a local HTTP server, same Node process, defaults plus only the options each package needs to reach localhost, satisfying this document's claim policy). Representative median from three runs, in ms per end-to-end preview on Darwin 25.6 arm64, Node v24.15.0, and npm 11.12.1. The linkpeek row was measured from the repository worktree that became 2.1.4; competitor rows use the pinned published versions: | Package | 0.6–2.3 kB pages | 489 kB page | | --- | ---: | ---: | | linkpeek 2.1.4 | 0.12–0.23 | 0.37 | | unfurl.js 6.4.0 | 0.08–0.16 | 2.69 | | link-preview-js 5.0.0 (fetch+parse)* | 0.18–0.31 | 16.72 | | url-metadata 5.10.0 | 0.39–0.61 | 15.81 | | metascraper 5.56.2 (+7 rules) | 0.28–0.40 | 19.03 | | open-graph-scraper 6.12.0 | 0.30–0.52 | 232.44 | \* link-preview-js rejects IP-literal hosts since its CVE fix and cannot fetch from a local server; measured via `getPreviewFromContent()` with native fetch. Honest reading: on small pages linkpeek and unfurl.js are tied within noise. On the large head-first fixture, linkpeek's byte cap and early stop avoid most body work. The controlled stream probe measures requested source chunks and retained decoded-body bytes, not exact network wire bytes. Do not quote the small-page column as "fastest" or infer live-network latency from this local benchmark. ## Security and runtime notes (sourced, as of 2026-08-26) Verify each claim against the source before citing it in marketing material. This data ages. - `link-preview-js`: HIGH-severity SSRF advisory [GHSA-4gp8-rjrq-ch6q](https://github.com/advisories/GHSA-4gp8-rjrq-ch6q) (CVE-2026-43897, published 2026-05-05) affects versions through 4.0.0; the benchmarked 5.0.0 release contains the advisory's fix. - `unfurl.js`: the [repository is archived](https://github.com/jacktuck/unfurl) and there has been no release since 2024-02; SSRF-protection PR [jacktuck/unfurl#117](https://github.com/jacktuck/unfurl/pull/117) (2026-05) documents that URLs pass to node-fetch without destination validation; unmerged at time of writing. - `open-graph-scraper`: the full package is Node-oriented; maintainers point edge/parse-only consumers to `open-graph-scraper-lite` in issues [#203](https://github.com/jshemas/openGraphScraper/issues/203) and [#252](https://github.com/jshemas/openGraphScraper/issues/252). - `url-metadata`: uses `node-fetch` and Node HTTP plumbing. Issue [#103](https://github.com/laurengarcia/url-metadata/issues/103) was fixed in 5.2.2; [#92](https://github.com/laurengarcia/url-metadata/issues/92) documents a narrower Node-stream compatibility problem and does not prove failure in every edge runtime. - `metascraper`: actively maintained (Microlink); browser/edge described as "not oriented for that" ([#746](https://github.com/microlinkhq/metascraper/issues/746)). - linkpeek's own documented limit: platform `fetch` owns final DNS resolution, so DNS-rebinding protection varies by runtime (see SECURITY.md). State this limit in any security-focused comparison; the same class of caveat applies to every package above. ## Claim Policy - Use `npm run benchmark` for local parser and package-footprint regression checks. - Use `npm run benchmark:live` only as a network smoke check. - Do not make competitive speed claims without a dedicated benchmark harness that uses the same corpus for every package. - Refresh package metadata with `npm view` before release notes, launch posts, or marketing pages. - Re-measure the footprint table and re-verify the sourced notes above before any launch post; update the dates when you do.