<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Daniel Igel</title>
    <description>The latest articles on DEV Community by Daniel Igel (@grenzfrei).</description>
    <link>https://dev.to/grenzfrei</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3982725%2F6c4b0484-702b-40c6-919f-93756e16fd33.png</url>
      <title>DEV Community: Daniel Igel</title>
      <link>https://dev.to/grenzfrei</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/grenzfrei"/>
    <language>en</language>
    <item>
      <title>Scrape any URL with one POST — Cloudflare bypass included</title>
      <dc:creator>Daniel Igel</dc:creator>
      <pubDate>Mon, 06 Jul 2026 14:18:44 +0000</pubDate>
      <link>https://dev.to/grenzfrei/scrape-any-url-with-one-post-cloudflare-bypass-included-49a7</link>
      <guid>https://dev.to/grenzfrei/scrape-any-url-with-one-post-cloudflare-bypass-included-49a7</guid>
      <description>&lt;p&gt;Most scraping libraries fall apart the moment they hit a Cloudflare-protected page — you get a &lt;code&gt;403&lt;/code&gt; or an infinite challenge loop. Running a headless browser locally isn't an option in a serverless or edge environment, and maintaining your own bypass infrastructure is a project in itself.&lt;/p&gt;

&lt;p&gt;One POST fetches any URL — Cloudflare-protected or not — and returns clean HTML, stripped text, or both in a single response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; &lt;span class="s1"&gt;'https://web-scraping-api.p.rapidapi.com/api/v1/scrape'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'x-rapidapi-key: YOUR_RAPIDAPI_KEY'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'x-rapidapi-host: web-scraping-api.p.rapidapi.com'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'{"url":"https://example.com","output":"text"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set &lt;code&gt;output&lt;/code&gt; to &lt;code&gt;html&lt;/code&gt; for the raw markup, &lt;code&gt;text&lt;/code&gt; for the stripped content (scripts and styles removed), or omit it to get both fields in one response alongside the final URL and HTTP status code.&lt;/p&gt;

&lt;p&gt;When you only need specific data points, use the &lt;code&gt;/extract&lt;/code&gt; endpoint with a CSS selector map:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://web-scraping-api.p.rapidapi.com/api/v1/extract&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-rapidapi-key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RAPIDAPI_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-rapidapi-host&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;web-scraping-api.p.rapidapi.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://example.com/products&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;selectors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;h1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;prices&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.price[]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;thumbnail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;img.hero@src&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// { title: "Widget Pro", prices: ["$9.99", "$19.99"], thumbnail: "https://..." }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Append &lt;code&gt;[]&lt;/code&gt; to a selector to collect all matching elements as an array; add &lt;code&gt;@attr&lt;/code&gt; to pull an attribute value instead of text content. No headless browser on your end — the bypass runs server-side.&lt;/p&gt;

&lt;p&gt;Free tier on RapidAPI: &lt;a href="https://rapidapi.com/danieligel/api/web-scraping-api" rel="noopener noreferrer"&gt;https://rapidapi.com/danieligel/api/web-scraping-api&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What's the site type that's been hardest to scrape reliably in your projects — Cloudflare, heavy SPAs, or login-gated content?&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Why your robots.txt is blocking AI crawlers (and how to fix it)</title>
      <dc:creator>Daniel Igel</dc:creator>
      <pubDate>Sun, 05 Jul 2026 06:27:29 +0000</pubDate>
      <link>https://dev.to/grenzfrei/why-your-robotstxt-is-blocking-ai-crawlers-and-how-to-fix-it-lgg</link>
      <guid>https://dev.to/grenzfrei/why-your-robotstxt-is-blocking-ai-crawlers-and-how-to-fix-it-lgg</guid>
      <description>&lt;p&gt;A &lt;code&gt;User-agent: *&lt;/code&gt; rule in robots.txt blocks every bot that doesn't have its own entry — including the AI crawlers that power ChatGPT Search, Perplexity, and Google AI Overviews.&lt;/p&gt;

&lt;p&gt;The bots that feed live AI answers have their own user-agent strings: &lt;code&gt;GPTBot&lt;/code&gt;, &lt;code&gt;OAI-SearchBot&lt;/code&gt;, &lt;code&gt;ChatGPT-User&lt;/code&gt;, &lt;code&gt;ClaudeBot&lt;/code&gt;, &lt;code&gt;PerplexityBot&lt;/code&gt;, &lt;code&gt;Perplexity-User&lt;/code&gt;, and &lt;code&gt;Google-Extended&lt;/code&gt;. If your robots.txt has a wildcard &lt;code&gt;Disallow:&lt;/code&gt; without explicit rules for these seven, they inherit the restriction — and your content won't appear in AI citations.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix
&lt;/h2&gt;

&lt;p&gt;Add explicit &lt;code&gt;Allow: /&lt;/code&gt; entries before your wildcard group:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User-agent: GPTBot
Allow: /

User-agent: OAI-SearchBot
Allow: /

User-agent: ChatGPT-User
Allow: /

User-agent: ClaudeBot
Allow: /

User-agent: PerplexityBot
Allow: /

User-agent: Perplexity-User
Allow: /

User-agent: Google-Extended
Allow: /

User-agent: *
Disallow: /private/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Specific rules always win over the wildcard (RFC 9309), so Googlebot and Bingbot are unaffected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Diagnose your robots.txt in one call
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://citeready-api.sprytools.com/v1/audit &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"content-type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"url":"https://yoursite.com"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;AI Crawler Access&lt;/code&gt; category reports a &lt;code&gt;pass&lt;/code&gt;, &lt;code&gt;warn&lt;/code&gt;, or &lt;code&gt;fail&lt;/code&gt; for each bot individually — including which user-agents are blocked and the exact fix to apply.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://citeready-api.sprytools.com/v1/audit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;content-type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://yoursite.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;categories&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;aiAccess&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;categories&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ai_access&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;aiAccess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;aiAccess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checks&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Free at &lt;a href="https://citeready.sprytools.com" rel="noopener noreferrer"&gt;https://citeready.sprytools.com&lt;/a&gt; — 3 checks/day, no signup.&lt;/p&gt;

