<?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: Yoon</title>
    <description>The latest articles on DEV Community by Yoon (@wonderfulian).</description>
    <link>https://dev.to/wonderfulian</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%2F4127228%2F2666baaa-5221-479a-ab48-f7620a0211b7.png</url>
      <title>DEV Community: Yoon</title>
      <link>https://dev.to/wonderfulian</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wonderfulian"/>
    <language>en</language>
    <item>
      <title>The data nobody sells: why I started taking a daily snapshot of 211,243 companies</title>
      <dc:creator>Yoon</dc:creator>
      <pubDate>Thu, 01 Oct 2026 00:14:57 +0000</pubDate>
      <link>https://dev.to/wonderfulian/the-data-nobody-sells-why-i-started-taking-a-daily-snapshot-of-211243-companies-3omb</link>
      <guid>https://dev.to/wonderfulian/the-data-nobody-sells-why-i-started-taking-a-daily-snapshot-of-211243-companies-3omb</guid>
      <description>&lt;p&gt;Korea's National Tax Service runs a clean public API for business registration status. Give it a 10-digit number, get back whether the company is active, suspended or closed. It's free, it's fast, it's accurate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It also has no memory.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ask it today and you learn the company is closed. Ask what it was last month and there's no answer, because that question isn't in the API. There's no as_of parameter, no history endpoint, no changelog. The present tense is the only tense available.&lt;/p&gt;

&lt;p&gt;That gap turned out to be the most interesting thing I've found while building an API for AI agents, so I did the obvious thing: I started writing down what I saw, every day.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I built&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A scheduled job, running once a day, that asks for the current status of every company in a fixed list and stores the answer. Then it diffs today against yesterday and writes a separate file containing only what changed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Three days in, the numbers look like this:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Date    Observations    Changes recorded&lt;br&gt;
Day 1   211,243 — (first run, everything is new)&lt;br&gt;
Day 2   211,243 70&lt;br&gt;
Day 3   211,243 94&lt;/p&gt;

&lt;p&gt;The daily snapshot is about 42 MB. The changes file is 1.7 MB. That ratio is the whole point: 99.2% of what I record is confirmation that nothing happened, and the remaining 0.8% is the product.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Here's one change, as stored:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;json&lt;br&gt;
{"business_number":"...","field":"status","previous":"active","current":"closed",&lt;br&gt;
 "previous_seen_at":"2026-09-29T00:05:14Z","detected_at":"2026-09-30T00:05:14Z"}&lt;/p&gt;

&lt;p&gt;A company that was operating when I looked on the 29th had closed by the 30th. I can't prove when it actually closed — only when I observed each state. So that's what I store: not "this company closed on the 29th", but "it was active when I looked, and closed when I looked again." Those are different claims, and for anything that might end up in a compliance report, the difference matters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why this is worth doing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Three reasons, in increasing order of how much I care.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One: it can't be bought. The upstream has no history. Neither does anyone else's wrapper around it. If a competitor decides tomorrow that company status history is valuable, their history starts tomorrow. Mine starts today. That gap only widens, and no amount of funding closes it.&lt;/p&gt;

&lt;p&gt;Two: it changes the shape of the business. A status lookup is a one-shot transaction — the agent asks, pays two cents, leaves, and may never return. A watchlist is a standing relationship: register 100 suppliers, and every day I check them and tell you only what moved. Small, recurring, and from my side it runs on infrastructure that's already doing the work.&lt;/p&gt;

&lt;p&gt;Three: change is the signal, not state. "This company is closed" is a fact. "This company suspended operations three months ago and resumed last week" is an inference about risk, and it's the kind of thing a procurement system actually wants to know. You cannot derive the second from any number of present-tense lookups. You can only accumulate it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The things that nearly broke it&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The job that ran nothing and exited 0. I wrote about this one &lt;a href="https://dev.to3%ED%8E%B8%20%EB%A7%81%ED%81%AC"&gt;in the last post&lt;/a&gt;: an entry-point guard that compared hand-built file URLs, worked on Windows, silently failed on Linux. The container started, did nothing, exited clean, and the scheduler showed green. If I'd trusted the schedule instead of running it manually first, I'd have "collected" a week of nothing.&lt;/p&gt;

