<?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: Kirill Strelnikov</title>
    <description>The latest articles on DEV Community by Kirill Strelnikov (@kirill_strelnikov_d8546b8).</description>
    <link>https://dev.to/kirill_strelnikov_d8546b8</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%2F3779127%2F58badd76-5b93-47f9-b63d-3e7357b751de.png</url>
      <title>DEV Community: Kirill Strelnikov</title>
      <link>https://dev.to/kirill_strelnikov_d8546b8</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kirill_strelnikov_d8546b8"/>
    <language>en</language>
    <item>
      <title>5 CRM Automations That Save 10+ Hours Per Week</title>
      <dc:creator>Kirill Strelnikov</dc:creator>
      <pubDate>Fri, 27 Mar 2026 20:11:14 +0000</pubDate>
      <link>https://dev.to/kirill_strelnikov_d8546b8/5-crm-automations-that-save-10-hours-per-week-31no</link>
      <guid>https://dev.to/kirill_strelnikov_d8546b8/5-crm-automations-that-save-10-hours-per-week-31no</guid>
      <description>&lt;p&gt;5 CRM automations that save your team 10+ hours per week. Lead routing, follow-up sequences, data entry, reporting, and churn alerts. Real examples.&lt;br&gt;
CRMAutomationProductivityBusiness&lt;br&gt;
Kirill Strelnikov&lt;br&gt;
— AI Systems Architect, Barcelona&lt;/p&gt;

&lt;p&gt;Most CRM users barely scratch the surface of what their system can do. I have seen sales teams spend 2-3 hours per day on manual CRM tasks that should be automated. As someone who builds CRM integrations and business automation solutions, here are the five automations that consistently save the most time.&lt;br&gt;
Automation 1: Intelligent Lead Routing (saves 3+ hours/week)&lt;br&gt;
The manual process:&lt;/p&gt;

&lt;p&gt;A manager reviews each new lead, checks the territory/industry/deal size, and manually assigns it to the right sales rep. This happens 20-50 times per day in a busy B2B company. It creates bottlenecks and delays first response time.&lt;br&gt;
The automated process:&lt;/p&gt;

&lt;p&gt;New leads are automatically scored and routed based on rules you define:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Geography: European leads go to EU sales team, US leads to US team
Deal size: Enterprise leads (&amp;gt;EUR 50K) go to senior reps
Industry: SaaS leads go to the rep who specializes in SaaS
Lead score: Hot leads (score &amp;gt;80) get immediate attention from top performers
Round-robin: Equal distribution when rules do not apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Result: leads get assigned in seconds instead of hours. First response time drops from 4 hours to under 15 minutes.&lt;br&gt;
Automation 2: Follow-Up Sequences (saves 2+ hours/week)&lt;br&gt;
The manual process:&lt;/p&gt;

&lt;p&gt;Sales reps manually remember to follow up with prospects. They write individual emails, forget some leads, and have inconsistent messaging. Studies show 80% of deals require 5+ follow-ups, but most reps give up after 2.&lt;br&gt;
The automated process:&lt;/p&gt;

&lt;p&gt;Automated email sequences triggered by specific events:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;New lead created -&amp;gt; Welcome email + value proposition (Day 0)
No response -&amp;gt; Follow-up with case study (Day 3)
Still no response -&amp;gt; Different angle, social proof (Day 7)
Opens but no reply -&amp;gt; Direct ask for meeting (Day 10)
Meeting completed -&amp;gt; Proposal follow-up sequence (Day 1 after meeting)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The sequence stops when the prospect replies or books a meeting. Personalization variables pull data from the CRM: name, company, industry, pain point mentioned on the website.&lt;br&gt;
Automation 3: Data Entry and Enrichment (saves 2+ hours/week)&lt;br&gt;
The manual process:&lt;/p&gt;

&lt;p&gt;Reps manually enter data from emails, forms, and calls into the CRM. They Google companies to find missing information like employee count, industry, and revenue. Data quality suffers because reps skip fields.&lt;br&gt;
The automated process:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Email parsing: New emails from contacts automatically logged to the CRM timeline
Form data: Website forms create or update CRM records instantly
Company enrichment: When a new company is added, automatically pull data from clearbit, LinkedIn, or company registries
Call logging: Phone system integration that logs call duration, outcome, and notes
Duplicate detection: Automatically merge duplicate contacts based on email matching
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Automation 4: Weekly Pipeline Reports (saves 1.5+ hours/week)&lt;br&gt;
The manual process:&lt;/p&gt;

&lt;p&gt;A manager spends Monday morning pulling data from the CRM, creating a spreadsheet, calculating pipeline metrics, and sending a report to the team and leadership. By the time it is done, the data is already outdated.&lt;br&gt;
The automated process:&lt;/p&gt;

&lt;p&gt;Every Monday at 9:00 AM, an automated report is generated and sent via email or Slack:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Pipeline value by stage with week-over-week changes
Deals stuck in stage for more than X days (at-risk deals)
Individual rep performance (new leads, meetings, closed deals)
Win/loss rate by source, industry, and deal size
Forecast vs actual for the current quarter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;No manual work required. Leadership gets real-time data every week.&lt;br&gt;
Automation 5: Churn Risk Alerts (saves 1.5+ hours/week)&lt;br&gt;
The manual process:&lt;/p&gt;

&lt;p&gt;Customer success managers check each account periodically, looking for signs of churn: support tickets, login frequency, feature usage. With 50+ accounts each, they miss warning signs until it is too late.&lt;br&gt;
The automated process:&lt;/p&gt;

&lt;p&gt;A churn scoring system monitors multiple signals and alerts the account manager when risk increases:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Support tickets: More than 3 tickets in 30 days -&amp;gt; risk score +20
Login frequency: 50% drop in weekly logins -&amp;gt; risk score +25
Feature usage: Key features not used in 14 days -&amp;gt; risk score +15
Payment: Failed payment or downgrade request -&amp;gt; risk score +30
NPS score: Detractor (0-6) on last survey -&amp;gt; risk score +20
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;When the risk score exceeds a threshold, the account manager gets a Slack alert with specific details: "Acme Corp risk score 65/100 -- 5 support tickets this month, 40% login drop, key feature unused for 12 days." This enables proactive outreach before the customer churns.&lt;br&gt;
Implementation Cost and Timeline&lt;/p&gt;

&lt;p&gt;Each automation typically takes 1-3 days to implement:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Lead routing: EUR 500-1,500 (1-2 days)
Follow-up sequences: EUR 500-1,000 (1-2 days)
Data enrichment: EUR 1,000-2,000 (2-3 days)
Automated reports: EUR 500-1,500 (1-2 days)
Churn alerts: EUR 1,000-2,500 (2-3 days)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Most businesses start with lead routing and follow-up sequences (biggest time savings), then add the others incrementally.&lt;/p&gt;

&lt;p&gt;I build these CRM automations for European businesses using HubSpot, Salesforce, and Pipedrive. Book a free automation audit and I will identify the highest-ROI automations for your team.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Django REST API Patterns for Scalable Apps</title>
      <dc:creator>Kirill Strelnikov</dc:creator>
      <pubDate>Mon, 23 Mar 2026 15:31:34 +0000</pubDate>
      <link>https://dev.to/kirill_strelnikov_d8546b8/django-rest-api-patterns-for-scalable-apps-2bcm</link>
      <guid>https://dev.to/kirill_strelnikov_d8546b8/django-rest-api-patterns-for-scalable-apps-2bcm</guid>
      <description>&lt;p&gt;Explore Django REST API design patterns for building scalable applications. Learn practical tips and code examples.&lt;br&gt;
DjangoREST APIScalable ApplicationsDesign PatternsDevelopment&lt;br&gt;
Kirill Strelnikov&lt;br&gt;
— AI Systems Architect, Barcelona&lt;br&gt;
Introduction&lt;/p&gt;

&lt;p&gt;Designing a REST API with Django that can handle growth efficiently is crucial for any application. Whether you are building a simple service or a complex system, following best practices in API design can help ensure scalability and performance. This guide will cover essential design patterns and practical tips to create robust and scalable Django REST APIs.&lt;br&gt;
Understanding REST API Design Principles&lt;/p&gt;

&lt;p&gt;Before diving into specific patterns, it's essential to understand the guiding principles of REST architecture. REST, or Representational State Transfer, relies on stateless communication and uses standard HTTP methods such as GET, POST, PUT, and DELETE. The key principles include:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Statelessness: Each request from client to server must contain all the information needed to understand and process the request.
Client-Server Architecture: Separation of concerns is achieved by having the client and server handle different aspects of the application.
Cacheability: Responses must define themselves as cacheable or not to prevent clients from reusing stale or inappropriate data.
Layered System: A client cannot ordinarily tell whether it is connected directly to the end server or an intermediary.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Essential Django REST API Design Patterns&lt;/p&gt;

&lt;p&gt;Let's explore some design patterns that can help make your Django REST API more scalable:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use of Generic Views&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Django REST Framework (DRF) provides powerful tools for creating RESTful APIs, including generic views that reduce boilerplate code. Generic views like ListCreateAPIView and RetrieveUpdateDestroyAPIView can be used to handle common database operations.&lt;/p&gt;

&lt;p&gt;from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView&lt;br&gt;
from .models import MyModel&lt;br&gt;
from .serializers import MyModelSerializer&lt;/p&gt;

&lt;p&gt;class MyModelListCreateView(ListCreateAPIView):&lt;br&gt;
    queryset = MyModel.objects.all()&lt;br&gt;
    serializer_class = MyModelSerializer&lt;/p&gt;

&lt;p&gt;class MyModelRetrieveUpdateDestroyView(RetrieveUpdateDestroyAPIView):&lt;br&gt;
    queryset = MyModel.objects.all()&lt;br&gt;
    serializer_class = MyModelSerializer&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Efficient QuerySet Usage&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Understanding and optimizing QuerySets can significantly improve performance. Use select_related and prefetch_related to reduce the number of queries:&lt;/p&gt;

&lt;p&gt;queryset = MyModel.objects.select_related('related_model').all()&lt;/p&gt;

&lt;p&gt;This approach helps decrease database load, a critical factor in scaling applications.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pagination and Filtering&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Implementing pagination and filtering not only improves user experience but also reduces server load. DRF supports pagination out of the box. Here’s how you can set it up:&lt;/p&gt;

&lt;p&gt;from rest_framework.pagination import PageNumberPagination&lt;/p&gt;

&lt;p&gt;class StandardResultsSetPagination(PageNumberPagination):&lt;br&gt;
    page_size = 10&lt;br&gt;
    page_size_query_param = 'page_size'&lt;br&gt;
    max_page_size = 100&lt;/p&gt;

&lt;p&gt;Enable pagination in your views:&lt;/p&gt;

&lt;p&gt;class MyModelListCreateView(ListCreateAPIView):&lt;br&gt;
    queryset = MyModel.objects.all()&lt;br&gt;
    serializer_class = MyModelSerializer&lt;br&gt;
    pagination_class = StandardResultsSetPagination&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Caching Strategies&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Caching can dramatically decrease the time taken to serve API responses. Django offers several caching backends like in-memory, file-based, and database caching. Here is an example of setting up a simple cache:&lt;/p&gt;

&lt;p&gt;from django.views.decorators.cache import cache_page&lt;/p&gt;

&lt;p&gt;@cache_page(60 * 15)&lt;br&gt;
def my_view(request):&lt;br&gt;
    # view code here...&lt;/p&gt;

&lt;p&gt;Implementing caching strategies can be a game-changer, especially for read-heavy applications.&lt;br&gt;
Advanced Concepts for Scalability&lt;/p&gt;

&lt;p&gt;Beyond basic patterns, there are advanced concepts to consider for highly scalable Django REST APIs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Asynchronous Task Queues&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For operations that require heavy lifting, such as sending emails or processing images, use asynchronous task queues like Celery. This offloads tasks to background workers, freeing up resources for immediate requests.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Rate Limiting&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To protect your API from abuse and ensure fair usage, implement rate limiting. DRF integrates well with Django’s built-in throttling classes.&lt;/p&gt;

&lt;p&gt;from rest_framework.throttling import UserRateThrottle&lt;/p&gt;

&lt;p&gt;class BurstRateThrottle(UserRateThrottle):&lt;br&gt;
    rate = '5/minute'&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Microservices Architecture&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Consider breaking down your application into microservices if it's growing rapidly. This allows you to scale components independently, improving resiliency and efficiency.&lt;/p&gt;

&lt;p&gt;For instance, integrating an AI chatbot development service into your architecture can significantly enhance interactivity and responsiveness, leveraging the benefits of microservices.&lt;br&gt;
Conclusion&lt;/p&gt;

&lt;p&gt;By applying these design patterns and principles, you can build Django REST APIs that are not only scalable but also maintainable and efficient. As your application grows, revisiting and refining these patterns can help ensure consistent performance and reliability.&lt;/p&gt;