&lt;p&gt;Which AI crawler did you find blocked after auditing your site?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>seo</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
    <item>
      <title>10-point AI citation readiness checklist for developers</title>
      <dc:creator>Daniel Igel</dc:creator>
      <pubDate>Thu, 02 Jul 2026 21:12:08 +0000</pubDate>
      <link>https://dev.to/grenzfrei/10-point-ai-citation-readiness-checklist-for-developers-396k</link>
      <guid>https://dev.to/grenzfrei/10-point-ai-citation-readiness-checklist-for-developers-396k</guid>
      <description>&lt;p&gt;Getting cited by ChatGPT Search, Perplexity, or Google AI Overviews isn't random — there's a specific set of technical signals those systems check. Here are the 10 that matter most.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. robots.txt is present.&lt;/strong&gt; Without it, crawlers assume everything is allowed, but you lose explicit AI-bot policy and the &lt;code&gt;Sitemap:&lt;/code&gt; hint.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. AI search bots are not blocked.&lt;/strong&gt; &lt;code&gt;GPTBot&lt;/code&gt;, &lt;code&gt;OAI-SearchBot&lt;/code&gt;, &lt;code&gt;ChatGPT-User&lt;/code&gt;, &lt;code&gt;ClaudeBot&lt;/code&gt;, &lt;code&gt;PerplexityBot&lt;/code&gt;, &lt;code&gt;Perplexity-User&lt;/code&gt;, and &lt;code&gt;Google-Extended&lt;/code&gt; feed live AI answers. A &lt;code&gt;Disallow: /&lt;/code&gt; that catches any of them removes your site from AI citations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Snippets are not restricted.&lt;/strong&gt; &lt;code&gt;&amp;lt;meta name="robots" content="nosnippet"&amp;gt;&lt;/code&gt;, &lt;code&gt;noindex&lt;/code&gt;, or &lt;code&gt;max-snippet:0&lt;/code&gt; — in either a meta tag or an &lt;code&gt;X-Robots-Tag&lt;/code&gt; response header — tell AI engines to skip quoting your content.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. JSON-LD is present and valid.&lt;/strong&gt; Invalid JSON-LD blocks are silently ignored. Test after every template change.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Organization or WebSite schema is present.&lt;/strong&gt; Identity markup lets AI engines attribute your content to your brand rather than treating the page as anonymous text.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Main content is 600+ words.&lt;/strong&gt; Strip scripts, nav, and footer before counting. Thin pages rarely become AI sources.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. Paragraphs are 20–120 words each.&lt;/strong&gt; That's the range AI engines extract quotes from. Break up walls of text; expand one-liners into full thoughts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8. Page serves over HTTPS with HTTP 200.&lt;/strong&gt; Insecure pages and error statuses are dropped from AI indexes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;9. Canonical URL is declared.&lt;/strong&gt; Without it, duplicate variants of the same page split citation signals instead of pooling them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;10. llms.txt is present at &lt;code&gt;/llms.txt&lt;/code&gt;.&lt;/strong&gt; A plain Markdown index of your key pages — AI crawlers increasingly use it to prioritize what to read.&lt;/p&gt;

&lt;h2&gt;
  
  
  Score all 10 in one call
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://citeready-api.sprytools.com/v1/audit &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"content-type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"url":"https://yoursite.com"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response gives a 0–100 score across four categories — AI Crawler Access, Structured Data, Content Citability, Technical Foundation — with a pass/warn/fail on every check and a prioritized fix list.&lt;/p&gt;

&lt;p&gt;Free at &lt;a href="https://citeready.sprytools.com" rel="noopener noreferrer"&gt;https://citeready.sprytools.com&lt;/a&gt; — 3 checks/day, no signup.&lt;/p&gt;

&lt;p&gt;How many of these 10 does your main site currently pass? Curious where developers most often lose points.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>developer</category>
      <category>seo</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Build link previews without parsing HTML: OG metadata in one API call</title>
      <dc:creator>Daniel Igel</dc:creator>
      <pubDate>Wed, 01 Jul 2026 13:00:01 +0000</pubDate>
      <link>https://dev.to/grenzfrei/build-link-previews-without-parsing-html-og-metadata-in-one-api-call-2pj8</link>
      <guid>https://dev.to/grenzfrei/build-link-previews-without-parsing-html-og-metadata-in-one-api-call-2pj8</guid>
      <description>&lt;p&gt;Building link unfurl panels — the preview cards you see in Slack, Notion, or iMessage — means fetching the raw page, parsing &lt;code&gt;og:&lt;/code&gt; meta tags, falling back to &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;meta name="description"&amp;gt;&lt;/code&gt; when OG is missing, and resolving relative favicon paths. It's tedious boilerplate that rarely fits cleanly into whatever HTTP client you're already using.&lt;/p&gt;

&lt;p&gt;One GET returns OG tags, title, description, canonical URL, and favicon for any URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; GET &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; &lt;span class="s1"&gt;'https://url-metadata-open-graph-api.p.rapidapi.com/api/v1/meta?url=https://github.com'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'x-rapidapi-key: YOUR_RAPIDAPI_KEY'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'x-rapidapi-host: url-metadata-open-graph-api.p.rapidapi.com'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You get back a normalized object — &lt;code&gt;og.title&lt;/code&gt;, &lt;code&gt;og.image&lt;/code&gt;, &lt;code&gt;og.description&lt;/code&gt;, &lt;code&gt;favicon&lt;/code&gt;, &lt;code&gt;canonical&lt;/code&gt; — regardless of whether the page has OG tags or falls back to standard meta. The response is cached for 10 minutes, so repeated calls to the same URL skip the origin fetch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;meta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s2"&gt;`https://url-metadata-open-graph-api.p.rapidapi.com/api/v1/meta?url=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-rapidapi-key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RAPIDAPI_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-rapidapi-host&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;url-metadata-open-graph-api.p.rapidapi.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Need to prefetch multiple links at once? &lt;code&gt;POST /api/v1/meta/batch&lt;/code&gt; with &lt;code&gt;{ urls: [...] }&lt;/code&gt; handles up to 5 URLs concurrently — one round trip instead of five.&lt;/p&gt;

&lt;p&gt;Free tier on RapidAPI: &lt;a href="https://rapidapi.com/danieligel/api/url-metadata-open-graph-api" rel="noopener noreferrer"&gt;https://rapidapi.com/danieligel/api/url-metadata-open-graph-api&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When building link previews, do you parse OG tags yourself or reach for a dedicated service?&lt;/p&gt;

</description>
      <category>api</category>
      <category>seo</category>
      <category>showdev</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Add llms.txt to your site in 5 minutes and get cited by ChatGPT, Perplexity &amp; Co.</title>
      <dc:creator>Daniel Igel</dc:creator>
      <pubDate>Thu, 25 Jun 2026 13:00:03 +0000</pubDate>
      <link>https://dev.to/grenzfrei/add-llmstxt-to-your-site-in-5-minutes-and-get-cited-by-chatgpt-perplexity-co-2eb8</link>
      <guid>https://dev.to/grenzfrei/add-llmstxt-to-your-site-in-5-minutes-and-get-cited-by-chatgpt-perplexity-co-2eb8</guid>
      <description>&lt;p&gt;Most AI search tools — ChatGPT Search, Perplexity, Bing Copilot — crawl the web to find pages they can cite. The problem: they can't always tell which parts of your site are worth reading, or what your site is actually about. &lt;code&gt;llms.txt&lt;/code&gt; fixes that.&lt;/p&gt;

&lt;p&gt;It's a plain-text Markdown file you put at &lt;code&gt;https://yoursite.com/llms.txt&lt;/code&gt;. Three lines are enough to get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  The format
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Your Site Name&lt;/span&gt;
&lt;span class="gt"&gt;
&amp;gt; One-sentence summary of what your site is and who it's for.&lt;/span&gt;

&lt;span class="gu"&gt;## Important pages&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Page Title&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://yoursite.com/page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: What this page covers.
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Another Page&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://yoursite.com/other&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: Short description.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first &lt;code&gt;#&lt;/code&gt; heading is your site name. The &lt;code&gt;&amp;gt;&lt;/code&gt; blockquote is the summary AI crawlers use as context when deciding whether your site is relevant to a query. Each &lt;code&gt;## Section&lt;/code&gt; lists the pages you want prioritized, with an optional short description per link.&lt;/p&gt;

&lt;p&gt;That's the entire format. No build step, no framework, no schema validation — just a file at the right path.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it matters
&lt;/h2&gt;

&lt;p&gt;Without &lt;code&gt;llms.txt&lt;/code&gt;, an AI crawler has to infer what your site is about from the content of individual pages. It might index your blog posts but miss your documentation. It might understand what your tool does but not know which page to cite for a specific question.&lt;/p&gt;

&lt;p&gt;With &lt;code&gt;llms.txt&lt;/code&gt;, you give it a map. You're saying: &lt;em&gt;this site is for developers who need X, and the pages most worth reading are these.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Perplexity, ChatGPT Search, and a growing number of AI crawlers actively look for this file. It's not mandatory, but skipping it is a missed signal that costs nothing to add.&lt;/p&gt;

&lt;h2&gt;
  
  
  A real example
&lt;/h2&gt;

&lt;p&gt;Here's a &lt;code&gt;llms.txt&lt;/code&gt; for a developer API tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Sprytools&lt;/span&gt;
&lt;span class="gt"&gt;
&amp;gt; Developer APIs for common backend tasks — email validation, IP geolocation, IBAN parsing, currency conversion — accessible behind one key.&lt;/span&gt;

&lt;span class="gu"&gt;## Key pages&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;API Hub &amp;amp; Pricing&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://sprytools.com/api&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: All APIs, pricing tiers, self-serve key management.
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Email Validation&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://sprytools.com/tools/email-validation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: Syntax + MX + disposable detection.
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;IP Geolocation&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://sprytools.com/tools/ip-geolocation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: IPv4/IPv6, country, city, ASN.
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;IBAN Validator&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://sprytools.com/tools/iban-validator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: MOD-97, 80+ countries, parsed output.

&lt;span class="gu"&gt;## About&lt;/span&gt;

All APIs share one key. No per-service sign-up. Free tier on each endpoint.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The description is factual, not aspirational. "Developer APIs for common backend tasks" tells a model what this site is in one phrase. "Empowering developers to build better APIs faster" does not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to put it
&lt;/h2&gt;

&lt;p&gt;The file goes at the root: &lt;code&gt;https://yourdomain.com/llms.txt&lt;/code&gt;. Subdirectory paths won't be auto-discovered.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Static site / Next.js:&lt;/strong&gt; drop it in &lt;code&gt;public/&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public/
  llms.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Express or Hono:&lt;/strong&gt; serve your &lt;code&gt;public/&lt;/code&gt; directory as static files, or add a route:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/llms.txt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`# My Site\n\n&amp;gt; Description.\n\n## Key pages\n\n- [Home](https://example.com/)\n`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Astro:&lt;/strong&gt; same as Next.js — put it in &lt;code&gt;public/llms.txt&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;After deploying, confirm it's reachable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-I&lt;/span&gt; https://yoursite.com/llms.txt
