<?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: Anh Nguyen</title>
    <description>The latest articles on DEV Community by Anh Nguyen (@anh_nguyen_589e1928a98305).</description>
    <link>https://dev.to/anh_nguyen_589e1928a98305</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3884949%2F461e3ed6-d528-4db8-a026-27e4ea55bf69.png</url>
      <title>DEV Community: Anh Nguyen</title>
      <link>https://dev.to/anh_nguyen_589e1928a98305</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/anh_nguyen_589e1928a98305"/>
    <language>en</language>
    <item>
      <title>I Built a Price Infrastructure API for AI Agents — Here's What I Learned</title>
      <dc:creator>Anh Nguyen</dc:creator>
      <pubDate>Tue, 21 Apr 2026 04:28:37 +0000</pubDate>
      <link>https://dev.to/anh_nguyen_589e1928a98305/i-built-a-price-infrastructure-api-for-ai-agents-heres-what-i-learned-1nga</link>
      <guid>https://dev.to/anh_nguyen_589e1928a98305/i-built-a-price-infrastructure-api-for-ai-agents-heres-what-i-learned-1nga</guid>
      <description>&lt;p&gt;A few months ago, I had a simple question: why is it so hard for AI agents to get reliable, structured product prices?&lt;/p&gt;

&lt;p&gt;LLMs can reason about almost anything — but ask one "what's the cheapest Raspberry Pi 5 right now?" and it either hallucinates a number or admits it doesn't know. The data exists on marketplaces. The problem is the bridge between that data and the agent.&lt;/p&gt;

&lt;p&gt;So I built &lt;a href="https://agentshare.dev/" rel="noopener noreferrer"&gt;AgentShare&lt;/a&gt; — a price infrastructure API designed specifically for AI agents, not human users.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it fits together (one picture)
&lt;/h2&gt;

&lt;p&gt;Rough pipeline: &lt;strong&gt;crawl &amp;amp; normalize → store with timestamps → REST API → MCP tools&lt;/strong&gt;. The API is the stable contract; crawlers and coverage can evolve behind it. That separation is what lets an agent depend on &lt;strong&gt;shapes and freshness&lt;/strong&gt; instead of scraping HTML on every turn.&lt;/p&gt;

&lt;h2&gt;
  
  
  The core idea
&lt;/h2&gt;

&lt;p&gt;Most price comparison tools are built for browsers: ads, affiliate banners, SEO-optimized pages. An agent doesn't need any of that. It needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Structured JSON, not HTML to parse&lt;/li&gt;
&lt;li&gt;Freshness signals, not just a number&lt;/li&gt;
&lt;li&gt;Predictable error shapes it can act on&lt;/li&gt;
&lt;li&gt;A stable interface it can call repeatedly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AgentShare is built around those four things.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it looks like in practice
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Security:&lt;/strong&gt; treat &lt;code&gt;X-API-Key&lt;/code&gt; like any secret — environment variables or a secrets manager, not committed files or screenshots. The examples below use &lt;code&gt;YOUR_KEY&lt;/code&gt; as a placeholder.&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;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-API-Key: YOUR_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://agentshare.dev/api/v1/offers/best?q=nvidia+jetson+orin"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example response (abbreviated):&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;"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;"ok"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"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;"best_offer"&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;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4875000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tiki"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"availability"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"crawled_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-04-15T02:00:20Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"data_age_seconds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;86400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"freshness_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;"stale"&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;"summary"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Best price at Tiki — 4,875,000₫"&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;"meta"&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;"remaining_requests"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"freshness_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;"stale"&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;Every response includes &lt;code&gt;freshness_status&lt;/code&gt; — &lt;code&gt;fresh&lt;/code&gt;, &lt;code&gt;stale&lt;/code&gt;, or &lt;code&gt;expired&lt;/code&gt; — so the agent can decide whether to caveat its answer or ask for a refresh.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Quota:&lt;/strong&gt; responses expose &lt;code&gt;remaining_requests&lt;/code&gt; in &lt;code&gt;meta&lt;/code&gt; on supported routes. The free tier is &lt;strong&gt;100 requests per month&lt;/strong&gt; with no credit card; plan agent behavior (retries, caching) accordingly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When auth or limits bite:&lt;/strong&gt; missing or invalid keys typically yield &lt;strong&gt;401&lt;/strong&gt;; exceeding quota often yields &lt;strong&gt;429&lt;/strong&gt; with a clear message — your agent should backoff and surface that to the user instead of looping.&lt;/p&gt;

&lt;h2&gt;
  
  
  MCP support
&lt;/h2&gt;

&lt;p&gt;If you're building with Claude Desktop, Cursor, or any MCP-compatible client, you can connect AgentShare as a tool server in one step:&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;"mcpServers"&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;"agentshare"&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;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&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;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mcp-remote"&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://agentshare.dev/mcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"--header"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"X-API-Key: YOUR_KEY"&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;Keep the key out of version-controlled config: use your client's env substitution or local overrides if available.&lt;/p&gt;

