Skip to main content

Karak

⚠️ Experimental: This project is a proof-of-concept exploring free-threaded Python (PEP 703) for HTTP frameworks. Not production-ready.

A pure-Python HTTP framework built for free-threaded Python 3.13+. No async/await — just threads with true parallelism.

2-5x faster than FastAPI on real workloads.

Website: karak.dev

Requirements

  • Python 3.13+ with free-threading enabled (python3.13t)
  • uv package manager

Installation

uv add karak

uv add karak[fast]

Development Setup

git clone https://github.com/grandimam/karak.git
cd karak

# Install
uv sync

# Run
uv run python examples/basic.py

# Test
curl http://localhost:8000/
curl http://localhost:8000/items/1
curl -X POST http://localhost:8000/items -H "Content-Type: application/json" -d '{"name":"Widget","price":9.99}'

Running Benchmarks

# Install dev dependencies
uv sync --dev

# Run benchmark
uv run python benchmarks/run_benchmark.py 1000 10

Quick Start

from typing import Annotated
from pydantic import BaseModel
from karak import Karak, Depends

app = Karak()

class Item(BaseModel):
    name: str
    price: float

@app.get("/")
def index() -> dict:
    return {"message": "Hello, World!"}

@app.get("/items/{item_id}")
def get_item(item_id: int) -> dict:
    return {"id": item_id}

@app.post("/items")
def create_item(body: Item) -> Item:
    return body

if __name__ == "__main__":
    app.run(host="127.0.0.1", port=8000, workers=4)

Features

  • Pure Python: No C extensions, no Rust, no Cython
  • Free-threaded: True parallelism without the GIL (Python 3.13t)
  • Type-driven: Pydantic models auto-parsed from request body
  • Dependency injection: Depends() with request-scoped caching
  • HTTP Keep-alive: Connection reuse for high throughput
  • Radix tree router: O(1) route matching
  • orjson support: Optional 3-5x faster JSON serialization
  • Minimal: ~500 lines of code in 5 files

Benchmarks

System

Component Value
CPU Apple M2 Pro
Cores 12
Python 3.13.0 (free-threaded)
Platform Darwin arm64

High Concurrency (2000 requests, 100 concurrent clients)

Scenario Free Threaded (16 threads) FastAPI (async) Difference
JSON 8,418 req/s 4,509 req/s Free Threaded: +87%
CPU Bound 1,425 req/s 266 req/s Free Threaded: +435%

Standard Load (1000 requests, 20 concurrent clients)

Scenario Free Threaded (4 threads) FastAPI (async) Difference
JSON 9,287 req/s 4,377 req/s Free Threaded: +112%
DB Query 8,284 req/s 2,302 req/s Free Threaded: +260%
CPU Bound 880 req/s 264 req/s Free Threaded: +233%

Thread Scaling (CPU-bound workload)

Workers req/s Scaling
4 608 1.0x
8 1,172 1.9x
16 1,297 2.1x
32 1,391 2.3x

Analysis

  • I/O-bound (JSON, DB): 2-3.5x faster due to simpler threading model and shared memory
  • CPU-bound: 5x faster — free-threaded Python enables true parallelism while async is single-threaded
  • Scales with cores: Adding threads directly improves CPU-bound throughput
  • Latency: Karak achieves lower p99 latency under load (no async task scheduling overhead)

Architecture

┌─────────────────────────────────────────────────────────┐
│                       Karak App                         │
│              (app.py: DI, validation, handlers)         │
├─────────────────────────────────────────────────────────┤
│                     Radix Router                        │
│              (router.py: O(1) route matching)           │
├─────────────────────────────────────────────────────────┤
│                    Request / Response                   │
│               (types.py: dataclasses)                   │
├─────────────────────────────────────────────────────────┤
│                      HTTP Parser                        │
│            (http.py: parse/write HTTP/1.1)              │
├─────────────────────────────────────────────────────────┤
│                   ThreadPoolExecutor                    │
│         (server.py: sockets, keep-alive, workers)       │
└─────────────────────────────────────────────────────────┘

Project Structure

src/karak/
├── __init__.py   # exports
├── app.py        # Karak, Depends, DI resolution
├── router.py     # RadixRouter, O(1) matching
├── types.py      # Request, Response, HTTPException
├── server.py     # Server, ThreadPool, keep-alive
└── http.py       # HTTPParser, write_response

Why Free-Threaded Python?

Traditional Python has the GIL (Global Interpreter Lock), which prevents true parallelism in threads. Web frameworks work around this using:

  • Async/await (FastAPI, Starlette): Cooperative multitasking
  • Multiprocessing (Gunicorn, uvicorn): Separate processes with IPC overhead

Free-threaded Python (PEP 703) removes the GIL, enabling:

  • Simple synchronous code that runs in parallel
  • Shared memory between threads (no serialization)
  • Lower overhead than multiprocessing

Limitations

  • Experimental and not battle-tested
  • HTTP/1.1 only (no HTTP/2, no WebSocket)
  • No middleware system (yet)
  • C extensions with internal locks don't parallelize

License

MIT

Metadata

Release files for karak 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for karak 0.1.0
File Size Uploaded Provenance Actions
karak-0.1.0.tar.gz 25.6 kB Without provenance Details for karak-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for karak 0.1.0
File Interpreter ABI Platform Actions
karak-0.1.0-py3-none-any.whl Python 3 none any Details for karak-0.1.0-py3-none-any.whl

Total release size: 35.5 kB

Release files / karak-0.1.0.tar.gz

Download URL karak-0.1.0.tar.gz
Size 25.6 kB
Tags Source
SHA-256 checksum
How to use checksums
ed2a7cdc74defe480f43ec13593cab7a6ed51c44d2c69017aa51e0fdc0b87318
BLAKE2b-256 checksum
How to use checksums
9a288d707a0fb0e8da22cc11ecd4b8e26baf52e2471ac542a844b7ec75ac03ce
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.0

Release files / karak-0.1.0-py3-none-any.whl

Download URL karak-0.1.0-py3-none-any.whl
Size 9.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
460f2efd4cedb0075d6bc931ca881951464d89367a08b2adaa392bc18fe24e32
BLAKE2b-256 checksum
How to use checksums
46fd1201107cccfb2a35d845045dabc71ed33abd6cb7bda42cafd6d90b665d00
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.0

Security for karak 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page