API Lens Python SDK with OpenTelemetry-based ingest forwarding
Project description
API Lens Python SDK
Production-ready Python ingest client for API Lens with OpenTelemetry integration.
Easy setup: your API key is project-level. You only need two values — the project's
api_keyand theapp_idof the app you're instrumenting. (project_slugis no longer required — the server derives the project from the key.)from fastapi import FastAPI from apilens.fastapi import ApiLensMiddleware app = FastAPI() app.add_middleware(ApiLensMiddleware, api_key="apilens_xxx", app_id="orders-api")
base_urldefaults tohttps://ingest.apilens.ai/v1(override withAPILENS_BASE_URLfor local dev).
Framework support matrix
| Framework | Integration Module | Integration Type | Client Type |
|---|---|---|---|
| FastAPI | apilens.fastapi |
ASGI Middleware | AsyncIO |
| Starlette | apilens.starlette |
ASGI Middleware | AsyncIO |
| Django REST Framework | apilens.django |
Django Middleware | Threading |
| Django Ninja | apilens.django |
Django Middleware | Threading |
| Flask | apilens.flask |
WSGI Wrapper | Threading |
| Litestar | apilens.litestar |
Plugin Protocol | AsyncIO |
| BlackSheep | apilens.blacksheep |
ASGI Middleware | AsyncIO |
What this SDK includes
- batched + retrying ingest client (
ApiLensClient) - OpenTelemetry span exporter (
apilens.otel) for teams already on OTel - first-class framework integrations listed above
- automatic request/response payload sampling (size-limited)
Install
pip install apilenss
With framework support:
pip install 'apilenss[all]'
# or only one
pip install 'apilenss[fastapi]'
pip install 'apilenss[flask]'
Local development install (from repo):
pip install ./packages/sdk-python
pip install './packages/sdk-python[all]'
The two values you need
api_key— created on a project in the dashboard. It is project-level: one key works for every app in that project.app_id— the slug of the specific app you're instrumenting, so the SDK knows which app the traffic belongs to.
Find both in the API Lens Dashboard: the API key on the project's API-keys page, the app slug on the app's page.
Quick start (manual capture)
from apilens import ApiLensClient, ApiLensConfig
client = ApiLensClient(
ApiLensConfig(api_key="apilens_xxx") # project-level key
)
client.capture(
app_id="orders-api", # which app in the project
method="GET",
path="/health",
status_code=200,
response_time_ms=12.4,
)
client.shutdown(flush=True)
FastAPI
No OpenTelemetry instrumentation is required for endpoint + payload monitoring.
from fastapi import FastAPI
from apilens.fastapi import ApiLensMiddleware, set_consumer
app = FastAPI()
app.add_middleware(
ApiLensMiddleware,
api_key="apilens_xxx", # project-level key
app_id="orders-api", # which app
)
Environment variables (recommended):
import os
from fastapi import FastAPI
from apilens.fastapi import ApiLensMiddleware
app = FastAPI()
app.add_middleware(
ApiLensMiddleware,
api_key=os.getenv("APILENS_API_KEY"),
app_id=os.getenv("APILENS_APP_ID"),
)
Identifying consumers
APILens never infers who the caller is — it does not read your auth headers, JWTs or session. You attach the identity explicitly after your own auth resolves. This keeps you in control of what identity (if any) leaves your process.
Use set_consumer(...) (alias: track_consumer). The request argument is
optional — omit it when calling from a lifecycle hook that runs before
the framework request object is in scope.
Flask — @app.before_request
from flask import Flask, g
from apilens import ApiLensClient, ApiLensConfig
from apilens.flask import instrument_flask, set_consumer
app = Flask(__name__)
client = ApiLensClient(ApiLensConfig(api_key="apilens_xxx"))
instrument_flask(app, client, app_id="my-flask-app")
@app.before_request
def identify_consumer():
if g.current_user: # however YOUR app sets g.current_user
set_consumer( # no request arg — uses a contextvar
identifier=g.current_user["email"], # required: stable id
name=g.current_user.get("name"), # optional: display name
group=g.current_user.get("role"), # optional: team / tier / org
)
FastAPI — Dependency injection
from fastapi import Depends, FastAPI, Request
from apilens.fastapi import ApiLensMiddleware, set_consumer
app = FastAPI()
app.add_middleware(ApiLensMiddleware, api_key="apilens_xxx", app_id="my-fastapi-app")
async def consumer_dep(request: Request):
user = getattr(request.state, "user", None) # set by your auth middleware
if user:
set_consumer(
request, # pass Request for ASGI state
identifier=user.id, # required
name=getattr(user, "username", None), # optional
group=getattr(user, "org_slug", None), # optional
)
@app.get("/orders")
async def list_orders(_: None = Depends(consumer_dep)):
...
Django — settings or view
Option A — centralized via APILENS_GET_CONSUMER in settings.py (runs
on every request automatically):
# settings.py
MIDDLEWARE = [
# ...
"apilens.django.ApiLensDjangoMiddleware",
]
APILENS_API_KEY = "apilens_xxx"
APILENS_APP_ID = "my-django-app"
def get_consumer(request):
if request.user.is_authenticated:
return {
"identifier": request.user.email,
"name": request.user.get_full_name(),
"group": getattr(request.user, "role", ""),
}
return None
APILENS_GET_CONSUMER = get_consumer
# or as a dotted import path:
# APILENS_GET_CONSUMER = "myapp.consumers.get_consumer"
Option B — inline from a view (explicit call wins over the setting):
from apilens.django import set_consumer
def my_view(request):
if request.user.is_authenticated:
set_consumer(request, identifier=request.user.email)
...
Starlette
from starlette.applications import Starlette
from starlette.requests import Request
from apilens import ApiLensClient, ApiLensConfig
from apilens.starlette import instrument_app, set_consumer
app = Starlette()
client = ApiLensClient(ApiLensConfig(api_key="apilens_xxx"))
instrument_app(app, client, app_id="my-starlette-app")
@app.route("/orders")
async def list_orders(request: Request):
user = request.state.user # set by your auth middleware
if user:
set_consumer(request, identifier=user["id"], name=user.get("name"))
...
What set_consumer accepts
| Arg | Type | Notes |
|---|---|---|
identifier |
str |
Required. Stable id — email, user id, API key prefix… |
name |
str | None |
Human-readable label shown in the dashboard |
group |
str | None |
Team, plan tier, org, role — use for grouping |
The function also accepts a plain string via normalize_consumer(value) or a
dict / object with id/identifier/name/group attributes, so you can
pass your user model directly to the centralized callbacks.
An explicit set_consumer(...) call always wins over a get_consumer callback.
Starlette
from starlette.applications import Starlette
from apilens import ApiLensClient, ApiLensConfig
from apilens.starlette import instrument_app
app = Starlette()
client = ApiLensClient(ApiLensConfig(api_key="apilens_xxx"))
instrument_app(app, client, app_id="orders-api")
Flask
from flask import Flask
from apilens import ApiLensClient, ApiLensConfig
from apilens.flask import instrument_app
app = Flask(__name__)
client = ApiLensClient(ApiLensConfig(api_key="apilens_xxx"))
instrument_app(app, client, app_id="orders-api")
Django (DRF + Django Ninja)
Add the middleware and two settings:
MIDDLEWARE = [
# ...
"apilens.django.ApiLensDjangoMiddleware",
]
APILENS_API_KEY = "apilens_xxx" # project-level key
APILENS_APP_ID = "orders-api" # which app
Litestar
from litestar import Litestar
from apilens import ApiLensClient, ApiLensConfig
from apilens.litestar import ApiLensPlugin
client = ApiLensClient(ApiLensConfig(api_key="apilens_xxx"))
app = Litestar(route_handlers=[], plugins=[ApiLensPlugin(client=client, app_id="orders-api")])
BlackSheep
from blacksheep import Application
from apilens import ApiLensClient, ApiLensConfig
from apilens.blacksheep import instrument_app
app = Application()
client = ApiLensClient(ApiLensConfig(api_key="apilens_xxx"))
instrument_app(app, client, app_id="orders-api")
Configuration Options
| Parameter | Required | Default | Description |
|---|---|---|---|
api_key |
Yes | - | Project-level API key from the dashboard |
app_id |
Yes | - | Slug of the app being instrumented |
project_slug |
No | derived from key | Only needed for clarity/validation; the key already identifies the project |
base_url |
No | https://ingest.apilens.ai/v1 |
API Lens ingest endpoint |
environment |
No | production |
Environment name (e.g., production, staging, dev) |
enable_request_logging |
No | True |
Enable request/response logging |
log_request_body |
No | False |
Log request body (up to max size) |
log_response_body |
No | False |
Log response body (up to max size) |
Notes
- Default flush interval:
3s - Default batch size:
200 - Max ingest batch payload sent per request: follows backend limit (
<= 1000) - Call
client.shutdown(flush=True)on graceful shutdown
Troubleshooting
Data not appearing in the dashboard
- Verify
api_keyis valid (and belongs to the right project). - Verify
app_idmatches an app in that project. - Ensure
base_urlpoints to the correct endpoint. - Check application logs for SDK errors.
- Wait up to ~30s for data to appear (batching delay).
422 Unprocessable Entity
A 422 from the ingest endpoint usually means app_id is missing or doesn't match
an app in your key's project. Set app_id to the app's slug from the dashboard.
Support
- 📧 Email: hello@apilens.ai
- 📖 Documentation: https://apilens.ai/docs
- 🐛 Issues: https://github.com/apilens/apilens/issues
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file apilenss-0.2.0.tar.gz.
File metadata
- Download URL: apilenss-0.2.0.tar.gz
- Upload date:
- Size: 80.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ec52dcfe8c1d12c2de5749a31abd1ed8ec31f37c207db6af75e1a3c97e8eb44d
|
|
| MD5 |
3a12326e6b07cf7298f6148381b4c92b
|
|
| BLAKE2b-256 |
e3571ee00350efbb81ec38a9b315200e4eae5ceae0c8a60c434d93e08ce28eb7
|
Provenance
The following attestation bundles were made for apilenss-0.2.0.tar.gz:
Publisher:
workflow.yml on apilens/apilens
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
apilenss-0.2.0.tar.gz -
Subject digest:
ec52dcfe8c1d12c2de5749a31abd1ed8ec31f37c207db6af75e1a3c97e8eb44d - Sigstore transparency entry: 1778595570
- Sigstore integration time:
-
Permalink:
apilens/apilens@2ebb83af2ea8e2703642b0a5fb7f07c8419a5b15 -
Branch / Tag:
refs/tags/apilens-sdk-v0.2.0 - Owner: https://github.com/apilens
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
workflow.yml@2ebb83af2ea8e2703642b0a5fb7f07c8419a5b15 -
Trigger Event:
push
-
Statement type:
File details
Details for the file apilenss-0.2.0-py3-none-any.whl.
File metadata
- Download URL: apilenss-0.2.0-py3-none-any.whl
- Upload date:
- Size: 24.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7ed0dfdd6332bd592a52b1877626ad6fed869d36b35186851410f69b6a3931bc
|
|
| MD5 |
c15d9cd938ca09b94170b888fdbc86f2
|
|
| BLAKE2b-256 |
f1ea3df0a41de0898de4c7787fbb6d8c1f196c355027cf5dff103b1fda751527
|
Provenance
The following attestation bundles were made for apilenss-0.2.0-py3-none-any.whl:
Publisher:
workflow.yml on apilens/apilens
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
apilenss-0.2.0-py3-none-any.whl -
Subject digest:
7ed0dfdd6332bd592a52b1877626ad6fed869d36b35186851410f69b6a3931bc - Sigstore transparency entry: 1778595740
- Sigstore integration time:
-
Permalink:
apilens/apilens@2ebb83af2ea8e2703642b0a5fb7f07c8419a5b15 -
Branch / Tag:
refs/tags/apilens-sdk-v0.2.0 - Owner: https://github.com/apilens
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
workflow.yml@2ebb83af2ea8e2703642b0a5fb7f07c8419a5b15 -
Trigger Event:
push
-
Statement type: