<?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: Eyvind Barthelemy</title>
    <description>The latest articles on DEV Community by Eyvind Barthelemy (@samedaydesk).</description>
    <link>https://dev.to/samedaydesk</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%2F3999871%2F1a1232e1-1361-49d8-af8b-7682fbe17eae.png</url>
      <title>DEV Community: Eyvind Barthelemy</title>
      <link>https://dev.to/samedaydesk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/samedaydesk"/>
    <language>en</language>
    <item>
      <title>The API promised industry keywords. Modern sites kept returning [].</title>
      <dc:creator>Eyvind Barthelemy</dc:creator>
      <pubDate>Sat, 08 Aug 2026 04:46:14 +0000</pubDate>
      <link>https://dev.to/samedaydesk/the-api-promised-industry-keywords-modern-sites-kept-returning--4bco</link>
      <guid>https://dev.to/samedaydesk/the-api-promised-industry-keywords-modern-sites-kept-returning--4bco</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/bugsmash"&gt;DEV's Summer Bug Smash: Smash Stories&lt;/a&gt; powered by &lt;a href="https://sentry.io/" rel="noopener noreferrer"&gt;Sentry&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The quietest kind of production bug
&lt;/h2&gt;

&lt;p&gt;The endpoint was healthy. Payment gating worked. The response matched its JSON schema. Nothing crashed, timed out, or lit up a red dashboard.&lt;/p&gt;

&lt;p&gt;It was still broken.&lt;/p&gt;

&lt;p&gt;I had launched a pay-per-call &lt;code&gt;/enrich&lt;/code&gt; endpoint for AI agents. Give it a domain and it returns structured public company intelligence: identity, contact surfaces, technology signals, DNS and email infrastructure, social profiles, AI-search readiness—and &lt;code&gt;company.keywords&lt;/code&gt;, advertised as industry keywords an agent could use for routing or qualification.&lt;/p&gt;

&lt;p&gt;For modern sites, that last field kept coming back as 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;"company"&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&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;"keywords"&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;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;The API had returned a successful, paid, structurally valid answer that omitted one of the signals it promised.&lt;/p&gt;

&lt;p&gt;That made this bug more interesting than a 500 error. Every shallow health check passed. Only checking whether the output was &lt;em&gt;useful&lt;/em&gt; revealed the failure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Root cause: a legacy assumption hiding behind a modern schema
&lt;/h2&gt;

&lt;p&gt;The original implementation populated the field from one HTML tag:&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;keywords&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keywords&lt;/span&gt; &lt;span class="o"&gt;||&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="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;,&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="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;clean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That means the entire field depended on:&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;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"keywords"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"payments, infrastructure, fintech"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Many contemporary sites do not publish that legacy tag. They express topics elsewhere: JSON-LD &lt;code&gt;keywords&lt;/code&gt;, &lt;code&gt;knowsAbout&lt;/code&gt;, &lt;code&gt;applicationCategory&lt;/code&gt;, article tags, titles, and descriptions.&lt;/p&gt;

&lt;p&gt;My parser had confused one old representation with the underlying concept. The schema said “industry keywords”; the code actually meant “the value of this one optional meta tag.”&lt;/p&gt;

&lt;h2&gt;
  
  
  The first tempting fix was the wrong one
&lt;/h2&gt;

&lt;p&gt;The easy patch would have been to split the page title and return the first few long words. That would make the array non-empty, but it would blur together two different kinds of evidence:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;topics the publisher explicitly declared; and&lt;/li&gt;
&lt;li&gt;terms the service derived from public copy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For an autonomous buyer, that distinction matters. A derived label should not masquerade as a company-authored claim.&lt;/p&gt;

&lt;p&gt;So the fix needed to improve coverage &lt;strong&gt;and&lt;/strong&gt; preserve provenance.&lt;/p&gt;

&lt;h2&gt;
  
  
  The repair: declared first, deterministic fallback second
&lt;/h2&gt;

&lt;p&gt;The corrected pipeline has three stages.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Collect declared signals
&lt;/h3&gt;

&lt;p&gt;It reads:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;meta[name="keywords"]&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;news_keywords&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;article:tag&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;JSON-LD &lt;code&gt;keywords&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;JSON-LD &lt;code&gt;knowsAbout&lt;/code&gt;; and&lt;/li&gt;
&lt;li&gt;JSON-LD &lt;code&gt;applicationCategory&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The JSON-LD walk handles nested graphs, arrays, strings, and &lt;code&gt;{ "name": "..." }&lt;/code&gt; objects.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Normalize without laundering the evidence
&lt;/h3&gt;

