<?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: 大橙子</title>
    <description>The latest articles on DEV Community by 大橙子 (@orange-big).</description>
    <link>https://dev.to/orange-big</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%2F3986407%2F84e1653f-58e6-42b7-9f30-2431efec9fed.png</url>
      <title>DEV Community: 大橙子</title>
      <link>https://dev.to/orange-big</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/orange-big"/>
    <language>en</language>
    <item>
      <title>Building a Self-Hosted Market Research Engine: Architecture, Scraping, and AI Analysis</title>
      <dc:creator>大橙子</dc:creator>
      <pubDate>Fri, 19 Jun 2026 10:55:44 +0000</pubDate>
      <link>https://dev.to/orange-big/building-a-self-hosted-market-research-engine-architecture-scraping-and-ai-analysis-2ph2</link>
      <guid>https://dev.to/orange-big/building-a-self-hosted-market-research-engine-architecture-scraping-and-ai-analysis-2ph2</guid>
      <description>&lt;h1&gt;
  
  
  Building a Self-Hosted Market Research Engine: Architecture, Scraping, and AI Analysis
&lt;/h1&gt;

&lt;p&gt;When I started my last indie project, I needed to know two things: who's competing in my space, and what are they saying? Simple question. But the tools I found were either $200/month (hello, Semrush) or locked me into their data silo.&lt;/p&gt;

&lt;p&gt;So I built my own. Self-hosted. Python-based. With actual control over my data.&lt;/p&gt;

&lt;p&gt;Here's how it works under the hood — and why I think most indie devs should consider this approach instead of signing up for yet another SaaS.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Core Architecture
&lt;/h2&gt;

&lt;p&gt;The system has three layers, running in Docker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────┐
│   Data Collection    │  ← Scrapy + Playwright
├─────────────────────┤
│   Analysis Pipeline  │  ← LLM summarization + NLP
├─────────────────────┤
│   API + Dashboard    │  ← FastAPI + SQLite/Postgres
└─────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All three run on a $12/month VPS. No external API calls except the LLM endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer 1: Not Your Grandma's Web Scraper
&lt;/h2&gt;

&lt;p&gt;The tricky part isn't scraping. It's scraping &lt;em&gt;at scale without getting blocked&lt;/em&gt;. Here's what I learned:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Proxy rotation isn't optional.&lt;/strong&gt; After about 50 requests to the same domain, you're cooked. I built a simple proxy rotator that pulls from a pool of residential proxies:&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;class&lt;/span&gt; &lt;span class="nc"&gt;RotatingProxyMiddleware&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;proxies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;proxies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;proxies&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_lock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&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="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_lock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;proxies&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_index&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;proxies&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_index&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;proxies&lt;/span&gt;&lt;span class="o"&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;http&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&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;Dead simple. But it solves 90% of the block problem. The last 10% is solved by random delays (2–5 seconds between requests) and realistic user-agent headers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Playwright for JS-heavy sites.&lt;/strong&gt; Some competitor sites are full SPA — no static HTML, all JavaScript rendering. For those, I drop into Playwright:&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;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;scrape_dynamic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&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="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;async_playwright&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;headless&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new_page&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wait_until&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;networkidle&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;content&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&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;content&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This adds about 3 seconds per page, but it's the only way to get the actual rendered content.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer 2: The Analysis Pipeline — Where AI Actually Helps
&lt;/h2&gt;

&lt;p&gt;Once you have the raw data (pricing pages, product descriptions, changelogs), you need to extract signal from noise. This is where LLMs shine — not for generating content, but for &lt;em&gt;structuring&lt;/em&gt; it.&lt;/p&gt;

&lt;p&gt;I use a cheap model (Claude Haiku or GPT-4o-mini) with a structured prompt:&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="n"&gt;ANALYSIS_PROMPT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Extract the following from this competitor page:
1. Pricing tiers and amounts (list all)
2. Target customer (individual/team/enterprise)
3. Key features mentioned
4. Landing page positioning
Return as JSON.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;analyze_page&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&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;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;llm_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;claude-3-haiku&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&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;role&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;user&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;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ANALYSIS_PROMPT&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;8000&lt;/span&gt;&lt;span class="p"&gt;]}],&lt;/span&gt;
        &lt;span class="n"&gt;response_format&lt;/span&gt;&lt;span class="o"&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;type&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;json_object&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key insight: &lt;strong&gt;you don't need a huge context window.&lt;/strong&gt; Most pages have the important info in the first 5000 characters. Truncate aggressively, batch cheap calls, and store results in SQLite.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer 3: The Dashboard
&lt;/h2&gt;

&lt;p&gt;I built a minimal FastAPI dashboard that shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Competitor pricing changes over time (screenshot diff)&lt;/li&gt;
&lt;li&gt;Feature comparison matrix (auto-populated)&lt;/li&gt;
&lt;li&gt;Raw changelog entries with AI summaries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The dashboard runs on the same VPS. No frontend framework — just Jinja2 templates and htmx for interactivity. It's ugly, but it works, and I can access it from my phone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Self-Hosted Matters
&lt;/h2&gt;

