diff --git a/apprise/plugins/NotifyTwitter.py b/apprise/plugins/NotifyTwitter.py
index cc68bee6..33f97c97 100644
--- a/apprise/plugins/NotifyTwitter.py
+++ b/apprise/plugins/NotifyTwitter.py
@@ -75,7 +75,7 @@ class NotifyTwitter(NotifyBase):
     service_url = 'https://twitter.com/'
 
     # The default secure protocol is twitter.
-    secure_protocol = 'twitter'
+    secure_protocol = ('twitter', 'tweet')
 
     # A URL that takes you to the setup/help of the specific protocol
     setup_url = 'https://github.com/caronc/apprise/wiki/Notify_twitter'
@@ -221,21 +221,21 @@ class NotifyTwitter(NotifyBase):
             raise TypeError(msg)
 
         # Store our webhook mode
-        self.mode = None \
+        self.mode = self.template_args['mode']['default'] \
             if not isinstance(mode, str) else mode.lower()
 
-        # Set Cache Flag
-        self.cache = cache
-
-        # Prepare Image Batch Mode Flag
-        self.batch = batch
-
         if self.mode not in TWITTER_MESSAGE_MODES:
             msg = 'The Twitter message mode specified ({}) is invalid.' \
                 .format(mode)
             self.logger.warning(msg)
             raise TypeError(msg)
 
+        # Set Cache Flag
+        self.cache = cache
+
+        # Prepare Image Batch Mode Flag
+        self.batch = batch
+
         # Track any errors
         has_error = False
 
@@ -249,7 +249,7 @@ class NotifyTwitter(NotifyBase):
 
             has_error = True
             self.logger.warning(
-                'Dropped invalid user ({}) specified.'.format(target),
+                'Dropped invalid Twitter user ({}) specified.'.format(target),
             )
 
         if has_error and not self.targets:
@@ -261,6 +261,10 @@ class NotifyTwitter(NotifyBase):
             self.logger.warning(msg)
             raise TypeError(msg)
 
