scrolltotop

new Get your brand recommended by ChatGPT, Perplexity & AI search engines. Discover GEO.

new We help companies keep track of engineering health with monthly reports. Start your review today.

The Web Is Being Tokenized. Serve Markdown.

Home Company Blog The Web Is Being Tokenized. Serve Markdown.
The Web Is Being Tokenized. Serve Markdown.

We are building LLMRank, a Shopify app available on the App Store that makes store catalogs discoverable by ChatGPT, Perplexity, Google AI Overviews, and every other system that reads the web through an LLM lens. It scores AI readiness, generates structured data as versioned metafields, provides llm.txt and /llm-index.json endpoints, simulates LLM queries against your catalog, and at the center of it all, serves clean Markdown snapshots of products and collections for AI crawlers to consume.

That Markdown layer needs to convert HTML reliably, at scale, across different source types. Cloudflare just shipped three services for this: Markdown for Agents (edge-level content negotiation), Workers AI toMarkdown (binary file conversion via REST), and Browser Rendering /markdown (headless Chromium for JS-heavy pages). Three services, three REST endpoints, three auth patterns. Wiring them into LLMRank's Laravel backend individually meant duplicating HTTP client setup, error handling, response parsing, and caching across each integration. So we built a unified adapter, extracted it, and open sourced it.

Why Markdown matters now

AI is at the core of how we work, and when AI systems start changing how they consume the web, we pay attention. Crawlers, coding agents, and LLM-powered search now tokenize everything they receive. Raw HTML is expensive: Cloudflare measured a single blog post at 16,180 tokens in HTML versus 3,150 in Markdown, an 80% reduction. At scale, the savings in context window budget and inference cost are substantial.

Cloudflare's three services each target a different conversion scenario. Markdown for Agents operates at the CDN edge via Accept: text/markdown content negotiation, returning Markdown with x-markdown-tokens and Content-Signal headers. No auth required. Workers AI toMarkdown accepts binary uploads (PDF, DOCX, XLSX, images, CSV, and more) through a multipart REST endpoint with batch support. Browser Rendering /markdown spins up Chromium, waits for a configurable load state, and converts the rendered DOM. Supports waitForSelector, request rejection, cookie injection, basic auth, and custom user agents.

One API for everything

Markdown for Agents for Laravel wraps all three services behind a single fluent interface with a driver-based architecture. Each Cloudflare service maps to a driver (agents, workers_ai, browser), and the package handles HTTP client setup, authentication, response normalization, caching, and error recovery.

composer require moneo/markdown-for-agents
php artisan vendor:publish --tag=markdown-for-agents-config
CF_ACCOUNT_ID=your-account-id
CF_API_TOKEN=your-api-token

The agents driver requires no credentials since it relies on content negotiation, not authenticated API calls.

Convert a URL

use Moneo\MarkdownForAgents\Facades\MarkdownForAgents;

$result = MarkdownForAgents::url('https://example.com')->convert();

$result->markdown;       // string: the converted content
$result->tokens;         // int: estimated token count
$result->contentSignals; // ?array: ['ai-train' => 'yes', ...]
$result->driver;         // string: which driver handled it
$result->fromCache;      // bool: served from cache?

Convert a file

$result = MarkdownForAgents::file('/path/to/document.pdf')->convert();

// Or from a Laravel upload
$result = MarkdownForAgents::file($request->file('document'))->convert();

Batch conversion

$results = MarkdownForAgents::files([$pdf, $image, $spreadsheet])->convert();

foreach ($results as $result) {
    echo "{$result->name}: {$result->tokens} tokens\n";
}

Switch drivers

// Default: agents driver (fastest, free)
$result = MarkdownForAgents::url($url)->convert();

// Browser driver for JavaScript-heavy pages
$result = MarkdownForAgents::driver('browser')
    ->url('https://spa-app.com')
    ->waitUntil('networkidle0')
    ->convert();

// Convert raw HTML through browser rendering
$result = MarkdownForAgents::driver('browser')
    ->html('<div><h1>Hello</h1><p>World</p></div>')
    ->convert();

