github caronc/apprise v2.0.0
Apprise v2.0.0

3 hours ago

What's Changed

🎉 Welcome to Apprise v2.0.0! 🎉

This is the first official release of Apprise v2. It's a big one. Much of how Apprise works under the hood was rebuilt, and many features people have asked for over the years are finally here:

  • template variables to keep secrets out of your config files
  • tag priorities and escalation
  • smarter retries
  • timeouts
  • a detailed AppriseResult object returned from every notify() call
  • ...and much more

Note: ⚠️ This is a breaking release for anyone using Apprise v1.x. If you are a developer embedding Apprise in your own project, please read the Breaking Changes section below before upgrading. If you are not ready to move yet, pin your pyproject.toml or requirements.txt to apprise >=1.0.0, <2.0.0. The v1 codebase lives on in the apprise-v1-release branch. I will keep patching it for security issues and bugs where practical for a year or two, but v2.x is the way forward.

⚠️ Breaking Changes

  • notify() now returns an AppriseResult object instead of True/False/None
    • bool(result) still gives you True/False, so most existing if apobj.notify(...): checks keep working. None can't come back from bool(), so a "nothing matched" result now reads as False.
    • The object also holds the logs for each individual service called, plus a status for each one, so your application can report exactly what worked and what didn't.
    • The status values line up with the CLI exit codes:
      • SUCCESS (0) - every service was notified successfully (the old True)
      • FAILURE (1) - the notification failed (the old False)
      • NOMATCH (3) - nothing matched the tags given, or no services were loaded (the old None)
      • PARTIAL (4) - some services succeeded but at least one failed (v1 returned False here)
      • TIMEOUT (5) - a service didn't finish before its time limit (new in v2)
  • Multiple body formats per service
    • In v1 each service supported a single body format. Services can now support more than one when the upstream service allows it (Telegram, for example, accepts both Markdown and HTML).
    • Apprise picks the best matching format automatically based on the format you send with, so it no longer rewrites your content when the service can already accept it as-is.
    • You can also choose the format yourself in the URL with ?format=.
  • Consistent error handling
    • Every error Apprise raises now comes from a single AppriseException family, so developers can catch everything in one place.
    • Each error still behaves like the plain Python error it used to be (a bad setting is still a ValueError, a disk problem is still an OSError), so existing try/except blocks keep working.
  • Deprecations removed - Apprise v2 starts with a clean slate:
    • The APPRISE_CONFIG environment variable is now APPRISE_CONFIG_PATH. The old name still works for now, but prints a deprecation warning.
    • The Email options use_pgp= and ?pgpkey= (deprecated back in v1) have been removed. Use pgp_mode= and ?pgppub= instead.
    • Bad settings in a URL (such as an unknown ?format=) now fail with a clear error message instead of quietly doing something unexpected.

📣 New Notification Services:

🐞 Bugfixes

  • DingTalk Markdown messages now set msgtype to markdown so they actually get delivered by @xtqxk in #1733
  • OneSignal now honors language= and keeps it in url() by @MaxFreedomPollard in #1720
  • SIGNL4 now keeps its query parameters in url() by @MaxFreedomPollard in #1732
  • VAPID now delivers to the subscription endpoint instead of a per-mode base URL by @RT530 in #1728
  • Fluxer now works with self-hosted servers by using the correct URL by @LaaZa in #1740
  • ntfy attach= filename detection fixed, and filename= is kept in url() by @SulimanAbdulrazzaq in #1744
  • Compressed IPv6 addresses are now handled properly in #1734
  • Fixed an issue with YAML/config parsing in #1743
  • Fixed a crash when reading certain responses back from Amazon SES/SNS in #1666
  • Markdown that gets cut in half by a message split is now repaired, so you no longer see broken links and stray * characters, in #1666
  • Fixed a test suite issue that made tests run far longer than needed (a full coverage run went from about 26 min to under 15 min) in #1666
  • ...and many more small bugfixes, cleanups and internal consistency improvements
  • Keep CC display names in url() for mailgun, ses, smtp2go and sparkpost by @SulimanAbdulrazzaq in #1749
  • Fixed o365:// inline attachment contentType sent as a literal string by @Rodrigo-Palma in #1747

💡 Features

  • Template Variables - YAML configurations can now use ${NAME} placeholders to keep secrets (API keys, passwords) out of your config file. Declare the names you use in a new template: section, then supply the values any of these ways:
    • on the command line: --template-var api_key=abc123 (or -tv)
    • from the environment: APPRISE_TEMPLATE_API_KEY=abc123
    • as a default written directly in the template: section
    • through the API: apobj.notify(body='test', template={'api_key': 'abc123'})
  • Tag Priorities & Escalation - tags can now carry a priority, such as 2:alerts. Using --tag alerts notifies your first priority group, and only moves on to the next group if something failed. Handy for "text me only if the email didn't go through". You can still target one group exactly with --tag "2:alerts".
  • Smarter Retries - ?retry= now keeps track of what already went out. If you're sending to 3 people and only 1 fails, the retry only re-sends to the one that failed instead of spamming everyone again. Retries can also be set on the fly with --tag "alerts:3".
  • Timeouts - you can now cap how long a notification is allowed to take instead of waiting forever on a hung server:
    • --limit 10 caps the entire run
    • --service-limit 5 caps each service on its own (defaults to 60 seconds)
  • Parallel delivery by default - notifications are now sent at the same time, so one slow service no longer holds up the rest. Use --disable-async if you prefer the old one-at-a-time behavior.
  • Native async support - developers embedding Apprise into their own async applications can now use await apobj.async_notify().
  • Message overflow/split handling was reworked and improved
  • HTML to Markdown conversion was reworked and improved
  • The apprise:// service (for talking to an Apprise API server) no longer puts your configuration ID in the web address, which keeps it out of server access logs
  • Improved secure logging so sensitive values are kept out of logs (CWE-312) in #1736
  • Matrix: added opt-in automatic SAS verification by @vampywiz17 in #1718
  • macOS: support for macOS v26 terminal-notifier in #1724
    • More improvements to the macOS terminal-notifier wrapper followed in #1742

❤️ Life-Cycle Support

  • Apprise v2 Release in #1666
    • The v1 codebase was branched off as apprise-v1-release for anyone who needs to stay on v1
  • Apprise can now be installed and managed with uv (see Installation below)
  • Multi-language support was added to line up with Apprise Mobile

Installation

Apprise is available on PyPI through pip:

# Install Apprise v2.0.0 from PyPI
pip install apprise==2.0.0

If you use uv, you can install the apprise command in its own isolated environment, or add Apprise to your uv project:

# Install the apprise command
uv tool install apprise==2.0.0

# Or add it to your own project
uv add "apprise>=2.0.0,<3.0.0"

Not ready for v2 yet? Stay on the v1 series with:

pip install "apprise>=1.0.0,<2.0.0"

New Contributors

Full Changelog: v1.13.1...v2.0.0

Don't miss a new apprise release

NewReleases is sending notifications on new releases.