<?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: Ihor Bezruchko</title>
    <description>The latest articles on DEV Community by Ihor Bezruchko (@ihorbbt).</description>
    <link>https://dev.to/ihorbbt</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%2F3978194%2F1f54f8fd-05ea-42cc-ac5c-4709b499dbd3.png</url>
      <title>DEV Community: Ihor Bezruchko</title>
      <link>https://dev.to/ihorbbt</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ihorbbt"/>
    <language>en</language>
    <item>
      <title>I Built a Freelance Job Hunting Automation on n8n — Here's Everything I Learned</title>
      <dc:creator>Ihor Bezruchko</dc:creator>
      <pubDate>Wed, 10 Jun 2026 18:21:33 +0000</pubDate>
      <link>https://dev.to/ihorbbt/i-built-a-freelance-job-hunting-automation-on-n8n-heres-everything-i-learned-4mi</link>
      <guid>https://dev.to/ihorbbt/i-built-a-freelance-job-hunting-automation-on-n8n-heres-everything-i-learned-4mi</guid>
      <description>&lt;p&gt;I'm a 17-year-old IT student from Luxembourg. A few months ago I got tired of spending 2-3 hours a day manually browsing Upwork, Malt, and Freelancer looking for projects.&lt;/p&gt;

&lt;p&gt;So I built an automation system that does it for me — 24/7, on a Raspberry Pi 3.&lt;/p&gt;

&lt;p&gt;Here's what it does, how I built it, and every painful lesson I learned along the way.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the system does
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Scans Upwork, Malt, and Freelancer every 30 minutes&lt;/li&gt;
&lt;li&gt;Scores each job 0–100 with AI based on my profile&lt;/li&gt;
&lt;li&gt;Generates proposals in English, French, and German&lt;/li&gt;
&lt;li&gt;Sends the best jobs to Telegram with inline A/B buttons&lt;/li&gt;
&lt;li&gt;Tracks which proposal style gets more replies&lt;/li&gt;
&lt;li&gt;Sends daily stats and weekly market trend reports&lt;/li&gt;
&lt;li&gt;Reminds me to follow up after 3 days&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;n8n&lt;/strong&gt; — self-hosted workflow automation (Docker on Raspberry Pi 3)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Groq API&lt;/strong&gt; (Llama 3.1-8b-instant) — AI scoring and proposal generation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Supabase&lt;/strong&gt; — PostgreSQL database for jobs, proposals, clients&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SerpAPI&lt;/strong&gt; — searching job boards via Google&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Apify&lt;/strong&gt; — scraping Upwork listings&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Telegram Bot API&lt;/strong&gt; — alerts and bot commands&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloudflare Tunnel&lt;/strong&gt; — HTTPS for webhooks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Total running cost: ~$5/month.&lt;/p&gt;




&lt;h2&gt;
  
  
  7 workflows
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;01 - Job Discovery&lt;/strong&gt; — runs every 30 minutes, searches 10+ sources, deduplicates via Supabase unique constraint on URL&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;02 - Proposal Generator&lt;/strong&gt; — AI scores the job, generates two proposal variants (formal vs hook-first), sends to Telegram with A/B buttons&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;03 - Follow-up Reminders&lt;/strong&gt; — checks Supabase every 3 days for unanswered proposals&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;04 - CRM via Telegram&lt;/strong&gt; — full client management through bot commands (/jobs, /stats, /clients)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;05 - Market Intelligence&lt;/strong&gt; — daily report: how many jobs found, average score, top platforms&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;06 - Trend Analysis&lt;/strong&gt; — weekly report on what skills are trending in automation&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;07 - Lead Generation&lt;/strong&gt; — finds companies actively using Zapier or Make who might want to switch to n8n&lt;/p&gt;




&lt;h2&gt;
  
  
  Lessons learned (the hard way)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Cyrillic text breaks JSON body nodes silently&lt;/strong&gt;&lt;br&gt;
If you have Cyrillic characters in a JSON body field with newlines, n8n throws a "Bad control character" error. Keep everything on a single line or use &lt;code&gt;\n&lt;/code&gt; instead of real line breaks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Multiple inputs into a Code node need a Merge node first&lt;/strong&gt;&lt;br&gt;
When two HTTP Request nodes feed into one Code node, use a Merge node (Combine by Position) in between. Otherwise &lt;code&gt;$('NodeName')&lt;/code&gt; throws "hasn't been executed" errors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Cloudflare tunnel URL changes on every restart&lt;/strong&gt;&lt;br&gt;
This breaks all Telegram webhooks. I solved it with a systemd service that restarts cloudflared and updates the &lt;code&gt;WEBHOOK_URL&lt;/code&gt; environment variable in docker-compose automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Deduplication belongs in the database, not in n8n&lt;/strong&gt;&lt;br&gt;
Adding a UNIQUE constraint on &lt;code&gt;jobs.url&lt;/code&gt; in Supabase and setting the insert node to "Continue on Fail" is cleaner than trying to check duplicates inside the workflow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Code nodes must always return items&lt;/strong&gt;&lt;br&gt;
If a Code node returns nothing (empty array or undefined), the workflow silently passes dummy data downstream. Always return &lt;code&gt;[]&lt;/code&gt; or a proper item.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. LLMs ignore "return only JSON" instructions&lt;/strong&gt;&lt;br&gt;
Groq kept wrapping JSON in &lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
json markdown blocks. Fix: strip the backticks before parsing, or use separator-based output instead of JSON.

---

## A/B testing proposals

This was the most interesting part to build. Instead of one proposal, the system now generates two variants with different tones:

- **Variant A** — formal, structured, under 100 words
- **Variant B** — hook-first, starts with the client's problem, under 70 words

Telegram sends both with buttons. When I tap which one I sent, the result gets stored in Supabase. Over time I'll have data on which style works better for which type of job.

---

## GitHub

All 7 workflow files (sanitized — API keys replaced with placeholders) are on GitHub:

👉 https://github.com/ihorbezruchko/n8n-freelance-autopilot

If you want the full working setup with the complete configuration guide:

👉 https://n8n-freelance-autopilot.netlify.app

---

Happy to answer questions about any part of the implementation. What would you build differently?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>javascript</category>
      <category>career</category>
    </item>
  </channel>
</rss>
