diff --git a/docs/usage.rst b/docs/usage.rst
index 9378a7c..8ec230f 100644
--- a/docs/usage.rst
+++ b/docs/usage.rst
@@ -10,7 +10,6 @@ To use PyPeri in a project::
     >>> from pyperi import Peri
     >>> pp = Peri()
     >>> user_info = pp.get_user_info('376827')
-    >>> print(user_info['username'])
     george_clinton
 
 
@@ -225,107 +224,6 @@ Example usage::
     }
 
 
-
-get_web_data_store(url)
------------------------
-
-Data in the Web API is contained within the JSON found in the `data-store`
-HTML data attribute on a `<div id="page-container">`. The
-`get_web_data_store(url)` method grabs this data from the given URL and
-returns it as a Dict.
-
-This can be a useful tool during debugging and development.
-
-Example usage::
-
-    >>> pp.get_web_data_store('https://www.periscope.tv/george_clinton')
-    {'Auth': {'cookie': None, 'user': None},
-     'Blocked': {},
-     'Broadcast': {'error': {'isNotFound': False,
-                             'isServerError': False,
-                             'isUnavailable': False},
-                   'failed': [],
-                   'forceUpdate': False,
-                   'pending': []},
-     ...
-     'Replay': {'available': False,
-                'duration': 1,
-                'elapsed': 0,
-                'percentage': 0,
-                'replayAvailable': False,
-                'requestedPosition': None},
-     ...
-     'ServiceToken': {'channels': {'failed': False,
-                                   'pending': False,
-                                   'token': None},
-                      'safety': {'failed': False, 'pending': False, 'token': None}},
-     'SessionToken': {'authed': {'broadcastHistory': {'expiry': 1482031213812,
-                                                      'token': {'session_id': '1gJPr-JgpLDOA7UvVagJfGZ4_rUK6acqwXFLzkYW_MLAHJXABHtpcI8M_MhVHCtIkAjVLoETaQyeOSVGCujUOw1oVza00M78tOGiG'}},
-                                 'serviceToken': {'expiry': 1482031213812,
-                                                  'token': {'session_id': '1KQrcgw8pD_sWSG5q7xz2NX_T0uTiQsnQrG109M7k6wvH0xFaOfRrIejX8tcbz3WcyNduut19WBhUUW_EymQ7fwDY6Pj5IUYLKdrh'}},
-                                 'thumbnailPlaylist': {'expiry': 1482031213812,
-                                                       'token': {'session_id': '1sgxhozS2x3xlaHXOu6p7d8-SCAWeAdAQCRbaMnCSx3lx3R1QIdgdPWvXY4tx7X5yzPVb1va2HnPbVKYwyu75TyKBrUHephsMdZttl0bwpi9xogmE'}}},
-                      'public': {'broadcastHistory': {'expiry': 1482031213812,
-                                                      'token': {'session_id': '1ZRK3wcy1OwIkNSyfOYPW6cmC53DrI2o9W742xvH7H16jcOX8dPXaa74wYeGuk21N5wcsP7sffZmhRox8EE1Y9vkPYPS0QOqDO2jh3D3ujCoufkSv'}},
-                                 'serviceToken': {'expiry': 1482031213812,
-                                                  'token': {'session_id': '1xI6XnW-7lsGUIkDIqHJLobjwXtcwHqttcP4R4OhLyeezHTOTp3F5ay7oLym6lPOWa85-fElUN7kqfi2Lmz509aJIBCD1i7Um90kMBOKSxm8p'}},
-                                 'thumbnailPlaylist': {'expiry': 1482031213812,
-                                                       'token': {'session_id': '1FCYL0froR-yukXuob4fDX9D1BxZLsErK0dhr2RJrR-4uaPWMbJ08m9IbeMpy7GhCTWIhNhm9mgpuPVQJ6D252J4ynAV2MLAi9lgV7TqdAsbT9x_x1klILS-z'}}}},
-     'Tracking': {'type': 'periscope/USER_BROADCAST_HISTORY_REQUEST',
-                  'userId': '376827'},
-     'User': {'error': {},
-              'failed': [],
-              'failedFollowing': [],
-              'failedUnfollowing': [],
-              'pending': [],
-              'pendingFollowing': [],
-              'pendingUnfollowing': []},
-     'UserBroadcastHistory': {'broadcastIds': None,
-                              'error': None,
-                              'failed': [],
-                              'pending': []},
-     'UserBroadcastHistoryCache': {'histories': {}},
-     'VideoPlayer': {'appAvailable': False,
-                     'audio': 'OFF',
-                     'autoplay': False,
-                     'cinema': False,
-                     'dimensions': {'height': 0, 'width': 0},
-                     'includeVideoUI': False,
-                     'isAutoplay': False,
-                     'isCinemaPlayer': False,
-                     'isConnected': False,
-                     'isConnecting': False,
-                     'isExpectingVideo': False,
-                     'isInterstitialPresentation': True,
-                     'isLiveMode': False,
-                     'isLoadingVideo': False,
-                     'isPaused': False,
-                     'isPlayback': False,
-                     'isPlaying': False,
-                     'isReplayMode': False,
-                     'isStalled': False,
-                     'isStopped': True,
-                     'isStoppedInterstitial': True,
-                     'isUnknownMode': True,
-                     'isVideoPresentation': False,
-                     'muted': True,
-                     'orientation': 0,
-                     'playbackSupported': False,
-                     'playerBackgroundReady': False,
-                     'playerIsUnavailable': False,
-                     'playerMode': 'UNKNOWN',
-                     'playerState': 'STOPPED',
-                     'presentation': 'INTERSTITIAL',
-                     'requestedTimecode': -1,
-                     'timecodePresent': False,
-                     'unmuted': False,
-                     'videoHasStarted': False},
-     'routing': {}}
-
-The structure and actual data returned by this method will vary wildly
-depending on the requested URL.
-
-
 create_user_url(self, user_id=None, username=None)
 --------------------------------------------------
 
