Skip to main content

reqly

Self-hosted API monitoring for FastAPI, Flask, Django, Starlette and Litestar — two lines of code. Latency percentiles, error rates and release tracking for every route, shipped to your own Reqly collector — which turns them into deploy-aware hourly alerts and weekly AI anomaly reports.

PyPI Python License: GPL v3


Install

pip install reqly

Usage

FastAPI

import reqly
from fastapi import FastAPI

app = FastAPI()
reqly.instrument(
    app,
    service_name="checkout-api",
    collector_url="https://reqly.example.com",
    api_key="your-ingest-key",
)
# Every route is now tracked: latency, errors, status codes, release

Flask

import reqly
from flask import Flask

app = Flask(__name__)
reqly.instrument(app, service_name="checkout-api")  # settings from REQLY_* env vars

Starlette / Litestar — same call:

reqly.instrument(app, service_name="checkout-api")

Django (also Django REST Framework and Django Ninja) — Django has no app object, so add the middleware first in MIDDLEWARE:

# settings.py
MIDDLEWARE = [
    "reqly.integrations.django.ReqlyMiddleware",
    # ...
]
REQLY = {"service_name": "checkout-api", "api_key": "your-ingest-key"}  # optional

Any other WSGI or ASGI app (Bottle, Pyramid, Falcon, CherryPy, a bare ASGI app) — wrap it and serve the result. A route_resolver returns the route template, because only the framework knows it; without one every request is recorded as __unmatched__, never as a raw path:

app = reqly.instrument_wsgi(
    app, service_name="checkout-api",
    route_resolver=lambda environ: environ["bottle.route"].rule,  # Bottle
)
# Pyramid: environ["bfg.routes.route"].pattern; ASGI: reqly.instrument_asgi(app, route_resolver=...)

Who is calling — tag each request with its API consumer. The id is hashed (HMAC-SHA256 with your secret salt) before it leaves the app:

reqly.instrument(app, consumer_header="X-API-Key", consumer_salt=os.environ["REQLY_CONSUMER_SALT"])
# or any logic: consumer=lambda info: info.headers.get("x-tenant-id")

LLM cost per route — record token usage where you call a model; the collector prices it:

completion = client.chat.completions.create(model="gpt-4o-mini", messages=messages)
reqly.record_llm_response(completion)   # OpenAI / Anthropic responses, or:
reqly.record_llm_usage("gpt-4o-mini", input_tokens=1200, output_tokens=240)

instrument() detects the framework by itself — no decorators, no middleware to wire up. Routes are recorded as templates in one style across frameworks: Django's users/<int:pk>/ and DRF's ^users/(?P<pk>[^/.]+)/$ both become /users/{pk}/. The release you're running is picked up automatically from your CI or host (GITHUB_SHA, RENDER_GIT_COMMIT, VERCEL_GIT_COMMIT_SHA, …), so deploys show up in Reqly with no extra code.

What you get

From the SDK, per request: method, route template (/orders/{id}, never the raw path), status code, duration, error type, host, release, environment and request/response body size.

In the Reqly dashboard and collector:

  • p50 / p95 / p99 latency per route and per service — real percentiles from mergeable sketches, not the max of per-route numbers
  • Error rates, status codes and top routes over 1h / 6h / 24h / 7d
  • Deploy markers and per-release health — each release's error rate and p95
  • Hourly alerts to Slack, Discord or a webhook when a route breaks from its usual weekday-hour pattern, with root-cause hints
  • Top consumers — requests and error rate per API client, and which clients an incident hit (in the alert itself)
  • LLM cost per route — tokens and estimated spend per route and model
  • API surface vs your OpenAPI spec — undocumented endpoints that get traffic, documented ones nobody calls, and deprecated ones still in use (push_openapi=True)
  • Weekly AI report — statistics find the anomalies, Groq (gpt-oss-120b) writes the summary; plain-text fallback without an API key

An alert from the demo data looks like this:

🔴 Anomaly — flask-demo /orders (Friday 15:00-16:00 UTC, z=5.37)
• error rate 30.0% vs 2.2% usual · p95 6588ms vs 1576ms usual
• running release v2 — vs v1: errors 2.6% → 33.1%, p95 2072ms → 4501ms
• 100% of errors came from host pod-3, which served 23% of requests

Not on Python? Node, Java, Go and .NET apps can report to the same collector through OpenTelemetry — no Reqly SDK needed. See the OpenTelemetry guide.

Configuration

Every option can be passed to instrument() or set as an environment variable. Resolution order: argument → environment variable → default.

