<?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: czcz0009</title>
    <description>The latest articles on DEV Community by czcz0009 (@czcz0009).</description>
    <link>https://dev.to/czcz0009</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%2F4074133%2Fa10dc32e-78db-482d-9d16-7ca7f1d6fb9d.png</url>
      <title>DEV Community: czcz0009</title>
      <link>https://dev.to/czcz0009</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/czcz0009"/>
    <language>en</language>
    <item>
      <title>3 Traps I Hit Building Apify Pay-Per-Event Actors as a Solo Node.js Dev</title>
      <dc:creator>czcz0009</dc:creator>
      <pubDate>Wed, 12 Aug 2026 06:16:57 +0000</pubDate>
      <link>https://dev.to/czcz0009/3-traps-i-hit-building-apify-pay-per-event-actors-as-a-solo-nodejs-dev-1pih</link>
      <guid>https://dev.to/czcz0009/3-traps-i-hit-building-apify-pay-per-event-actors-as-a-solo-nodejs-dev-1pih</guid>
      <description>&lt;h2&gt;
  
  
  Why Apify Store?
&lt;/h2&gt;

&lt;p&gt;As a Node.js/TypeScript engineer with a day job, I've been looking for a side project that fits into 15-minute-to-1-hour fragments of time, doesn't require sales calls or synchronous client work, and doesn't need me to build my own distribution channel.&lt;/p&gt;

&lt;p&gt;Apify Store checked those boxes: it's a marketplace, so discovery (search, categories) is handled by the platform. You build, you publish, and — in theory — people find you through search.&lt;/p&gt;

&lt;p&gt;This post covers three concrete traps I ran into while building a handful of pay-per-event (PPE) Actors, in case they save someone else a debugging session.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trap 1: The hidden double-charge from &lt;code&gt;apify-default-dataset-item&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Apify's PPE model charges whenever you call &lt;code&gt;Actor.charge('your-event-name')&lt;/code&gt;. I assumed that was the only thing being billed. It isn't.&lt;/p&gt;

&lt;p&gt;There's a built-in synthetic event called &lt;code&gt;apify-default-dataset-item&lt;/code&gt; that auto-charges $0.00001 every time you call &lt;code&gt;Actor.pushData()&lt;/code&gt; into the default dataset — regardless of whatever custom charge events you've already fired for that same record.&lt;/p&gt;

&lt;p&gt;If your &lt;code&gt;main.js&lt;/code&gt; charges &lt;code&gt;product-listed&lt;/code&gt; for each item and then also pushes that item to the default dataset, you're billing the same unit of value twice without realizing it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; open your Actor's Pay-per-event settings in Apify Console and check &lt;code&gt;chargedEventCounts&lt;/code&gt; after a real run. If &lt;code&gt;apify-default-dataset-item&lt;/code&gt; shows up alongside your own events, delete it from the Console-side event list.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trap 2: &lt;code&gt;apify-actor-start&lt;/code&gt; billing scales with memory allocation
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;apify-actor-start&lt;/code&gt; is Apify's synthetic "run start" event. It covers the first 5 seconds of compute for free and Apify explicitly recommends keeping its price at the default ($0.00005) to stay competitive.&lt;/p&gt;

&lt;p&gt;What's easy to miss: &lt;strong&gt;the number of times this event is charged scales with your memory allocation.&lt;/strong&gt; Every full 1GB over the base counts as an extra charge. I had an Actor running with &lt;code&gt;memoryMbytes: 4096&lt;/code&gt; while actual peak usage was around 100MB — a 40x over-allocation that was quietly multiplying this charge.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; set &lt;code&gt;defaultMemoryMbytes&lt;/code&gt; (and &lt;code&gt;minMemoryMbytes&lt;/code&gt;/&lt;code&gt;maxMemoryMbytes&lt;/code&gt;) based on your actual measured peak, not a safe-sounding round number.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trap 3: ToS-clean API, IP-allowlist blocked
&lt;/h2&gt;