@@ -360,19 +258,6 @@ Example usage::
     {'broadcast_id': '1eaKbRMEMEQKX', 'username': None, 'user_id': None}
 
 
-create_api_request_url(endpoint, **params)
-------------------------------------------
-
-Craft a URL to the Periscope API with the supplied endpoint and params.
-
-The params will be added to URL in a consistent, alphabetical order.
-
-Example usage::
-
-    >>> pp.create_api_request_url('getUserPublic', user_id='123456', a='b')
-    'https://api.periscope.tv/api/v2/getUserPublic?a=b&user_id=123456'
-
-
 request_api(endpoint, **params)
 -------------------------------
 
diff --git a/pyperi/pyperi.py b/pyperi/pyperi.py
index cc1096e..4bf24cf 100644
--- a/pyperi/pyperi.py
+++ b/pyperi/pyperi.py
@@ -14,26 +14,11 @@ class Peri(object):
         """
         Make a request against the Periscope API and return the result.
         """
-        url = self.create_api_request_url(endpoint, **params)
+        url = self._create_api_request_url(endpoint, **params)
         r = requests.get(url)
         r.raise_for_status()
         return r.json()
 
-    def create_api_request_url(self, endpoint, **params):
-        """
-        Craft a URL to the Periscope API with the supplied endpoint and params.
-        """
-        # For easier testing, make sure all params are always in a consistent
-        # order.
-        params_ordered = OrderedDict(sorted(params.items()))
-
-        url = '{base_url}/{endpoint}?{params}'.format(
-            base_url=Peri.PERISCOPE_API_BASE_URL,
-            endpoint=endpoint,
-            params=urllib.parse.urlencode(params_ordered),
-        )
-        return url
-
     def get_broadcast_info(self, broadcast_id):
         """
         Retrieve a dict of information about a specific Broadcast.
@@ -67,19 +52,6 @@ class Peri(object):
         response = self.request_api(endpoint, **params)
         return response['broadcasts']
 
-    def get_web_data_store(self, url):
-        """
-        Retrieve and return the 'data-store' HTML data attribute from the
-        given URL as a Dict.
-        """
-        r = requests.get(url)
-        r.raise_for_status()
-
-        soup = BeautifulSoup(r.content, 'html.parser')
-        page_container = soup.find(id='page-container')
-        data_store = json.loads(page_container['data-store'])
-        return data_store
-
     def get_web_public_user_session_tokens(self, user_id=None, username=None):
         """
         Request new Public Session Tokens to access endpoints relating to a
@@ -95,7 +67,7 @@ class Peri(object):
         """
         user_url = self.create_user_url(user_id=user_id, username=username)
 
-        data_store = self.get_web_data_store(user_url)
+        data_store = self._get_web_data_store(user_url)
         public_tokens = data_store['SessionToken']['public']
 
         token_names = [
@@ -108,7 +80,8 @@ class Peri(object):
 
         return out
 
-    def create_user_url(self, user_id=None, username=None):
+    @classmethod
+    def create_user_url(cls, user_id=None, username=None):
         """
         Create the URL to a User's Periscope page.
         """
@@ -125,7 +98,8 @@ class Peri(object):
 
         raise ValueError('Must specify either user_id or username')
 
-    def parse_periscope_url(self, url):
+    @classmethod
+    def parse_periscope_url(cls, url):
         """
         Get any key information available from the supplied URL.
 