&lt;p&gt;Three reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Your data stays yours.&lt;/strong&gt; Every market analysis tool I tried uploads your competitors' data to &lt;em&gt;their&lt;/em&gt; databases. That's fine until you're analyzing something sensitive.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cost scales to zero.&lt;/strong&gt; The VPS costs $12/month regardless of how many competitors I track. SaaS tools charge per project/per seat.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;You own the pipeline.&lt;/strong&gt; Found a new data source? Add it in 20 lines of Python. Want a custom metric? Write it. No submitting feature requests.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Stack I Wish I Started With
&lt;/h2&gt;

&lt;p&gt;If I were building this again today, I'd start with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scrapy&lt;/strong&gt; for crawling (not raw requests — Scrapy handles retries, backpressure, and pipelines out of the box)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SQLite&lt;/strong&gt; for storage (faster than Postgres for read-heavy workloads, and zero ops)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Claude Haiku&lt;/strong&gt; for analysis (cheapest capable model I've found)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;htmx&lt;/strong&gt; for dashboard (no JS build step)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;I've packaged this into a tool called &lt;strong&gt;MarketEye&lt;/strong&gt; — it's the exact system I described above, with a ready-to-run Docker setup and a few pre-built scrapers for common competitor sources (Product Hunt, G2, Crunchbase, etc.).&lt;/p&gt;

&lt;p&gt;If you want to check it out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub (open-source core):&lt;/strong&gt; &lt;a href="https://github.com/dachengzi065-gif/marketeYE" rel="noopener noreferrer"&gt;https://github.com/dachengzi065-gif/marketeYE&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gumroad (pre-built Docker image + scrapers):&lt;/strong&gt; &lt;a href="https://gumroad.com/l/kvnkhb" rel="noopener noreferrer"&gt;https://gumroad.com/l/kvnkhb&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But honestly? Even if you never use MarketEye, I hope this post shows you that building your own self-hosted intelligence pipeline is totally doable. You don't need enterprise tools. You just need Python, a VPS, and a weekend.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Have you built any self-hosted tooling for your indie projects? I'd love to hear what you're running — drop a comment below.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>showdev</category>
      <category>webdev</category>
      <category>opensource</category>
    </item>
    <item>
      <title>I Open-Sourced MarketEye — Here's Why (and the GitHub Link)</title>
      <dc:creator>大橙子</dc:creator>
      <pubDate>Tue, 16 Jun 2026 03:35:29 +0000</pubDate>
      <link>https://dev.to/orange-big/i-open-sourced-marketeye-heres-why-and-the-github-link-2aop</link>
      <guid>https://dev.to/orange-big/i-open-sourced-marketeye-heres-why-and-the-github-link-2aop</guid>
      <description>&lt;p&gt;I open-sourced MarketEye today.&lt;/p&gt;

&lt;p&gt;For anyone who missed the first post: MarketEye is a self-hosted competitor price monitor I built because I didn't want to pay $99/month for Prisync.&lt;/p&gt;

&lt;p&gt;The code is now up on GitHub under MIT license.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/dachengzi065-gif/marketeye" rel="noopener noreferrer"&gt;github.com/dachengzi065-gif/marketeye&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why open source?
&lt;/h2&gt;