&lt;span class="c"&gt;# Should return HTTP/2 200, content-type: text/plain&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Point to it from robots.txt
&lt;/h2&gt;

&lt;p&gt;Add a pointer in your &lt;code&gt;robots.txt&lt;/code&gt; file alongside the Sitemap directive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Sitemap: https://yoursite.com/sitemap.xml
LLMs: https://yoursite.com/llms.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;LLMs:&lt;/code&gt; line isn't part of the original robots.txt spec, but it mirrors the established &lt;code&gt;Sitemap:&lt;/code&gt; convention. Some parsers already read it, and it serves as documentation for anyone reading the file manually.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common mistakes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Wrong path&lt;/strong&gt; — must be &lt;code&gt;/llms.txt&lt;/code&gt; at the root, not &lt;code&gt;/docs/llms.txt&lt;/code&gt; or &lt;code&gt;/blog/llms.txt&lt;/code&gt;. AI crawlers look for it at the root; subdirectory paths won't be found automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Blocking AI crawlers in &lt;code&gt;robots.txt&lt;/code&gt; while adding &lt;code&gt;llms.txt&lt;/code&gt;&lt;/strong&gt; — the crawlers still need &lt;code&gt;Allow&lt;/code&gt; in &lt;code&gt;robots.txt&lt;/code&gt; to fetch &lt;em&gt;either&lt;/em&gt; file. A &lt;code&gt;Disallow: /&lt;/code&gt; wildcard blocks &lt;code&gt;llms.txt&lt;/code&gt; too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Generic description&lt;/strong&gt; — the &lt;code&gt;&amp;gt;&lt;/code&gt; blockquote is the most important line. "Platform for developers" tells a model nothing. "REST API for email validation, DNS lookup, and IBAN parsing" tells it exactly what kind of queries your site can answer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Missing UTF-8&lt;/strong&gt; — save as UTF-8 without BOM. Non-ASCII characters in site names or descriptions can cause parse errors in some crawlers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verify with CiteReady
&lt;/h2&gt;

&lt;p&gt;After deploying, you can verify the full picture — llms.txt &lt;em&gt;plus&lt;/em&gt; the four other AI readiness signals — in one call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://citeready-api.sprytools.com/v1/audit &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"url":"https://yoursite.com"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;llms_txt&lt;/code&gt; check in the response tells you whether the file was found and parseable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"llms_txt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pass"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"details"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"llms.txt found at /llms.txt"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A missing or unreachable file returns &lt;code&gt;fail&lt;/code&gt;. A file with wrong content-type or a 404 after deploy is the most common issue — worth verifying with a &lt;code&gt;curl -I&lt;/code&gt; before assuming it's working.&lt;/p&gt;

&lt;p&gt;Free tier at &lt;a href="https://citeready.sprytools.com" rel="noopener noreferrer"&gt;https://citeready.sprytools.com&lt;/a&gt; — 3 checks/day, no signup.&lt;/p&gt;

&lt;p&gt;Have you added &lt;code&gt;llms.txt&lt;/code&gt; to any of your projects? Curious whether anyone has noticed a measurable change in Perplexity or ChatGPT citations after adding it.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How to audit your site for AI search readiness (step-by-step with CiteReady)</title>
      <dc:creator>Daniel Igel</dc:creator>
      <pubDate>Wed, 24 Jun 2026 14:33:49 +0000</pubDate>
      <link>https://dev.to/grenzfrei/how-to-audit-your-site-for-ai-search-readiness-step-by-step-with-citeready-jp1</link>
      <guid>https://dev.to/grenzfrei/how-to-audit-your-site-for-ai-search-readiness-step-by-step-with-citeready-jp1</guid>
      <description>&lt;p&gt;Your site ranks well — but AI search engines like ChatGPT, Perplexity, and Google's AI Overviews still won't cite it. Classic SEO tools measure rankings; they don't tell you whether GPTBot can crawl your page or whether your content is structured enough for a model to lift a clean passage.&lt;/p&gt;

&lt;p&gt;Here's how to audit and fix that, category by category.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Run your first audit (free, no signup)
&lt;/h2&gt;

&lt;p&gt;Paste your URL into &lt;a href="https://citeready.sprytools.com" rel="noopener noreferrer"&gt;https://citeready.sprytools.com&lt;/a&gt; or call the API directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://citeready-api.sprytools.com/v1/audit &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"content-type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"url":"https://yoursite.com"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You get a score 0–100, a letter grade (A–F), and a per-category breakdown with pass/warn/fail on each check. The response looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;54&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"grade"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"D"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"categories"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ai_crawlers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fail"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"recommendation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Unblock PerplexityBot, GPTBot in robots.txt"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"llms_txt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fail"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"recommendation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Add llms.txt to improve LLM discoverability"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"structured_data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"warn"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"citability"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pass"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"technical"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pass"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That score of 54 is surprisingly common. Let's walk through each category.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Understand the five categories
&lt;/h2&gt;

&lt;h3&gt;
  
  
  AI Crawler access
&lt;/h3&gt;

