Skip to main content

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_key and the app_id of the app you're instrumenting. (project_slug is 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_url defaults to https://ingest.apilens.ai/v1 (override with APILENS_BASE_URL for 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

  1. Verify api_key is valid (and belongs to the right project).
  2. Verify app_id matches an app in that project.
  3. Ensure base_url points to the correct endpoint.
  4. Check application logs for SDK errors.
  5. 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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

apilenss-0.2.0.tar.gz (80.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

apilenss-0.2.0-py3-none-any.whl (24.8 kB view details)

Uploaded Python 3

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

Hashes for apilenss-0.2.0.tar.gz
Algorithm Hash digest
SHA256 ec52dcfe8c1d12c2de5749a31abd1ed8ec31f37c207db6af75e1a3c97e8eb44d
MD5 3a12326e6b07cf7298f6148381b4c92b
BLAKE2b-256 e3571ee00350efbb81ec38a9b315200e4eae5ceae0c8a60c434d93e08ce28eb7

See more details on using hashes here.

Provenance

The following attestation bundles were made for apilenss-0.2.0.tar.gz:

Publisher: workflow.yml on apilens/apilens

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for apilenss-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7ed0dfdd6332bd592a52b1877626ad6fed869d36b35186851410f69b6a3931bc
MD5 c15d9cd938ca09b94170b888fdbc86f2
BLAKE2b-256 f1ea3df0a41de0898de4c7787fbb6d8c1f196c355027cf5dff103b1fda751527

See more details on using hashes here.

Provenance

The following attestation bundles were made for apilenss-0.2.0-py3-none-any.whl:

Publisher: workflow.yml on apilens/apilens

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page