&lt;p&gt;If you need expert guidance, consider reaching out for my development services or hire a Django developer in Barcelona to assist you with advanced solutions.&lt;/p&gt;

&lt;p&gt;Ready to scale your Django application with expert solutions?&lt;br&gt;
Get in touch →&lt;/p&gt;

</description>
      <category>api</category>
      <category>architecture</category>
      <category>django</category>
      <category>python</category>
    </item>
    <item>
      <title>Build a Production RAG Chatbot with Django + pgvector + OpenAI (Full Guide)</title>
      <dc:creator>Kirill Strelnikov</dc:creator>
      <pubDate>Mon, 09 Mar 2026 01:20:00 +0000</pubDate>
      <link>https://dev.to/kirill_strelnikov_d8546b8/build-a-production-rag-chatbot-with-django-pgvector-openai-full-guide-560n</link>
      <guid>https://dev.to/kirill_strelnikov_d8546b8/build-a-production-rag-chatbot-with-django-pgvector-openai-full-guide-560n</guid>
      <description>&lt;p&gt;I'm Kirill Strelnikov, a freelance AI/Django developer in Barcelona. I've built RAG chatbots that automated 70% of customer support for e-commerce clients. This is a practical guide to building a production-ready RAG chatbot with Django, pgvector, and OpenAI — not a toy demo, but the actual architecture I use in client projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is RAG and Why It Matters
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;RAG (Retrieval-Augmented Generation)&lt;/strong&gt; = vector search + LLM. Instead of hoping the LLM "knows" your business data, you:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Store your documents as vector embeddings&lt;/li&gt;
&lt;li&gt;When a user asks a question, find the most relevant documents&lt;/li&gt;
&lt;li&gt;Feed those documents to the LLM as context&lt;/li&gt;
&lt;li&gt;The LLM generates an answer based on YOUR data&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Why RAG beats fine-tuning for business chatbots:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No retraining when data changes (just re-embed)&lt;/li&gt;
&lt;li&gt;Works with any LLM (swap GPT-4 for Claude without rebuilding)&lt;/li&gt;
&lt;li&gt;Answers are grounded in real documents (reduces hallucination)&lt;/li&gt;
&lt;li&gt;You can show sources ("Based on: Return Policy, Section 3")&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Set Up pgvector in Django
&lt;/h2&gt;

&lt;p&gt;pgvector is a PostgreSQL extension for vector similarity search. No separate vector database needed — your embeddings live alongside your regular data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install pgvector on PostgreSQL&lt;/span&gt;
&lt;span class="c"&gt;# Ubuntu/Debian:&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;postgresql-16-pgvector

&lt;span class="c"&gt;# Or via Docker:&lt;/span&gt;
&lt;span class="c"&gt;# Use image: pgvector/pgvector:pg16&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;pgvector django-pgvector
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# models.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pgvector.django&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;VectorField&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;255&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="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TextField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# "faq", "product", "policy"
&lt;/span&gt;    &lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;VectorField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dimensions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1536&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="c1"&gt;# text-embedding-3-small
&lt;/span&gt;    &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DateTimeField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auto_now_add&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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;indexes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&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;source&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;def&lt;/span&gt; &lt;span class="nf"&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="k"&gt;return&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;title&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# migration: enable pgvector extension
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;migrations&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;dependencies&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;chatbot&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;0001_initial&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;operations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;RunSQL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CREATE EXTENSION IF NOT EXISTS vector;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;reverse_sql&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DROP EXTENSION IF EXISTS vector;&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Embed Your Documents
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# embeddings.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&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;get_embedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&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;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Get embedding vector for a text string.&lt;/span&gt;&lt;span class="sh"&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;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;embeddings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&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;text-embedding-3-small&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;text&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;chunk_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&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;chunk_size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;overlap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;50&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;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="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Split text into overlapping chunks by token count (approximate).&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;words&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&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;words&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;chunk_size&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;overlap&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;words&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;chunk_size&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="n"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk&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;chunks&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;embed_document&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&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;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="n"&gt;source&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="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Chunk a document and store each chunk with its embedding.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;chunk_text&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="n"&gt;documents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_embedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nc"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; (part &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)&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="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bulk_create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&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;documents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Chunking matters.&lt;/strong&gt; I use 300-token chunks with 50-token overlap. Too small = lost context. Too large = diluted relevance. This size works well for FAQ and product data. For longer documents (legal, technical docs), I increase to 500 tokens.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Vector Search
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# search.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pgvector.django&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;CosineDistance&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;search_documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&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;top_k&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Find the most relevant documents for a query.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;query_embedding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_embedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;qs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;annotate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;distance&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;CosineDistance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;embedding&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query_embedding&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;qs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;qs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;source&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;qs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;order_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;distance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[:&lt;/span&gt;&lt;span class="n"&gt;top_k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;pgvector's cosine distance search is fast enough for most business chatbots (sub-100ms for 100K documents). For larger datasets, add an IVFFlat or HNSW index:&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="c1"&gt;# migration for HNSW index (faster search for large datasets)
&lt;/span&gt;&lt;span class="n"&gt;migrations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;RunSQL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CREATE INDEX ON chatbot_document USING hnsw (embedding vector_cosine_ops);&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4: Generate Answers
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# chatbot.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;SYSTEM_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;You are a helpful customer support assistant.
Answer questions using ONLY the provided context.
If the context doesn&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;t contain the answer, say &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;I don&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;t have information about that. Let me connect you with a human agent.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;
Always be concise and helpful. Cite the source document when relevant.&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;get_chatbot_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_message&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;conversation_history&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Generate a chatbot response using RAG.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="c1"&gt;# 1. Retrieve relevant documents
&lt;/span&gt;    &lt;span class="n"&gt;relevant_docs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;search_documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;top_k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 2. Build context string
&lt;/span&gt;    &lt;span class="n"&gt;context_parts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;sources&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;relevant_docs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;context_parts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;]: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;context&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context_parts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 3. Build messages
&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="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;system&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;SYSTEM_PROMPT&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;Context:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="si"&gt;}&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="c1"&gt;# Add conversation history for multi-turn
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;conversation_history&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conversation_history&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;:])&lt;/span&gt;  &lt;span class="c1"&gt;# Last 3 exchanges
&lt;/span&gt;
    &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&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;user_message&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="c1"&gt;# 4. Generate response
&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;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&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;gpt-4&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="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# Low temperature = more factual
&lt;/span&gt;        &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;answer&lt;/span&gt; &lt;span class="o"&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;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;

    &lt;span class="c1"&gt;# 5. Confidence check
&lt;/span&gt;    &lt;span class="n"&gt;min_distance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;relevant_docs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;distance&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;relevant_docs&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;
    &lt;span class="n"&gt;needs_escalation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;min_distance&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.4&lt;/span&gt;  &lt;span class="c1"&gt;# Threshold tuned per project
&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;answer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sources&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;needs_escalation&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;needs_escalation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;confidence&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;min_distance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 5: Django REST API
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# views.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;rest_framework.decorators&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;api_view&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;rest_framework.response&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;

&lt;span class="nd"&gt;@api_view&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&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;chat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&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="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;session_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;session_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&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;Message required&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Get conversation history from session
&lt;/span&gt;    &lt;span class="n"&gt;history&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_session_history&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Generate response
&lt;/span&gt;    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_chatbot_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Save to history
&lt;/span&gt;    &lt;span class="nf"&gt;save_to_history&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;answer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="c1"&gt;# If low confidence, notify human agent
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;needs_escalation&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="nf"&gt;notify_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;answer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;answer&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;sources&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sources&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;confidence&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;confidence&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 6: Keep Embeddings Fresh
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# tasks.py (Celery)
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;celery&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;shared_task&lt;/span&gt;

&lt;span class="nd"&gt;@shared_task&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;refresh_product_embeddings&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Re-embed products that changed since last sync.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;shop.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Product&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;updated_at__gte&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;last_sync_time&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. Price: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; EUR.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="c1"&gt;# Delete old embeddings
&lt;/span&gt;        &lt;span class="n"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;product&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;title__startswith&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
        &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="c1"&gt;# Create new ones
&lt;/span&gt;        &lt;span class="nf"&gt;embed_document&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&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="n"&gt;source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;product&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Schedule this with Celery Beat to run hourly or on product updates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Production Results
&lt;/h2&gt;

&lt;p&gt;From my e-commerce client project:&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;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Documents embedded&lt;/td&gt;
&lt;td&gt;~2,000 chunks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Avg search latency&lt;/td&gt;
&lt;td&gt;45ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Answer accuracy&lt;/td&gt;
&lt;td&gt;~92% (human-evaluated)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Support automation rate&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;70%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conversion rate increase&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;+35%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monthly API cost&lt;/td&gt;
&lt;td&gt;EUR 50-80&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The 70% automation rate means 7 out of 10 customer questions are answered correctly without human intervention. The remaining 30% get escalated with full context, so the human agent can resolve them faster too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Pitfalls
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Embedding stale data.&lt;/strong&gt; If your product catalog changes, your chatbot answers are wrong. Automate re-embedding.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No confidence threshold.&lt;/strong&gt; Without escalation logic, the chatbot will confidently hallucinate. Always add a distance threshold.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ignoring conversation context.&lt;/strong&gt; A user asking "what about the blue one?" after asking about a dress needs multi-turn context. Pass conversation history.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Using a separate vector DB for small datasets.&lt;/strong&gt; pgvector handles 100K+ documents easily. You don't need Pinecone or Weaviate until you hit millions.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Cost to Build
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scope&lt;/th&gt;
&lt;th&gt;Timeline&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Basic RAG chatbot (FAQ only)&lt;/td&gt;
&lt;td&gt;1-2 weeks&lt;/td&gt;
&lt;td&gt;EUR 800-1,500&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RAG + product catalog + CRM&lt;/td&gt;
&lt;td&gt;2-4 weeks&lt;/td&gt;
&lt;td&gt;EUR 1,500-3,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-channel (web + Telegram + WhatsApp)&lt;/td&gt;
&lt;td&gt;4-6 weeks&lt;/td&gt;
&lt;td&gt;EUR 3,000-5,000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Detailed pricing: &lt;a href="https://kirweb.site/cost-guides/ai-chatbot-development-cost/" rel="noopener noreferrer"&gt;AI Chatbot Development Cost&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;I'm Kirill Strelnikov — I build production RAG chatbots, SaaS platforms, and Telegram bots as a freelance developer in Barcelona, Spain. 15+ projects delivered, EU-based, GDPR-compliant.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Website: &lt;a href="https://kirweb.site" rel="noopener noreferrer"&gt;kirweb.site&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Telegram: &lt;a href="https://t.me/KirBcn" rel="noopener noreferrer"&gt;@KirBcn&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;More AI case studies: &lt;a href="https://kirweb.site/ai-chatbots/" rel="noopener noreferrer"&gt;kirweb.site/ai-chatbots&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>django</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>The Django SaaS MVP Stack I Use in 2026 (Ships in 4-6 Weeks)</title>
      <dc:creator>Kirill Strelnikov</dc:creator>
      <pubDate>Mon, 09 Mar 2026 01:19:35 +0000</pubDate>
      <link>https://dev.to/kirill_strelnikov_d8546b8/the-django-saas-mvp-stack-i-use-in-2026-ships-in-4-6-weeks-bnc</link>
      <guid>https://dev.to/kirill_strelnikov_d8546b8/the-django-saas-mvp-stack-i-use-in-2026-ships-in-4-6-weeks-bnc</guid>
      <description>&lt;p&gt;I'm Kirill Strelnikov, a freelance Python/Django developer in Barcelona. I've built 5+ SaaS platforms with Django — from time-tracking systems for restaurant chains to multi-tenant platforms with Stripe billing. Here's the exact stack and architecture I use in 2026 to ship SaaS MVPs in 4-6 weeks.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Stack
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Django 5.x          → Web framework (admin, auth, ORM, REST)
PostgreSQL 16       → Primary database (with schema-per-tenant)
Redis               → Cache, sessions, Celery broker
Celery              → Background tasks, scheduled jobs
Django REST Framework → API layer
Stripe              → Subscription billing
Docker + Nginx      → Deployment
Gunicorn            → WSGI server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This isn't theoretical. Every component here has been battle-tested across real projects with paying customers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Django for SaaS (Still, in 2026)
&lt;/h2&gt;

&lt;p&gt;I get asked "why not FastAPI?" or "why not Node.js?" constantly. Here's my honest take after building with all three:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Django gives you 60% of a SaaS for free:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authentication + permissions (including social auth via django-allauth)&lt;/li&gt;
&lt;li&gt;Admin panel (your first "back office" dashboard — free)&lt;/li&gt;
&lt;li&gt;ORM with migrations (schema changes without SQL)&lt;/li&gt;
&lt;li&gt;Form validation and CSRF protection&lt;/li&gt;
&lt;li&gt;Session management&lt;/li&gt;
&lt;li&gt;Built-in security middleware&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;With FastAPI, you build all of that yourself.&lt;/strong&gt; FastAPI is great for microservices and high-throughput APIs. But for a SaaS MVP where you need auth, admin, billing, and a dashboard? Django saves 2-3 weeks of development.&lt;/p&gt;