+        # Initialize our cache values
+        self._whoami_cache = None
+        self._user_cache = {}
+
         return
 
     def send(self, body, title='', notify_type=NotifyType.INFO, attach=None,
@@ -293,7 +297,7 @@ class NotifyTwitter(NotifyBase):
                     continue
 
                 self.logger.debug(
-                    'Preparing Twiter attachment {}'.format(
+                    'Preparing Twitter attachment {}'.format(
                         attachment.url(privacy=True)))
 
                 # Upload our image and get our id associated with it
@@ -536,16 +540,9 @@ class NotifyTwitter(NotifyBase):
 
         """
 
-        # Prepare a whoami key; this is to prevent conflict with other
-        # NotifyTwitter declarations that may or may not use a different
-        # set of authentication keys
-        whoami_key = '{}{}{}{}'.format(
-            self.ckey, self.csecret, self.akey, self.asecret)
-
-        if lazy and hasattr(NotifyTwitter, '_whoami_cache') \
-                and whoami_key in getattr(NotifyTwitter, '_whoami_cache'):
+        if lazy and self._whoami_cache is not None:
             # Use cached response
-            return getattr(NotifyTwitter, '_whoami_cache')[whoami_key]
+            return self._whoami_cache
 
         # Contains a mapping of screen_name to id
         results = {}
@@ -560,22 +557,11 @@ class NotifyTwitter(NotifyBase):
         if postokay:
             try:
                 results[response['screen_name']] = response['id']
+                self._whoami_cache = {
+                    response['screen_name']: response['id'],
+                }
 
-                if lazy:
-                    # Cache our response for future references
-                    if not hasattr(NotifyTwitter, '_whoami_cache'):
-                        setattr(
-                            NotifyTwitter, '_whoami_cache',
-                            {whoami_key: results})
-                    else:
-                        getattr(NotifyTwitter, '_whoami_cache')\
-                            .update({whoami_key: results})
-
-                    # Update our user cache as well
-                    if not hasattr(NotifyTwitter, '_user_cache'):
-                        setattr(NotifyTwitter, '_user_cache', results)
-                    else:
-                        getattr(NotifyTwitter, '_user_cache').update(results)
+                self._user_cache.update(results)
 
             except (TypeError, KeyError):
                 pass
@@ -595,10 +581,10 @@ class NotifyTwitter(NotifyBase):
         # Build a unique set of names
         names = parse_list(screen_name)
 
-        if lazy and hasattr(NotifyTwitter, '_user_cache'):
+        if lazy and self._user_cache:
             # Use cached response
-            results = {k: v for k, v in getattr(
-                NotifyTwitter, '_user_cache').items() if k in names}
+            results = {
+                k: v for k, v in self._user_cache.items() if k in names}
 
             # limit our names if they already exist in our cache
             names = [name for name in names if name not in results]
@@ -612,7 +598,7 @@ class NotifyTwitter(NotifyBase):
         # https://developer.twitter.com/en/docs/accounts-and-users/\
         #     follow-search-get-users/api-reference/get-users-lookup
         for i in range(0, len(names), 100):
-            # Send Twitter DM
+            # Look up our names by their screen_name
             postokay, response = self._fetch(
                 self.twitter_lookup,
                 payload={
@@ -635,11 +621,7 @@ class NotifyTwitter(NotifyBase):
 
         # Cache our response for future use; this saves on un-nessisary extra
         # hits against the Twitter API when we already know the answer
-        if lazy:
-            if not hasattr(NotifyTwitter, '_user_cache'):
-                setattr(NotifyTwitter, '_user_cache', results)
-            else:
-                getattr(NotifyTwitter, '_user_cache').update(results)
+        self._user_cache.update(results)
 
         return results
 
@@ -686,7 +668,7 @@ class NotifyTwitter(NotifyBase):
             # Determine how long we should wait for or if we should wait at
             # all. This isn't fool-proof because we can't be sure the client
             # time (calling this script) is completely synced up with the
-            # Gitter server.  One would hope we're on NTP and our clocks are
+            # Twitter server.  One would hope we're on NTP and our clocks are
             # the same allowing this to role smoothly:
 
             now = datetime.utcnow()
@@ -810,7 +792,7 @@ class NotifyTwitter(NotifyBase):
 
         return '{schema}://{ckey}/{csecret}/{akey}/{asecret}' \
             '/{targets}/?{params}'.format(
-                schema=self.secure_protocol,
+                schema=self.secure_protocol[0],
                 ckey=self.pprint(self.ckey, privacy, safe=''),
                 csecret=self.pprint(
                     self.csecret, privacy, mode=PrivacyMode.Secret, safe=''),
@@ -818,7 +800,7 @@ class NotifyTwitter(NotifyBase):
                 asecret=self.pprint(
                     self.asecret, privacy, mode=PrivacyMode.Secret, safe=''),
                 targets='/'.join(
-                    [NotifyTwitter.quote('@{}'.format(target), safe='')
+                    [NotifyTwitter.quote('@{}'.format(target), safe='@')
                      for target in self.targets]),
                 params=NotifyTwitter.urlencode(params))
 
@@ -862,6 +844,9 @@ class NotifyTwitter(NotifyBase):
             results['mode'] = \
                 NotifyTwitter.unquote(results['qsd']['mode'])
 
+        elif results['schema'].startswith('tweet'):
+            results['mode'] = TwitterMessageMode.TWEET
+
         results['targets'] = []
 
         # if a user has been defined, add it to the list of targets
diff --git a/test/test_plugin_twitter.py b/test/test_plugin_twitter.py
index 9959124d..5a7dfcc2 100644
--- a/test/test_plugin_twitter.py
+++ b/test/test_plugin_twitter.py
@@ -64,11 +64,11 @@ apprise_url_tests = (
         # Missing Keys
         'instance': TypeError,
     }),
-    ('twitter://consumer_key/consumer_secret/access_token/', {
+    ('twitter://consumer_key/consumer_secret/atoken1/', {
         # Missing Access Secret
         'instance': TypeError,
     }),
-    ('twitter://consumer_key/consumer_secret/access_token/access_secret', {
+    ('twitter://consumer_key/consumer_secret/atoken2/access_secret', {
         # No user mean's we message ourselves
         'instance': NotifyTwitter,
         # Expected notify() response False (because we won't be able
@@ -76,9 +76,9 @@ apprise_url_tests = (
         'notify_response': False,
 
         # Our expected url(privacy=True) startswith() response:
-        'privacy_url': 'twitter://c...y/****/a...n/****',
+        'privacy_url': 'twitter://c...y/****/a...2/****',
     }),
-    ('twitter://consumer_key/consumer_secret/access_token/access_secret'
+    ('twitter://consumer_key/consumer_secret/atoken3/access_secret'
         '?cache=no', {
             # No user mean's we message ourselves
             'instance': NotifyTwitter,
@@ -90,7 +90,7 @@ apprise_url_tests = (
                 'media_id': 123,
             },
         }),
-    ('twitter://consumer_key/consumer_secret/access_token/access_secret', {
+    ('twitter://consumer_key/consumer_secret/atoken4/access_secret', {
         # No user mean's we message ourselves
         'instance': NotifyTwitter,
         # However we'll be okay if we return a proper response
@@ -102,7 +102,7 @@ apprise_url_tests = (
         },
     }),
     # A duplicate of the entry above, this will cause cache to be referenced
-    ('twitter://consumer_key/consumer_secret/access_token/access_secret', {
+    ('twitter://consumer_key/consumer_secret/atoken5/access_secret', {
         # No user mean's we message ourselves
         'instance': NotifyTwitter,
         # However we'll be okay if we return a proper response
@@ -115,7 +115,7 @@ apprise_url_tests = (
     }),
     # handle cases where the screen_name is missing from the response causing
     # an exception during parsing
-    ('twitter://consumer_key/consumer_secret2/access_token/access_secret', {
+    ('twitter://consumer_key/consumer_secret2/atoken6/access_secret', {
         # No user mean's we message ourselves
         'instance': NotifyTwitter,
         # However we'll be okay if we return a proper response
@@ -127,14 +127,14 @@ apprise_url_tests = (
         # due to a mangled response_text we'll fail
         'notify_response': False,
     }),
-    ('twitter://user@consumer_key/csecret2/access_token/access_secret/-/%/', {
+    ('twitter://user@consumer_key/csecret2/atoken7/access_secret/-/%/', {
         # One Invalid User
         'instance': NotifyTwitter,
         # Expected notify() response False (because we won't be able
         # to detect our user)
         'notify_response': False,
     }),
-    ('twitter://user@consumer_key/csecret/access_token/access_secret'
+    ('twitter://user@consumer_key/csecret/atoken8/access_secret'
         '?cache=No&batch=No', {
             # No Cache & No Batch
             'instance': NotifyTwitter,
@@ -143,7 +143,7 @@ apprise_url_tests = (
                 'screen_name': 'user'
             }],
         }),
-    ('twitter://user@consumer_key/csecret/access_token/access_secret', {
+    ('twitter://user@consumer_key/csecret/atoken9/access_secret', {
         # We're good!
         'instance': NotifyTwitter,
         'requests_response_text': [{
@@ -151,21 +151,20 @@ apprise_url_tests = (
             'screen_name': 'user'
         }],
     }),
-    # A duplicate of the entry above, this will cause cache to be referenced
-    # for this reason, we don't even need to return a valid response
-    ('twitter://user@consumer_key/csecret/access_token/access_secret', {
+    ('twitter://user@consumer_key/csecret/atoken11/access_secret', {
         # We're identifying the same user we already sent to
         'instance': NotifyTwitter,
+        'notify_response': False,
     }),
-    ('twitter://ckey/csecret/access_token/access_secret?mode=tweet', {
+    ('tweet://ckey/csecret/atoken12/access_secret', {
         # A Public Tweet
         'instance': NotifyTwitter,
     }),
-    ('twitter://user@ckey/csecret/access_token/access_secret?mode=invalid', {
+    ('twitter://user@ckey/csecret/atoken13/access_secret?mode=invalid', {
         # An invalid mode
         'instance': TypeError,
     }),
-    ('twitter://usera@consumer_key/consumer_secret/access_token/'
+    ('twitter://usera@consumer_key/consumer_secret/atoken14/'
         'access_secret/user/?to=userb', {
             # We're good!
             'instance': NotifyTwitter,
@@ -180,19 +179,19 @@ apprise_url_tests = (
                 'id': 123,
             }],
         }),
-    ('twitter://ckey/csecret/access_token/access_secret', {
+    ('twitter://ckey/csecret/atoken15/access_secret', {
         'instance': NotifyTwitter,
         # throw a bizzare code forcing us to fail to look it up
         'response': False,
         'requests_response_code': 999,
     }),
-    ('twitter://ckey/csecret/access_token/access_secret', {
+    ('twitter://ckey/csecret/atoken16/access_secret', {
         'instance': NotifyTwitter,
         # Throws a series of connection and transfer exceptions when this flag
         # is set and tests that we gracfully handle them
         'test_requests_exceptions': True,
     }),
-    ('twitter://ckey/csecret/access_token/access_secret?mode=tweet', {
+    ('twitter://ckey/csecret/atoken17/access_secret?mode=tweet', {
         'instance': NotifyTwitter,
         # Throws a series of connection and transfer exceptions when this flag
         # is set and tests that we gracfully handle them
@@ -320,7 +319,7 @@ def test_plugin_twitter_general(mock_post, mock_get):
     assert obj.send(body="test") is True
 
     # Flush our cache forcing it's re-creating
-    del NotifyTwitter._user_cache
+    NotifyTwitter._user_cache = {}
     assert obj.send(body="test") is True
 
     # Cause content response to be None
@@ -350,7 +349,7 @@ def test_plugin_twitter_general(mock_post, mock_get):
     # Set ourselves up to handle whoami calls
 
     # Flush out our cache
-    del NotifyTwitter._user_cache
+    NotifyTwitter._user_cache = {}
 
     response_obj = {
         'screen_name': screen_name,
@@ -367,12 +366,12 @@ def test_plugin_twitter_general(mock_post, mock_get):
     assert obj.send(body="test") is True
 
     # Alter the key forcing us to look up a new value of ourselves again
-    del NotifyTwitter._user_cache
-    del NotifyTwitter._whoami_cache
+    NotifyTwitter._user_cache = {}
+    NotifyTwitter._whoami_cache = None
     obj.ckey = 'different.then.it.was'
     assert obj.send(body="test") is True
 
-    del NotifyTwitter._whoami_cache
+    NotifyTwitter._whoami_cache = None
     obj.ckey = 'different.again'
     assert obj.send(body="test") is True
 
@@ -440,10 +439,17 @@ def test_plugin_twitter_dm_attachments(mock_get, mock_post):
         'id': 9876,
     }
 
+    # Epoch time:
+    epoch = datetime.utcfromtimestamp(0)
+
     # Prepare a good DM response
     good_dm_response = mock.Mock()
     good_dm_response.content = dumps(good_dm_response_obj)
     good_dm_response.status_code = requests.codes.ok
+    good_dm_response.headers = {
+        'x-rate-limit-reset': (datetime.utcnow() - epoch).total_seconds(),
+        'x-rate-limit-remaining': 1,
+    }
 
     # Prepare bad response
     bad_response = mock.Mock()
@@ -540,7 +546,10 @@ def test_plugin_twitter_dm_attachments(mock_get, mock_post):
         body='body', title='title', notify_type=NotifyType.INFO,
         attach=attach) is False
 
-    assert mock_get.call_count == 0
+    assert mock_get.call_count == 1
+    assert mock_get.call_args_list[0][0][0] == \
+        'https://api.twitter.com/1.1/account/verify_credentials.json'
+
     # No get request as cached response is used
     assert mock_post.call_count == 2
     assert mock_post.call_args_list[0][0][0] == \