&lt;p&gt;Tools exposed: &lt;code&gt;price_search&lt;/code&gt;, &lt;code&gt;best_offer&lt;/code&gt;, &lt;code&gt;best_offer_under_budget&lt;/code&gt;, &lt;code&gt;service_meta&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Each tool returns two content blocks: a one-line human-readable summary and a full JSON payload — so the agent has both a quick answer and machine-parseable data in the same response.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three technical decisions worth sharing
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Freshness metadata is not optional
&lt;/h3&gt;

&lt;p&gt;A price from six days ago is not the same as a price from two hours ago. An agent recommending a product without knowing data age is doing the user a disservice.&lt;/p&gt;

&lt;p&gt;Every response includes &lt;code&gt;crawled_at&lt;/code&gt;, &lt;code&gt;data_age_seconds&lt;/code&gt;, and &lt;code&gt;freshness_status&lt;/code&gt;. The agent can use this to decide whether to add a caveat, trigger a refresh, or tell the user this price is a few days old.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. "Not found" is not one problem — it's two
&lt;/h3&gt;

&lt;p&gt;Early on, a missing product returned a generic &lt;code&gt;NOT_FOUND&lt;/code&gt; error. An agent receiving that has no idea what to do next.&lt;/p&gt;

&lt;p&gt;Now the API distinguishes:&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;"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;"no_data"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"out_of_coverage"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"coverage_url"&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://agentshare.dev/coverage"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"suggestions"&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;"similar product A"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"similar product B"&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;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;"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;"no_data"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pending_crawl"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"estimated_available"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-04-22T00:00:00Z"&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;ul&gt;
&lt;li&gt;
&lt;code&gt;out_of_coverage&lt;/code&gt; → the agent redirects or tells the user clearly.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pending_crawl&lt;/code&gt; → the agent knows to try again later.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. The async deadlock I almost shipped
&lt;/h3&gt;

&lt;p&gt;FastMCP handler calling the REST API with &lt;code&gt;requests.get()&lt;/code&gt; — synchronously — inside an async handler on a single Uvicorn worker. The tool call would hang for exactly 120 seconds before failing.&lt;/p&gt;

&lt;p&gt;Root cause: the synchronous HTTP call blocked the event loop, which meant the inbound REST request never got processed, which meant the MCP tool call waited forever.&lt;/p&gt;

&lt;p&gt;Fix: move all API calls inside MCP tools to &lt;code&gt;asyncio.to_thread()&lt;/code&gt; so &lt;code&gt;requests&lt;/code&gt; runs on a thread pool, freeing the event loop.&lt;/p&gt;

&lt;p&gt;If you're building MCP servers with FastMCP on a single worker, watch out for this pattern.&lt;/p&gt;

&lt;h2&gt;
  
  
  Current focus areas
&lt;/h2&gt;

&lt;p&gt;I'm honest about what's covered well versus what's still being built.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;High quality, updated regularly:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI edge hardware: Raspberry Pi, NVIDIA Jetson, Orange Pi, Google Coral, Rockchip SBCs&lt;/li&gt;
&lt;li&gt;Mini PCs: Intel NUC, Beelink, GMKtec&lt;/li&gt;
&lt;li&gt;Components: SSD, RAM, power supplies, cooling&lt;/li&gt;
&lt;li&gt;Robotics: kits, servo motors, arms, LiPo/LiFePO4 battery packs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Expanding (beta — data may be incomplete):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mobile phones, tablets, general consumer electronics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The focus list reflects what matters most to people building AI agents locally — the folks running LLMs on edge hardware or sourcing parts for robot projects.&lt;/p&gt;

&lt;p&gt;Full coverage spec: &lt;a href="https://agentshare.dev/coverage" rel="noopener noreferrer"&gt;agentshare.dev/coverage&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd genuinely like feedback on
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Response shape&lt;/strong&gt; — is the current JSON easy to work with in your agent framework, or are there fields you'd add or remove?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Freshness thresholds&lt;/strong&gt; — how old is "too old" for a price recommendation in your use case?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Coverage gaps&lt;/strong&gt; — what hardware or component categories are you looking for that aren't covered yet?&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Docs: &lt;a href="https://agentshare.dev/docs" rel="noopener noreferrer"&gt;agentshare.dev/docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Free tier: 100 requests/month, no credit card required&lt;/li&gt;
&lt;li&gt;OpenAPI spec: &lt;a href="https://agentshare.dev/openapi.json" rel="noopener noreferrer"&gt;agentshare.dev/openapi.json&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Agent discovery: &lt;a href="https://agentshare.dev/agent.json" rel="noopener noreferrer"&gt;agentshare.dev/agent.json&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a &lt;strong&gt;solo project&lt;/strong&gt;, shipped in public — I build iteratively and rely heavily on good tooling (including AI-assisted development) to move fast. If the approach helps what you're building, or something is broken or confusing, I'd genuinely like to know.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>api</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