&lt;p&gt;Declared values are cleaned, capped at 40 characters, deduplicated case-insensitively, and limited to 15 public response items.&lt;/p&gt;

&lt;p&gt;If fewer than six declared topics exist, the service deterministically derives a top-up from title and description text. It removes stopwords and numeric noise, ranks by frequency, and preserves first-appearance order as the tie-breaker.&lt;/p&gt;

&lt;p&gt;There is no model call and no random generation. The same public input produces the same result.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Label the provenance
&lt;/h3&gt;

&lt;p&gt;The response now states where its list came from:&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;"company"&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;"keywords"&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="s2"&gt;"stripe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"financial"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"infrastructure"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"grow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"revenue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"services"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"platform"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"types"&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;"keywordsSource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"derived"&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;The label is one of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;declared&lt;/code&gt;: the page provided a complete set;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mixed&lt;/code&gt;: declared topics plus a deterministic top-up; or&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;derived&lt;/code&gt;: no declared topics were available.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That small field keeps the improvement honest. A consuming agent can accept derived signals for discovery, or require declared signals for a higher-stakes decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before and after
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BEFORE
legacy meta keywords absent
        └─ company.keywords = []
             └─ HTTP 200, schema valid, weak answer

AFTER
declared meta + JSON-LD topics
        ├─ enough signals ──────────────&amp;gt; declared
        └─ sparse or absent
             └─ deterministic top-up ──&amp;gt; mixed / derived

all paths: case-insensitive dedupe + 15-item cap + provenance label
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The endpoint still uses only public page data. The payment rail, input contract, URL security guard, and all unrelated response fields are unchanged.&lt;/p&gt;

&lt;h2&gt;
  
  
  Locking the semantic contract down
&lt;/h2&gt;

&lt;p&gt;The original repair shipped on June 24. While preparing this retrospective, I noticed the lesson had not been encoded in a focused test. That is exactly how a quiet semantic bug can return during a later refactor.&lt;/p&gt;

&lt;p&gt;I extracted the keyword pipeline behind a pure &lt;code&gt;keywordSignals(...)&lt;/code&gt; seam and added four deterministic regression cases:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;No legacy metadata still produces a non-empty derived set.&lt;/li&gt;
&lt;li&gt;Meta and nested JSON-LD topics merge and deduplicate before a top-up.&lt;/li&gt;
&lt;li&gt;Six declared topics remain purely &lt;code&gt;declared&lt;/code&gt;; marketing copy is not appended.&lt;/li&gt;
&lt;li&gt;The public response cap remains 15 after normalization.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The full current suite also preserves the gateway's referral safety tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npm test
tests 8
pass 8
fail 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A live keyless CLI probe against &lt;code&gt;stripe.com&lt;/code&gt; now returns eight keywords and &lt;code&gt;keywordsSource: "derived"&lt;/code&gt; rather than the empty field that exposed the bug.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code and evidence
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/epistemedeus/x402-url-extractor/commit/660018c88004e1a526e3b52e96578f4a7bacb121" rel="noopener noreferrer"&gt;Original keyword fix commit &lt;code&gt;660018c&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/epistemedeus/x402-url-extractor/commit/7a17214" rel="noopener noreferrer"&gt;Regression hardening commit &lt;code&gt;7a17214&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/epistemedeus/x402-url-extractor/blob/7a17214/enrich.mjs" rel="noopener noreferrer"&gt;Current enrichment implementation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/epistemedeus/x402-url-extractor/blob/7a17214/enrich.test.mjs" rel="noopener noreferrer"&gt;Focused regression tests&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://x402-url-extractor-production.up.railway.app/.well-known/x402" rel="noopener noreferrer"&gt;Live x402 resource manifest&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I learned
&lt;/h2&gt;

&lt;h3&gt;
  
  
  A valid payload can still violate the product promise
&lt;/h3&gt;

&lt;p&gt;Status codes, JSON parsing, and schema validation prove transport and shape. They do not prove that the signal a customer is buying is present or useful.&lt;/p&gt;

&lt;p&gt;For data products, a test should assert semantic invariants: the advertised field is populated for representative modern inputs, its provenance is visible, and its bounds remain stable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prefer concepts over historical representations
&lt;/h3&gt;