&lt;p&gt;I wrote a detailed comparison: &lt;a href="https://kirweb.site/comparisons/django-vs-fastapi-for-saas/" rel="noopener noreferrer"&gt;Django vs FastAPI for SaaS&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Multi-Tenant Architecture
&lt;/h2&gt;

&lt;p&gt;For SaaS, you need tenant isolation. I use &lt;strong&gt;schema-per-tenant&lt;/strong&gt; with &lt;code&gt;django-tenants&lt;/code&gt;:&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="c1"&gt;# settings.py
&lt;/span&gt;&lt;span class="n"&gt;DATABASES&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;default&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ENGINE&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;django_tenants.postgresql_backend&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;NAME&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;saas_db&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="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;TENANT_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;customers.Client&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;TENANT_DOMAIN_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;customers.Domain&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;SHARED_APPS&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;django_tenants&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;customers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;       &lt;span class="c1"&gt;# Tenant management
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;django.contrib.admin&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;django.contrib.auth&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="n"&gt;TENANT_APPS&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;core&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;            &lt;span class="c1"&gt;# Your SaaS app
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;billing&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;analytics&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each tenant gets their own PostgreSQL schema. Data isolation is guaranteed at the database level — no &lt;code&gt;WHERE tenant_id = X&lt;/code&gt; scattered through your code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real example:&lt;/strong&gt; I built a time-tracking SaaS for a chain of 5 cafes in Barcelona. Each cafe is a separate tenant with its own employees, shifts, and payroll data. Result: &lt;strong&gt;80% reduction in payroll processing time&lt;/strong&gt; across all locations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stripe Billing Pattern
&lt;/h2&gt;

&lt;p&gt;Every SaaS needs billing. Here's my standard Stripe integration:&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="c1"&gt;# billing/models.py
&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Subscription&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;tenant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;OneToOneField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;on_delete&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CASCADE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;stripe_customer_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;stripe_subscription_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blank&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;plan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;choices&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;free&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;Free&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pro&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;Pro&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;business&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;Business&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="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;active&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# billing/webhooks.py
&lt;/span&gt;&lt;span class="nd"&gt;@csrf_exempt&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;stripe_webhook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;
    &lt;span class="n"&gt;sig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;META&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;HTTP_STRIPE_SIGNATURE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Webhook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;construct_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WEBHOOK_SECRET&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;invoice.paid&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;handle_successful_payment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;customer.subscription.deleted&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;handle_cancellation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;HttpResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I always use webhooks instead of polling. Stripe sends events in real-time — your app reacts immediately.&lt;/p&gt;

&lt;h2&gt;
  
  
  Background Tasks with Celery
&lt;/h2&gt;

&lt;p&gt;SaaS apps need background processing: sending emails, generating reports, syncing data. Celery handles all of this:&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="c1"&gt;# tasks.py
&lt;/span&gt;&lt;span class="nd"&gt;@shared_task&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_monthly_report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tenant_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;tenant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&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="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tenant_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;tenant_context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tenant&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;aggregate_monthly_metrics&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;pdf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;render_report_pdf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;send_report_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tenant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Scheduled task (Celery Beat)
&lt;/span&gt;&lt;span class="n"&gt;CELERY_BEAT_SCHEDULE&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;monthly-reports&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;task&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;tasks.generate_monthly_report_all&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;schedule&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;crontab&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;day_of_month&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hour&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deployment: Docker + Nginx + Gunicorn
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# docker-compose.yml&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gunicorn config.wsgi:application --bind 0.0.0.0:8000 --workers &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;static:/app/static&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;redis&lt;/span&gt;

  &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:16&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pgdata:/var/lib/postgresql/data&lt;/span&gt;

  &lt;span class="na"&gt;redis&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis:7-alpine&lt;/span&gt;

  &lt;span class="na"&gt;celery&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;celery -A config worker -l info&lt;/span&gt;

  &lt;span class="na"&gt;nginx&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx:alpine&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;80:80"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;443:443"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;static:/app/static&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./nginx.conf:/etc/nginx/conf.d/default.conf&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One &lt;code&gt;docker compose up&lt;/code&gt; and your entire stack is running. Same on development, staging, and production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Timeline: 4-6 Weeks to MVP
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Week&lt;/th&gt;
&lt;th&gt;Deliverable&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Project setup, multi-tenant config, auth, basic models&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Core features (the thing your SaaS actually does)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Dashboard, admin panel, REST API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Stripe billing, subscription management&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Deployment, monitoring, load testing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Buffer for feedback and iterations&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Week 2 is the only week that varies&lt;/strong&gt; between projects. Everything else is a repeatable pattern I've refined over 5+ SaaS builds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cost
&lt;/h2&gt;

&lt;p&gt;Based on my project history:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tier&lt;/th&gt;
&lt;th&gt;What you get&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;th&gt;Timeline&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Basic MVP&lt;/td&gt;
&lt;td&gt;Auth, core feature, admin, basic billing&lt;/td&gt;
&lt;td&gt;EUR 1,500-4,000&lt;/td&gt;
&lt;td&gt;3-4 weeks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Standard&lt;/td&gt;
&lt;td&gt;Multi-tenant, Stripe, dashboard, API&lt;/td&gt;
&lt;td&gt;EUR 4,000-10,000&lt;/td&gt;
&lt;td&gt;5-8 weeks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enterprise&lt;/td&gt;
&lt;td&gt;Custom integrations, analytics, multi-region&lt;/td&gt;
&lt;td&gt;EUR 10,000+&lt;/td&gt;
&lt;td&gt;8-12 weeks&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Detailed breakdown: &lt;a href="https://kirweb.site/cost-guides/saas-mvp-development-cost/" rel="noopener noreferrer"&gt;SaaS MVP Development Cost&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Mistakes I See
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Building auth from scratch.&lt;/strong&gt; Use django-allauth. It handles email verification, social login, password reset, and 2FA. Don't reinvent this.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Skipping multi-tenancy early.&lt;/strong&gt; Adding tenant isolation to an existing codebase is painful. Start with django-tenants from day 1.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No background tasks.&lt;/strong&gt; Everything runs in the request cycle. First heavy operation = timeout. Add Celery from the start.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Over-engineering the MVP.&lt;/strong&gt; Your first version needs: auth, one core feature, billing, and a way to contact you. Ship that. Iterate after real users give feedback.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;I'm Kirill Strelnikov — freelance Python/Django developer in Barcelona, Spain. I build SaaS platforms, AI chatbots, and Telegram bots. 15+ projects delivered across Europe.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Website: &lt;a href="https://kirweb.site" rel="noopener noreferrer"&gt;kirweb.site&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Telegram: &lt;a href="https://t.me/KirBcn" rel="noopener noreferrer"&gt;@KirBcn&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;SaaS launch checklist: &lt;a href="https://kirweb.site/guides/saas-mvp-launch-checklist/" rel="noopener noreferrer"&gt;50 things before going live&lt;/a&gt;
Author: Kirill Strelnikov
Published: March 11, 2026
Original source: kirweb.site&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>django</category>
      <category>saas</category>
      <category>python</category>
      <category>startup</category>
    </item>
    <item>
      <title>How I Automated 70% of Customer Support with an AI Chatbot (Django + OpenAI)</title>
      <dc:creator>Kirill Strelnikov</dc:creator>
      <pubDate>Thu, 05 Mar 2026 06:32:15 +0000</pubDate>
      <link>https://dev.to/kirill_strelnikov_d8546b8/how-i-automated-70-of-customer-support-with-an-ai-chatbot-django-openai-41c5</link>
      <guid>https://dev.to/kirill_strelnikov_d8546b8/how-i-automated-70-of-customer-support-with-an-ai-chatbot-django-openai-41c5</guid>
      <description>&lt;p&gt;I'm Kirill Strelnikov, a freelance Python/Django developer based in Barcelona, Spain. I build AI-powered products for businesses across Europe. This is a real case study from a client project.&lt;/p&gt;

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

&lt;p&gt;An e-commerce clothing store was drowning in repetitive support tickets: "Where is my order?", "What's your return policy?", "Do you have size X in stock?" — the same 15-20 questions making up 80% of all inquiries. Two full-time support agents were spending most of their day copy-pasting answers.&lt;/p&gt;

&lt;p&gt;The client asked me to build an AI chatbot that could handle these repetitive queries automatically, while escalating complex issues to human agents.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: RAG Chatbot with Django + OpenAI
&lt;/h2&gt;

&lt;p&gt;I built a &lt;strong&gt;Retrieval-Augmented Generation (RAG) chatbot&lt;/strong&gt; — not a simple FAQ bot, but one that understands context and generates natural answers from the store's actual data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Architecture
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User message
  → Django backend (receives via REST API)
  → Vector search (pgvector) finds relevant docs
  → OpenAI GPT-4 generates answer using retrieved context
  → Response sent back to user
  → If confidence &amp;lt; threshold → escalate to human
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key technical decisions:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;pgvector for embeddings&lt;/strong&gt; — stored product catalog, FAQ, and policy documents as vector embeddings directly in PostgreSQL. No separate vector DB needed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Chunking strategy&lt;/strong&gt; — split documents into 300-token chunks with 50-token overlap. This gave the best retrieval accuracy for product-related queries.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Confidence scoring&lt;/strong&gt; — each response gets a confidence score. Below 0.7 → automatic escalation to human agent with full conversation context.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Django admin integration&lt;/strong&gt; — the client's team can update FAQ entries, add product info, and review chatbot conversations through Django admin. No developer needed for content updates.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The code pattern (simplified)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pgvector.django&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;CosineDistance&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_chatbot_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_message&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;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# 1. Create embedding for user's question
&lt;/span&gt;    &lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;embeddings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&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;text-embedding-3-small&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;user_message&lt;/span&gt;
    &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt;

    &lt;span class="c1"&gt;# 2. Find relevant documents via vector search
&lt;/span&gt;    &lt;span class="n"&gt;relevant_docs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;annotate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;distance&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;CosineDistance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;embedding&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;order_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;distance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;context&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&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;relevant_docs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 3. Generate response with context
&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;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&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;gpt-4&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="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;system&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Answer using this context:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="si"&gt;}&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="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;user_message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.3&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;answer&lt;/span&gt;&lt;span class="sh"&gt;"&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;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;message&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;confidence&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;relevant_docs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;distance&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;relevant_docs&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sources&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="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;relevant_docs&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Results After 3 Months
&lt;/h2&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;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Automated inquiries&lt;/td&gt;
&lt;td&gt;0%&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;70%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Avg response time&lt;/td&gt;
&lt;td&gt;4 hours&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;8 seconds&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conversion rate&lt;/td&gt;
&lt;td&gt;baseline&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;+35%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Support staff needed&lt;/td&gt;
&lt;td&gt;2 full-time&lt;/td&gt;
&lt;td&gt;1 part-time&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The &lt;strong&gt;35% conversion increase&lt;/strong&gt; was unexpected — turns out, instant answers to "is this in stock?" and "what size should I get?" directly drive purchases. Customers who got instant chatbot answers were far more likely to complete checkout.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;RAG beats fine-tuning for business chatbots.&lt;/strong&gt; The client's product catalog changes weekly. With RAG, you just re-embed the new data. No retraining needed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Confidence-based escalation is critical.&lt;/strong&gt; The chatbot knows when it doesn't know. This prevents hallucination and maintains customer trust.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Django admin is your secret weapon.&lt;/strong&gt; Non-technical staff can manage the knowledge base. This makes the chatbot self-sustaining.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Start with the top 20 questions.&lt;/strong&gt; Don't try to automate everything. The Pareto principle applies: 20% of question types cover 80% of volume.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Cost Breakdown
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Development: ~EUR 2,500 (3 weeks)&lt;/li&gt;
&lt;li&gt;OpenAI API: ~EUR 50-80/month at their volume&lt;/li&gt;
&lt;li&gt;Hosting (Django + PostgreSQL): ~EUR 30/month&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ROI: Paid for itself in month 1&lt;/strong&gt; (saved 1.5 FTE support salary)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tech Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Python 3.12, Django 5.0, Django REST Framework&lt;/li&gt;
&lt;li&gt;PostgreSQL with pgvector extension&lt;/li&gt;
&lt;li&gt;OpenAI GPT-4 + text-embedding-3-small&lt;/li&gt;
&lt;li&gt;Celery for async embedding updates&lt;/li&gt;
&lt;li&gt;Docker for deployment&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;I'm Kirill Strelnikov — I build AI chatbots, SaaS platforms, and Telegram bots as a freelance developer in Barcelona. If you have a similar project, feel free to reach out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Website: &lt;a href="https://kirweb.site" rel="noopener noreferrer"&gt;kirweb.site&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Telegram: &lt;a href="https://t.me/KirBcn" rel="noopener noreferrer"&gt;@KirBcn&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Email: &lt;a href="mailto:Kir.S100@protonmail.com"&gt;Kir.S100@protonmail.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;More case studies: &lt;a href="https://kirweb.site/services/" rel="noopener noreferrer"&gt;kirweb.site/services&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>django</category>
      <category>python</category>
      <category>chatbot</category>
    </item>
    <item>
      <title>How My Telegram Bot Got 500 Paying Users in 3 Months (Python + aiogram)</title>
      <dc:creator>Kirill Strelnikov</dc:creator>
      <pubDate>Thu, 05 Mar 2026 05:30:24 +0000</pubDate>
      <link>https://dev.to/kirill_strelnikov_d8546b8/how-my-telegram-bot-got-500-paying-users-in-3-months-python-aiogram-3ok3</link>
      <guid>https://dev.to/kirill_strelnikov_d8546b8/how-my-telegram-bot-got-500-paying-users-in-3-months-python-aiogram-3ok3</guid>
      <description>&lt;p&gt;I'm Kirill Strelnikov, a freelance developer in Barcelona. I build Telegram bots, AI chatbots, and SaaS platforms. This is the story of a Telegram AI aggregator bot I built that hit 500 paying users in 3 months — and was profitable from month 1.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Idea