&lt;p&gt;Day one proves nothing. On the first run, every record is new, so the diff path never executes. The output looked perfect and told me nothing about whether the comparison logic worked. I only got real validation on day two, when 70 changes appeared and I could check that the previous values, current values and both timestamps were all populated. If you build something that compares today to yesterday, you don't have a working system until the day after you ship it.&lt;/p&gt;

&lt;p&gt;Scope discipline is a security feature. The obvious way to decide which companies to track is to watch what users look up. I deliberately didn't. The corpus is built only from public registries — the procurement vendor list and the debarment list — and I enforced that with a runtime guard and a test, not just a comment, because a comment doesn't stop a future version of me who is in a hurry. Query contents never enter the corpus, which means the dataset contains nothing I learned from a customer.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;The cost&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
Roughly 2,100 upstream calls a day against a limit of 1,000,000. Storage is a few hundred won a month. The job runs in about two and a half minutes. It's the cheapest thing in the entire project and probably the most valuable, because every other part of it could be rebuilt by someone else in a week, and this one can't.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The general version&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you're wrapping a public data source for agents, it's worth asking what the source doesn't answer. Usually there's a tense problem: it tells you what is, not what was, not what changed. That absence is the only part of the data you can own.&lt;/p&gt;

&lt;p&gt;Everything else you expose is a thinner version of something the upstream already provides. The history you accumulate is yours, and it starts the day you decide to write things down.&lt;/p&gt;

&lt;p&gt;Which is a long way of saying: if you're going to do this, start the clock today rather than when you need it. The data you don't record is the only kind you can never get back.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>mcp</category>
    </item>
    <item>
      <title>Three bugs that reported success: what "exit 0" doesn't tell you</title>
      <dc:creator>Yoon</dc:creator>
      <pubDate>Mon, 28 Sep 2026 03:28:51 +0000</pubDate>
      <link>https://dev.to/wonderfulian/three-bugs-that-reported-success-what-exit-0-doesnt-tell-you-1dcf</link>
      <guid>https://dev.to/wonderfulian/three-bugs-that-reported-success-what-exit-0-doesnt-tell-you-1dcf</guid>
      <description>&lt;p&gt;Last week I wrote about shipping a paid API for AI agents and getting two real calls in a week. The follow-up was supposed to be about fixing the product. It turned into something else, because in the process of fixing it I hit three bugs in a row that had one thing in common:&lt;/p&gt;

&lt;p&gt;None of them reported an error. All three looked like success.&lt;/p&gt;

&lt;p&gt;That turns out to be the failure mode that costs the most time, and I don't see it discussed much, so here they are.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bug 1: the government API that answers "zero" instead of "wrong"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I was adding debarment records — public sanctions against companies barred from public procurement — to my business verification API. The endpoint took a parameter called inqryDiv. I passed 1.&lt;/p&gt;

&lt;p&gt;It returned HTTP 200 with an empty result set. No error, no warning, no hint. The obvious reading: this company has no sanctions on record.&lt;/p&gt;

&lt;p&gt;The correct value was 2. With 1, the API answers a question nobody asked and returns nothing, forever, for every company.&lt;/p&gt;

&lt;p&gt;If I'd shipped that, my API would have told users "no sanctions found" for every single company in Korea, with a straight face and a 200 status code. That's not a broken feature. That's a feature that confidently lies.&lt;/p&gt;

&lt;p&gt;What saved me: I happened to test against a company I knew was sanctioned. If I'd tested with a random company — which would also have returned zero, correctly — I'd never have caught it.&lt;br&gt;
**&lt;br&gt;
Bug 2: the Cloud Run job that ran nothing and exited 0**&lt;/p&gt;