&lt;p&gt;This one wasn't a pricing bug — it was an infrastructure mismatch. I integrated a public, officially documented, commercial-use-approved API from a Japanese travel platform. Registration was instant, no review process. Great, I thought — this is the clean path.&lt;/p&gt;

&lt;p&gt;Then every run on Apify failed with &lt;code&gt;403 CLIENT_IP_NOT_ALLOWED&lt;/code&gt;. The API required registering an allowed IP address, and Apify's serverless runs use dynamic, shared IPs that change per run — there's no fixed IP to register.&lt;/p&gt;

&lt;p&gt;Options I considered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Apify's Dedicated Proxy Group&lt;/strong&gt; — a paid product that gives you a fixed IP range you can register. Reliable, but adds recurring cost.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spoof the request origin to bypass the restriction&lt;/strong&gt; — technically possible, but this crosses from "the ToS is ambiguous" into "actively defeating an access control the provider put in place." I ruled this out.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Widen the allowlist to &lt;code&gt;0.0.0.0/0&lt;/code&gt; via the provider's own registration form&lt;/strong&gt; — this uses the provider's own official input format for exactly this purpose. Not a workaround, just a config choice with a real tradeoff: your API key becomes the only line of defense, so treat it as &lt;code&gt;isSecret: true&lt;/code&gt; and keep it out of your repo.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I went with the third option. It's a legitimate setting, but it does mean you're now relying entirely on credential secrecy instead of network-level restriction — worth flagging in your own docs if you build something similar.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I think about ToS gray zones
&lt;/h2&gt;

&lt;p&gt;A lot of Japanese-market data sources fall somewhere between "explicitly fine" and "explicitly forbidden." When deciding whether to proceed, I look at four things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Is there a specific liquidated-damages clause?&lt;/strong&gt; (e.g., "X yen per unauthorized item") — if so, treat it as a hard stop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How aggressive is the bot defense?&lt;/strong&gt; (Cloudflare Bot Management vs. nothing) — aggressive defense usually means the operator cares a lot, which correlates with risk.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is there an explicit anti-AI stance?&lt;/strong&gt; (e.g., &lt;code&gt;robots.txt&lt;/code&gt; blocking GPTBot/Google-Extended) — a signal the operator has thought about this exact use case and said no.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is there existing, tolerated commercial scraping activity in the same space?&lt;/strong&gt; — not a legal green light, but a useful signal for how the operator has historically responded.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;None of this is legal advice — just the framework I use to decide what's worth the risk of an IP block versus what isn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I ended up building
&lt;/h2&gt;

&lt;p&gt;Applying all of the above, I published a handful of PPE Actors around Japanese-market data — VRChat-related asset listings, real-estate yield estimation, PR-release sales-signal detection, hotel recommendations, and used-car valuation. All of them are early and still building up usage, but the architecture and the lessons above are the same across all of them.&lt;/p&gt;

&lt;p&gt;If you're curious about the details or want to see the PPE pricing/output-schema setup in practice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://apify.com/woolen_snake/booth-vrchat-3d-products" rel="noopener noreferrer"&gt;BOOTH VRChat 3D Products Scraper&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://apify.com/woolen_snake/suumo-investment-analyzer" rel="noopener noreferrer"&gt;SUUMO Property Investment Analyzer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://apify.com/woolen_snake/prtimes-sales-signal-classifier" rel="noopener noreferrer"&gt;PR TIMES Sales Signal Classifier&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://apify.com/woolen_snake/rakuten-travel-purpose-recommender" rel="noopener noreferrer"&gt;Rakuten Travel Purpose-Based Hotel Recommender&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://apify.com/woolen_snake/carsensor-resale-value-scout" rel="noopener noreferrer"&gt;carsensor.net Used-Car Resale Value Scout&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy to answer questions about PPE pricing design, dataset schemas, or any of the ToS-judgment stuff above in the comments.&lt;/p&gt;

</description>
      <category>apify</category>
      <category>node</category>
      <category>webscraping</category>
    </item>
  </channel>
</rss>