@@ -137,10 +111,10 @@ class Peri(object):
         """
 
         url_parsers = [
-            self.parse_periscope_w_url,
-            self.parse_periscope_u_url,
-            self.parse_periscope_username_broadcast_id_url,
-            self.parse_periscope_username_url,
+            cls._parse_periscope_w_url,
+            cls._parse_periscope_u_url,
+            cls._parse_periscope_username_broadcast_id_url,
+            cls._parse_periscope_username_url,
         ]
 
         out = {
@@ -156,7 +130,37 @@ class Peri(object):
 
         return out
 
-    def parse_periscope_w_url(self, url):
+    def _get_web_data_store(self, url):
+        """
+        Retrieve and return the 'data-store' HTML data attribute from the
+        given URL as a Dict.
+        """
+        r = requests.get(url)
+        r.raise_for_status()
+
+        soup = BeautifulSoup(r.content, 'html.parser')
+        page_container = soup.find(id='page-container')
+        data_store = json.loads(page_container['data-store'])
+        return data_store
+
+    @classmethod
+    def _create_api_request_url(cls, endpoint, **params):
+        """
+        Craft a URL to the Periscope API with the supplied endpoint and params.
+        """
+        # For easier testing, make sure all params are always in a consistent
+        # order.
+        params_ordered = OrderedDict(sorted(params.items()))
+
+        url = '{base_url}/{endpoint}?{params}'.format(
+            base_url=cls.PERISCOPE_API_BASE_URL,
+            endpoint=endpoint,
+            params=urllib.parse.urlencode(params_ordered),
+        )
+        return url
+
+    @classmethod
+    def _parse_periscope_w_url(cls, url):
         """
         Retrieve the `broadcast_id` from the supplied "/w/" URL.
 
@@ -182,7 +186,8 @@ class Peri(object):
             out['broadcast_id'] = broadcast_id
         return out
 
-    def parse_periscope_username_url(self, url):
+    @classmethod
+    def _parse_periscope_username_url(cls, url):
         """
         Extracts the username from URLs formatted like this:
 
@@ -205,7 +210,8 @@ class Peri(object):
             out['username'] = username
         return out
 
-    def parse_periscope_username_broadcast_id_url(self, url):
+    @classmethod
+    def _parse_periscope_username_broadcast_id_url(cls, url):
         """
         Extract the `username`and `broadcast_id` from URLs formatted like this:
 
@@ -230,7 +236,8 @@ class Peri(object):
             out['broadcast_id'] = broadcast_id
         return out
 