argument environment variable default
service_name REQLY_SERVICE_NAME sys.argv[0] basename
collector_url REQLY_COLLECTOR_URL http://localhost:8000
api_key REQLY_API_KEY None
release REQLY_RELEASE, then CI variables (GITHUB_SHA, CI_COMMIT_SHA, RENDER_GIT_COMMIT, VERCEL_GIT_COMMIT_SHA, RAILWAY_GIT_COMMIT_SHA, HEROKU_SLUG_COMMIT, K_REVISION, …) auto-detected, else None
environment REQLY_ENVIRONMENT None
sample_rate REQLY_SAMPLE_RATE 1.0
flush_interval_seconds REQLY_FLUSH_INTERVAL_SECONDS 5.0
max_batch_size REQLY_MAX_BATCH_SIZE 200
max_queue_size REQLY_MAX_QUEUE_SIZE 2000
ignore_routes REQLY_IGNORE_ROUTES (comma-separated) /health,/metrics
consumer_header REQLY_CONSUMER_HEADER None — header that identifies the caller, e.g. X-API-Key
consumer — None — callable(RequestInfo) -> str | None, instead of a header
consumer_salt REQLY_CONSUMER_SALT None — secret for hashing consumer ids (set it)
hash_consumer REQLY_HASH_CONSUMER True — False sends ids unhashed (only for non-secret ids)
push_openapi REQLY_PUSH_OPENAPI False — upload the app's OpenAPI spec (FastAPI, Litestar) on the first request
capture_request_body REQLY_CAPTURE_REQUEST_BODY False (not implemented yet)

With sample_rate below 1.0, request counts in the dashboard are the sampled volume; latency percentiles and error rates stay unbiased.

Design guarantees

Small overhead — about 13 µs per request on FastAPI and Starlette and 33 µs on Flask, measured in-process with consumer tracking on and the shipper running (benchmark).

Fail-open — any internal SDK error is caught and logged once; instrumentation disables itself rather than raise into your app. A slow or unreachable collector never blocks request threads — shipping happens on a background thread with strict HTTP timeouts.

Bounded cardinality — routes are recorded as the framework's matched template (/users/{id}), never the raw path (/users/123). Unmatched paths (404s, scanners) collapse into a single __unmatched__ bucket.

Bounded memory — events wait in a fixed-size in-memory queue; under backpressure the oldest events are dropped and counted instead of growing without limit.

Safe retries — batches are retried with exponential backoff on 408, 429 and any 5xx (for example a collector restart behind a proxy); other 4xx responses are dropped immediately. Every event carries a unique event_id the collector deduplicates on, so a retry never double-counts.

Pre-fork servers — under gunicorn --preload (or uWSGI without lazy-apps) each forked worker restarts its own flush thread and HTTP connection pool, so workers' events are shipped instead of silently queuing forever.

Compatibility

Supported
Python 3.9 – 3.13
FastAPI 0.100+ (including routes in app.mount()ed sub-apps)
Starlette 0.27+ (including Mount)
Litestar 2.0+
Flask 2.3+
Django 4.2+, sync and async views; DRF and Django Ninja
Other WSGI / ASGI any, with instrument_wsgi() / instrument_asgi() and a route_resolver
Collector any version; release, environment and body sizes are stored by collector 0.3.0+ and ignored by older ones; push_openapi needs 0.7.0+; consumer and LLM views need 0.8.0+

Self-hosting the collector

The SDK sends data to a Reqly collector you run. The full stack — collector, TimescaleDB and dashboard — starts with Docker Compose:

git clone https://github.com/tanisheesh/reqly.git
cd reqly
docker compose up -d

Setup, configuration and AWS deployment: docs/SETUP.md · infra/DEPLOY.md · ingest API spec

Live demo

Reqly monitors EventFlow, a Flask event management app, in production:

Log in as Administrator (admin@eventhub.com / Admin@123) → click Metrics in the nav.

Changelog

See CHANGELOG.md.

License

GPL-3.0-or-later — see LICENSE.

Metadata

Release files for reqly 0.5.2

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

Source distribution (sdist)

Source distribution for reqly 0.5.2
File Size Uploaded
reqly-0.5.2.tar.gz 39.1 kB Details

Built distribution (wheel)

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

Total release size: 71.1 kB

Release files / reqly-0.5.2.tar.gz

Download URL reqly-0.5.2.tar.gz
Size 39.1 kB
Tags Source
SHA-256 checksum
How to use checksums
1df87bf26de63997cbfee3e3b700543cc2503933de5983597407e53c7b0c816d
BLAKE2b-256 checksum
How to use checksums
49298a136cfd5280e670a496cb12fc53e6c33fc6dffdddab2bdd7d907e10d9b4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 10, 2026.

Transparency log

Release files / reqly-0.5.2-py3-none-any.whl

Download URL reqly-0.5.2-py3-none-any.whl
Size 32.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e545dfbda5d0c9f89ee5744843daa0cd5815890ccd4ae254b2fb9527df20adff
BLAKE2b-256 checksum
How to use checksums
84515113dbca1ffc6aa39e367ead3c42ad3ae8cddf34b7a94935a06ee4b61395
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 10, 2026.

Transparency log

Release history Release notifications | RSS feed

0.5.6

2 release files

0.5.5

2 release files

0.5.4

2 release files

0.5.3

2 release files

This release

0.5.2 This release

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

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