&lt;p&gt;“Industry topics” can appear in several public formats. Binding the concept to one legacy tag made the implementation brittle even though the code looked reasonable in isolation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Derived data needs an honesty label
&lt;/h3&gt;

&lt;p&gt;Filling an empty field is not enough. The consumer should know whether a value was declared, inferred deterministically, or generated. Provenance turns a fallback from a hidden guess into a usable choice.&lt;/p&gt;

&lt;p&gt;The lasting fix was not merely making &lt;code&gt;[]&lt;/code&gt; non-empty. It was making the endpoint say what it knows, how it knows it, and keeping that contract executable in tests.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>bugsmash</category>
      <category>javascript</category>
      <category>web3</category>
    </item>
    <item>
      <title>The RPC said Base was healthy. The first contract read still failed.</title>
      <dc:creator>Eyvind Barthelemy</dc:creator>
      <pubDate>Sat, 08 Aug 2026 04:26:59 +0000</pubDate>
      <link>https://dev.to/samedaydesk/the-rpc-said-base-was-healthy-the-first-contract-read-still-failed-3fg6</link>
      <guid>https://dev.to/samedaydesk/the-rpc-said-base-was-healthy-the-first-contract-read-still-failed-3fg6</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/bugsmash"&gt;DEV's Summer Bug Smash: Clear the Lineup&lt;/a&gt; powered by &lt;a href="https://sentry.io/" rel="noopener noreferrer"&gt;Sentry&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Overview
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/NSPG13/agent-bounties" rel="noopener noreferrer"&gt;Agent Bounties&lt;/a&gt; is an open-source, payment-first bounty network. One of its GitHub Actions turns an exact GitHub identity and wallet into a participant record on Base before that participant can enter certain funded bounty flows.&lt;/p&gt;

&lt;p&gt;That registration path is deliberately fail-closed: if the action cannot prove the right chain, registry, attester, identity, transaction receipt, and final eligibility state, it must stop rather than pretending registration succeeded.&lt;/p&gt;

&lt;p&gt;While using the real registration workflow, I found a narrower but consequential reliability bug. The action considered an RPC endpoint healthy after only an &lt;code&gt;eth_chainId&lt;/code&gt; check. The endpoint correctly reported Base mainnet, but refused the first participant-registry archive read with HTTP 403. Because selection had already committed to that endpoint, the ordered fallbacks were never tried.&lt;/p&gt;

&lt;p&gt;The result was not a cosmetic error. Two exact GitHub-hosted runs, from different runner regions, failed before broadcasting a transaction, blocking the participant registration that the bounty path required.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bug Fix or Performance Improvement
&lt;/h2&gt;

&lt;p&gt;The old health check answered only this question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Does this endpoint say it is Base mainnet?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The workflow actually needed a stronger answer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Does this endpoint say it is Base mainnet &lt;strong&gt;and&lt;/strong&gt; successfully perform the immutable registry read that registration depends on?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That difference matters because an RPC can serve a cheap chain-ID response while rate-limiting, gating, or rejecting historical contract reads.&lt;/p&gt;

&lt;p&gt;The failure looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;configured RPC
    └─ eth_chainId == 8453       ✓ selected
         └─ registry attester()  ✗ HTTP 403
              └─ registration   ✗ fail closed

fallback RPCs                    never reached
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I changed endpoint selection to probe the real capability before committing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;select_base_rpc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;configured&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Probe ordered RPC fallbacks and commit to one usable Base endpoint.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="n"&gt;endpoints&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parse_rpc_urls&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;configured&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;endpoint&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;endpoints&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chain-id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--rpc-url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;BASE_CHAIN_ID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;continue&lt;/span&gt;
            &lt;span class="n"&gt;attester&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;call&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--rpc-url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;attester()(address)&lt;/span&gt;&lt;span class="sh"&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;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ADDRESS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fullmatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attester&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;endpoint&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;RegistrationError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RegistrationError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;no usable Base mainnet RPC endpoint&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The workflow now has three ordered candidates:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The operator-configured endpoint, preserving the existing preference.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;https://mainnet.base.org&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;https://base.drpc.org&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;An endpoint is selected only after it returns Base chain ID &lt;code&gt;8453&lt;/code&gt; &lt;strong&gt;and&lt;/strong&gt; a valid address from the registry's &lt;code&gt;attester()&lt;/code&gt; call. If none can do both, the workflow still fails closed.&lt;/p&gt;