&lt;p&gt;I built a scheduled job to enrich my index with registration numbers. Registered it with Cloud Scheduler. Ran it once manually before trusting the schedule.&lt;/p&gt;

&lt;p&gt;Exit code 0. Duration: under a second. Cloud Run console: green check.&lt;/p&gt;

&lt;p&gt;Nothing had happened.&lt;/p&gt;

&lt;p&gt;The entry point had a guard to detect whether the module was being run directly, and it built the comparison URL by hand:&lt;/p&gt;

&lt;p&gt;js&lt;br&gt;
// Works on my machine. Not on Linux.&lt;br&gt;
if (import.meta.url === &lt;code&gt;file:///${process.argv[1]}&lt;/code&gt;) { main(); }&lt;/p&gt;

&lt;p&gt;On Windows, process.argv[1] looks like C:\path\to\file.js, so prefixing file:/// is right. On Linux it's already /app/dist/job.js, so you get file:////app/dist/job.js — four slashes — which never matches. The guard silently evaluated false, main() never ran, the process exited cleanly, and the platform reported success.&lt;/p&gt;

&lt;p&gt;The fix is one line (pathToFileURL(process.argv[1]).href), but that's not the interesting part. The interesting part is that a green checkmark in a scheduler dashboard is not evidence that your code ran. It's evidence that a container started and stopped without crashing. Those are very different claims.&lt;/p&gt;

&lt;p&gt;If I had trusted the schedule instead of running it once by hand, I would have seen green checkmarks every morning for a week while the index sat untouched, and I'd have gone looking for the problem in the API, the credentials, the data — anywhere but the one line that decides whether the program starts at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bug 3: the query that returned nothing because of my terminal&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With the job fixed, I tested search with Korean company names over curl in Git Bash. Zero results. Every time. English names worked fine.&lt;/p&gt;

&lt;p&gt;I was minutes away from digging into the normalization pipeline — Korean text has its own encoding pitfalls, so a bug there was entirely plausible — when I ran the identical query from a local Node script instead. It worked.&lt;/p&gt;

&lt;p&gt;The server was fine. Git Bash was mangling the UTF-8 in the URL before it ever left my machine.&lt;/p&gt;

&lt;p&gt;This one cost the least time but taught the sharpest lesson: when a test fails, the test environment is a suspect too. I'd been treating my terminal as a neutral observer. It wasn't.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The pattern&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Three different layers — a government API, a container platform, a shell — and the same shape each time. Something went wrong, and every signal I had available said things were fine.&lt;/p&gt;

&lt;p&gt;What they have in common is that each one answered a different question than the one I was asking.&lt;/p&gt;

&lt;p&gt;I asked "does this company have sanctions?" The API answered "here are the results for a query type you didn't intend": zero.&lt;br&gt;
I asked "did my enrichment run?" The platform answered "did the container exit without crashing?": yes.&lt;br&gt;
I asked "does search work?" My terminal answered "does search work when the input is corrupted?": no.&lt;/p&gt;

&lt;p&gt;Every layer answered truthfully. None of them answered my question.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I do differently now&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Three rules, written into my project docs so future-me can't argue with them:&lt;/p&gt;

&lt;p&gt;Verify against a known-positive, not a random sample. Testing sanctions lookup with a company that has no sanctions proves nothing, because a broken implementation and a correct one give identical output. You need a case where the right answer is not empty.&lt;/p&gt;

&lt;p&gt;Exit code 0 is not evidence of work. Check the artifact. Did the file's timestamp change? Did the row count move? Did the summary log print? A scheduler dashboard tells you about the container's lifecycle, not about your program's intent.&lt;/p&gt;

&lt;p&gt;Run scheduled jobs manually once, immediately after registering them. The gap between "registered" and "actually working" is where silent failures live for days. Five minutes now versus a week of green checkmarks later.&lt;br&gt;
**&lt;br&gt;
The broader version of this**&lt;/p&gt;

&lt;p&gt;I've been building an API whose customers are AI agents rather than people, and this problem gets worse in that context, not better. A human user who gets "no sanctions found" for a company they know is sanctioned will email you. An agent will write it into a report and move on.&lt;/p&gt;

&lt;p&gt;Agents don't have the intuition that catches a wrong-but-plausible answer. They can't tell "confidently empty" from "correctly empty." Whatever you return, they'll act on.&lt;/p&gt;

&lt;p&gt;Which means for agent-facing APIs, the distinction between empty and broken isn't a nice-to-have in your error handling. It's the product. I ended up encoding it explicitly: a genuine no-match returns 200 with an empty array and a note saying this is a definitive result, not an error; an upstream failure returns 503 with an error code. Same absence of data, two completely different meanings, and the consumer has to be able to tell them apart without guessing.&lt;/p&gt;

&lt;p&gt;Three bugs that said "success" taught me that. I'd rather have learned it from someone else's post, which is why I'm writing this one.&lt;/p&gt;

</description>
      <category>debugging</category>
      <category>mcp</category>
      <category>webdev</category>
      <category>ai</category>
    </item>
    <item>
      <title>I optimized an API for AI agents instead of humans. Here's what one week of data looked like.</title>
      <dc:creator>Yoon</dc:creator>
      <pubDate>Wed, 23 Sep 2026 00:43:57 +0000</pubDate>
      <link>https://dev.to/wonderfulian/i-optimized-an-api-for-ai-agents-instead-of-humans-heres-what-one-week-of-data-looked-like-ec6</link>
      <guid>https://dev.to/wonderfulian/i-optimized-an-api-for-ai-agents-instead-of-humans-heres-what-one-week-of-data-looked-like-ec6</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/wonderfulian/i-made-korean-government-data-agent-payable-with-mcp-and-x402-heres-what-actually-happened-40e8" class="crayons-story__hidden-navigation-link"&gt;I made Korean government data agent-payable with MCP and x402 — here's what actually happened&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/wonderfulian" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4127228%2F2666baaa-5221-479a-ab48-f7620a0211b7.png" alt="wonderfulian profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/wonderfulian" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Yoon
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Yoon
                
                
              
              &lt;div id="story-author-preview-content-4663924" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/wonderfulian" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4127228%2F2666baaa-5221-479a-ab48-f7620a0211b7.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Yoon&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/wonderfulian/i-made-korean-government-data-agent-payable-with-mcp-and-x402-heres-what-actually-happened-40e8" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Sep 16&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/wonderfulian/i-made-korean-government-data-agent-payable-with-mcp-and-x402-heres-what-actually-happened-40e8" id="article-link-4663924"&gt;
          I made Korean government data agent-payable with MCP and x402 — here's what actually happened
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/mcp"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;mcp&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/cryptocurrency"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;cryptocurrency&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/wonderfulian/i-made-korean-government-data-agent-payable-with-mcp-and-x402-heres-what-actually-happened-40e8" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;1&lt;span class="hidden s:inline"&gt;&amp;nbsp;reaction&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/wonderfulian/i-made-korean-government-data-agent-payable-with-mcp-and-x402-heres-what-actually-happened-40e8#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              7&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            5 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


&lt;p&gt;Two weeks ago I put a paid MCP server online: Korean business verification, priced per call in USDC over x402. &lt;a href="https://dev.to1%ED%8E%B8%20%EB%A7%81%ED%81%AC"&gt;I wrote up the build here.&lt;/a&gt; This is the follow-up nobody writes — the part where you check whether anyone actually showed up.&lt;/p&gt;

&lt;p&gt;Short version: 23,980 requests in seven days, and two of them were real.&lt;/p&gt;

&lt;p&gt;Here's what I learned from the gap between those two numbers.&lt;/p&gt;

&lt;p&gt;The experiment&lt;/p&gt;

&lt;p&gt;One hypothesis, one number to falsify it. Hypothesis: if agents can discover a priced API and pay for it autonomously, some of them will. Success bar: 20 real tool calls in the first week after launch. I wrote that number down before launching, which turned out to be the single most useful thing I did.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Day 0&lt;/th&gt;
&lt;th&gt;Week 1&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Requests (7 days)&lt;/td&gt;
&lt;td&gt;~8,700&lt;/td&gt;
&lt;td&gt;23,980&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Real external tool calls&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Third-party payments&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Requests nearly tripled. Real usage went from zero to two. Every payment in my receiving wallet is still one I made myself.&lt;/p&gt;

&lt;p&gt;Breaking the 41 logged tool calls down by origin: 7 were my own indexing payments, 32 were crawlers requesting the literal path template from the discovery catalog (/v1/business/:brno/status — they call the placeholder, not a real number), and 2 were actual external calls. Both on the same day, hours apart, then nothing for six days.&lt;/p&gt;

&lt;p&gt;So: missed the bar by a factor of ten.&lt;/p&gt;

&lt;p&gt;The traffic wasn't fake. It just wasn't people.&lt;/p&gt;

&lt;p&gt;After getting listed in the x402 Bazaar, a new class of visitor showed up: kkj-x402-trust-index (its user agent says it verifies Bazaar-listed x402 resources), allow402-quote, x402-observer, x402-census-probe, plus the usual liveness bots that announce "never invokes tools" in their UA string.&lt;/p&gt;

&lt;p&gt;An ecosystem of watchers has grown up around agent payments, and it found me within days. There's something genuinely encouraging in that: the infrastructure works. Discovery works. Listings propagate. Scanners verify you. What hasn't arrived is the buyer.&lt;/p&gt;

&lt;p&gt;The thing I'd fix first if I started over&lt;/p&gt;

&lt;p&gt;Here's the mistake I only saw when I stopped looking at request counts and started reading connection logs.&lt;/p&gt;

&lt;p&gt;69 handshakes. 17 curl calls. 39 from python-requests. People connected. Almost none of them executed a tool.&lt;/p&gt;

&lt;p&gt;My server takes a 10-digit Korean business registration number as input. But if you're an agent outside Korea evaluating a supplier, what you have is a name — "Hanil Trading Co., Ltd" — not a registration number. That number lives on invoices and contracts, which means by the time you can use my API, you already have a relationship with the company.&lt;/p&gt;

&lt;p&gt;I built a tool that requires you to already have the thing you came looking for.&lt;/p&gt;

&lt;p&gt;That's not a discovery problem or a pricing problem. It's the shape of the product. No amount of catalog optimization helps when the first input field is one your user can't fill in.&lt;/p&gt;

&lt;p&gt;Three things I'd tell anyone shipping a priced API for agents&lt;/p&gt;

&lt;p&gt;Start from the agent's task, not your data. "Look up a registration number" is a data endpoint. "Decide whether to transact with this supplier" is a task. Agents arrive with tasks. If your endpoint sits in the middle of one, you have to own the entrance too, or someone else's tool will.&lt;/p&gt;

&lt;p&gt;A free tier is a discovery liability, not just a pricing decision. The Bazaar validator checks that an unpaid request returns 402. My friendly free tier returned a cheerful 200, so the catalog concluded the resource wasn't payable and skipped it entirely. Default-paid with opt-in free (?free=1) fixed it — and closed the hole where rotating IPs got unlimited free calls.&lt;/p&gt;

&lt;p&gt;Catalogs index routes, not servers, and only after a payment settles on that route. Each of my three endpoints stayed invisible until I'd paid for that specific one myself. Worse, all three had near-identical descriptions, so every query surfaced the same route. Rewriting them by intent — status for "is this company still operating", batch for "screen this supplier list", verify for "KYB identity match" — made queries that never mention Korea start returning the right one. Highest-leverage hour I spent all month.&lt;/p&gt;

&lt;p&gt;Where the next two weeks go&lt;/p&gt;

&lt;p&gt;Not into more distribution. Two things:&lt;/p&gt;

&lt;p&gt;An entrance. Resolve a company name to a registration number, so an agent can start from what it actually has. Harder than it sounds — name matching is fuzzy, so the honest design returns ranked candidates with evidence, not one confident answer.&lt;br&gt;
Signals worth paying for. Debarment records, sanction history, status changes over time. One call that answers "should I transact with this company" instead of "does this number exist".&lt;/p&gt;

&lt;p&gt;Then I measure again the same way: number written down first, honest count after.&lt;/p&gt;

&lt;p&gt;If you're building something agent-facing and have run your own numbers, I'd like to compare. My suspicion is that a lot of us are shipping data endpoints into a market that only buys finished tasks — and that the distance between 23,980 and 2 is mostly the shape of what we built, not the size of the market.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>api</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I made Korean government data agent-payable with MCP and x402 — here's what actually happened</title>
      <dc:creator>Yoon</dc:creator>
      <pubDate>Wed, 16 Sep 2026 05:04:40 +0000</pubDate>
      <link>https://dev.to/wonderfulian/i-made-korean-government-data-agent-payable-with-mcp-and-x402-heres-what-actually-happened-40e8</link>
      <guid>https://dev.to/wonderfulian/i-made-korean-government-data-agent-payable-with-mcp-and-x402-heres-what-actually-happened-40e8</guid>
      <description>&lt;p&gt;TL;DR: I built a remote MCP server that lets any AI agent verify Korean businesses using official National Tax Service data — and charge for it per call in USDC via x402. It works end-to-end: here's the first on-chain payment — an agent hit a 402, paid $0.02, and got real government data back, no human in the loop. It's now listed on the x402 Bazaar, Coinbase's discovery catalog for agent-payable APIs, where it currently ranks at the top for "korean business registration verification" — agents find it by searching, not by reading docs. Along the way I learned that directory listings get you 1,250 bot requests a day, that a free tier can silently disqualify you from payment marketplaces, and a few other things nobody tells you.&lt;/p&gt;

&lt;p&gt;Why Korean business data&lt;/p&gt;

&lt;p&gt;If you're a procurement or compliance agent outside Korea and you need to answer "is this Korean supplier a real, active company?", you're stuck. The official data exists — Korea's National Tax Service (NTS) runs a free public API — but it sits behind a Korean-language portal, phone-verification signup, and API key issuance that an autonomous agent simply cannot do. An agent can't pass Korean SMS verification. It can't read the docs. It gives up or hallucinates.&lt;/p&gt;

&lt;p&gt;That gap is the product: Korea Business Verify (KBV) — one URL, no signup, no API key. Ask it about a 10-digit business registration number and get back active/suspended/closed status, tax type, and KYB identity matching, in clean English JSON, straight from NTS. Up to 100 companies in a single batch call.&lt;/p&gt;

&lt;p&gt;Server: &lt;a href="https://kbv-server-f7vfitmlkq-du.a.run.app/mcp" rel="noopener noreferrer"&gt;https://kbv-server-f7vfitmlkq-du.a.run.app/mcp&lt;/a&gt; (Streamable HTTP)&lt;br&gt;
Source: &lt;a href="https://github.com/Wonderfulian/kbv-server" rel="noopener noreferrer"&gt;https://github.com/Wonderfulian/kbv-server&lt;/a&gt; (MIT)&lt;br&gt;
Free: 10 calls/day. Beyond that: $0.02 per status check, $0.05 per identity verification, $0.02/company for batch — paid in USDC on Base via x402.&lt;br&gt;
The stack, briefly&lt;/p&gt;

&lt;p&gt;NTS public API → TypeScript + official MCP SDK (+ Express for REST) → Cloud Run (scale-to-zero, so idle cost ≈ $0) → x402 v2 (@x402/express, @x402/core, @x402/evm) → Coinbase CDP facilitator → USDC on Base. The server never touches a private key — it only knows the receiving address. Settlement verification is the facilitator's job.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdtu1quii2xerpeqbb9p8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdtu1quii2xerpeqbb9p8.png" alt=" " width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One paid call, end to end. The agent never talks to a human — it discovers the price, signs the payment, and gets the data.&lt;/p&gt;

&lt;p&gt;Total cash spent to build, deploy, and verify all of this: about $3 (one exchange transfer fee plus test payments to myself).&lt;/p&gt;

&lt;p&gt;What nobody tells you (the honest part)&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Directory traffic is a mirage. After listing on the official MCP Registry, Glama, and Smithery, my server started receiving ~1,250 requests a day. Exciting! Except a User-Agent breakdown showed nearly all of it was health-check and security-scanner bots — some literally announce "never invokes tools" in their UA string. Actual external tool calls in the first week: effectively zero. If you're building an MCP server: request count is not a popularity metric. Count tools/call, nothing else.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Your free tier can disqualify you from payment marketplaces. x402 has a discovery marketplace (the Bazaar) where paying agents find priced resources. Its validator checks that an unpaid request returns HTTP 402. My server had a friendly free tier — 10 calls/day per IP — so the validator got a free 200 and concluded the resource wasn't payable. The free tier also meant anyone rotating IPs could scrape indefinitely. Fix: REST now returns 402 by default, and the free tier became opt-in via ?free=1. Default-paid, intentionally-free turns out to be the right default for the agent economy.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Everything is three months newer than the docs. The MCP registry rejected my first two publishes because the server.json schema had changed weeks earlier. x402 moved to v2 (new headers, new packages) while most tutorials still show v1. Cloud Run's URL took four hours to propagate on a fresh project. None of these are hard problems — but every one of them cost hours, and none of them appeared in any guide. Budget for the ecosystem shifting under your feet.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The "receiving crypto without an exchange account" part is easier than expected. I'm in Korea, where Coinbase's retail exchange isn't available. Didn't matter: receiving x402 payments needs only a wallet address. The facilitator handles verification. My keys live in MetaMask, never on the server.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Independent scanners will find you before customers do. Within days, a trust-scoring service (M8ven) had auto-indexed the server, run static analysis (clean: no credential exfiltration, no obfuscation), and emailed me one legitimate fix — my tools were missing the four MCP annotation hints (readOnlyHint etc.), which OpenAI's directory requires. The agent ecosystem is growing an immune system faster than it's growing customers. I claimed the listing and fixed the hints the same day.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Discovery catalogs index routes, not servers — and only after someone pays. The x402 Bazaar only indexed the exact route I had actually settled a payment on. My other two endpoints stayed invisible until I paid for each of them myself. And the indexing happened the instant I paid — but the public listing lagged by days, long enough that I concluded I'd failed when I had actually succeeded. If you're doing this: pay once per route, then wait before you panic.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Where this stands&lt;/p&gt;

&lt;p&gt;KBV is brand new — I completed the end-to-end mainnet payment verification just this week, and this post is its public debut. It's discoverable on the x402 Bazaar with full input/output schemas, so a paying agent can find it and call it correctly without any documentation. The x402 buyer pool is real but still early — this is a land-grab phase, not a gold rush. What I have is a verified, monetized, trust-scored piece of infrastructure that cost ~$3 and a few weekends, sitting on a corner (Korean official data, agent-payable, zero-setup) that nobody else occupies yet. When agentic procurement becomes normal, the pipes are already laid.&lt;/p&gt;

&lt;p&gt;Try it&lt;/p&gt;

&lt;p&gt;Add the URL as a custom connector in Claude or ChatGPT and ask it to verify a Korean business — Samsung Electronics is 124-81-00998 if you need a test number. First 10 calls a day are free (?free=1 on REST). Code, schemas, and pricing are in the repo. I'd genuinely love to hear what breaks.&lt;/p&gt;

&lt;p&gt;Built by a solo non-engineer in Seoul with heavy help from Claude (design/review in chat, implementation via Claude Code). Happy to answer anything about MCP monetization, x402 v2, or wrapping government data for agents.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>webdev</category>
      <category>cryptocurrency</category>
    </item>
  </channel>
</rss>