The driver is selected automatically based on input type, or explicitly via driver(). The same ConversionResult DTO is returned regardless of which backend handled the request.

Automatic fallback

If the primary driver throws (zone does not support Markdown for Agents, network timeout, unexpected response), the package can retry with a different driver:

$result = MarkdownForAgents::url('https://example.com')
    ->withFallback('browser')
    ->convert();

This is particularly useful when targeting URLs where you cannot guarantee Cloudflare zone configuration. The agents driver fails fast with a non-Markdown Content-Type, and the browser driver picks up transparently.

Middleware: serve Markdown from your own routes

The package includes a middleware that implements Cloudflare's content negotiation pattern on your own Laravel routes. No Cloudflare zone required. No external API calls. The conversion runs locally using league/html-to-markdown.

Route::get('/blog/{slug}', [BlogController::class, 'show'])
    ->middleware('markdown-for-agents');

When an incoming request includes Accept: text/markdown, the middleware intercepts the HTML response after it is rendered, converts it, and returns it with the following headers:

  • Content-Type: text/markdown; charset=utf-8
  • Vary: accept
  • x-markdown-tokens: {count}
  • Content-Signal: ai-train=yes, search=yes, ai-input=yes

Regular browser requests pass through untouched. The middleware only activates on content negotiation.

This is already a live pattern. Claude Code, OpenCode, and several OpenAI crawlers send Accept: text/markdown in their request headers today. If your application can respond to that, you deliver structured content directly to the agent's context window instead of forcing it to parse your HTML and burn tokens on layout noise.

Everything else

Caching. URL and HTML conversions are cached by default, keyed by source hash and driver name. TTL is configurable globally or per request via ->cache(7200). Bypass with ->noCache(). Invalidate with MarkdownForAgents::clearCache($url) or flushCache().

Browser driver options. The browser driver exposes waitUntil (maps to Puppeteer's goto options), rejectPatterns (blocks matching requests before render), userAgent, cookies, and authenticate for pages behind auth walls.

Events. MarkdownConverted and ConversionFailed events are dispatched on every conversion. Wire them to your logging, monitoring, or alerting stack.

Artisan commands. php artisan markdown:convert converts URLs or files from the CLI with --driver and --save options. markdown:formats lists supported file types. markdown:cache:clear manages the cache.

Extensibility. Every driver implements MarkdownConverterInterface with a single method: convert(PendingConversion): ConversionResult. Register custom drivers via MarkdownForAgents::extend() and the rest of the package (caching, events, fallback) works with them out of the box.

Driver selection guide:

Scenario Recommended Driver
Cloudflare-enabled site agents (fastest, free)
PDF, DOCX, XLSX, images workers_ai (only option)
SPA or JavaScript-heavy page browser (full render)
Non-Cloudflare site browser
Raw HTML string browser
Making your app agent ready Middleware (local, no API)

Why we open sourced it

This package was extracted from LLMRank. The conversion logic is entirely generic, and any Laravel team integrating with Cloudflare's Markdown services will face the same wiring problem. The middleware came from the same GEO work: making brands visible in AI-generated answers requires both consuming structured content and serving it. When an internal abstraction is clean enough to stand on its own, we extract and open source it, just like we did with Laravel Usage Limiter.

Give it a try

Markdown for Agents for Laravel is on Packagist. PHP 8.2+, Laravel 11 or 12. Tests run via orchestra/testbench with mocked HTTP, no real API calls.

Source, issues, and PRs on GitHub. If you need help building AI-powered Laravel applications or making your infrastructure agent-ready, reach out.


Links:

Moneo as Your Enterprise Partner

We collaborate closely with enterprise teams to design, deliver, and operate systems built for the long run.

Start Partnership
Emir Karşıyakalı

Emir Karşıyakalı

Founder & CEO

Jump to

Moneo as Your Enterprise Partner

We collaborate closely with enterprise teams to design, deliver, and operate systems built for the long run.

Start Partnership
Partnership is at the core of what we do.

Unsure where to start?
Let’s figure it out together 👋

Contact Us