reqly
API monitoring for Python that tells you what broke, when, and which deploy did it.
FastAPI · Flask · Django · Starlette · Litestar · any WSGI/ASGI app, in one line.
Documentation · Quickstart · Live demo · GitHub
Install
pip install reqly
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")
That's it: every route now reports its latency, errors, status codes and release to your own Reqly collector. No decorators, no agent, no vendor account.
What you get
| 📈 Real percentiles | p50 / p95 / p99 per route and per service, merged exactly across routes and time |
| 🚀 Deploy-aware | The release is picked up from your CI or host (GITHUB_SHA, RENDER_GIT_COMMIT, …); every alert says which release was running |
| 🔔 Alerts that explain | Hourly checks against each route's weekday × hour baseline, naming the host, error type and clients behind a spike; to Slack, Discord or a webhook |
| 💬 Ask Reqly | "Why did /orders start failing?" answered from your own data, every number checked |
| 🎯 SLOs | Availability and latency objectives with error budgets and burn-rate alerts |
| 🧾 OpenAPI drift | Undocumented, unused and deprecated-but-called endpoints (push_openapi=True) |
| 👥 API consumers | Who calls your API and who an incident hit, with ids hashed in the SDK |
| 💸 LLM cost per route | Tokens and cost per route and model, and an alert when it spikes |
Your framework
| Framework | What to write |
|---|---|
| FastAPI, Starlette, Litestar, Flask | reqly.instrument(app, service_name="..."), detected automatically |
| Django, DRF, Django Ninja | "reqly.integrations.django.ReqlyMiddleware" first in MIDDLEWARE |
| Bottle, Pyramid, Falcon, any WSGI app | app = reqly.instrument_wsgi(app, route_resolver=...) |
| Any ASGI app | app = reqly.instrument_asgi(app, route_resolver=...) |
# settings.py (Django)
MIDDLEWARE = ["reqly.integrations.django.ReqlyMiddleware", ...]
REQLY = {"service_name": "checkout-api", "api_key": "your-ingest-key"}
# Bottle: tell Reqly where the route template is
app = reqly.instrument_wsgi(app, service_name="checkout-api",
route_resolver=lambda environ: environ["bottle.route"].rule)
Routes are recorded as templates (/users/{id}), never raw paths; requests no route
matched become __unmatched__, so 404 scanners can't flood your data.
Who is calling, and what it costs
reqly.instrument(app, consumer_header="X-API-Key", consumer_salt=os.environ["REQLY_CONSUMER_SALT"])
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)
Consumer ids are HMAC-SHA256-hashed with your salt before they leave the app; the collector never sees an API key.
An alert 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
Configuration
Pass options to instrument() or set environment variables (argument → environment variable → default).
| Argument | Environment variable | Default |
|---|---|---|
service_name |
REQLY_SERVICE_NAME |
the script name |
collector_url |
REQLY_COLLECTOR_URL |
http://localhost:8000 |
api_key |
REQLY_API_KEY |
None |
release |
REQLY_RELEASE, then CI variables |
auto-detected |
environment |
REQLY_ENVIRONMENT |
None |
sample_rate |
REQLY_SAMPLE_RATE |
1.0 |
flush_interval_seconds |
REQLY_FLUSH_INTERVAL_SECONDS (or _MS) |
5.0; a full batch is sent at once |
max_batch_size / max_queue_size |
REQLY_MAX_BATCH_SIZE / REQLY_MAX_QUEUE_SIZE |
200 / 2000 |
ignore_routes |
REQLY_IGNORE_ROUTES |
/health,/metrics |
consumer_header / consumer |
REQLY_CONSUMER_HEADER / — |
None |
consumer_salt / hash_consumer |
REQLY_CONSUMER_SALT / REQLY_HASH_CONSUMER |
None / True |
push_openapi |
REQLY_PUSH_OPENAPI |
False (FastAPI, Litestar) |
Every option, explained: Python SDK docs.
Built to stay out of your way
- ⚡ ~13 µs per request on FastAPI and Starlette, ~33 µs on Flask (benchmark)
- 🛡️ Fail-open: an internal error is logged once and instrumentation turns itself off; it never raises into your app
- 🧵 Off the request path: a background thread ships batches with strict timeouts, so a slow collector never blocks a request
- 📦 Bounded: a fixed-size queue (oldest dropped first) and route templates only
- 🔁 Safe retries on 408, 429 and 5xx, deduplicated by the collector
- 🍴 Pre-fork servers (gunicorn
--preload, uWSGI) restart the shipper in each worker
Compatibility
| Python | 3.9 – 3.13 |
| FastAPI | 0.100+, including app.mount()ed sub-apps |
| Starlette · Litestar · Flask | 0.27+ · 2.0+ · 2.3+ |
| Django | 4.2+, sync and async views; DRF and Django Ninja |
| Collector | consumer and LLM views need 0.8.0+, push_openapi 0.7.0+ |
Not on Python? There's a Node.js SDK, and any language can report through OpenTelemetry.
Run the collector
The SDK sends to a Reqly collector you host (TimescaleDB + collector + dashboard):
git clone https://github.com/tanisheesh/reqly && cd reqly && docker compose up -d
# or on Kubernetes
helm install reqly oci://ghcr.io/tanisheesh/charts/reqly -n reqly --create-namespace
Quickstart · Deploy to production · Changelog · License: GPL-3.0
Metadata
Release files for reqly 0.5.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| reqly-0.5.3.tar.gz | 38.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| reqly-0.5.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 70.3 kB
Release files / reqly-0.5.3.tar.gz
| Download URL | reqly-0.5.3.tar.gz |
|---|---|
| Size | 38.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
83fac655ae93887a5103cc58083376ae6f158f0a379f6ad6063ade6439cc5fa0
|
|
BLAKE2b-256 checksum How to use checksums |
79aa8bd4bf0dfdc53f4eb0d68305c28191fd3c1d5e2e12b5f903f0895f96f34b
|
| 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 logRelease files / reqly-0.5.3-py3-none-any.whl
| Download URL | reqly-0.5.3-py3-none-any.whl |
|---|---|
| Size | 31.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2717611dcd70f67c499732b9b2ca43583e86b23231025f8e856480ed40f7cded
|
|
BLAKE2b-256 checksum How to use checksums |
851156989326a0c5801368a142e76285fed8d31d0df26d80f49cc81f93c039bc
|
| 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