&lt;p&gt;I also fixed a related consistency error: the final &lt;code&gt;eligibleAt(...)&lt;/code&gt; confirmation had been passed the original comma-separated configuration rather than the single endpoint selected by the probe. Every chain operation now uses the same proven endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/NSPG13/agent-bounties/pull/808" rel="noopener noreferrer"&gt;Upstream pull request #808&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/epistemedeus/agent-bounties/commit/f58dbbbccb09fb109fb78348289562066839665d" rel="noopener noreferrer"&gt;Immutable fix commit &lt;code&gt;f58dbbb&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/NSPG13/agent-bounties/actions/runs/31209137234" rel="noopener noreferrer"&gt;Second production failure, including the successful contract test job and fail-closed registration job&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/NSPG13/agent-bounties/pull/808#issuecomment-5220914221" rel="noopener noreferrer"&gt;Incident evidence and runner-region comparison&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The PR changes three files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.github/workflows/participant-registration.yml |   2 +-
scripts/register_participant.py                |  20 +++-
scripts/test_register_participant.py           | 133 +++++++++++++++++++++++--
3 files changed, 141 insertions(+), 14 deletions(-)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The upstream PR is open and currently mergeable, with its automated claimed-work check green. The challenge FAQ says an OSS contribution does not need to be merged upstream, so the public fork commit above is the stable, reviewable implementation while maintainer review proceeds independently.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Improvements
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. I reproduced the failure in the real workflow
&lt;/h3&gt;

&lt;p&gt;This was not inferred from a synthetic timeout. Two authorized registration attempts failed on the same provider from different GitHub runner regions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ORD runner: the endpoint passed chain ID, then rejected the registry read.&lt;/li&gt;
&lt;li&gt;SJC runner: the same sequence failed again on a later retry.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Direct workstation probes between the two attempts showed that all three candidates could answer both calls at that moment. That contrast isolated the issue: a developer-side smoke test was not a reliable proxy for the GitHub runner's ability to perform the actual contract read.&lt;/p&gt;

&lt;p&gt;Both workflow attempts stopped before a transaction. That is the correct safety outcome, but the wrong availability outcome when healthy fallbacks exist.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. I made the probe match the operation
&lt;/h3&gt;

&lt;p&gt;The core fix is intentionally small: test the cheapest real dependency before selecting a provider. &lt;code&gt;attester()&lt;/code&gt; is read-only, stable, inexpensive, and exercises the same registry access class the registration flow requires.&lt;/p&gt;

&lt;p&gt;This avoids a dangerous overcorrection. The change does not weaken receipt checks, bypass eligibility, retry a transaction blindly, or alter any signing authority. It improves endpoint selection while preserving every existing payment and identity invariant.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. I added deterministic regression coverage
&lt;/h3&gt;

&lt;p&gt;The focused suite now covers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an unavailable endpoint;&lt;/li&gt;
&lt;li&gt;an endpoint on the wrong chain;&lt;/li&gt;
&lt;li&gt;an endpoint that passes &lt;code&gt;chain-id&lt;/code&gt; but refuses the registry read;&lt;/li&gt;
&lt;li&gt;ordered fallback selection;&lt;/li&gt;
&lt;li&gt;safe parsing of the endpoint list;&lt;/li&gt;
&lt;li&gt;an end-to-end fixture proving every chain operation uses the selected endpoint;&lt;/li&gt;
&lt;li&gt;preservation of transaction evidence after a post-receipt error;&lt;/li&gt;
&lt;li&gt;strict identity, repository, PR, registration-record, and eligibility checks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Current verification:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ python3 scripts/test_register_participant.py -v
Ran 10 tests in 0.002s
OK

$ bash scripts/preflight.sh core
preflight=core ok

$ git diff --check
# clean
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. I preserved the failure boundary
&lt;/h3&gt;

&lt;p&gt;If no endpoint both reports Base mainnet and reads the expected registry, the action stops. No transaction is attempted. The change does not modify the contract, registry address, attester or keeper authority, participant identity derivation, validity window, or settlement behavior.&lt;/p&gt;

&lt;p&gt;That was the most important design constraint: failover should improve availability without turning uncertainty into an on-chain side effect.&lt;/p&gt;

&lt;h3&gt;
  
  
  Result
&lt;/h3&gt;

