Python SDK

hypequery.serve

Reference for Python Serve, the framework-neutral HTTP API and its adapters.

hypequery.serve needs no web framework. start_server and hypequery dev need uvicorn, from the server extra; jwt_strategy and analytics_token_issuer need the jwt extra.

Building an API

ExportSignaturePurpose
create_apicreate_api(*, auth, credentials=None, tenant=None, security=None, production=None, client=None, datasets=None, metrics=None, discovery=True, limits=(), events=None, base_path="", max_body_bytes=1048576, development_docs=False, openapi=None, client_mount_path="")TypeScript's createAPI. Returns an Api; auth maps a Credential to a Principal or None
Api.add_routeadd_route(method, path, handler, *, public=False, input=None, limits=(), output=None, summary=None, tags=(), documented=True)Your own route. handler is an async function from Call to ServeResponse
Api.handleawait api.handle(request)Serve one ServeRequest with no server, as in tests
Callrequest, state, auth, input, hostWhat a handler receives. call.state.request_id is the request's id
ServeRequest, ServeResponse, Headers, QueryParamsThe framework-neutral request and response. ServeResponse.json(value, status=200, headers=None) builds a JSON answer
OpenApiInfo, openapi_document(api)Describe and return the development OpenAPI 3.1 document

Serving

ExportSignaturePurpose
to_asgi_appto_asgi_app(api, *, lifespan=None)A pure ASGI application, for uvicorn and for mounting in FastAPI, Starlette or any ASGI framework
to_wsgi_appto_wsgi_app(api)A WSGI application, for gunicorn, waitress or any WSGI framework
hypequery.contrib.django.urlsurls(api)URL patterns serving the API natively from Django, for include() below any prefix
hypequery.contrib.flask.blueprintblueprint(api, *, name="hypequery", url_prefix=None)A blueprint serving the API natively from Flask
start_server (alias run_production)start_server(api_or_asgi_app)Run an API created with production=ProductionProfile(...) under uvicorn
serve_dev (alias run_dev)serve_dev(app, *, host="127.0.0.1", port=8000, reload=False)Local development runner for an Api, its ASGI app, or an import string; hypequery dev wraps it

Endpoints

ExportPurpose
create_dataset_endpoint(*, dataset, client, policy=EndpointPolicy(), diagnostics=None, events=None, client_profile=None, include_related=())A dataset query endpoint, for create_api(datasets=...) or .install(api, path)
create_metric_endpoint(*, dataset, measure, name=None, client, policy=…, …)A metric fixed to one dataset measure, for create_api(metrics=...)
add_dataset_endpoint(api, path, *, dataset, client, …, limits=())Create and serve a dataset endpoint at path
add_metric_endpoint(api, path, *, dataset, measure, …, limits=())Create and serve a metric endpoint at path
add_discovery_endpoint(api, path="/discovery", *, registry, policy=…, max_bytes=262144)A frozen projection of the registry, with no physical details
add_client_contract_endpoint(api, path="/client-contract", *, policy=…, max_bytes=262144)Caller-scoped discovery of the API's strict client endpoints
export_client_contract(api, *, production=None, mount_path=None)The build contract of the installed client endpoints; api may be the Api or its ASGI or WSGI app
DiagnosticAccess(authorize, audit)Opt-in, audited SQL diagnostics for trusted callers

Authentication

ExportPurpose
jwt_strategy(*, secret=None, jwks_url=None, issuer=None, audience=None, algorithms=None, leeway=0, map_claims=None, …)Verify JWT bearer tokens, as TypeScript's createJwtStrategy does. Needs the jwt extra
analytics_token_issuer(secret, *, expires_in=timedelta(minutes=15), issuer=None, audience=None, algorithm="HS256")Mint tokens that either language's jwt_strategy accepts
from_context(extract, *, same_origin=True)Authenticate with your app's own login: extract(request) reads the session from request.raw. TypeScript's fromContext
hypequery.contrib.django.django_user(to_principal, *, same_origin=True)from_context for Django's request.user
hypequery.contrib.flask.flask_session(to_principal, *, same_origin=True)from_context for Flask-Login's current_user, or else Flask's session
api_key(*, header="x-api-key")Read a bare API key from a header
bearer_token(*, header="authorization")Read Bearer <token> as RFC 6750 defines it
Credential, CredentialKind, CredentialTransportWhat was presented and how it was read
PrincipalThe authenticated caller, with subject, roles, scopes and tenant_id
default_tenant_resolver(principal)Scope the request to the principal's tenant_id, or to no tenant
InvalidCredential, RequestAuth, Authenticator, TenantResolver, JwtStrategy, AnalyticsTokenIssuer, ClaimsMapper, ContextStrategy, ContextExtractorTypes, and the error to raise for bad credentials

Policy

EndpointPolicy(public=False, required_roles=frozenset(), required_scopes=frozenset(), tenant="optional", max_limit=1000)

tenant is "required", "optional" or "forbidden".

HTTP security

ExportPurpose
HttpSecurity(allowed_hosts, cors=None, trusted_proxies=())The HTTP profile for a served API
CorsPolicy(origins, allow_credentials=False, allow_methods=("GET", "POST"), allow_headers=("Authorization", "Content-Type", "X-Request-ID", "X-Include-Meta", "X-Hypequery-Client-Schema"), max_age=600)Cross-origin access. With no policy, no CORS headers are sent
DEFAULT_MAX_BODY_BYTESThe default request body limit

Rate limiting

RateLimit(*, max=100, window_seconds=60, key=None, store=None, fail_open=True, headers=True, message=…) is a fixed-window limit, for create_api(limits=...), an endpoint's limits= or a route's. MemoryRateLimitStore is the in-process store, and RateLimitStore is the protocol for your own. RateLimitKey types the key function, which receives the ServeRequest and the caller's RequestAuth.

Production

ProductionProfile(host="127.0.0.1", port=8000, allow_external_bind=False, debug=False, reload=False, max_concurrency=32, timeout_seconds=30, max_result_rows=10001, max_result_bytes=8388608, max_threads=4) sets per-process ceilings. create_api refuses it without an HttpSecurity, with development docs, or with from_context(..., same_origin=False).

Errors

Raise ServeError(status, type, message, *, details=None, headers=None) from a handler to return a specific canonical error. type is a ServeErrorType:

TypeMeaning
VALIDATION_ERRORThe request failed validation
UNAUTHORIZEDMissing or invalid credentials
FORBIDDENAuthenticated, but not allowed
NOT_FOUNDUnknown route or resource
PAYLOAD_TOO_LARGEThe body exceeded the limit
RATE_LIMITEDThe rate limit was exceeded
QUERY_FAILUREThe query failed
CLICKHOUSE_UNREACHABLEClickHouse could not be reached
GATEWAY_TIMEOUTThe query timed out
SERVICE_UNAVAILABLEThe service cannot take the request
INTERNAL_SERVER_ERRORAn unexpected failure

Events and request IDs

QueryEvents(*, target, sink) emits query events to a sink, for create_api(events=...) or one endpoint's events=. A handler reads the server-generated request id as call.state.request_id. sanitize_correlation_id(value) validates a caller's correlation id before it is echoed, and MAX_CORRELATION_ID_BYTES is its bound.

On this page

Get early access to hypequery cloud.

Cloud hosts the same model you run in your backend, with a managed API and MCP server, versions, and rollback. We're letting teams in a few at a time. Join the waitlist and we'll reach out when your project can go live.

How do you want to build your analytics layer? (Optional)
What features do you most need? (Optional)

Pick as many as you like.

Only your email is required. All other questions are optional.