DEV Community

CBT Tools
CBT Tools

Posted on

I Built a CBT Thought Journal in 50 Lines of Python 🧠

Last week I caught myself thinking: "I always mess everything up."

My therapist would recognize this as overgeneralization — a cognitive distortion where one mistake becomes "everything." But I didn't want to wait for my next session to check. So I built a journal that flags distortions automatically, using a free API I deployed myself.

Here's the whole thing in 50 lines.

The API: CBT Thought Analyzer

I deployed a CBT (Cognitive Behavioral Therapy) thought analyzer as a free API. It detects:

  • 11 cognitive distortions (catastrophizing, all-or-nothing, overgeneralization, mind reading, fortune telling, should statements, labeling, personalization, etc.)
  • 8 procrastination patterns (perfectionism block, fear of failure, task overwhelm, etc.)
  • 4 attachment styles (Secure, Preoccupied, Fearful, Dismissing)

No AI model needed — it uses evidence-based keyword pattern matching for instant analysis. Based on David Burns' Feeling Good framework.

Endpoint: https://cbt-thought-analyzer.onrender.com

The Journal: 50 Lines of Python

import requests
from datetime import datetime
import json

BASE_URL = "https://cbt-thought-analyzer.onrender.com"

def analyze_thought(thought: str) -> dict:
    """Send a thought to the CBT analyzer and get distortions back."""
    resp = requests.post(f"{BASE_URL}/analyze", json={"text": thought}, timeout=30)
    resp.raise_for_status()
    return resp.json()

def check_procrastination(description: str) -> dict:
    """Check which procrastination patterns are keeping you stuck."""
    resp = requests.post(f"{BASE_URL}/procrastination", json={"text": description}, timeout=30)
    resp.raise_for_status()
    return resp.json()

def journal_entry(thought: str) -> None:
    """Write a journal entry with automatic CBT analysis."""
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M")

    print(f"\n📝 {timestamp}")
    print(f"Thought: \"{thought}\"\n")

    # Analyze for0 for cognitive distortions
    result = analyze_thought(thought)
    distortions = result.get("distortions", [])

    if distortions:
        print("🔍 Detected distortions:")
        for d in distortions:
            print(f"{d['name']} (confidence: {d['confidence']}%)")
            print(f"{d['reframe']}")
    else:
        print("✅ No distortions detected — this thought looks balanced.")

    # Check for procrastination patterns
    proc = check_procrastination(thought)
    patterns = proc.get("patterns", [])

    if patterns:
        print("\n⏰ Procrastination patterns:")
        for p in patterns:
            print(f"{p['name']}: {p['intervention']}")

# --- Daily Journal ---
thoughts = [
    "I always mess everything up.",
    "I can't start this project until I have the perfect plan.",
    "If I fail this presentation, my career is over.",
]

for thought in thoughts:
    journal_entry(thought)
    print("-" * 50)
Enter fullscreen mode Exit fullscreen mode

What the Output Looks Like

📝 2026-09-19 09:30
Thought: "I always mess everything up."

🔍 Detected distortions:
  • Overgeneralization (confidence: 92%)
    → "Always" is a strong word. Can you think of a specific exception?
  • Labeling (confidence: 78%)
    → You're labeling yourself based on one event. You are not your mistakes.

⏰ Procrastination patterns:
  • All-or-nothing approach: Try breaking the task into smaller steps.
--------------------------------------------------
📝 2026-09-19 09:30
Thought: "I can't start this project until I have the perfect plan."

🔍 Detected distortions:
  • Should statements (confidence: 85%)
    → "Can't" until "perfect" is a should statement. What's "good enough"?
  • All-or-nothing (confidence: 88%)
    → Perfect vs. not-started is a false binary. What's the middle ground?

⏰ Procrastination patterns:
  • Perfectionism block: Start with a rough draft. You can refine later.
  • Waiting for motivation: Action precedes motivation, not the other way.
--------------------------------------------------
Enter fullscreen mode Exit fullscreen mode

How It Works

The API uses keyword pattern matching — no AI model, no API key needed for the free tier. Each distortion has a set of linguistic markers:

  • Overgeneralization: "always", "never", "every time", "nobody"
  • Catastrophizing: "if...then disaster", "what if...terrible"
  • Should statements: "should", "must", "have to", "can't until"
  • Mind reading: "they think", "everyone knows", "he probably"

When you send a thought, the API matches it against these patterns and returns the distortion name, a confidence score (based on how many markers matched), and a reframe prompt — a question that helps you challenge the distortion.

Why This Is Better Than a Chatbot

  1. Instant — no waiting for an LLM to generate a response
  2. Transparent — you can see exactly which keywords triggered the detection
  3. Free — no API costs, no rate limits on the free tier
  4. Private — your thoughts aren't sent to an AI company's servers
  5. Evidence-based — the patterns come from David Burns' Feeling Good, not a model's guess

Try It Yourself

# Test the API with curl
curl -X POST https://cbt-thought-analyzer.onrender.com/analyze \
  -H "Content-Type: application/json" \
  -d '{"text": "I always fail at everything."}'

# Or with Python
pip install requests
python -c "
import requests
r = requests.post('https://cbt-thought-analyzer.onrender.com/analyze', json={'text': 'I always fail at everything.'})
print(r.json())
"
Enter fullscreen mode Exit fullscreen mode

What's Next

I'm working on:

  • A RapidAPI listing so you can subscribe with a single click
  • A Python SDK and JavaScript SDK for easier integration
  • A Postman collection for testing all endpoints
  • An OpenAPI spec for auto-generating clients in any language

The API is open and free to use right now. If you build something with it, let me know in the comments — I'd love to see what you create.


This is part of a CBT toolkit — 24 free mental health tools, all open source.

Top comments (0)