&lt;p&gt;Three reasons:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. People actually asked for it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After my first post here, a few people DM'd me asking to see the code. They're developers too — they want to modify it, extend it, make it their own. That's fair. Selling source code to devs is like selling ice to eskimos.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Trust.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A closed-source price tracker that "runs on your machine" — you either trust the author or you don't. Open source removes that doubt. You can read every line, check what data leaves your machine (nothing), and build it yourself if you want.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Longevity.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Self-hosted tools have a dirty secret: if the developer disappears, you're stuck with a broken tool. Open source changes that. Even if I get hit by a bus tomorrow, you can fork the repo and keep going.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this means for the $49 version
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://gumroad.com/l/kvnkhb" rel="noopener noreferrer"&gt;Gumroad package&lt;/a&gt; still exists. It includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The same code, pre-packaged&lt;/li&gt;
&lt;li&gt;Email support (I'll help you set it up)&lt;/li&gt;
&lt;li&gt;A clear conscience subscription (you're paying for convenience, not software)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But honestly? If you can run &lt;code&gt;pip install&lt;/code&gt;, just clone the repo. It's free.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;I'm actively working on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Docker image&lt;/strong&gt; (one-command deploy)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;More scrapers&lt;/strong&gt; (plugins for different sites)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Discord/Telegram bot alerts&lt;/strong&gt; (requested by several people)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;PRs welcome. Issues welcome. Feedback welcome.&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/dachengzi065-gif/marketeye" rel="noopener noreferrer"&gt;github.com/dachengzi065-gif/marketeye&lt;/a&gt;&lt;/p&gt;




</description>
      <category>opensource</category>
      <category>python</category>
      <category>selfhosted</category>
      <category>showdev</category>
    </item>
    <item>
      <title>I Built a Self-Hosted Competitor Price Monitor Because SaaS Pricing Is Out of Control</title>
      <dc:creator>大橙子</dc:creator>
      <pubDate>Tue, 16 Jun 2026 01:29:40 +0000</pubDate>
      <link>https://dev.to/orange-big/i-built-a-self-hosted-competitor-price-monitor-because-saas-pricing-is-out-of-control-5742</link>
      <guid>https://dev.to/orange-big/i-built-a-self-hosted-competitor-price-monitor-because-saas-pricing-is-out-of-control-5742</guid>
      <description>&lt;h1&gt;
  
  
  I Built a Self-Hosted Competitor Price Monitor Because SaaS Pricing Is Out of Control
&lt;/h1&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;I run a small online store. To stay competitive, I needed to track my competitors' pricing. The obvious solution? Sign up for a SaaS price monitoring tool.&lt;/p&gt;

&lt;p&gt;Then I saw the prices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prisync&lt;/strong&gt;: $99/month&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Price2Spy&lt;/strong&gt;: $20-58/month&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keepa&lt;/strong&gt;: $29/month (Amazon only)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a small operation, $99/month is absurd. So I built my own.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built: MarketEye
&lt;/h2&gt;

&lt;p&gt;MarketEye is a &lt;strong&gt;self-hosted&lt;/strong&gt; competitor price monitor. You run it on your own machine, point it at any product URL, and it tracks price changes automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tech stack:&lt;/strong&gt; Python + FastAPI + SQLite + APScheduler&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Features:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Drop any product URL → auto price tracking&lt;/li&gt;
&lt;li&gt;Email + webhook alerts on price changes&lt;/li&gt;
&lt;li&gt;Price history charts + CSV/JSON export&lt;/li&gt;
&lt;li&gt;Works with Amazon, Shopify, JD, Taobao, any HTML page&lt;/li&gt;
&lt;li&gt;Web dashboard included (no CLI needed)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Add a product URL through the web dashboard&lt;/li&gt;
&lt;li&gt;MarketEye scrapes the page at configurable intervals&lt;/li&gt;
&lt;li&gt;When a price changes, you get an alert&lt;/li&gt;
&lt;li&gt;All data stays on your machine - no cloud, no third party&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The scraping engine handles various e-commerce platforms out of the box and provides a plugin interface for custom scrapers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Self-Hosted?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One-time payment&lt;/strong&gt;: $49 vs $99/month ($3,564 over 3 years)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unlimited products&lt;/strong&gt;: no per-product fees&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data privacy&lt;/strong&gt;: your pricing data never leaves your machine&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customizable&lt;/strong&gt;: full Python source, extend it however you want&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;I'll be sharing more technical deep-dives on the architecture, scraping challenges, and lessons learned from building and shipping this as a solo developer.&lt;/p&gt;

&lt;p&gt;Check it out: &lt;a href="https://gumroad.com/l/kvnkhb" rel="noopener noreferrer"&gt;MarketEye on Gumroad&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Questions and feedback welcome! 🚀&lt;/p&gt;

</description>
      <category>python</category>
      <category>selfhosted</category>
      <category>fastapi</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Hello Dev.to 👋 — A Developer Joining the Community</title>
      <dc:creator>大橙子</dc:creator>
      <pubDate>Tue, 16 Jun 2026 01:22:25 +0000</pubDate>
      <link>https://dev.to/orange-big/hello-devto-a-developer-joining-the-community-1k3n</link>
      <guid>https://dev.to/orange-big/hello-devto-a-developer-joining-the-community-1k3n</guid>
      <description>&lt;p&gt;Hey everyone!&lt;/p&gt;

&lt;p&gt;Just created my Dev.to account and wanted to say hi properly.&lt;/p&gt;

&lt;p&gt;I'm a software developer, working with Python and &lt;br&gt;
building side projects that solve real problems.&lt;/p&gt;

&lt;p&gt;My latest project is MarketEye — a self-hosted competitor price &lt;br&gt;
monitor I built because I refused to pay $99/month for basic &lt;br&gt;
price tracking. Python + FastAPI + SQLite, scrapes product &lt;br&gt;
pages, sends alerts on price changes, generates history charts.&lt;/p&gt;

&lt;p&gt;What I want to share here:&lt;br&gt;
• Building and shipping a self-hosted product from idea to launch&lt;br&gt;
• Web scraping techniques that actually work (and the pitfalls)&lt;br&gt;
• Pricing and marketing indie software as a dev selling globally&lt;br&gt;
• Technical deep-dives on Python, FastAPI, and deployment&lt;br&gt;
• Getting your first customer without spending on ads&lt;/p&gt;

&lt;p&gt;Looking for honest feedback and connecting with other indie &lt;br&gt;
builders. Happy to help with anything Python/scraping related.&lt;/p&gt;

&lt;p&gt;Thanks for having me! 🚀&lt;/p&gt;

</description>
      <category>python</category>
      <category>showdev</category>
      <category>sideprojects</category>
      <category>webscraping</category>
    </item>
  </channel>
</rss>