&lt;p&gt;The fixed path now distinguishes "this server knows Base's chain ID" from "this server can actually support participant registration." A degraded configured provider no longer prevents the workflow from trying its healthy fallbacks, and the selected endpoint is used consistently through the final eligibility check.&lt;/p&gt;

&lt;p&gt;The broader lesson is simple: health checks should prove the capability the next operation needs, not merely the cheapest fact a service can return.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>bugsmash</category>
      <category>python</category>
      <category>web3</category>
    </item>
    <item>
      <title>Give your AI agent company + on-chain wallet enrichment in 30 seconds (x402, pay-per-call USDC)</title>
      <dc:creator>Eyvind Barthelemy</dc:creator>
      <pubDate>Thu, 25 Jun 2026 03:34:35 +0000</pubDate>
      <link>https://dev.to/samedaydesk/give-your-ai-agent-company-on-chain-wallet-enrichment-in-30-seconds-x402-pay-per-call-usdc-2g6p</link>
      <guid>https://dev.to/samedaydesk/give-your-ai-agent-company-on-chain-wallet-enrichment-in-30-seconds-x402-pay-per-call-usdc-2g6p</guid>
      <description>&lt;p&gt;If you're building autonomous agents, you've hit this wall: your agent needs to &lt;strong&gt;enrich a company by domain&lt;/strong&gt; or &lt;strong&gt;size up a wallet before it sends funds&lt;/strong&gt;, but every good data source (Clearbit, Apollo, Nansen) is signup- and KYC-gated. An autonomous agent can't click "Sign up," verify an email, and paste an API key.&lt;/p&gt;

&lt;p&gt;So I put a small data gateway behind &lt;strong&gt;x402&lt;/strong&gt; — HTTP 402 + pay-per-call &lt;strong&gt;USDC on Base&lt;/strong&gt;. No account, no API key, no subscription. Your agent does a GET, gets a &lt;code&gt;402&lt;/code&gt; with the price, pays a couple cents of USDC, and replays the request. That's it.&lt;/p&gt;

&lt;h2&gt;
  
  
  30-second install (Claude / Cursor / any MCP agent)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx skills add epistemedeus/x402-data-gateway-skills &lt;span class="nt"&gt;--all&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That drops in &lt;a href="https://github.com/vercel-labs/skills" rel="noopener noreferrer"&gt;Agent Skills&lt;/a&gt; that teach your agent &lt;em&gt;when&lt;/em&gt; to call each endpoint — so "enrich this company" or "profile this wallet" just works.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two I actually built this for
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;company-enrich&lt;/code&gt;&lt;/strong&gt; — a domain → clean firmographics (name, description, tech stack, socials, contact), &lt;strong&gt;plus&lt;/strong&gt; two layers generic enrichment APIs don't return:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DNS / email-infra (MX, SPF, DMARC — great for outreach validation)&lt;/li&gt;
&lt;li&gt;an &lt;strong&gt;AI-search-readiness&lt;/strong&gt; score (JSON-LD, OpenGraph, robots/sitemap/llms.txt) — "can an AI assistant actually read and cite this site?"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;wallet-enrich&lt;/code&gt;&lt;/strong&gt; — a Base/EVM &lt;code&gt;0x&lt;/code&gt; address → on-chain profile: EOA vs contract, native + token holdings, ERC-20/721/1155 metadata, EIP-1967 proxy detection, activity, and the &lt;strong&gt;Basename&lt;/strong&gt; (e.g. &lt;code&gt;jesse.base.eth&lt;/code&gt;), forward-confirmed so it's never wrong. The crypto-native counterpart for sizing up a counterparty before you transact.&lt;/p&gt;

&lt;h2&gt;
  
  
  Calling it from code (x402-fetch)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;wrapFetchWithPayment&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x402-fetch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// walletClient = a viem wallet funded with a little USDC on Base&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchPaid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;wrapFetchWithPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;walletClient&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;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;fetchPaid&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://x402-url-extractor-production.up.railway.app/wallet-enrich?address=0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913&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="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;// -&amp;gt; { type: "contract", contract: { standard: "ERC-20 (token)", token: { symbol: "USDC", ... } }, profile: "token-contract:USDC" }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Settlement is non-custodial: the facilitator verifies your signed EIP-3009 authorization and the USDC moves straight to the service wallet. Nobody holds your funds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Also exposed as an MCP server
&lt;/h2&gt;

