Novu
raw JSON → 3.13.0 verified Tue May 12 auth: no python install: verified quickstart: stale
Open-source notification infrastructure. The official Python SDK is novu-py (pip install novu-py, import novu_py), NOT the older community packages novu or novu-python. Current version is 3.13.0 (Jan 2026). Three separate PyPI packages exist with different APIs — using the wrong one is the #1 footgun.
pip install novu-py Common errors
error ModuleNotFoundError: No module named 'novu' ↓
cause The user has installed the official `novu-py` SDK but is attempting to import an older, community-maintained package named `novu` instead of `novu_py`.
fix
Change the import statement from
import novu or from novu import Novu to import novu_py or from novu_py import Novu. error AttributeError: 'Novu' object has no attribute 'trigger' ↓
cause The user has likely imported a `Novu` class from an older, incompatible community package (e.g., `novu` or `novu-python`) but is attempting to call methods (`trigger`, `subscribers`, etc.) that are specific to the official `novu-py` SDK's API.
fix
Ensure the
novu-py package is installed (pip install novu-py) and update the import statement to from novu_py import Novu and instantiate Novu from the novu_py module. error ModuleNotFoundError: No module named 'novu_py' ↓
cause The `novu-py` package is not installed in the active Python environment, or the Python interpreter cannot find the installed package.
fix
Install the official SDK using pip:
pip install novu-py. If already installed, ensure you are running your script in the correct Python environment where novu-py is installed. error requests.exceptions.HTTPError: 401 Client Error: Unauthorized for url ↓
cause The API key (secret key) provided when initializing the `Novu` client is either missing, incorrect, or invalid, preventing successful authentication with the Novu API.
fix
Ensure that a valid
secret_key is passed to the Novu client constructor, preferably by loading it from an environment variable. Example: from novu_py import Novu; import os; novu = Novu(secret_key=os.getenv('NOVU_SECRET_KEY')). error requests.exceptions.HTTPError: 400 Client Error: Bad Request for url: https://api.novu.co/v1/layouts ↓
cause The data provided in the request body (e.g., a DTO object like `novu_py.TriggerEventRequestDto`) does not conform to the Novu API's expected schema, such as a missing required field or incorrect data type.
fix
Consult the Novu API documentation for the specific endpoint being called and ensure all required fields in the Data Transfer Object (DTO) are correctly provided and formatted according to the schema.
Warnings
breaking Three separate PyPI packages exist: novu (community, dead), novu-python (community, dead), novu-py (official, active). pip install novu installs the wrong unmaintained package. LLMs almost always generate the wrong package name. ↓
fix pip install novu-py — import as from novu_py import Novu. Never use pip install novu or pip install novu-python for new projects.
breaking The old community novu package uses EventApi with name= (template name) and recipients= parameters. The official novu-py uses Novu.trigger() with workflow_id= and to= parameters. Code from tutorials using the old package will fail with ImportError or TypeError on the official SDK. ↓
fix Migrate from: EventApi(url, key).trigger(name=, recipients=) to: Novu(secret_key=).trigger(workflow_id=, to=)
breaking novu-py went through extremely rapid versioning: 0.x → 1.x → 3.x in under a year (Jan–Nov 2025). Each major version has breaking changes. Code from tutorials written for 0.x will not work on 3.x. ↓
fix Pin the version in requirements. Check the GitHub releases page before upgrading.
gotcha The Novu class should be used as a context manager in long-lived applications to properly close HTTPX connections. Forgetting to close the client leaks HTTP connections. ↓
fix Use with Novu(secret_key=...) as novu: pattern, or call novu.close() explicitly after use.
gotcha Novu requires subscribers to exist before triggering notifications. Triggering to a non-existent subscriber_id auto-creates it, but without email/phone data the notification may silently fail to deliver. ↓
fix Create or upsert subscribers with full contact info before triggering: novu.subscribers.identify(subscriber_id='user-123', email='user@example.com').
Install
pip install novu pip install novu-python Install compatibility verified last tested: 2026-05-12
python os / libc variant status wheel install import disk
3.10 alpine (musl) novu - - - -
3.10 alpine (musl) novu-py - - 3.02s 37.5M
3.10 alpine (musl) novu-python - - - -
3.10 slim (glibc) novu - - - -
3.10 slim (glibc) novu-py - - 2.09s 37M
3.10 slim (glibc) novu-python - - - -
3.11 alpine (musl) novu - - - -
3.11 alpine (musl) novu-py - - 4.09s 41.3M
3.11 alpine (musl) novu-python - - - -
3.11 slim (glibc) novu - - - -
3.11 slim (glibc) novu-py - - 3.38s 41M
3.11 slim (glibc) novu-python - - - -
3.12 alpine (musl) novu - - - -
3.12 alpine (musl) novu-py - - 3.37s 32.6M
3.12 alpine (musl) novu-python - - - -
3.12 slim (glibc) novu - - - -
3.12 slim (glibc) novu-py - - 3.26s 32M
3.12 slim (glibc) novu-python - - - -
3.13 alpine (musl) novu - - - -
3.13 alpine (musl) novu-py - - 3.19s 32.3M
3.13 alpine (musl) novu-python - - - -
3.13 slim (glibc) novu - - - -
3.13 slim (glibc) novu-py - - 3.13s 32M
3.13 slim (glibc) novu-python - - - -
3.9 alpine (musl) novu - - - -
3.9 alpine (musl) novu-py - - 2.52s 36.6M
3.9 alpine (musl) novu-python - - - -
3.9 slim (glibc) novu - - - -
3.9 slim (glibc) novu-py - - 2.17s 36M
3.9 slim (glibc) novu-python - - - -
Imports
- Novu wrong
# Wrong package — novu community package (unmaintained): from novu.api import EventApi event_api = EventApi('https://api.novu.co/api/', '<NOVU_API_KEY>') event_api.trigger( name='<YOUR_TEMPLATE_NAME>', # old API uses 'name', not 'workflow_id' recipients='<SUBSCRIBER_ID>', # old API uses 'recipients' payload={} )correctfrom novu_py import Novu with Novu(secret_key='YOUR_SECRET_KEY') as novu: response = novu.trigger( workflow_id='welcome-onboarding', to={'subscriber_id': 'user-123'}, payload={'firstName': 'Alice'} )
Quickstart stale last tested: 2026-04-23
from novu_py import Novu
# Initialize with your Novu API key
novu = Novu(secret_key='YOUR_NOVU_API_KEY')
# Trigger a notification workflow
response = novu.trigger(
workflow_id='welcome-email', # workflow ID from Novu dashboard
to={
'subscriber_id': 'user-123',
'email': 'alice@example.com',
'first_name': 'Alice'
},
payload={
'company': 'Acme Corp',
'action_url': 'https://app.example.com'
}
)
print(response)
# Use as context manager for proper cleanup
with Novu(secret_key='YOUR_NOVU_API_KEY') as novu:
novu.trigger(
workflow_id='password-reset',
to={'subscriber_id': 'user-456'},
payload={'reset_url': 'https://app.example.com/reset/token'}
)