&lt;/h2&gt;

&lt;p&gt;The client wanted a Telegram bot that aggregates multiple AI models (GPT-4, Claude, Gemini) into a single chat interface. Users pick a model, send a message, get a response — all without leaving Telegram. The monetization model: credit-based billing.&lt;/p&gt;

&lt;p&gt;Simple concept. The execution is where it gets interesting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Telegram user
  → aiogram bot handler
  → Django backend (REST API)
  → Credit system checks balance
  → Route to selected AI model (OpenAI / Anthropic / Google)
  → Stream response back to Telegram
  → Deduct credits
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key components:
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Credit-based billing system&lt;/strong&gt;&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;UserCredits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;OneToOneField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;on_delete&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CASCADE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DecimalField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_digits&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;decimal_places&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&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;deduct&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;model&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;tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&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;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;cost&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="nf"&gt;calculate_cost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&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;balance&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;cost&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;balance&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;cost&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;update_fields&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;balance&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="n"&gt;CreditTransaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;user&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;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;=-&lt;/span&gt;&lt;span class="n"&gt;cost&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="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;calculate_cost&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;model&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;tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&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;Decimal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;rates&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;gpt-4&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.003&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;      &lt;span class="c1"&gt;# per 1K tokens
&lt;/span&gt;            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-3.5&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.0005&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;claude-3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.002&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;gemini&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.001&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;rates&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;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.002&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Multi-model router&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Each AI provider has its own adapter class. The router picks the right one based on user selection:&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;ModelRouter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;adapters&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;gpt-4&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;OpenAIAdapter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;claude-3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AnthropicAdapter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gemini&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;GoogleAdapter&lt;/span&gt;&lt;span class="p"&gt;,&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;def&lt;/span&gt; &lt;span class="nf"&gt;generate&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;model&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;messages&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="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="n"&gt;adapter&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;adapters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;]()&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;adapter&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;messages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Telegram payment integration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Users buy credits directly in Telegram using Stripe:&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="nd"&gt;@router.message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;buy&lt;/span&gt;&lt;span class="sh"&gt;"&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;def&lt;/span&gt; &lt;span class="nf"&gt;buy_credits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;prices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nc"&gt;LabeledPrice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;100 credits&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;499&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;   &lt;span class="c1"&gt;# $4.99
&lt;/span&gt;        &lt;span class="nc"&gt;LabeledPrice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;500 credits&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1999&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;   &lt;span class="c1"&gt;# $19.99
&lt;/span&gt;        &lt;span class="nc"&gt;LabeledPrice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2000 credits&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5999&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;  &lt;span class="c1"&gt;# $59.99
&lt;/span&gt;    &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="c1"&gt;# Send Telegram invoice
&lt;/span&gt;    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;answer_invoice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AI Credits&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Credits for AI model usage&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;credits_purchase&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;provider_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;STRIPE_TOKEN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;currency&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;USD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;prices&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;prices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;  &lt;span class="c1"&gt;# Default package
&lt;/span&gt;    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Growth Metrics
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Month&lt;/th&gt;
&lt;th&gt;Users&lt;/th&gt;
&lt;th&gt;Paying users&lt;/th&gt;
&lt;th&gt;Revenue&lt;/th&gt;
&lt;th&gt;MRR&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;800&lt;/td&gt;
&lt;td&gt;85&lt;/td&gt;
&lt;td&gt;$1,200&lt;/td&gt;
&lt;td&gt;$1,200&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2,100&lt;/td&gt;
&lt;td&gt;280&lt;/td&gt;
&lt;td&gt;$3,800&lt;/td&gt;
&lt;td&gt;$3,800&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3,500&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td&gt;$6,200&lt;/td&gt;
&lt;td&gt;$6,200&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Conversion rate:&lt;/strong&gt; ~14% free-to-paid (industry average for bots is 2-5%).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why so high?&lt;/strong&gt; Free tier gives 20 credits — enough to try all models but not enough for daily use. The "aha moment" happens when users compare GPT-4 vs Claude on the same prompt. Once they see the value of model switching, they pay.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Made It Work
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Telegram's 900M+ user base
&lt;/h3&gt;

&lt;p&gt;No app to install. No signup form. Users just open the bot and start chatting. The friction-to-value ratio is unbeatable.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Credit-based pricing (not subscription)
&lt;/h3&gt;

&lt;p&gt;Users pay for what they use. Heavy users spend $20-60/month. Light users spend $5. Nobody feels locked into a subscription they don't use. This increased retention significantly vs subscription-only models.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Message open rates: 80%+
&lt;/h3&gt;

&lt;p&gt;Telegram notifications actually get read. When we sent "New model added: Claude 3 Opus" — 82% open rate. Compare that to email marketing (20-25%).&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Viral inline mode
&lt;/h3&gt;

&lt;p&gt;Users can use the bot inline in any chat: type &lt;code&gt;@botname what is Django?&lt;/code&gt; in a group chat and get an AI answer. This drove organic growth — other group members see the bot in action and try it themselves.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tech Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bot framework:&lt;/strong&gt; Python 3.12 + aiogram 3.x (async)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend:&lt;/strong&gt; Django 5.0 + Django REST Framework&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database:&lt;/strong&gt; PostgreSQL (user accounts, credits, transactions)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache:&lt;/strong&gt; Redis (rate limiting, session data)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Task queue:&lt;/strong&gt; Celery (webhook processing, analytics)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI providers:&lt;/strong&gt; OpenAI, Anthropic, Google AI APIs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payments:&lt;/strong&gt; Stripe via Telegram Payments API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment:&lt;/strong&gt; Docker + Nginx on a VPS&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Monetize from day 1.&lt;/strong&gt; Don't wait for "enough users." The free tier should be a taste, not a full meal.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Credits &amp;gt; subscriptions for bots.&lt;/strong&gt; Bot usage is spiky. People use it heavily for a week, then pause. Credits accommodate this pattern naturally.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Telegram bots are underrated as a business channel.&lt;/strong&gt; 900M+ MAU, 80%+ open rates, built-in payments, inline sharing. For many use cases, a Telegram bot is better than a mobile app.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Multi-model is the moat.&lt;/strong&gt; Any single-model wrapper gets commoditized instantly. The value is in comparison and switching between models.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Cost to Build Something Similar
&lt;/h2&gt;

&lt;p&gt;Based on my experience building this and similar bots:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Complexity&lt;/th&gt;
&lt;th&gt;Timeline&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Simple bot (1 model, basic billing)&lt;/td&gt;
&lt;td&gt;1-2 weeks&lt;/td&gt;
&lt;td&gt;EUR 500-1,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-model with credits&lt;/td&gt;
&lt;td&gt;3-4 weeks&lt;/td&gt;
&lt;td&gt;EUR 1,500-3,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Full platform with admin, analytics&lt;/td&gt;
&lt;td&gt;5-8 weeks&lt;/td&gt;
&lt;td&gt;EUR 3,000-6,000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;p&gt;I'm Kirill Strelnikov, freelance Python developer in Barcelona, Spain. I build Telegram bots, AI chatbots, and SaaS platforms for businesses across Europe. 15+ projects delivered.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Website: &lt;a href="https://kirweb.site" rel="noopener noreferrer"&gt;kirweb.site&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Telegram: &lt;a href="https://t.me/KirBcn" rel="noopener noreferrer"&gt;@KirBcn&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Cost guide: &lt;a href="https://kirweb.site/cost-guides/telegram-bot-development-cost/" rel="noopener noreferrer"&gt;How much does a Telegram bot cost?&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>telegram</category>
      <category>startup</category>
      <category>monetization</category>
    </item>
    <item>
      <title>Freelancer vs Agency: Real Cost Comparison for Software Projects in Europe (2026)</title>
      <dc:creator>Kirill Strelnikov</dc:creator>
      <pubDate>Thu, 05 Mar 2026 05:28:27 +0000</pubDate>
      <link>https://dev.to/kirill_strelnikov_d8546b8/freelancer-vs-agency-real-cost-comparison-for-software-projects-in-europe-2026-m6h</link>
      <guid>https://dev.to/kirill_strelnikov_d8546b8/freelancer-vs-agency-real-cost-comparison-for-software-projects-in-europe-2026-m6h</guid>
      <description>&lt;p&gt;I'm Kirill Strelnikov, a freelance Python developer based in Barcelona, Spain. I've been on both sides — working at agencies and now running my own freelance practice with 15+ delivered projects. Here's an honest cost comparison based on real European market rates in 2026.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Three Options
&lt;/h2&gt;