&lt;p&gt;AI systems need to crawl your pages before they can cite them. The problem is that many sites block them accidentally. A common &lt;code&gt;robots.txt&lt;/code&gt; pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;User&lt;/span&gt;-&lt;span class="n"&gt;agent&lt;/span&gt;: *
&lt;span class="n"&gt;Disallow&lt;/span&gt;: /
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This blocks every crawler — including GPTBot (ChatGPT), OAI-SearchBot (OpenAI's separate indexing crawler), PerplexityBot, ClaudeBot, and Google-Extended (used for AI Overviews). You won't see warnings in Google Search Console because these aren't the traditional Googlebot.&lt;/p&gt;

&lt;p&gt;Even a partial block causes problems. &lt;code&gt;Disallow: /blog/&lt;/code&gt; hides your best content from AI citation. &lt;code&gt;Disallow: /api/&lt;/code&gt; is usually fine, but check that you're not also blocking content pages by proximity.&lt;/p&gt;

&lt;h3&gt;
  
  
  llms.txt
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;llms.txt&lt;/code&gt; is a plain Markdown file at &lt;code&gt;yourdomain.com/llms.txt&lt;/code&gt;. It tells language models what your site contains and where your important content is — similar in spirit to &lt;code&gt;robots.txt&lt;/code&gt;, but written for models to read, not crawlers to obey.&lt;/p&gt;

&lt;p&gt;It's optional, but it's a cheap signal. A three-line file can move you from fail to pass on this category.&lt;/p&gt;

&lt;h3&gt;
  
  
  JSON-LD structured data
&lt;/h3&gt;

&lt;p&gt;Schema.org markup gives AI systems a machine-readable description of your content. Without it, a model has to infer your page's topic, author, and date from prose. With it, that context is unambiguous.&lt;/p&gt;

&lt;p&gt;The most relevant types for AI citation: &lt;code&gt;Article&lt;/code&gt;, &lt;code&gt;WebPage&lt;/code&gt;, &lt;code&gt;FAQPage&lt;/code&gt;, &lt;code&gt;HowTo&lt;/code&gt;, &lt;code&gt;WebApplication&lt;/code&gt;. A &lt;code&gt;noindex&lt;/code&gt; page with perfect JSON-LD still scores zero — structured data amplifies citability, it doesn't create it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Content citability
&lt;/h3&gt;

&lt;p&gt;This is the hardest category to improve quickly because it depends on how you write. AI engines prefer passages that are self-contained — a sentence the model can quote without the surrounding paragraphs to make sense of it.&lt;/p&gt;

&lt;p&gt;Low-citability: &lt;em&gt;"As mentioned earlier, the tool integrates with all major platforms seamlessly."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;High-citability: &lt;em&gt;"CiteReady checks five signals: AI crawler access, llms.txt, JSON-LD, passage citability, and technical hygiene. Each category returns a pass/warn/fail with a specific fix."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The second version can be lifted from the article and quoted accurately. The first can't be cited without context and doesn't contain a quotable claim.&lt;/p&gt;

&lt;h3&gt;
  
  
  Technical signals
&lt;/h3&gt;

&lt;p&gt;A few checks that either block AI content extraction entirely or reduce confidence:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;HTTPS&lt;/strong&gt; — most AI crawlers skip HTTP-only pages&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;nosnippet&lt;/code&gt; meta tag&lt;/strong&gt; — this explicitly prevents excerpt extraction; if it's set, your citability score is zero regardless of content quality&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Canonical tags&lt;/strong&gt; — the canonical should point to the URL you want cited; a chain of redirects adds ambiguity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sitemap&lt;/strong&gt; — a sitemap at &lt;code&gt;/sitemap.xml&lt;/code&gt; helps crawlers discover content; the &lt;code&gt;robots.txt&lt;/code&gt; &lt;code&gt;Sitemap:&lt;/code&gt; directive points them to it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;lang&lt;/code&gt; attribute&lt;/strong&gt; — language identification helps models pick the right passages for a given query&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 3: Close the gaps
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Fix AI crawler blocks first
&lt;/h3&gt;

&lt;p&gt;If the audit returns &lt;code&gt;fail&lt;/code&gt; on AI crawlers, this is your highest-leverage fix. Add explicit allow rules before any blocking rules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="c"&gt;# Allow AI crawlers
&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;-&lt;span class="n"&gt;agent&lt;/span&gt;: &lt;span class="n"&gt;GPTBot&lt;/span&gt;
&lt;span class="n"&gt;Allow&lt;/span&gt;: /

&lt;span class="n"&gt;User&lt;/span&gt;-&lt;span class="n"&gt;agent&lt;/span&gt;: &lt;span class="n"&gt;OAI&lt;/span&gt;-&lt;span class="n"&gt;SearchBot&lt;/span&gt;
&lt;span class="n"&gt;Allow&lt;/span&gt;: /

&lt;span class="n"&gt;User&lt;/span&gt;-&lt;span class="n"&gt;agent&lt;/span&gt;: &lt;span class="n"&gt;PerplexityBot&lt;/span&gt;
&lt;span class="n"&gt;Allow&lt;/span&gt;: /

&lt;span class="n"&gt;User&lt;/span&gt;-&lt;span class="n"&gt;agent&lt;/span&gt;: &lt;span class="n"&gt;ClaudeBot&lt;/span&gt;
&lt;span class="n"&gt;Allow&lt;/span&gt;: /

&lt;span class="n"&gt;User&lt;/span&gt;-&lt;span class="n"&gt;agent&lt;/span&gt;: &lt;span class="n"&gt;Google&lt;/span&gt;-&lt;span class="n"&gt;Extended&lt;/span&gt;
&lt;span class="n"&gt;Allow&lt;/span&gt;: /

&lt;span class="c"&gt;# Your existing rules below
&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;-&lt;span class="n"&gt;agent&lt;/span&gt;: *
&lt;span class="n"&gt;Disallow&lt;/span&gt;: /&lt;span class="n"&gt;private&lt;/span&gt;/
&lt;span class="n"&gt;Disallow&lt;/span&gt;: /&lt;span class="n"&gt;admin&lt;/span&gt;/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have a wildcard &lt;code&gt;Disallow: /&lt;/code&gt;, you'll need to restructure the file. Crawler-specific rules listed before the wildcard take precedence in most parsers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Remove nosnippet
&lt;/h3&gt;

&lt;p&gt;If you have &lt;code&gt;&amp;lt;meta name="robots" content="nosnippet"&amp;gt;&lt;/code&gt; anywhere, remove it unless you have a specific reason for it (legal content you don't want excerpted, for example). This one tag blocks all AI passage extraction.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add JSON-LD
&lt;/h3&gt;

&lt;p&gt;A minimal Article schema takes 10 minutes and typically improves your score by 10–15 points:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"application/ld+json"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@context&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://schema.org&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Article&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;headline&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Your page title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;description&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;One-sentence summary&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;author&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Person&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Your Name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;datePublished&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2026-06-23&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;url&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://yoursite.com/this-page&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For tool or SaaS pages, use &lt;code&gt;WebApplication&lt;/code&gt; or &lt;code&gt;SoftwareApplication&lt;/code&gt; instead of &lt;code&gt;Article&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create llms.txt
&lt;/h3&gt;

&lt;p&gt;Drop a file at &lt;code&gt;yourdomain.com/llms.txt&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Site Name&lt;/span&gt;
&lt;span class="gt"&gt;
&amp;gt; One-sentence description of what this site is and who it's for.&lt;/span&gt;

&lt;span class="gu"&gt;## Key pages&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://example.com/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Docs&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://example.com/docs/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;About&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://example.com/about/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. The description line (the &lt;code&gt;&amp;gt;&lt;/code&gt; blockquote) is the most important part — write it as a factual summary, not a marketing tagline.&lt;/p&gt;

&lt;h3&gt;
  
  
  Improve prose citability (longer-term)
&lt;/h3&gt;

&lt;p&gt;The structural fixes above move most sites from the 40–60 range to 65–80. Getting above 80 requires improving the prose itself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open each section with the conclusion, not the setup&lt;/li&gt;
&lt;li&gt;Keep paragraphs to 3–4 sentences with a single claim each&lt;/li&gt;
&lt;li&gt;Replace vague language with specific numbers, names, and outcomes&lt;/li&gt;
&lt;li&gt;Avoid forward/backward references ("as mentioned above", "we'll cover this later")&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Run the audit again after each change to measure the effect. The per-category breakdown tells you which fix had the most impact.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to expect after fixing
&lt;/h2&gt;

&lt;p&gt;A typical site that fixes crawler blocks + removes nosnippet + adds basic JSON-LD goes from ~50 to ~75 in one afternoon. The citability score moves more slowly because it depends on content rewriting, but it's the signal with the highest ceiling.&lt;/p&gt;

&lt;p&gt;Free tier: 3 audits/day, no signup → &lt;a href="https://citeready.sprytools.com" rel="noopener noreferrer"&gt;https://citeready.sprytools.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What's the most surprising thing the audit flags on your site? I'm curious whether structured data gaps or crawler blocks are more common in the dev.to audience.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>tooling</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
    <item>
      <title>What is GEO and why your site needs it in 2026</title>
      <dc:creator>Daniel Igel</dc:creator>
      <pubDate>Tue, 23 Jun 2026 11:45:50 +0000</pubDate>
      <link>https://dev.to/grenzfrei/what-is-geo-and-why-your-site-needs-it-in-2026-2473</link>
      <guid>https://dev.to/grenzfrei/what-is-geo-and-why-your-site-needs-it-in-2026-2473</guid>
      <description>&lt;p&gt;Traditional SEO optimizes for &lt;em&gt;ranking&lt;/em&gt;. GEO (Generative Engine Optimization) optimizes for &lt;em&gt;being cited&lt;/em&gt;. ChatGPT Search, Perplexity, and Google's AI Overviews don't send users down a list of blue links — they pick a handful of sources to quote inline. If your site can't be crawled and understood by AI systems, you're invisible in that layer no matter how well you rank.&lt;/p&gt;

&lt;p&gt;The signals are different from classic SEO. There are four categories that matter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AI crawler access&lt;/strong&gt; — does your &lt;code&gt;robots.txt&lt;/code&gt; allow GPTBot, OAI-SearchBot, PerplexityBot, ClaudeBot, and Google-Extended? A surprising number of sites accidentally block them with &lt;code&gt;Disallow: /&lt;/code&gt; or a wildcard &lt;code&gt;User-agent: *&lt;/code&gt; rule.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structured data&lt;/strong&gt; — JSON-LD (&lt;code&gt;Article&lt;/code&gt;, &lt;code&gt;WebPage&lt;/code&gt;, &lt;code&gt;Organization&lt;/code&gt;) gives AI engines a machine-readable handle on what your page is about.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Content citability&lt;/strong&gt; — can a model lift a clean, self-contained passage? Dense prose with a clear claim scores better than generic marketing copy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Technical foundation&lt;/strong&gt; — HTTPS, canonical tags, and no &lt;code&gt;noindex&lt;/code&gt;/&lt;code&gt;nosnippet&lt;/code&gt; directives that prevent excerpt extraction.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can audit any public URL in 30 seconds — no API key, no signup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://citeready-api.sprytools.com/v1/audit &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"content-type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"url":"https://yoursite.com"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response is a score 0–100 with a letter grade (A–F), a per-category breakdown, and concrete next steps — e.g. &lt;em&gt;"Unblock PerplexityBot in robots.txt — blocking it removes your site from Perplexity citations."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Prefer a UI? &lt;a href="https://citeready.sprytools.com" rel="noopener noreferrer"&gt;https://citeready.sprytools.com&lt;/a&gt; — free tier is 3 audits/day.&lt;/p&gt;

&lt;p&gt;What's the GEO gap you keep seeing that most developers overlook?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>marketing</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Validate EU VAT numbers against VIES (with company lookup) in one GET</title>
      <dc:creator>Daniel Igel</dc:creator>
      <pubDate>Thu, 18 Jun 2026 13:00:02 +0000</pubDate>
      <link>https://dev.to/grenzfrei/validate-eu-vat-numbers-against-vies-with-company-lookup-in-one-get-3265</link>
      <guid>https://dev.to/grenzfrei/validate-eu-vat-numbers-against-vies-with-company-lookup-in-one-get-3265</guid>
      <description>&lt;p&gt;If you sell B2B in the EU, you need valid VAT numbers for reverse-charge invoicing. The official source is the EU's VIES service — but its SOAP endpoint is clunky, rate-limited, and occasionally down per member state, so you end up writing retry + parsing glue.&lt;/p&gt;

&lt;p&gt;This wraps VIES in a clean REST call across all 27 member states (+ XI for Northern Ireland), with a 1-hour cache:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; GET &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; &lt;span class="s1"&gt;'https://eu-vat-number-validator-api.p.rapidapi.com/api/v1/validate?vat=DE811569869'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'x-rapidapi-key: YOUR_RAPIDAPI_KEY'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'x-rapidapi-host: eu-vat-number-validator-api.p.rapidapi.com'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;GET /api/v1/lookup?vat=...&lt;/code&gt; additionally returns the registered company name and address (where the member state exposes it — useful to prefill an invoice). &lt;code&gt;POST /api/v1/validate/batch&lt;/code&gt; validates several numbers at once.&lt;/p&gt;

&lt;p&gt;Free tier on RapidAPI: &lt;a href="https://rapidapi.com/danieligel/api/eu-vat-number-validator-api" rel="noopener noreferrer"&gt;https://rapidapi.com/danieligel/api/eu-vat-number-validator-api&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Built this to gate EU B2B signups for reverse-charge billing. How does your stack handle VAT validation — VIES directly, or a provider?&lt;/p&gt;

</description>
      <category>api</category>
      <category>backend</category>
      <category>showdev</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Validate &amp; parse IBANs (ISO 13616, 80+ countries) without a paid banking API</title>
      <dc:creator>Daniel Igel</dc:creator>
      <pubDate>Wed, 17 Jun 2026 13:00:01 +0000</pubDate>
      <link>https://dev.to/grenzfrei/validate-parse-ibans-iso-13616-80-countries-without-a-paid-banking-api-48jh</link>
      <guid>https://dev.to/grenzfrei/validate-parse-ibans-iso-13616-80-countries-without-a-paid-banking-api-48jh</guid>
      <description>&lt;p&gt;A "valid" IBAN isn't just a regex — it's the MOD-97 checksum over a country-specific length and structure. Get that wrong and you accept payment details that bounce later. Most devs either copy a sketchy gist or pay for a banking API they don't otherwise need.&lt;/p&gt;

&lt;p&gt;This validates the checksum and structure for 80+ countries, and can parse an IBAN into its parts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; GET &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; &lt;span class="s1"&gt;'https://iban-validator-parser-api.p.rapidapi.com/api/v1/validate?iban=DE89370400440532013000'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'x-rapidapi-key: YOUR_RAPIDAPI_KEY'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'x-rapidapi-host: iban-validator-parser-api.p.rapidapi.com'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;GET /api/v1/parse?iban=...&lt;/code&gt; returns the country, bank code, account number and a pretty-printed form (&lt;code&gt;DE89 3704 0044 0532 0130 00&lt;/code&gt;). There's &lt;code&gt;POST /api/v1/validate/batch&lt;/code&gt; for up to 100 IBANs, and &lt;code&gt;GET /api/v1/countries&lt;/code&gt; for the supported list.&lt;/p&gt;

&lt;p&gt;Free tier on RapidAPI: &lt;a href="https://rapidapi.com/danieligel/api/iban-validator-parser-api" rel="noopener noreferrer"&gt;https://rapidapi.com/danieligel/api/iban-validator-parser-api&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Built this for a checkout form so I didn't have to vendor MOD-97 logic. What are you using for IBAN checks today?&lt;/p&gt;

</description>
      <category>api</category>
      <category>backend</category>
      <category>showdev</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Block abusive IPs before they hit your app: one call against 7 DNSBLs</title>
      <dc:creator>Daniel Igel</dc:creator>
      <pubDate>Tue, 16 Jun 2026 13:00:01 +0000</pubDate>
      <link>https://dev.to/grenzfrei/block-abusive-ips-before-they-hit-your-app-one-call-against-7-dnsbls-20al</link>
      <guid>https://dev.to/grenzfrei/block-abusive-ips-before-they-hit-your-app-one-call-against-7-dnsbls-20al</guid>
      <description>&lt;p&gt;Querying DNSBLs by hand means reversing the IP, appending each blacklist's zone, doing a DNS query per list, and interpreting the return codes — times seven if you want decent coverage. It's a lot of boilerplate for "is this IP a known spam/abuse source?".&lt;/p&gt;

&lt;p&gt;One GET checks an IP against Spamhaus, Barracuda, SORBS, SpamCop, UCEPROTECT and more, and tells you which lists flagged it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; GET &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; &lt;span class="s1"&gt;'https://ip-reputation-blacklist-checker-api.p.rapidapi.com/api/v1/check?ip=1.2.3.4'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'x-rapidapi-key: YOUR_RAPIDAPI_KEY'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'x-rapidapi-host: ip-reputation-blacklist-checker-api.p.rapidapi.com'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You get a per-list breakdown plus an overall verdict. &lt;code&gt;GET /api/v1/lists&lt;/code&gt; shows the active blacklists, and &lt;code&gt;POST /api/v1/check/batch&lt;/code&gt; checks up to 10 IPs at once — good for scoring signups or filtering a request log.&lt;/p&gt;

&lt;p&gt;Free tier on RapidAPI: &lt;a href="https://rapidapi.com/danieligel/api/ip-reputation-blacklist-checker-api" rel="noopener noreferrer"&gt;https://rapidapi.com/danieligel/api/ip-reputation-blacklist-checker-api&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I built this to gate form submissions without standing up my own DNSBL resolver. How do you currently screen inbound IPs?&lt;/p&gt;

</description>
      <category>api</category>
      <category>networking</category>
      <category>security</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Is your site visible to AI search? Check it with one API call</title>
      <dc:creator>Daniel Igel</dc:creator>
      <pubDate>Mon, 15 Jun 2026 13:00:02 +0000</pubDate>
      <link>https://dev.to/grenzfrei/is-your-site-visible-to-ai-search-check-it-with-one-api-call-13fi</link>
      <guid>https://dev.to/grenzfrei/is-your-site-visible-to-ai-search-check-it-with-one-api-call-13fi</guid>
      <description>&lt;p&gt;AI assistants — ChatGPT Search, Perplexity, Google's AI Overviews — increasingly answer questions by &lt;em&gt;citing&lt;/em&gt; web pages instead of sending a click. Whether your site can be crawled, parsed, and quoted by them comes down to a handful of signals most SEO tools never check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do your &lt;code&gt;robots.txt&lt;/code&gt; rules actually let AI crawlers in (GPTBot, OAI-SearchBot, PerplexityBot, ClaudeBot)? A surprising number of sites block them by accident.&lt;/li&gt;
&lt;li&gt;Is there an &lt;code&gt;llms.txt&lt;/code&gt; pointing models at your key content?&lt;/li&gt;
&lt;li&gt;Is your content marked up with JSON-LD structured data?&lt;/li&gt;
&lt;li&gt;Are your passages written so a model can lift a clean, self-contained citation?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I built a small audit engine that scores a URL 0–100 across exactly these dimensions and tells you what's failing. It's keyless to try:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://citeready-api.sprytools.com/v1/audit &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"content-type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"url":"https://example.com"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You get a per-category breakdown — AI crawler access, llms.txt, structured data, citability, technical — with a pass/warn/fail on each check and a concrete recommendation, e.g. &lt;em&gt;"No robots.txt found — you lose the Sitemap hint and explicit AI-bot policy."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Prefer pasting a URL? There's a web version: &lt;a href="https://citeready.sprytools.com" rel="noopener noreferrer"&gt;https://citeready.sprytools.com&lt;/a&gt; — free tier is 3 audits/day, no signup.&lt;/p&gt;

&lt;p&gt;Curious what scores people get here — what's the most surprising thing it flags on your site?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>showdev</category>
      <category>webdev</category>
    </item>
    <item>
      <title>DNS lookups (A/MX/TXT/reverse) as a single JSON endpoint — no resolver wrangling</title>
      <dc:creator>Daniel Igel</dc:creator>
      <pubDate>Sun, 14 Jun 2026 09:23:34 +0000</pubDate>
      <link>https://dev.to/grenzfrei/dns-lookups-amxtxtreverse-as-a-single-json-endpoint-no-resolver-wrangling-15kc</link>
      <guid>https://dev.to/grenzfrei/dns-lookups-amxtxtreverse-as-a-single-json-endpoint-no-resolver-wrangling-15kc</guid>
      <description>&lt;p&gt;Doing DNS lookups from app code is more annoying than it should be: &lt;code&gt;dig&lt;/code&gt; isn't available everywhere, Node's &lt;code&gt;dns/promises&lt;/code&gt; returns a different shape per record type, and you end up writing a switch statement for A vs MX vs TXT vs SOA every single time.&lt;/p&gt;

&lt;p&gt;This wraps all of that into one GET. Ask for a single type, or pass &lt;code&gt;type=ALL&lt;/code&gt; to get A, AAAA, MX, TXT, NS, CNAME and SOA in one round-trip.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; GET &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; &lt;span class="s1"&gt;'https://dns-lookup-api3.p.rapidapi.com/api/v1/lookup?domain=example.com&amp;amp;type=ALL'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'x-rapidapi-key: YOUR_RAPIDAPI_KEY'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'x-rapidapi-host: dns-lookup-api3.p.rapidapi.com'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's also &lt;code&gt;/api/v1/mx?domain=gmail.com&lt;/code&gt; (MX sorted by priority — handy for mail-deliverability checks), &lt;code&gt;/api/v1/reverse?ip=8.8.8.8&lt;/code&gt; for reverse DNS / PTR, and &lt;code&gt;POST /api/v1/lookup/batch&lt;/code&gt; for up to 20 domain/type pairs in one call (useful for auditing a list of domains).&lt;/p&gt;

&lt;p&gt;Free tier on RapidAPI: &lt;a href="https://rapidapi.com/danieligel/api/dns-lookup-api3" rel="noopener noreferrer"&gt;https://rapidapi.com/danieligel/api/dns-lookup-api3&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Built this because every project re-implemented the same record-type plumbing. What do you reach for today — shelling out to &lt;code&gt;dig&lt;/code&gt;, or a library?&lt;/p&gt;

</description>
      <category>api</category>
      <category>networking</category>
      <category>showdev</category>
      <category>tooling</category>
    </item>
  </channel>
</rss>
