Product Decisions This Supports
- Feature feasibility / scope sizing. "Reader view," "save for later," and "clean article body" move from a multi-month heuristics project to a ~1–2 week spike. Graby bundles readability extraction, per-site configs, lazy-load image handling, multi-page/print-view following, link rewriting to absolute URLs, and XSS filtering behind one call — that is a lot of undifferentiated work you would otherwise own.
- Build vs. buy vs. self-host. The real decision this package informs is self-hosted OSS extraction vs. per-URL extraction APIs (Diffbot, Postlight/Mercury, ScrapingBee). Graby lets you model unit economics at volume: MIT license, no per-request fee, cost is engineering time + compute instead of a metered line item.
- Roadmap unblocking. Digest emails, RSS/news enrichment, AI summarization and RAG ingestion, citation/archive features, and moderation link previews all share the same prerequisite: clean article text plus metadata. Choosing this package decides that prerequisite once, for all of them.
- Coverage ceiling as a product constraint. Graby does not render JavaScript. That forces an explicit product call: accept "works on most article sites" or fund a second-tier headless-browser service for SPAs and paywalled/JS-gated publishers. Deciding this early prevents a "works everywhere" promise you cannot keep.
- Data handling and compliance posture. Self-hosting keeps fetched content and user reading data inside your infrastructure — relevant if you have data-residency or third-party-processor review requirements.
- Legal/product policy. Extraction of third-party pages is a policy question, not a technical one.
allowed_urls / blocked_urls config gives you a concrete enforcement lever for a partner-allowlist or publisher-blocklist policy.
When to Consider This Package
Adopt when:
- You are on PHP 8.2+ and can enable the
tidy and curl extensions in your runtime/containers (this is the most common blocker in practice — verify before committing).
- Extraction is an internal capability (aggregation, research, RAG, digests) or operates on licensed/partner/permitted content.
- Volume is low-to-medium and spiky enough that per-URL vendor pricing is unattractive or hard to forecast.
- You have Laravel engineers who can own a thin wrapper: service provider, config file, a queued job, and an interface boundary so the extractor can be swapped later.
- You want your existing HTTP stack to apply — it is HTTPlug-decoupled, so your Guzzle timeouts, proxies, and retry policy carry over.
Look elsewhere when:
- You need JS-rendered pages, login walls, or guaranteed high-fidelity extraction across the long tail — that is a rendering/browser problem Graby does not solve.
- You need an SLA, support contract, or vendor accountability.
- You have no PHP team, or the runtime cannot add native extensions.
- Version stability matters more than features. The current v3 documentation is explicitly
dev-master and API-incompatible with stable 2.x. Plan to pin a commit and budget for a breaking upgrade, or start on 2.x if you need the documented-stable API.
- Ecosystem depth matters. 391 stars and 0 listed dependents is thin — expect to debug edge cases yourself rather than find prior answers.
How to Pitch It (Stakeholders)
To executives: This is a free, MIT-licensed component that replaces a category of per-request SaaS spend and compresses a 2–3 month content-extraction build into roughly 2–3 weeks of integration. It unlocks reader mode, digests, and AI-summary features on one shared foundation. The trade is ownership: we maintain the integration and absorb upstream churn, and the community around it is small, so we should not expect vendor-grade support.
To engineering: composer require and go. No framework lock-in — the HTTP client is injected via HTTPlug, so our Guzzle configuration (timeouts, proxies, user agent) applies unchanged. It returns a structured result object (title, date, authors, language, lead image, summary, status, headers) rather than throwing on HTTP errors, so error handling is ours to design — a feature, not a bug. It also accepts already-fetched HTML and offers cleanupHtml() for sanitizing content we obtain elsewhere, which makes it useful beyond scraping. Hard requirements: PHP 8.2+, tidy, curl.
Recommendation: Fund a timeboxed spike — one engineer, two weeks, three representative sites plus your worst-case publisher. Wrap it behind an internal ContentExtractor interface, pin the dependency, and treat the extraction vendor as replaceable. If the spike shows acceptable coverage on your real targets, adopt; if coverage collapses on JS-heavy sources, you have lost two weeks and gained a clear business case for a managed API.