&lt;p&gt;When you need software built, you have three choices:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Freelancer&lt;/strong&gt; — one person, direct communication&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agency&lt;/strong&gt; — team with project manager, designers, developers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In-house hire&lt;/strong&gt; — full-time employee on your payroll&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each has a clear sweet spot. Let me show you when each makes sense, with real numbers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cost Comparison Table
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Factor&lt;/th&gt;
&lt;th&gt;Freelancer&lt;/th&gt;
&lt;th&gt;Agency&lt;/th&gt;
&lt;th&gt;In-House&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Hourly rate (Europe)&lt;/td&gt;
&lt;td&gt;EUR 40-100&lt;/td&gt;
&lt;td&gt;EUR 80-200&lt;/td&gt;
&lt;td&gt;EUR 30-70 (salary equiv.)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Simple chatbot (2 weeks)&lt;/td&gt;
&lt;td&gt;EUR 800-2,000&lt;/td&gt;
&lt;td&gt;EUR 3,000-8,000&lt;/td&gt;
&lt;td&gt;N/A (hiring takes longer)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SaaS MVP (6 weeks)&lt;/td&gt;
&lt;td&gt;EUR 3,000-10,000&lt;/td&gt;
&lt;td&gt;EUR 15,000-50,000&lt;/td&gt;
&lt;td&gt;~EUR 12,000 (salary only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Telegram bot (1-2 weeks)&lt;/td&gt;
&lt;td&gt;EUR 500-1,500&lt;/td&gt;
&lt;td&gt;EUR 2,000-5,000&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Communication overhead&lt;/td&gt;
&lt;td&gt;Low (direct)&lt;/td&gt;
&lt;td&gt;High (via PM)&lt;/td&gt;
&lt;td&gt;Low (direct)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Time to start&lt;/td&gt;
&lt;td&gt;Days&lt;/td&gt;
&lt;td&gt;2-4 weeks&lt;/td&gt;
&lt;td&gt;2-3 months&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scalability&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Slow&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The key insight:&lt;/strong&gt; For projects under EUR 25,000, a senior freelancer almost always delivers better value than an agency. The agency's overhead (project managers, office, sales team) adds 2-3x to the cost without proportional quality improvement.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to Hire a Freelancer
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Projects with a clear scope, EUR 500-25,000 budget, and need for fast delivery.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Direct communication.&lt;/strong&gt; You talk to the person writing your code. No telephone game through project managers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lower cost.&lt;/strong&gt; No agency overhead. A EUR 80/hour freelancer costs you EUR 80/hour. A EUR 80/hour agency developer costs you EUR 160/hour after the agency's margin.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Faster iteration.&lt;/strong&gt; Decision to code to deployment in days, not weeks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Personal accountability.&lt;/strong&gt; One person owns the entire project. No "that's not my department."&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Bus factor of 1 (mitigated by clean code and documentation)&lt;/li&gt;
&lt;li&gt;Limited to one person's skill set&lt;/li&gt;
&lt;li&gt;Availability gaps if they take on too many clients&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Real example:&lt;/strong&gt; I built an AI chatbot for an e-commerce store in 3 weeks for EUR 2,500. An agency quoted the same client EUR 8,000 for a 6-week timeline. Same deliverable. The chatbot automated 70% of customer support and increased conversions by 35%.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to Hire an Agency
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Large projects (EUR 50,000+), need for multiple specialists simultaneously (design + frontend + backend + mobile), or when you need ongoing team augmentation.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Multiple specialists available immediately&lt;/li&gt;
&lt;li&gt;Project management included&lt;/li&gt;
&lt;li&gt;Continuity if one developer leaves&lt;/li&gt;
&lt;li&gt;Can scale team up/down&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;2-3x higher cost&lt;/li&gt;
&lt;li&gt;Communication through intermediaries&lt;/li&gt;
&lt;li&gt;Developer turnover mid-project&lt;/li&gt;
&lt;li&gt;Incentive to extend timelines&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When to Hire In-House
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Core product development that will need continuous work for 12+ months.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost reality in Europe:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Junior developer salary: EUR 30,000-45,000/year&lt;/li&gt;
&lt;li&gt;Senior developer salary: EUR 55,000-85,000/year&lt;/li&gt;
&lt;li&gt;Add ~30-40% for taxes, benefits, equipment: EUR 70,000-120,000/year total cost&lt;/li&gt;
&lt;li&gt;Plus: 2-3 months to hire, onboarding time, management overhead&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Hire in-house when:&lt;/strong&gt; The project IS your business (you're a tech company) and you need continuous development for years. Don't hire full-time for a one-off project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decision Framework
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Is your project scope clear and under EUR 25,000?
  YES → Freelancer

Do you need 3+ different specialists simultaneously?
  YES → Agency

Will you need continuous development for 12+ months?
  YES → In-house

Still unsure?
  → Start with a freelancer for the MVP, then decide
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How to Evaluate a Freelancer
&lt;/h2&gt;

&lt;p&gt;After hiring many freelancers myself (at agencies) and now being one, here are the real signals:&lt;/p&gt;

&lt;h3&gt;
  
  
  Green flags:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Published portfolio with real case studies&lt;/strong&gt; (not just "beautiful designs" but actual business results)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fixed-price quotes&lt;/strong&gt; (means they've done similar projects before and can estimate accurately)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Asks detailed questions before quoting&lt;/strong&gt; (understands the problem before proposing a solution)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transparent about limitations&lt;/strong&gt; ("I don't do mobile apps, but I can recommend someone")&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Communication speed&lt;/strong&gt; (if they take 3 days to respond to your inquiry, imagine mid-project)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Red flags:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Only shows "personal projects" with no client work&lt;/li&gt;
&lt;li&gt;Can't provide a fixed estimate ("it depends" for everything)&lt;/li&gt;
&lt;li&gt;Hourly-only billing with no cap&lt;/li&gt;
&lt;li&gt;No deployment/DevOps experience (they can code but can't ship)&lt;/li&gt;
&lt;li&gt;Wants to start coding before understanding your business&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Pricing Models Compared
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Freelancer&lt;/th&gt;
&lt;th&gt;Agency&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Fixed price&lt;/td&gt;
&lt;td&gt;Common (EUR 500-25,000)&lt;/td&gt;
&lt;td&gt;Rare (EUR 10,000+)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hourly&lt;/td&gt;
&lt;td&gt;EUR 40-100/hr&lt;/td&gt;
&lt;td&gt;EUR 80-200/hr&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monthly retainer&lt;/td&gt;
&lt;td&gt;EUR 500-3,000/mo&lt;/td&gt;
&lt;td&gt;EUR 3,000-15,000/mo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Milestone-based&lt;/td&gt;
&lt;td&gt;Common (30/30/40)&lt;/td&gt;
&lt;td&gt;Sometimes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;I use milestone-based fixed pricing:&lt;/strong&gt; 30% upfront, 30% at midpoint demo, 40% on delivery. This aligns incentives — I want to finish fast, you want to pay for results, not hours.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Rates (For Reference)
&lt;/h2&gt;

&lt;p&gt;As a freelance Python/Django developer with 5+ years and 15+ projects:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Fixed Price&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AI Chatbot&lt;/td&gt;
&lt;td&gt;EUR 800-3,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Telegram Bot&lt;/td&gt;
&lt;td&gt;EUR 500-2,500&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SaaS MVP&lt;/td&gt;
&lt;td&gt;EUR 1,500-10,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backend/API&lt;/td&gt;
&lt;td&gt;EUR 1,000-6,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WhatsApp Bot&lt;/td&gt;
&lt;td&gt;EUR 500-2,000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Free consultation included. I scope the project, give a fixed quote, and stick to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bottom Line
&lt;/h2&gt;

&lt;p&gt;For most startups and SMBs building their first product:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Start with a freelancer&lt;/strong&gt; for the MVP (EUR 1,500-10,000, 4-8 weeks)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validate with real users&lt;/strong&gt; before investing more&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scale to agency or in-house&lt;/strong&gt; only when you've proven product-market fit&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The biggest waste of money I see: companies spending EUR 50,000 at an agency for an MVP that nobody uses. Build small, test fast, iterate.&lt;/p&gt;




&lt;p&gt;I'm Kirill Strelnikov — freelance Python/Django developer and AI engineer in Barcelona, Spain. 15+ projects delivered for clients across Europe. Registered autónomo, EU invoicing, GDPR-compliant.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Website: &lt;a href="https://kirweb.site" rel="noopener noreferrer"&gt;kirweb.site&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Telegram: &lt;a href="https://t.me/KirBcn" rel="noopener noreferrer"&gt;@KirBcn&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Full comparison: &lt;a href="https://kirweb.site/comparisons/freelancer-vs-agency-vs-inhouse/" rel="noopener noreferrer"&gt;Freelancer vs Agency vs In-House&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Cost guides: &lt;a href="https://kirweb.site/cost-guides/" rel="noopener noreferrer"&gt;kirweb.site/cost-guides&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>freelancing</category>
      <category>startup</category>
      <category>business</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Custom CRM vs Off-the-Shelf: Which Is Right for Your Business?</title>
      <dc:creator>Kirill Strelnikov</dc:creator>
      <pubDate>Fri, 27 Feb 2026 05:33:56 +0000</pubDate>
      <link>https://dev.to/kirill_strelnikov_d8546b8/custom-crm-vs-off-the-shelf-which-is-right-for-your-business-4073</link>
      <guid>https://dev.to/kirill_strelnikov_d8546b8/custom-crm-vs-off-the-shelf-which-is-right-for-your-business-4073</guid>
      <description>&lt;h2&gt;
  
  
  Custom CRM vs Off-the-Shelf: Which Is Right for Your Business?
&lt;/h2&gt;

&lt;p&gt;Every growing business eventually outgrows spreadsheets and needs a CRM (Customer Relationship Management) system. The first decision is whether to use an off-the-shelf solution like Salesforce, HubSpot, or Pipedrive, or to build a custom CRM tailored to your specific workflow. Both have their place — and choosing wrong can cost you thousands of euros and months of wasted effort.&lt;/p&gt;

&lt;h2&gt;
  
  
  Off-the-Shelf CRMs: Pros and Cons
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Popular Options and Pricing
&lt;/h3&gt;

&lt;p&gt;| CRM | Starting Price | Best For &lt;/p&gt;

&lt;p&gt;| HubSpot | Free (paid from €45/mo) | Marketing-focused businesses, startups &lt;/p&gt;

&lt;p&gt;| Salesforce | €25/user/mo | Enterprise, complex sales processes &lt;/p&gt;

&lt;p&gt;| Pipedrive | €14/user/mo | Sales teams, pipeline management &lt;/p&gt;

&lt;p&gt;| Zoho CRM | €14/user/mo | Budget-conscious, all-in-one needs &lt;/p&gt;

&lt;p&gt;| Monday.com CRM | €12/user/mo | Teams already using Monday for PM &lt;/p&gt;

&lt;h3&gt;
  
  
  Advantages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Immediate availability:&lt;/strong&gt; Sign up and start using it today.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proven reliability:&lt;/strong&gt; Millions of users, battle-tested infrastructure, 99.9% uptime.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Built-in features:&lt;/strong&gt; Email tracking, reporting, mobile apps, integrations marketplace.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regular updates:&lt;/strong&gt; New features and security patches without your involvement.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Support and documentation:&lt;/strong&gt; Large communities, certified consultants, extensive help docs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Disadvantages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Monthly costs add up:&lt;/strong&gt; At €50/user/month for 10 users, you pay €6,000/year — and that is before add-ons. Salesforce implementations routinely cost €50,000–200,000 for mid-size companies when you include customization, training, and consulting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Forced to adapt your workflow:&lt;/strong&gt; Off-the-shelf CRMs have opinions about how sales, support, and marketing should work. If your process does not match, you either change your process or fight the tool.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feature bloat:&lt;/strong&gt; You pay for hundreds of features but use 20%. The interface is cluttered with options irrelevant to your business.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limited customization:&lt;/strong&gt; You can configure fields and workflows, but fundamental changes to how data flows or how the UI works require expensive third-party developers or are simply impossible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vendor lock-in:&lt;/strong&gt; Migrating away from Salesforce or HubSpot is a major project. Your data, automations, and integrations are all tied to their ecosystem.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Per-user pricing:&lt;/strong&gt; As your team grows, costs scale linearly. Custom CRMs have a fixed hosting cost regardless of user count.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Custom CRM Development: Pros and Cons
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Typical Cost Ranges
&lt;/h3&gt;

&lt;p&gt;| Scope | Price Range | Timeline &lt;/p&gt;

&lt;p&gt;| Basic CRM (contacts, deals, tasks) | €3,000–8,000 | 3–6 weeks &lt;/p&gt;

&lt;p&gt;| Advanced CRM (automations, reporting, integrations) | €8,000–20,000 | 6–12 weeks &lt;/p&gt;

&lt;p&gt;| Enterprise CRM (multi-team, advanced analytics, AI) | €20,000+ | 3–6 months &lt;/p&gt;

&lt;h3&gt;
  
  
  Advantages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Perfect fit for your workflow:&lt;/strong&gt; Every field, every automation, every view is designed for how your team actually works. No compromises.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No per-user fees:&lt;/strong&gt; Whether you have 5 users or 500, hosting costs the same €50–200/month.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full data ownership:&lt;/strong&gt; Your data lives on your server. No vendor lock-in, no data export limitations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unlimited customization:&lt;/strong&gt; Need a custom dashboard, a unique approval workflow, or integration with an obscure API? You can build anything.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Competitive advantage:&lt;/strong&gt; Your CRM becomes part of your competitive moat. Competitors cannot replicate a process they cannot see.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI integration:&lt;/strong&gt; Add AI features like lead scoring, automated follow-ups, or natural language search — exactly how you want them, without paying for an expensive CRM add-on.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Disadvantages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Higher upfront cost:&lt;/strong&gt; You pay the full development cost before launch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintenance responsibility:&lt;/strong&gt; You need to plan for ongoing updates, security patches, and bug fixes. Budget 10–20% of the initial cost per year for maintenance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Longer time to first use:&lt;/strong&gt; Even a basic custom CRM takes 3–6 weeks to build, versus signing up for HubSpot in 5 minutes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No built-in marketplace:&lt;/strong&gt; Integrations must be built or configured individually.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When to Choose Off-the-Shelf
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;You have a standard sales process that fits the CRM's model.&lt;/li&gt;
&lt;li&gt;Your team is small (under 5 users) and budget is tight.&lt;/li&gt;
&lt;li&gt;You need to start immediately with minimal setup.&lt;/li&gt;
&lt;li&gt;You primarily need email marketing + basic pipeline tracking (HubSpot free tier).&lt;/li&gt;
&lt;li&gt;You do not have specific workflow requirements that differ from industry standard.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When to Choose Custom CRM
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Your business process is unique and does not fit standard CRM workflows.&lt;/li&gt;
&lt;li&gt;You have 10+ users and the per-user SaaS cost is becoming significant.&lt;/li&gt;
&lt;li&gt;You need deep integrations with internal tools, ERPs, or industry-specific systems.&lt;/li&gt;
&lt;li&gt;Data privacy regulations require you to keep data on your own infrastructure.&lt;/li&gt;
&lt;li&gt;You want AI features (auto-classification, lead scoring, smart follow-ups) without paying enterprise SaaS prices.&lt;/li&gt;
&lt;li&gt;You have tried an off-the-shelf CRM and find yourself constantly fighting its limitations.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Middle Ground: CRM Setup and Customization
&lt;/h2&gt;

&lt;p&gt;There is a third option that many businesses overlook: hiring a developer to properly set up and customize an off-the-shelf CRM. Most businesses use less than 30% of their CRM's capabilities because they never configured it properly. A specialist can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Configure custom fields, pipelines, and views for your specific workflow.&lt;/li&gt;
&lt;li&gt;Set up automations (email sequences, task assignment, lead routing).&lt;/li&gt;
&lt;li&gt;Build integrations with your other tools via APIs.&lt;/li&gt;
&lt;li&gt;Import and clean your existing data.&lt;/li&gt;
&lt;li&gt;Train your team on the optimized setup.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This typically costs &lt;strong&gt;€500–2,500&lt;/strong&gt; and can transform a frustrating CRM experience into a smooth one — without the cost and timeline of a fully custom build.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making the Decision: A Framework
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Map your current workflow&lt;/strong&gt; — Document exactly how your team handles leads, customers, and deals today.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Try a free tier&lt;/strong&gt; — Spend one week using HubSpot or Pipedrive's free plan. Does your workflow fit? Where do you hit walls?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Calculate the 3-year cost&lt;/strong&gt; — Add up SaaS fees, per-user costs, premium add-ons, and integration costs. Compare with custom development + hosting + maintenance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consider growth&lt;/strong&gt; — If you plan to 3x your team in the next two years, per-user pricing becomes very expensive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Evaluate your unique needs&lt;/strong&gt; — If more than 30% of your requirements are "custom" or "not available," lean toward custom development.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Off-the-shelf CRMs are the right choice when your needs are standard and your team is small. Custom CRM development makes financial and operational sense when your workflow is unique, your team is growing, or you need capabilities that SaaS products charge enterprise prices for.&lt;/p&gt;

&lt;p&gt;I help businesses evaluate and implement both approaches — from &lt;a href="https://kirweb.site/services/" rel="noopener noreferrer"&gt;CRM setup and customization&lt;/a&gt; (starting at €500) to fully custom CRM development. If you are not sure which path is right for your business, &lt;a href="https://kirweb.site/#contact" rel="noopener noreferrer"&gt;let's discuss it&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://kirweb.site/blog/custom-crm-vs-off-the-shelf/" rel="noopener noreferrer"&gt;kirweb.site&lt;/a&gt;. I build AI chatbots, SaaS platforms, and automation for businesses. &lt;a href="https://kirweb.site/#contact" rel="noopener noreferrer"&gt;Free consultation&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>crm</category>
      <category>customdevelopment</category>
      <category>salesforce</category>
      <category>hubspot</category>
    </item>
    <item>
      <title>How to Choose a Freelance Developer for Your Startup: A Founder's Checklist</title>
      <dc:creator>Kirill Strelnikov</dc:creator>
      <pubDate>Fri, 27 Feb 2026 05:33:03 +0000</pubDate>
      <link>https://dev.to/kirill_strelnikov_d8546b8/how-to-choose-a-freelance-developer-for-your-startup-a-founders-checklist-38pf</link>
      <guid>https://dev.to/kirill_strelnikov_d8546b8/how-to-choose-a-freelance-developer-for-your-startup-a-founders-checklist-38pf</guid>
      <description>&lt;h2&gt;
  
  
  How to Choose a Freelance Developer for Your Startup: A Founder's Checklist
&lt;/h2&gt;

&lt;p&gt;Hiring the right freelance developer can make or break your startup's first product. A good hire delivers a working MVP in weeks and becomes a long-term technical partner. A bad hire wastes months and thousands of euros on code that needs to be rewritten. Having worked with dozens of startups as a freelance developer, I have seen both outcomes — and the difference almost always comes down to how the founder approached the hiring process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before You Start Searching: Preparation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Define Your MVP Scope
&lt;/h3&gt;

&lt;p&gt;Before contacting any developer, write down exactly what your MVP needs to do. Not what your final product will do — what the &lt;em&gt;minimum&lt;/em&gt; version needs. List every feature and mark each as "must-have" or "nice-to-have." A common startup mistake is trying to build the full vision in v1. The result is a project that takes 6 months instead of 6 weeks and burns through the budget before launch.&lt;/p&gt;

&lt;h3&gt;
  
  
  Set a Realistic Budget
&lt;/h3&gt;

&lt;p&gt;Typical MVP costs in 2026:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Simple web app (CRUD, auth, basic UI):&lt;/strong&gt; €2,000–5,000&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SaaS platform (multi-tenant, billing, dashboards):&lt;/strong&gt; €4,000–15,000&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI-powered product (chatbot, automation, ML features):&lt;/strong&gt; €3,000–10,000&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mobile app backend + API:&lt;/strong&gt; €3,000–8,000&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Marketplace/platform:&lt;/strong&gt; €8,000–25,000&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your budget is significantly below these ranges, reconsider your scope or timeline. Under-budgeting leads to cutting corners on security, testing, and architecture — which costs more to fix later.&lt;/p&gt;

&lt;h3&gt;
  
  
  Choose Your Tech Stack (or Let the Developer Choose)
&lt;/h3&gt;

&lt;p&gt;If you have a technical co-founder, they likely have stack preferences. If not, trust the developer's recommendation — but make sure they explain the trade-offs. For most startups building web applications, &lt;strong&gt;Python/Django&lt;/strong&gt; or &lt;strong&gt;Node.js&lt;/strong&gt; for the backend and &lt;strong&gt;React&lt;/strong&gt; or &lt;strong&gt;Next.js&lt;/strong&gt; for the frontend is a battle-tested combination. Avoid exotic stacks for your MVP; you want a technology with a large talent pool so you can hire additional developers later.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Evaluation Checklist
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Relevant Portfolio (Weight: 30%)
&lt;/h3&gt;

&lt;p&gt;Look for projects similar to yours in complexity and type. A developer who has built three SaaS products will build your SaaS faster and better than someone who has only done static websites, even if the latter charges less. Specific things to check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Are the portfolio projects live and working? Click through them.&lt;/li&gt;
&lt;li&gt;Do case studies mention specific technical decisions and their reasoning?&lt;/li&gt;
&lt;li&gt;Are there measurable outcomes (users, performance, revenue impact)?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Communication Quality (Weight: 25%)
&lt;/h3&gt;

&lt;p&gt;This is the most underrated factor. During your first conversation, assess:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do they ask clarifying questions about your project, or just quote a price?&lt;/li&gt;
&lt;li&gt;Can they explain technical concepts in plain language?&lt;/li&gt;
&lt;li&gt;How quickly do they respond? (Within 24 hours is reasonable; within 2 hours shows they are serious.)&lt;/li&gt;
&lt;li&gt;Do they push back on unrealistic expectations, or just say yes to everything?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A developer who says "that's technically possible but I'd recommend a simpler approach for your MVP" is more valuable than one who agrees to build anything you describe.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Technical Depth (Weight: 20%)
&lt;/h3&gt;

&lt;p&gt;You do not need to be technical to evaluate this. Ask these questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"How would you structure the database for [your main feature]?" — A good developer will think out loud and mention trade-offs.&lt;/li&gt;
&lt;li&gt;"What happens when 1,000 users are using the app simultaneously?" — They should mention caching, database optimization, or scaling strategies.&lt;/li&gt;
&lt;li&gt;"How do you handle security (authentication, data protection)?" — They should have a clear, standard approach.&lt;/li&gt;
&lt;li&gt;"What is your testing strategy?" — Any answer beyond "I test manually" is a good sign.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Process and Project Management (Weight: 15%)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Do they use milestones with deliverables?&lt;/li&gt;
&lt;li&gt;What project management tool do they use? (Trello, Linear, Notion, etc.)&lt;/li&gt;
&lt;li&gt;How often will they send updates?&lt;/li&gt;
&lt;li&gt;Do they use version control (Git)? (This should be a given, but ask.)&lt;/li&gt;
&lt;li&gt;What is their deployment process?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. Pricing Transparency (Weight: 10%)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Can they give you a range after hearing your requirements? (A firm quote after a 15-minute conversation is either very experienced or guessing.)&lt;/li&gt;
&lt;li&gt;Do they break down the estimate by feature or milestone?&lt;/li&gt;
&lt;li&gt;What is included in the price? (Design? Deployment? One month of bug fixes?)&lt;/li&gt;
&lt;li&gt;How do they handle scope changes?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Red Flags That Should Make You Walk Away
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;"I can build anything."&lt;/strong&gt; — Every good developer has specializations and limitations they are honest about.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No portfolio or NDA on everything.&lt;/strong&gt; — Some past work may be under NDA, but a developer with zero public work to show is risky.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Price significantly below market.&lt;/strong&gt; — If everyone else quotes €5,000–8,000 and one person says €1,500, they either do not understand the scope or plan to cut corners.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No questions about your business.&lt;/strong&gt; — A developer who does not ask about your users, your goals, or your constraints will build the wrong thing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refuses milestones or partial payments.&lt;/strong&gt; — Asking for 100% upfront is a major red flag. Standard structures: 30/30/40 or 50/50.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cannot show code quality.&lt;/strong&gt; — Ask for a GitHub profile or a code sample. Clean, well-organized code is a strong signal of professionalism.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Contract and Payment Structure
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Essential Contract Elements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scope of work:&lt;/strong&gt; Detailed feature list and deliverables.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timeline:&lt;/strong&gt; Milestones with dates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payment schedule:&lt;/strong&gt; Tied to milestones, not calendar dates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IP ownership:&lt;/strong&gt; You own the code upon payment. This is non-negotiable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bug fix period:&lt;/strong&gt; 30–90 days of free bug fixes after launch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Termination clause:&lt;/strong&gt; What happens if either party wants to end the engagement early.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Recommended Payment Structure
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;30% upfront&lt;/strong&gt; — Upon signing, to start work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;30% at midpoint&lt;/strong&gt; — Upon delivery of the first major milestone (working demo).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;40% at completion&lt;/strong&gt; — Upon final delivery and acceptance testing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  After Hiring: Setting Up for Success
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Share your full context.&lt;/strong&gt; Give the developer access to your business plan, competitor analysis, and user research. The more context they have, the better decisions they will make.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Be available.&lt;/strong&gt; The fastest way to slow down a project is to take three days to answer the developer's questions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trust their technical judgment.&lt;/strong&gt; You hired an expert. If they recommend a different approach than what you imagined, hear them out.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test early and often.&lt;/strong&gt; Do not wait until the end to try the product. Use every milestone delivery to test and give feedback.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plan for post-launch.&lt;/strong&gt; Your MVP launch is the beginning, not the end. Budget for iterations based on user feedback.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The best freelance developer for your startup is not necessarily the cheapest or the one with the most impressive resume. It is the one who understands your business, communicates clearly, has built similar products before, and structures the engagement for mutual success. Use this checklist during your evaluation, and you will dramatically increase your chances of a successful collaboration.&lt;/p&gt;

&lt;p&gt;I work with startups to build MVPs and scale products using Python, Django, and AI. If you are looking for a technical partner for your project, check out my &lt;a href="https://kirweb.site/services/" rel="noopener noreferrer"&gt;services&lt;/a&gt; or &lt;a href="https://kirweb.site/#contact" rel="noopener noreferrer"&gt;get in touch&lt;/a&gt; for a free consultation.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://kirweb.site/blog/choose-freelance-developer-startup-checklist/" rel="noopener noreferrer"&gt;kirweb.site&lt;/a&gt;. I build AI chatbots, SaaS platforms, and automation for businesses. &lt;a href="https://kirweb.site/#contact" rel="noopener noreferrer"&gt;Free consultation&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>freelance</category>
      <category>startup</category>
      <category>hiring</category>
      <category>developer</category>
    </item>
    <item>
      <title>AI Automation for Small Business: 10 Ways to Save Time and Money in 2026</title>
      <dc:creator>Kirill Strelnikov</dc:creator>
      <pubDate>Fri, 27 Feb 2026 05:31:39 +0000</pubDate>
      <link>https://dev.to/kirill_strelnikov_d8546b8/ai-automation-for-small-business-10-ways-to-save-time-and-money-in-2026-4a4j</link>
      <guid>https://dev.to/kirill_strelnikov_d8546b8/ai-automation-for-small-business-10-ways-to-save-time-and-money-in-2026-4a4j</guid>
      <description>&lt;h2&gt;
  
  
  AI Automation for Small Business: 10 Ways to Save Time and Money in 2026
&lt;/h2&gt;

&lt;p&gt;AI is no longer a luxury for enterprises with million-dollar budgets. In 2026, small businesses with 5–50 employees can automate significant parts of their operations using AI tools and custom solutions — often for less than €1,000. The key is knowing which tasks to automate first and choosing the right approach for each.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Customer Support Chatbot
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; An AI chatbot on your website or WhatsApp handles routine customer questions 24/7 — business hours, pricing, product details, order status, return policies. It escalates complex issues to human agents with full context.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected savings:&lt;/strong&gt; 40–70% reduction in support tickets. For a business handling 50 inquiries per day, that saves 2–3 hours of human time daily.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost:&lt;/strong&gt; €800–3,000 for a custom chatbot. No-code platforms like Tidio or Intercom start at €30–100/month but lack depth.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; E-commerce, SaaS, service businesses, clinics, restaurants.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Email Triage and Auto-Response
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; AI reads incoming emails, categorizes them (sales inquiry, support request, spam, partnership), drafts responses for routine messages, and routes important emails to the right team member.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected savings:&lt;/strong&gt; 30–60 minutes per day for anyone who processes high email volume.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost:&lt;/strong&gt; €400–1,500 for a custom solution using OpenAI API + email integration. Or use built-in AI features in Gmail/Outlook (limited but free).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Professional services, agencies, any business receiving 50+ emails daily.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Lead Qualification and Scoring
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; AI analyzes incoming leads based on criteria you define (company size, budget, industry, urgency) and assigns a score. High-scoring leads get immediate attention; low-scoring leads enter a nurture sequence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected savings:&lt;/strong&gt; Sales teams spend 30–50% less time on unqualified leads. Conversion rates typically increase by 20–35%.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost:&lt;/strong&gt; €500–2,000 as part of a CRM automation setup.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; B2B companies, agencies, consultants, SaaS businesses.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Invoice Processing and Bookkeeping
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; AI extracts data from invoices (vendor, amount, date, line items), categorizes expenses, and enters them into your accounting software. It can also flag anomalies (duplicate invoices, unusual amounts).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected savings:&lt;/strong&gt; 2–5 hours per week for businesses processing 50+ invoices per month.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost:&lt;/strong&gt; €500–1,500 for a custom extraction pipeline. Tools like Dext or AutoEntry charge €20–50/month.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Any business with regular invoice processing.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Social Media Content Generation
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; AI generates social media posts, captions, and hashtags based on your brand voice and content calendar. You review and approve before publishing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected savings:&lt;/strong&gt; 3–5 hours per week on content creation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost:&lt;/strong&gt; ChatGPT Plus at €20/month handles this for most businesses. Custom solutions with brand voice training cost €500–1,500.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Any business with active social media presence.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Appointment Scheduling with AI
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; An AI assistant handles appointment booking via WhatsApp, website chat, or email. It checks availability, suggests times, sends confirmations and reminders, handles rescheduling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected savings:&lt;/strong&gt; 1–2 hours per day for businesses with heavy booking volume. No-show rates drop 30–50% with automated reminders.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost:&lt;/strong&gt; €500–2,000 for a custom bot integrated with your calendar. Tools like Cal.com (free) handle basic scheduling without AI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Clinics, salons, consultants, agencies, fitness studios.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Document Generation
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; AI generates proposals, contracts, reports, and other documents from templates and data. A sales rep fills in key details; AI produces a polished, personalized document in seconds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected savings:&lt;/strong&gt; 1–3 hours per document. For a team creating 10 proposals per week, that is 10–30 hours saved.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost:&lt;/strong&gt; €400–1,200 for a custom template-based generator. PandaDoc and Proposify offer this as SaaS at €30–80/month.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Agencies, consultants, law firms, real estate.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Inventory and Demand Forecasting
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; AI analyzes historical sales data, seasonality, and market trends to predict demand. It suggests optimal stock levels and alerts you when to reorder.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected savings:&lt;/strong&gt; 15–30% reduction in overstock costs. Fewer stockouts mean fewer lost sales.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost:&lt;/strong&gt; €1,000–3,000 for a custom forecasting model. Some ERP systems include basic forecasting features.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Retail, e-commerce, food and beverage, manufacturing.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Recruitment Screening
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; AI screens resumes against job requirements, ranks candidates, and generates shortlists. It can also draft personalized rejection emails and schedule interviews with top candidates.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected savings:&lt;/strong&gt; 5–10 hours per hire on initial screening. Faster time-to-hire means less productivity lost from unfilled positions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost:&lt;/strong&gt; €500–1,500 for a custom screening tool. SaaS options like Greenhouse AI features cost €100–500/month.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Any business hiring regularly (3+ positions per quarter).&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Data Entry and Migration
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; AI extracts data from various sources (PDFs, spreadsheets, emails, images), normalizes it, and enters it into your systems. Particularly powerful for migrating data between tools or consolidating information from multiple sources.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected savings:&lt;/strong&gt; 80–95% reduction in manual data entry time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost:&lt;/strong&gt; €400–2,000 depending on data complexity and volume.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Any business dealing with manual data entry or system migrations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to Start: The AI Automation Priority Matrix
&lt;/h2&gt;

&lt;p&gt;Do not try to automate everything at once. Use this framework:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;High volume + low complexity = automate first.&lt;/strong&gt; Customer FAQ responses, email triage, data entry. These give the fastest ROI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High volume + high complexity = automate with human oversight.&lt;/strong&gt; Lead qualification, document generation. AI does 80% of the work, human reviews.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Low volume + low complexity = automate later or not at all.&lt;/strong&gt; If something takes 10 minutes per week, the automation cost may not be justified.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Low volume + high complexity = keep human for now.&lt;/strong&gt; Strategic decisions, complex negotiations, creative direction.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Real Costs vs. Real Savings
&lt;/h2&gt;

&lt;p&gt;For a typical small business implementing their first three AI automations (customer support chatbot + email triage + lead qualification):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Total implementation cost:&lt;/strong&gt; €1,500–4,000&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monthly running cost:&lt;/strong&gt; €50–200 (AI API calls + hosting)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monthly time saved:&lt;/strong&gt; 40–80 hours&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monthly value of saved time:&lt;/strong&gt; €800–2,000 (at €20–25/hour)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payback period:&lt;/strong&gt; 2–4 months&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;Pick one high-impact, low-complexity automation from this list and implement it. Measure the results over 30 days. Then add the next one. Building incrementally is cheaper and less risky than trying to automate everything at once.&lt;/p&gt;

&lt;p&gt;I specialize in &lt;a href="https://kirweb.site/services/" rel="noopener noreferrer"&gt;AI automation and chatbot development&lt;/a&gt; for small and mid-size businesses. If you want help identifying the best automation opportunities for your business, &lt;a href="https://kirweb.site/#contact" rel="noopener noreferrer"&gt;book a free consultation&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://kirweb.site/blog/ai-automation-small-business-2026/" rel="noopener noreferrer"&gt;kirweb.site&lt;/a&gt;. I build AI chatbots, SaaS platforms, and automation for businesses. &lt;a href="https://kirweb.site/#contact" rel="noopener noreferrer"&gt;Free consultation&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>automation</category>
      <category>smallbusiness</category>
      <category>chatgpt</category>
    </item>
    <item>
      <title>WhatsApp Chatbot for Business: Cost, Features, and How to Get Started</title>
      <dc:creator>Kirill Strelnikov</dc:creator>
      <pubDate>Fri, 27 Feb 2026 05:30:35 +0000</pubDate>
      <link>https://dev.to/kirill_strelnikov_d8546b8/whatsapp-chatbot-for-business-cost-features-and-how-to-get-started-5602</link>
      <guid>https://dev.to/kirill_strelnikov_d8546b8/whatsapp-chatbot-for-business-cost-features-and-how-to-get-started-5602</guid>
      <description>&lt;h2&gt;
  
  
  WhatsApp Chatbot for Business: Cost, Features, and How to Get Started
&lt;/h2&gt;

&lt;p&gt;WhatsApp has over 2 billion active users. For businesses, especially in Europe, Latin America, and Asia, it is often the primary channel customers use to ask questions, place orders, and request support. A WhatsApp chatbot automates these conversations, handling routine inquiries 24/7 while routing complex cases to human agents.&lt;/p&gt;

&lt;p&gt;In this guide I explain what a WhatsApp chatbot can do, how much it costs to build one, and the technical requirements you need to know before starting.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Can a WhatsApp Chatbot Do?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Customer Support Automation
&lt;/h3&gt;

&lt;p&gt;The most common use case. A chatbot handles FAQs (business hours, shipping policies, return processes, pricing), provides order status updates, and collects information before handing off to a human agent when needed. Businesses typically automate 40–70% of incoming messages this way.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sales and Lead Qualification
&lt;/h3&gt;

&lt;p&gt;A chatbot can ask qualifying questions (budget, timeline, requirements), present product catalogs, send quotes, and schedule appointments — all within the WhatsApp conversation. This works especially well for service businesses, real estate, and B2B companies where the sales cycle starts with an inquiry.&lt;/p&gt;

&lt;h3&gt;
  
  
  Order Management
&lt;/h3&gt;

&lt;p&gt;For e-commerce and food delivery, chatbots can take orders, process payments (via payment links or integrated gateways), send order confirmations, and provide delivery tracking. In markets where WhatsApp is dominant, this converts better than a website checkout because customers never leave their preferred app.&lt;/p&gt;

&lt;h3&gt;
  
  
  Appointment Booking
&lt;/h3&gt;

&lt;p&gt;Clinics, salons, consultants, and service providers use WhatsApp chatbots to manage bookings. The bot shows available slots, confirms appointments, sends reminders, and handles rescheduling — reducing no-shows by 30–50%.&lt;/p&gt;

&lt;h3&gt;
  
  
  AI-Powered Conversations
&lt;/h3&gt;

&lt;p&gt;Modern chatbots use large language models (GPT-4, Claude) to have natural, context-aware conversations. Instead of rigid menu-based flows, the bot understands free-text messages and responds intelligently. It can reference your product catalog, knowledge base, or FAQ documentation to give accurate answers in any language your customers speak.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Much Does a WhatsApp Chatbot Cost?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Development Costs
&lt;/h3&gt;

&lt;p&gt;| Tier | Price Range | What You Get &lt;/p&gt;

&lt;p&gt;| Basic | €800–1,500 | FAQ automation, keyword-based responses, single-language, basic analytics, up to 20 conversation flows &lt;/p&gt;

&lt;p&gt;| Advanced | €1,500–3,500 | AI-powered responses (GPT-4/Claude), multi-language, CRM integration, lead capture, conversation memory, handoff to human agents &lt;/p&gt;

&lt;p&gt;| Enterprise | €3,500+ | Custom AI training on your data, multi-agent system, payment processing, advanced analytics dashboard, API integrations, multi-number support &lt;/p&gt;

&lt;h3&gt;
  
  
  Ongoing Costs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;WhatsApp Business API:&lt;/strong&gt; Meta charges per conversation. Business-initiated messages cost €0.05–0.15 depending on the country. User-initiated service conversations are free for the first 1,000 per month.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI API costs:&lt;/strong&gt; If using GPT-4 or Claude, expect €0.01–0.05 per conversation depending on message length and model choice. For most businesses, this is €50–200/month.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hosting:&lt;/strong&gt; A basic server costs €10–50/month. Higher traffic may need €50–200/month.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BSP fees:&lt;/strong&gt; Business Solution Providers like Twilio, 360dialog, or WATI charge €50–200/month for API access and additional features.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Technical Requirements: WhatsApp Business API
&lt;/h2&gt;

&lt;h3&gt;
  
  
  WhatsApp Business App vs. API
&lt;/h3&gt;

&lt;p&gt;The free WhatsApp Business App works for small businesses with low message volume. But for chatbot automation, you need the &lt;strong&gt;WhatsApp Business API&lt;/strong&gt;. The key differences:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Business App:&lt;/strong&gt; Free, manual replies, limited automation (quick replies, greeting messages), single device, no API access.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Business API:&lt;/strong&gt; Programmable, full automation, webhooks, multi-agent support, analytics, message templates, CRM integration. Requires a Business Solution Provider (BSP) or direct access through Meta.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Getting API Access
&lt;/h3&gt;

&lt;p&gt;You can access the WhatsApp Business API through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Meta Cloud API (direct):&lt;/strong&gt; Free to set up through Meta for Developers. You get a test number immediately and can go to production after business verification. This is the most cost-effective option.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BSP (Twilio, 360dialog, WATI, etc.):&lt;/strong&gt; Easier setup, managed infrastructure, additional features like analytics dashboards and template management. Monthly fees apply.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Message Templates
&lt;/h3&gt;

&lt;p&gt;WhatsApp requires pre-approved message templates for business-initiated conversations. These templates must be submitted to Meta for review and typically get approved within 24 hours. Your chatbot can reply freely to user-initiated messages within a 24-hour window without templates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building a WhatsApp Chatbot: Architecture
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Typical Tech Stack
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Backend:&lt;/strong&gt; Python (Django/Flask) or Node.js&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI:&lt;/strong&gt; OpenAI GPT-4o, Anthropic Claude, or open-source models&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WhatsApp API:&lt;/strong&gt; Meta Cloud API or BSP SDK&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database:&lt;/strong&gt; PostgreSQL for conversation history and user data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Queue:&lt;/strong&gt; Redis + Celery for async message processing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hosting:&lt;/strong&gt; VPS, AWS, or DigitalOcean&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Customer sends a message on WhatsApp&lt;/li&gt;
&lt;li&gt;WhatsApp forwards it to your server via webhook&lt;/li&gt;
&lt;li&gt;Your backend processes the message (NLP, AI, business logic)&lt;/li&gt;
&lt;li&gt;The response is sent back via WhatsApp API&lt;/li&gt;
&lt;li&gt;Conversation is logged in your database/CRM&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Choosing Between a No-Code Platform and Custom Development
&lt;/h2&gt;

&lt;h3&gt;
  
  
  No-Code Platforms (ManyChat, Chatfuel, WATI)
&lt;/h3&gt;

&lt;p&gt;Good for simple, template-based flows. You can set up a basic FAQ bot in a few hours. Limitations: rigid conversation flows, limited AI capabilities, vendor lock-in, monthly subscription costs that scale with message volume.&lt;/p&gt;

&lt;h3&gt;
  
  
  Custom Development
&lt;/h3&gt;

&lt;p&gt;Better for businesses that need AI-powered conversations, complex integrations (CRM, ERP, payment systems), or custom business logic. Higher upfront cost but lower per-message cost and full flexibility. You own the code and can modify anything.&lt;/p&gt;

&lt;p&gt;For most businesses doing under 500 conversations per month with simple FAQ needs, a no-code platform works fine. For anything more sophisticated — AI conversations, lead qualification, order processing — custom development pays for itself within months through better conversion rates and lower per-interaction costs.&lt;/p&gt;

&lt;h2&gt;
  
  
  ROI: When Does a WhatsApp Chatbot Pay for Itself?
&lt;/h2&gt;

&lt;p&gt;Consider a service business that receives 50 WhatsApp inquiries per day. A human agent handles each inquiry in 5 minutes on average. That is roughly 4 hours of work per day, or about €2,000/month in labor costs for a dedicated support agent.&lt;/p&gt;

&lt;p&gt;A chatbot that handles 60% of inquiries automatically saves €1,200/month. A custom chatbot costing €2,000 to build pays for itself in under two months. Adding lead qualification and sales automation increases revenue on top of the cost savings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;If you are considering a WhatsApp chatbot for your business, here is how to start:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Define your use case:&lt;/strong&gt; Support automation? Sales? Bookings? Orders?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Map your conversation flows:&lt;/strong&gt; What questions do customers ask? What information do you need?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Choose your approach:&lt;/strong&gt; No-code platform for simple flows, custom development for AI and integrations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Get WhatsApp API access:&lt;/strong&gt; Apply through Meta for Developers or sign up with a BSP.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build and test:&lt;/strong&gt; Start with a few core flows, test with real customers, iterate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I specialize in building &lt;a href="https://kirweb.site/chatbot-whatsapp-empresas/" rel="noopener noreferrer"&gt;custom WhatsApp chatbots&lt;/a&gt; with AI capabilities for businesses across Europe. If you want to discuss your use case, &lt;a href="https://kirweb.site/#contact" rel="noopener noreferrer"&gt;reach out for a free consultation&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://kirweb.site/blog/whatsapp-chatbot-business-cost-guide/" rel="noopener noreferrer"&gt;kirweb.site&lt;/a&gt;. I build AI chatbots, SaaS platforms, and automation for businesses. &lt;a href="https://kirweb.site/#contact" rel="noopener noreferrer"&gt;Free consultation&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>whatsapp</category>
      <category>chatbot</category>
      <category>business</category>
      <category>ai</category>
    </item>
    <item>
      <title>How to Hire a Freelance Developer in Barcelona: Complete Guide for 2026</title>
      <dc:creator>Kirill Strelnikov</dc:creator>
      <pubDate>Fri, 27 Feb 2026 05:30:34 +0000</pubDate>
      <link>https://dev.to/kirill_strelnikov_d8546b8/how-to-hire-a-freelance-developer-in-barcelona-complete-guide-for-2026-3ac5</link>
      <guid>https://dev.to/kirill_strelnikov_d8546b8/how-to-hire-a-freelance-developer-in-barcelona-complete-guide-for-2026-3ac5</guid>
      <description>&lt;h2&gt;
  
  
  How to Hire a Freelance Developer in Barcelona: Complete Guide for 2026
&lt;/h2&gt;

&lt;p&gt;Barcelona has become one of Europe's top tech hubs, home to thousands of skilled freelance developers. Whether you need a web application, a mobile app backend, or an AI-powered chatbot, hiring a freelance developer in Barcelona gives you access to senior talent at rates significantly lower than London, Paris, or San Francisco.&lt;/p&gt;

&lt;p&gt;In this guide I cover everything you need to know: typical rates, where to find developers, how to evaluate candidates, and how to structure a successful engagement.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Barcelona for Freelance Development?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Strong Tech Ecosystem
&lt;/h3&gt;

&lt;p&gt;Barcelona hosts Mobile World Congress, Smart City Expo, and dozens of tech meetups every month. The city has a thriving startup scene with over 2,000 active startups and a growing pool of senior developers who chose freelancing over corporate positions. Many speak three or more languages — English, Spanish, and Catalan at minimum — making communication easy for international clients.&lt;/p&gt;

&lt;h3&gt;
  
  
  Competitive Rates
&lt;/h3&gt;

&lt;p&gt;Freelance developer rates in Barcelona typically range from €40 to €100 per hour depending on experience and specialization. For comparison, equivalent talent in London charges €80–150/hour, and in San Francisco $120–200/hour. This means you can get the same quality of work for 40–60% less.&lt;/p&gt;

&lt;h3&gt;
  
  
  EU Time Zone Advantage
&lt;/h3&gt;

&lt;p&gt;CET/CEST time zone overlaps well with both European and American East Coast business hours. For European companies, same-timezone collaboration means real-time communication during the workday. For US clients, the overlap covers the morning hours, which is usually enough for daily syncs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Typical Rates for Barcelona Freelance Developers in 2026
&lt;/h2&gt;

&lt;p&gt;| Specialization | Junior (1-3 years) | Mid (3-5 years) | Senior (5+ years) &lt;/p&gt;

&lt;p&gt;| Python/Django | €35–50/hr | €50–75/hr | €75–100/hr &lt;/p&gt;

&lt;p&gt;| JavaScript/React | €35–50/hr | €50–80/hr | €80–120/hr &lt;/p&gt;

&lt;p&gt;| AI/ML Engineer | €45–60/hr | €60–90/hr | €90–130/hr &lt;/p&gt;

&lt;p&gt;| Mobile (iOS/Android) | €40–55/hr | €55–80/hr | €80–110/hr &lt;/p&gt;

&lt;p&gt;| DevOps/Cloud | €40–55/hr | €55–85/hr | €85–120/hr &lt;/p&gt;

&lt;p&gt;For fixed-price projects, expect the following ranges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Simple website or landing page:&lt;/strong&gt; €500–2,000&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Web application with backend:&lt;/strong&gt; €2,000–8,000&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SaaS MVP:&lt;/strong&gt; €3,000–15,000&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI chatbot:&lt;/strong&gt; €800–3,000&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Telegram/WhatsApp bot:&lt;/strong&gt; €500–2,500&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where to Find Freelance Developers in Barcelona
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Specialized Portfolios and Personal Websites
&lt;/h3&gt;

&lt;p&gt;The best freelancers maintain their own websites with case studies, pricing, and contact forms. Search for specific skills plus "Barcelona" or "freelance" — for example, &lt;a href="https://kirweb.site/django-developer-barcelona/" rel="noopener noreferrer"&gt;"Django developer Barcelona"&lt;/a&gt;. Developers who invest in their web presence tend to be more professional and reliable.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. LinkedIn
&lt;/h3&gt;

&lt;p&gt;Filter by location (Barcelona Metropolitan Area), set the headline filter to "freelance developer" or the specific technology you need. Look for profiles with recommendations from previous clients, not just colleagues.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Freelance Platforms
&lt;/h3&gt;

&lt;p&gt;Upwork, Toptal, and Malt (popular in Spain) all have Barcelona-based developers. Toptal pre-screens candidates, so you pay a premium but save vetting time. Malt is particularly strong in Spain and France. Upwork gives you the widest selection but requires more due diligence.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Tech Meetups and Communities
&lt;/h3&gt;

&lt;p&gt;Barcelona has active communities: Python Barcelona, Barcelona.js, AI/ML Barcelona, and many more. Attending a meetup (or joining their Slack/Discord) lets you meet developers in person and gauge their expertise from their talks and contributions.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Evaluate a Freelance Developer
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Check Their Portfolio
&lt;/h3&gt;

&lt;p&gt;Look for projects similar to yours. If you need a SaaS platform, a developer who has built three SaaS products will deliver faster and avoid common pitfalls. Ask for specific metrics: How many users does the app serve? What was the uptime? Did they handle the DevOps too?&lt;/p&gt;

&lt;h3&gt;
  
  
  Read Case Studies, Not Just Testimonials
&lt;/h3&gt;

&lt;p&gt;Testimonials are easy to fabricate. Detailed case studies with specific challenges, solutions, and results are much harder to fake and much more informative. A developer who writes about their process shows they think methodically.&lt;/p&gt;

&lt;h3&gt;
  
  
  Technical Assessment
&lt;/h3&gt;

&lt;p&gt;For a backend developer, ask about their approach to database design, authentication, error handling, and deployment. You do not need to give them a coding test — a 30-minute technical conversation is enough to gauge their depth. Ask about trade-offs they have made in previous projects.&lt;/p&gt;

&lt;h3&gt;
  
  
  Communication Skills
&lt;/h3&gt;

&lt;p&gt;A freelancer who communicates clearly, sets expectations, and provides regular updates is worth more than a slightly better coder who disappears for days. During initial conversations, notice how quickly they respond, how clearly they explain concepts, and whether they ask clarifying questions about your project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Structuring a Successful Engagement
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Fixed Price vs. Hourly
&lt;/h3&gt;

&lt;p&gt;For well-defined projects with clear requirements, fixed price works well — you know the total cost upfront. For exploratory work, ongoing maintenance, or projects where requirements may change, hourly billing gives you flexibility. Many freelancers offer both and can advise which model fits your situation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Milestones and Deliverables
&lt;/h3&gt;

&lt;p&gt;Break the project into milestones with specific deliverables. For example: Week 1 — database design and API skeleton. Week 2 — core features. Week 3 — integrations and testing. Each milestone should produce something you can review and test, so problems surface early.&lt;/p&gt;

&lt;h3&gt;
  
  
  Communication Cadence
&lt;/h3&gt;

&lt;p&gt;Agree on a communication schedule upfront. A daily async update (even just a few sentences) plus one weekly video call works well for most projects. Use a shared project management tool (Trello, Linear, or even a shared document) to track progress.&lt;/p&gt;

&lt;h2&gt;
  
  
  Red Flags to Watch For
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No portfolio or case studies:&lt;/strong&gt; Every experienced developer has something to show.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vague estimates:&lt;/strong&gt; "It depends" is not a useful answer. A good developer can give you a range with assumptions stated.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No questions about your project:&lt;/strong&gt; If they immediately quote a price without understanding your requirements, they are guessing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resistance to milestones:&lt;/strong&gt; A professional welcomes structured deliverables because it protects both parties.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Too cheap:&lt;/strong&gt; If someone quotes 50% below market rate, they will either deliver poor quality or disappear mid-project.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Barcelona offers an excellent pool of freelance developers at competitive European rates. The key to a successful hire is doing your homework: check portfolios, read case studies, have a technical conversation, and structure the engagement with clear milestones. Whether you need a &lt;a href="https://kirweb.site/django-developer-barcelona/" rel="noopener noreferrer"&gt;Django developer&lt;/a&gt;, an &lt;a href="https://kirweb.site/ai-chatbot-development/" rel="noopener noreferrer"&gt;AI chatbot specialist&lt;/a&gt;, or a &lt;a href="https://kirweb.site/backend-developer-freelancer/" rel="noopener noreferrer"&gt;backend architect&lt;/a&gt;, the city has talent to match your needs.&lt;/p&gt;

&lt;p&gt;If you are looking for a freelance Python/Django developer in Barcelona with experience in AI, SaaS, and automation, feel free to &lt;a href="https://kirweb.site/#contact" rel="noopener noreferrer"&gt;get in touch&lt;/a&gt; for a free consultation.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://kirweb.site/blog/hire-freelance-developer-barcelona-guide/" rel="noopener noreferrer"&gt;kirweb.site&lt;/a&gt;. I build AI chatbots, SaaS platforms, and automation for businesses. &lt;a href="https://kirweb.site/#contact" rel="noopener noreferrer"&gt;Free consultation&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>freelance</category>
      <category>barcelona</category>
      <category>developer</category>
      <category>hiring</category>
    </item>
  </channel>
</rss>