-    def parse_periscope_u_url(self, url):
+    @classmethod
+    def _parse_periscope_u_url(cls, url):
         """
         Retrieve the `user_id` from the supplied "/u/" URL.
 

Check the private, static, class and member methods are all sane
Currently everything is just a public member method - not the best.
diff --git a/tests/test_pyperi.py b/tests/test_pyperi.py
index 16d85f4..4687ca9 100644
--- a/tests/test_pyperi.py
+++ b/tests/test_pyperi.py
@@ -31,7 +31,7 @@ def test_request_api():
 
 def test_create_api_request_url():
     pp = Peri()
-    url = pp.create_api_request_url(
+    url = pp._create_api_request_url(
         'testEndpoint', test_param='something', test_param2='else'
     )
     assert url == (
@@ -39,7 +39,7 @@ def test_create_api_request_url():
         'test_param=something&test_param2=else'
     )
 
-    url_no_params = pp.create_api_request_url('testEndpoint')
+    url_no_params = pp._create_api_request_url('testEndpoint')
     assert url_no_params == 'https://api.periscope.tv/api/v2/testEndpoint?'
 
 
@@ -162,7 +162,7 @@ def test_get_web_data_store():
     httpretty.register_uri(httpretty.GET, url, mock_body)
 
     pp = Peri()
-    data_store = pp.get_web_data_store(url)
+    data_store = pp._get_web_data_store(url)
 
     # Check useful session tokens are available
     session_tokens = data_store['SessionToken']
@@ -308,7 +308,7 @@ def test_parse_periscope_w_url():
         w_url = 'https://www.periscope.tv/w/{broadcast_id}'.format(
             broadcast_id=broadcast_id
         )
-        assert pp.parse_periscope_w_url(w_url) == {
+        assert pp._parse_periscope_w_url(w_url) == {
             'user_id': None,
             'username': None,
             'broadcast_id': broadcast_id,
@@ -327,7 +327,7 @@ def test_parse_periscope_username_url():
         username_url = 'https://www.periscope.tv/{username}'.format(
             username=username
         )
-        assert pp.parse_periscope_username_url(username_url) == {
+        assert pp._parse_periscope_username_url(username_url) == {
             'user_id': None,
             'username': username,
             'broadcast_id': None,
@@ -351,7 +351,7 @@ def test_parse_periscope_username_broadcast_id_url():
                 username=username,
                 broadcast_id=broadcast_id,
             )
-            assert pp.parse_periscope_username_broadcast_id_url(username_url) == {
+            assert pp._parse_periscope_username_broadcast_id_url(username_url) == {
                 'user_id': None,
                 'username': username,
                 'broadcast_id': broadcast_id,
@@ -363,11 +363,11 @@ def test_parse_periscope_username_broadcast_id_url():
         'broadcast_id': None,
     }
 
-    assert pp.parse_periscope_username_broadcast_id_url(
+    assert pp._parse_periscope_username_broadcast_id_url(
         'https://www.periscope.tv/okUsername/'
     ) == blank_result
 
-    assert pp.parse_periscope_username_broadcast_id_url(
+    assert pp._parse_periscope_username_broadcast_id_url(
         'https://www.periscope.tv/okUsername/'
     ) == blank_result
 
@@ -382,7 +382,7 @@ def test_parse_periscope_u_url():
         url = 'https://www.periscope.tv/u/{user_id}'.format(
             user_id=user_id
         )
-        assert pp.parse_periscope_u_url(url) == {
+        assert pp._parse_periscope_u_url(url) == {
             'username': None,
             'user_id': user_id,
             'broadcast_id': None,

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest pytest-cov pytest-xdist pytest-mock pytest-asyncio httpretty
apt-get update apt-get install -y gcc
3.9
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
beautifulsoup4==4.5.1
coverage==7.8.0
exceptiongroup==1.2.2
execnet==2.1.1
httpretty==1.1.4
iniconfig==2.1.0
packaging==24.2
pluggy==1.5.0
-e git+https://github.com/takeontom/PyPeri.git@2686c574074daea16035e8ddfe04de52034863fb#egg=pyperi
pytest==8.3.5
pytest-asyncio==0.26.0
pytest-cov==6.0.0
pytest-mock==3.14.0
pytest-xdist==3.6.1
requests==2.12.4
tomli==2.2.1
typing_extensions==4.13.0

name: PyPeri
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - beautifulsoup4==4.5.1
      - coverage==7.8.0
      - exceptiongroup==1.2.2
      - execnet==2.1.1
      - httpretty==1.1.4
      - iniconfig==2.1.0
      - packaging==24.2
      - pluggy==1.5.0
      - pytest==8.3.5
      - pytest-asyncio==0.26.0
      - pytest-cov==6.0.0
      - pytest-mock==3.14.0
      - pytest-xdist==3.6.1
      - requests==2.12.4
      - tomli==2.2.1
      - typing-extensions==4.13.0
prefix: /opt/conda/envs/PyPeri

tests/test_pyperi.py::test_create_api_request_url tests/test_pyperi.py::test_get_web_data_store tests/test_pyperi.py::test_parse_periscope_w_url tests/test_pyperi.py::test_parse_periscope_username_url tests/test_pyperi.py::test_parse_periscope_username_broadcast_id_url tests/test_pyperi.py::test_parse_periscope_u_url
tests/test_pyperi.py::test_request_api tests/test_pyperi.py::test_get_broadcast_info tests/test_pyperi.py::test_get_user_info tests/test_pyperi.py::test_get_user_broadcast_history__username tests/test_pyperi.py::test_get_user_broadcast_history__user_id tests/test_pyperi.py::test_get_web_public_user_session_tokens__username tests/test_pyperi.py::test_get_web_public_user_session_tokens__user_id tests/test_pyperi.py::test_create_user_url tests/test_pyperi.py::test_parse_periscope_url_w tests/test_pyperi.py::test_parse_periscope_url_username tests/test_pyperi.py::test_parse_periscope_url_username_broadcast_id tests/test_pyperi.py::test_parse_periscope_url_u
pyperi/pyperi.py docs/usage.rst
pyperi/pyperi.py docs/usage.rst