&lt;p&gt;Prefer MCP? The same tools are an x402-gated MCP server (&lt;code&gt;tools/list&lt;/code&gt; is free, &lt;code&gt;tools/call&lt;/code&gt; is paid), listed in the official MCP Registry as &lt;code&gt;io.github.epistemedeus/x402-data-gateway&lt;/code&gt;. &lt;code&gt;tools/list&lt;/code&gt; to see all six; pay-per-call on the ones you use.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's in the gateway
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Skill&lt;/th&gt;
&lt;th&gt;Does&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;company-enrich&lt;/td&gt;
&lt;td&gt;domain → company intel + AI-readiness + DNS&lt;/td&gt;
&lt;td&gt;$0.02&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;wallet-enrich&lt;/td&gt;
&lt;td&gt;0x address → on-chain profile + Basename&lt;/td&gt;
&lt;td&gt;$0.02&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;web-extract&lt;/td&gt;
&lt;td&gt;URL → structured JSON or LLM-ready Markdown&lt;/td&gt;
&lt;td&gt;$0.05&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;repo-security-scan&lt;/td&gt;
&lt;td&gt;static supply-chain scan of a GitHub repo before you install it&lt;/td&gt;
&lt;td&gt;$0.20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;schema-generate&lt;/td&gt;
&lt;td&gt;business site → paste-ready JSON-LD bundle&lt;/td&gt;
&lt;td&gt;$0.25&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Public data only, deterministic, pay-per-call. Repo + full docs: &lt;strong&gt;&lt;a href="https://github.com/epistemedeus/x402-data-gateway-skills" rel="noopener noreferrer"&gt;https://github.com/epistemedeus/x402-data-gateway-skills&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you wire it into an agent, I'd genuinely like to hear what task you used it for.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>web3</category>
      <category>opensource</category>
    </item>
    <item>
      <title>I checked the structured data on 61 med-spa sites AI assistants surface. Only 8% have FAQ markup.</title>
      <dc:creator>Eyvind Barthelemy</dc:creator>
      <pubDate>Wed, 24 Jun 2026 19:57:30 +0000</pubDate>
      <link>https://dev.to/samedaydesk/i-checked-the-structured-data-on-61-med-spa-sites-ai-assistants-surface-only-8-have-faq-markup-e7m</link>
      <guid>https://dev.to/samedaydesk/i-checked-the-structured-data-on-61-med-spa-sites-ai-assistants-surface-only-8-have-faq-markup-e7m</guid>
      <description>&lt;p&gt;I run a small AI-search readiness tool, so I got curious about a concrete question: when someone asks ChatGPT or Perplexity "best med spa near me for Botox," what structured data do the businesses that surface actually have?&lt;/p&gt;

&lt;p&gt;So I checked. I took the high-intent queries a real patient would run — "best med spa in [city]", "Botox [city]", "laser hair removal [city]" — across 8 US metros (Austin, Scottsdale, Miami, Dallas, Nashville, Los Angeles, Chicago, Denver), collected the individual-clinic pages that surfaced, fetched each one, and parsed its JSON-LD. 62 clinic URLs, 61 reachable.&lt;/p&gt;

&lt;p&gt;The result surprised me.&lt;/p&gt;

&lt;h2&gt;
  
  
  The basics are common. The quotable fields are not.
&lt;/h2&gt;

