DEV Community

German Yamil
German Yamil

Posted on

How to Self-Publish a Technical Ebook with Python: From Outline to KDP in 4-6 Hours

Four to six hours is the realistic window to go from a topic outline to a live KDP listing, using a Python pipeline. Here is the exact breakdown.

The Timeline

Hour Work
0:00–0:30 Outline, niche validation, KDP keyword research
0:30–1:30 Run generation pipeline (Claude API, 10 chapters)
1:30–2:00 Review and fix validation errors
2:00–2:30 Compile EPUB, check in Calibre
2:30–3:30 Cover design (Canva or automated)
3:30–4:30 KDP dashboard: metadata, categories, pricing
4:30–5:00 Gumroad listing (parallel distribution)
5:00–6:00 Buffer for re-uploads, preview errors, fixes

The generation and compilation steps are automated. You spend your time on judgment calls: is the outline good, is the cover clear, are the chapter titles searchable.

Step 1: Outline Generation

import anthropic

def generate_outline(topic: str, target_pages: int = 80) -> str:
    client = anthropic.Anthropic()
    response = client.messages.create(
        model="claude-sonnet-4-5",
        max_tokens=1024,
        messages=[{
            "role": "user",
            "content": (
                f"Create a detailed outline for a {target_pages}-page technical ebook "
                f"on: {topic}\n\n"
                "Format: numbered chapters with 3-5 bullet sub-topics each. "
                "Target audience: working developers. "
                "Include practical code examples in every chapter."
            )
        }]
    )
    return response.content[0].text

outline = generate_outline("FastAPI production deployment on AWS")
print(outline)
Enter fullscreen mode Exit fullscreen mode

Spend 15 minutes reviewing the outline before running generation. Fixing the outline is free. Regenerating 10 chapters because the structure was wrong costs time and API credits.

Step 2: Chapter Generation with Checkpointing

import json
from pathlib import Path

CHECKPOINT = Path("checkpoint.json")

def load_checkpoint() -> dict:
    if CHECKPOINT.exists():
        return json.loads(CHECKPOINT.read_text())
    return {}

def save_checkpoint(data: dict):
    CHECKPOINT.write_text(json.dumps(data, indent=2))

def generate_chapters(client, chapters: list[dict]) -> Path:
    out_dir = Path("chapters")
    out_dir.mkdir(exist_ok=True)
    checkpoint = load_checkpoint()

    for ch in chapters:
        slug = ch["slug"]
        if slug in checkpoint:
            print(f"  Skip (cached): {slug}")
            continue

        print(f"  Generating: {ch['title']}")
        msg = client.messages.create(
            model="claude-sonnet-4-5",
            max_tokens=4096,
            messages=[{
                "role": "user",
                "content": (
                    f"Write chapter '{ch['title']}' for a technical ebook.\n"
                    f"Topics: {', '.join(ch['topics'])}\n"
                    "Requirements: ~1200 words, working Python examples, no filler."
                )
            }]
        )
        content = msg.content[0].text
        path = out_dir / f"{slug}.md"
        path.write_text(content)
        checkpoint[slug] = str(path)
        save_checkpoint(checkpoint)

    return out_dir
Enter fullscreen mode Exit fullscreen mode

Step 3: EPUB Compilation for KDP

KDP has specific requirements that trip up first-time publishers:

  • EPUB 3.0 or higher
  • No DRM-incompatible fonts embedded without license
  • Cover image: exactly 2560 x 1600 px (or 1:1.6 ratio)
  • File size: under 650 MB (rarely an issue for text books)
  • No JavaScript in EPUB content
import subprocess
from pathlib import Path

def compile_epub(
    chapters_dir: Path,
    output: Path,
    title: str,
    author: str,
    cover: Path,
    css: Path | None = None
) -> Path:
    chapter_files = sorted(chapters_dir.glob("*.md"))
    if not chapter_files:
        raise ValueError("No chapter files found")

    cmd = [
        "pandoc",
        "--from", "markdown+fenced_code_blocks",
        "--to", "epub3",
        "--metadata", f"title={title}",
        "--metadata", f"author={author}",
        "--metadata", "lang=en-US",
        "--epub-cover-image", str(cover),
        "--toc",
        "--toc-depth", "2",
        "-o", str(output),
    ]
    if css:
        cmd += ["--css", str(css)]
    cmd += [str(f) for f in chapter_files]

    result = subprocess.run(cmd, capture_output=True, text=True)
    if result.returncode != 0:
        raise RuntimeError(f"Pandoc error:\n{result.stderr}")

    size_kb = output.stat().st_size // 1024
    print(f"EPUB ready: {output} ({size_kb} KB)")
    return output
Enter fullscreen mode Exit fullscreen mode

Validate the output in Calibre's EPUB viewer before uploading. KDP's previewer is slower and less informative about formatting issues.

Step 4: Cover at KDP Spec

from PIL import Image, ImageDraw, ImageFont

def generate_cover(
    title: str,
    subtitle: str,
    output_path: Path,
    bg_color: tuple = (15, 23, 42),   # dark slate
    text_color: tuple = (248, 250, 252)
) -> Path:
    # KDP required: 2560 x 1600 px minimum at 300 DPI
    W, H = 1600, 2560
    img = Image.new("RGB", (W, H), bg_color)
    draw = ImageDraw.Draw(img)

    # Title block — use a real font in production
    draw.text((80, 200), title, fill=text_color)
    draw.text((80, 380), subtitle, fill=(148, 163, 184))

    img.save(output_path, "JPEG", quality=95, dpi=(300, 300))
    print(f"Cover: {output_path} ({W}x{H})")
    return output_path
Enter fullscreen mode Exit fullscreen mode

Step 5: KDP Metadata Checklist

When you reach the KDP dashboard, have these ready before you start:

  • Title (exact, no subtitle in title field)
  • Subtitle (separate field)
  • Two categories (use the lookup, not guessing)
  • 7 keywords — use Publisher Rocket or manual search volume checks
  • Price: $9.99 US is the sweet spot for 70% royalty with good conversion
  • Description: first 150 characters appear above the fold on mobile

Break-Even Math

At $12.99 launch price with 70% KDP royalty:

Royalty per sale = $12.99 × 0.70 = $9.09
Delivery fee    = ~$0.05 (text-only ebook)
Net per sale    = ~$9.04

Pipeline costs:
  Claude API     = ~$1.50/book
  Cover design   = $0 (automated) or $15 (Fiverr)
  KDP enrollment = $0

Break-even = ceil($0 / $9.04) = 0 (no fixed monthly cost for KDP alone)
Enter fullscreen mode Exit fullscreen mode

If you also run a Gumroad listing ($20/month for Creator plan), break-even is 3 sales. The second book is pure catalog leverage — same Gumroad subscription, no additional fixed cost.

What the 4-6 Hours Does Not Include

  • Editing for depth (plan an extra 2–4 hours for technical accuracy review)
  • SEO optimization of the KDP listing after publication
  • Marketing and promotional work

The pipeline gets you to "published and purchasable." Getting to "selling consistently" is a separate loop.


The complete pipeline — all scripts, prompts, validation logic, cover generator, and KDP checklist — is packaged in the Python Ebook Automation Pipeline ($12.99, 30-day refund).


📋 Free: AI Publishing Checklist — 7 steps to ship a technical ebook with Python (PDF, free)

Full pipeline + 10 scripts: germy5.gumroad.com/l/xhxkzz — $12.99 launch price

Top comments (0)