&lt;p&gt;Of the 61 reachable clinic pages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;77%&lt;/strong&gt; have &lt;em&gt;some&lt;/em&gt; JSON-LD structured data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;59%&lt;/strong&gt; have a &lt;code&gt;LocalBusiness&lt;/code&gt; / &lt;code&gt;MedicalBusiness&lt;/code&gt; block&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;59%&lt;/strong&gt; have a &lt;code&gt;telephone&lt;/code&gt;, &lt;strong&gt;57%&lt;/strong&gt; a postal &lt;code&gt;address&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;…but only &lt;strong&gt;31%&lt;/strong&gt; have &lt;code&gt;AggregateRating&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;only &lt;strong&gt;25%&lt;/strong&gt; have &lt;code&gt;priceRange&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;only &lt;strong&gt;11%&lt;/strong&gt; mark up their services (&lt;code&gt;Service&lt;/code&gt; / &lt;code&gt;OfferCatalog&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;only &lt;strong&gt;8%&lt;/strong&gt; have &lt;code&gt;FAQPage&lt;/code&gt; markup&lt;/li&gt;
&lt;li&gt;only &lt;strong&gt;2%&lt;/strong&gt; have &lt;code&gt;Review&lt;/code&gt; markup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So clinics nail the &lt;em&gt;identity&lt;/em&gt; basics (name, address, phone) and skip the &lt;em&gt;answer-rich&lt;/em&gt; markup — the exact fields an assistant uses to &lt;strong&gt;quote&lt;/strong&gt; a business rather than just list it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the distinction matters
&lt;/h2&gt;

&lt;p&gt;When an LLM answers a local "best X near me" question, it's assembling facts: which businesses exist, what they offer, what it costs, how they're rated, and what people ask. A page with a bare &lt;code&gt;LocalBusiness&lt;/code&gt; block can be &lt;em&gt;named&lt;/em&gt;. A page that also carries &lt;code&gt;Service&lt;/code&gt; + &lt;code&gt;priceRange&lt;/code&gt;, &lt;code&gt;FAQPage&lt;/code&gt;, and real &lt;code&gt;Review&lt;/code&gt;/&lt;code&gt;AggregateRating&lt;/code&gt; markup gives the model the actual sentences and numbers to cite.&lt;/p&gt;

&lt;p&gt;The data says the second kind of page barely exists in this category. That's not a doom stat — it's the cheapest competitive edge in the space right now, and it's a one-time paste into your &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "good" looks like
&lt;/h2&gt;

&lt;p&gt;A complete bundle for a local business is a &lt;code&gt;@graph&lt;/code&gt; with the business entity, its services as an &lt;code&gt;OfferCatalog&lt;/code&gt;, an &lt;code&gt;FAQPage&lt;/code&gt;, and real ratings:&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;"@context"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://schema.org"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@graph"&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;span class="nl"&gt;"@type"&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="s2"&gt;"MedicalBusiness"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HealthAndBeautyBusiness"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Example Aesthetics"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"telephone"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"+1-512-555-0100"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"priceRange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&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;"address"&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;"@type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PostalAddress"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"addressLocality"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Austin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"addressRegion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TX"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"addressCountry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"US"&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;"hasOfferCatalog"&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;"@type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OfferCatalog"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"itemListElement"&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;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Offer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"itemOffered"&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;"@type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Service"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Botox"&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;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Offer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"itemOffered"&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;"@type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Service"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Dermal Fillers"&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;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"aggregateRating"&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;"@type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AggregateRating"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"ratingValue"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4.9"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"reviewCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"214"&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;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FAQPage"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"mainEntity"&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;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Question"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"How much does Botox cost?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"acceptedAnswer"&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;"@type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Answer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Botox is priced per unit; most treatments range from $10–$18 per unit."&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;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;One honest caveat: put your &lt;strong&gt;real&lt;/strong&gt; ratings and reviews in there. The structure is the easy part; fabricated ratings are both against Google's guidelines and pointless.&lt;/p&gt;

&lt;h2&gt;
  
  
  Method + caveats
&lt;/h2&gt;

&lt;p&gt;This is a structured-data census of the &lt;em&gt;surfaced set&lt;/em&gt; — it measures what makes a page &lt;strong&gt;eligible&lt;/strong&gt; to be cited, not a live count of any single engine's citations (I don't have ChatGPT/Perplexity API access at scale, and I'd rather report what I can verify first-party than guess). I excluded aggregators and listicles (Yelp, etc.) from the clinic tally. Full data, the metro list, and the methodology are in the report below. I refresh it monthly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check your own site
&lt;/h2&gt;

&lt;p&gt;Free, open-source:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx github:epistemedeus/ai-readiness yoursite.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Full data + a free starter-block generator (and, if you want the complete bundle generated against your live site with a gap diff, a paid option): &lt;strong&gt;the med-spa AI-citation schema gap report&lt;/strong&gt; — linked as the canonical source below.&lt;/p&gt;

&lt;p&gt;If you've measured this for another vertical, I'd love to compare notes.&lt;/p&gt;

</description>
      <category>seo</category>
      <category>ai</category>
      <category>webdev</category>
      <category>marketing</category>
    </item>
    <item>
      <title>I scanned 136 companies to see if AI search can actually read their sites - the data (and how to fix yours)</title>
      <dc:creator>Eyvind Barthelemy</dc:creator>
      <pubDate>Wed, 24 Jun 2026 06:13:47 +0000</pubDate>
      <link>https://dev.to/samedaydesk/i-scanned-136-companies-to-see-if-ai-search-can-actually-read-their-sites-the-data-and-how-to-3oh</link>
      <guid>https://dev.to/samedaydesk/i-scanned-136-companies-to-see-if-ai-search-can-actually-read-their-sites-the-data-and-how-to-3oh</guid>
      <description>&lt;p&gt;AI answer engines (ChatGPT, Perplexity, Google AI Overviews, Claude) are turning into a real referral source. But they can only cite pages they can actually &lt;strong&gt;crawl and parse&lt;/strong&gt;. So I built a tiny, open-source checker and ran it against the homepages of &lt;strong&gt;136 well-known companies&lt;/strong&gt; across 7 industries to see how AI-ready the web really is.&lt;/p&gt;

&lt;p&gt;The results weren't what I expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I measured
&lt;/h2&gt;

&lt;p&gt;Each homepage scored 0-100 across six things AI crawlers and answer-engines rely on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AI-crawler access&lt;/strong&gt; - does &lt;code&gt;robots.txt&lt;/code&gt; allow GPTBot, OAI-SearchBot, PerplexityBot, Google-Extended, ClaudeBot?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structured data&lt;/strong&gt; - JSON-LD (Organization, WebSite, FAQPage)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Title + meta description&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open Graph&lt;/strong&gt; tags&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;XML sitemap&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;llms.txt&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The findings
&lt;/h2&gt;

&lt;p&gt;Average score by industry (higher = easier for AI to read):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Industry&lt;/th&gt;
&lt;th&gt;Avg score&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Marketing agencies&lt;/td&gt;
&lt;td&gt;92&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SaaS&lt;/td&gt;
&lt;td&gt;88&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dev tools&lt;/td&gt;
&lt;td&gt;86&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;E-commerce&lt;/td&gt;
&lt;td&gt;85&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI startups&lt;/td&gt;
&lt;td&gt;83&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fintech&lt;/td&gt;
&lt;td&gt;74&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Healthtech&lt;/td&gt;
&lt;td&gt;63&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;What jumped out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Structured data is the #1 gap.&lt;/strong&gt; A large share of sites ship &lt;em&gt;no&lt;/em&gt; Organization or FAQPage JSON-LD - which is exactly the format AI answers like to quote.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plenty of well-known tech companies score in the C/D range,&lt;/strong&gt; almost always because of missing schema, not anything hard.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The bar is low.&lt;/strong&gt; A deliberate, clean setup puts a tiny site ahead of companies a thousand times its size in how readable it is to AI search.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to fix yours (copy-paste)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Let the AI crawlers in&lt;/strong&gt; - &lt;code&gt;robots.txt&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;User-agent: GPTBot
Allow: /
User-agent: OAI-SearchBot
Allow: /
User-agent: PerplexityBot
Allow: /
User-agent: Google-Extended
Allow: /
User-agent: *
Allow: /
Sitemap: https://yourdomain.com/sitemap.xml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Tell engines who you are&lt;/strong&gt; - Organization JSON-LD in your &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;:&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;Organization&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 COMPANY&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://yourdomain.com&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;sameAs&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://www.linkedin.com/company/yourco&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://x.com/yourco&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;&lt;strong&gt;3. Add FAQPage JSON-LD&lt;/strong&gt; to any page with Q&amp;amp;A - it's the structure AI answers quote most.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check your own site (free, open source)
&lt;/h2&gt;

&lt;p&gt;I open-sourced the checker. No install needed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx github:epistemedeus/ai-readiness https://yourdomain.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It prints a 0-100 score plus the exact gaps and fixes. Browser version (no install): &lt;a href="https://samedaydesk.com/tools/ai-readiness" rel="noopener noreferrer"&gt;https://samedaydesk.com/tools/ai-readiness&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you'd rather grab every template ready to paste (robots.txt, all the JSON-LD, sitemap, meta/OG) in one file, I bundled them into a $9 kit: &lt;a href="https://buy.stripe.com/9B66oI1BEdTV6116oieZ20j" rel="noopener noreferrer"&gt;https://buy.stripe.com/9B66oI1BEdTV6116oieZ20j&lt;/a&gt; - but the free checker plus the snippets above honestly get most sites 80% of the way there.&lt;/p&gt;

&lt;p&gt;What does your site score? The thing that surprised me most: a bunch of &lt;em&gt;AI&lt;/em&gt; companies can't be read by AI. 🤔&lt;/p&gt;

</description>
      <category>ai</category>
      <category>seo</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
