diff --git a/mapbox/services/base.py b/mapbox/services/base.py
index 4a25402..efd0eeb 100644
--- a/mapbox/services/base.py
+++ b/mapbox/services/base.py
@@ -1,6 +1,8 @@
 """Base Service class"""
 
 import os
+import base64
+import json
 
 import requests
 
@@ -27,6 +29,20 @@ class Service:
         """A product token for use in User-Agent headers."""
         return 'mapbox-sdk-py/{0}'.format(__version__)
 
+    @property
+    def username(self):
+        """Get username from access token
+        Token contains base64 encoded json object with username"""
+        token = self.session.params['access_token']
+        if not token:
+            raise ValueError("session does not have a valid access_token param")
+        data = token.split('.')[1]
+        data = data.replace('-', '+').replace('_', '/')
+        try:
+            return json.loads(base64.b64decode(data).decode('utf-8'))['u']
+        except (ValueError, KeyError):
+            raise ValueError("access_token does not contain username")
+
     def handle_http_error(self, response, custom_messages=None,
                           raise_for_status=False):
         if not custom_messages:
diff --git a/mapbox/services/uploads.py b/mapbox/services/uploads.py
index 47e2451..635a53e 100644
--- a/mapbox/services/uploads.py
+++ b/mapbox/services/uploads.py
@@ -11,7 +11,7 @@ class Uploader(Service):
 
         from mapbox import Uploader
 
-        u = Uploader('username')
+        u = Uploader()
         url = u.stage('test.tif')
         job = u.create(url, 'test1').json()
 
@@ -24,8 +24,7 @@ class Uploader(Service):
         assert job not in u.list().json()
     """
 
-    def __init__(self, username, access_token=None):
-        self.username = username
+    def __init__(self, access_token=None):
         self.baseuri = 'https://api.mapbox.com/uploads/v1'
         self.session = self.get_session(access_token)
 

Get user from token
As in https://github.com/mapbox/mapbox-sdk-js/blob/master/lib/get_user.js. This allows us to eliminate a duplicate parameter when creating dataset and upload service instances.
diff --git a/tests/test_base.py b/tests/test_base.py
index ff773d7..39a3311 100644
--- a/tests/test_base.py
+++ b/tests/test_base.py
@@ -1,3 +1,6 @@
+import base64
+import os
+
 import pytest
 import requests
 import responses
@@ -60,3 +63,36 @@ def test_custom_messages():
     with pytest.raises(requests.exceptions.HTTPError) as exc:
         assert service.handle_http_error(response, raise_for_status=True)
         assert "401" in exc.value.message
+
+
+class MockService(mapbox.Service):
+    def __init__(self, access_token=None):
+        # In order to get a username, a session must be created on init
+        self.session = self.get_session(access_token)
+
+def test_username(monkeypatch):
+    token = 'pk.{0}.test'.format(base64.b64encode(b'{"u":"testuser"}').decode('utf-8'))
+    service = MockService(access_token=token)
+    assert service.username == 'testuser'
+
+def test_username_failures(monkeypatch):
+    # If your child class doesn't create a session
+    service = mapbox.Service()
+    with pytest.raises(AttributeError) as exc:
+        service.username
+        assert 'session' in exc.value.message
+
+    if 'MAPBOX_ACCESS_TOKEN' in os.environ:
+        monkeypatch.delenv('MAPBOX_ACCESS_TOKEN')
+    service = MockService()
+    with pytest.raises(ValueError) as exc:
+        service.username
+        assert 'access_token' in exc.value.message
+        assert 'param' in exc.value.message
+
+    token = "not.good"
+    service = MockService(access_token=token)
+    with pytest.raises(ValueError) as exc:
+        service.username
+        assert 'access_token' in exc.value.message
+        assert 'username' in exc.value.message
diff --git a/tests/test_upload.py b/tests/test_upload.py
index 252c540..9d92810 100644
--- a/tests/test_upload.py
+++ b/tests/test_upload.py
@@ -1,4 +1,5 @@
 import json
+import base64
 
 import responses
 
@@ -6,6 +7,9 @@ import mapbox
 
 
 username = 'testuser'
+access_token = 'pk.{0}.test'.format(
+    base64.b64encode(b'{"u":"testuser"}').decode('utf-8'))
+
 upload_response_body = """
     {{"progress": 0,
     "modified": "date.test",
@@ -30,12 +34,12 @@ def test_get_credentials():
 
     responses.add(
         responses.GET,
-        'https://api.mapbox.com/uploads/v1/{0}/credentials?access_token=pk.test'.format(username),
+        'https://api.mapbox.com/uploads/v1/{0}/credentials?access_token={1}'.format(username, access_token),
         match_querystring=True,
         body=query_body, status=200,
         content_type='application/json')
 
-    res = mapbox.Uploader(username, access_token='pk.test')._get_credentials()
+    res = mapbox.Uploader(access_token=access_token)._get_credentials()
     assert res.status_code == 200
     creds = res.json()
     assert username in creds['url']
@@ -48,18 +52,18 @@ def test_get_credentials():
 def test_create():
     responses.add(
         responses.POST,
-        'https://api.mapbox.com/uploads/v1/{0}?access_token=pk.test'.format(username),
+        'https://api.mapbox.com/uploads/v1/{0}?access_token={1}'.format(username, access_token),
         match_querystring=True,
         body=upload_response_body, status=201,
         content_type='application/json')
 
-    res = mapbox.Uploader(username, access_token='pk.test').create(
+    res = mapbox.Uploader(access_token=access_token).create(
         'http://example.com/test.json', 'test1')  # without username prefix
     assert res.status_code == 201
     job = res.json()
     assert job['tileset'] == "{0}.test1".format(username)
 
-    res2 = mapbox.Uploader(username, access_token='pk.test').create(
+    res2 = mapbox.Uploader(access_token=access_token).create(
         'http://example.com/test.json', 'testuser.test1')  # also takes full tileset
     assert res2.status_code == 201
     job = res2.json()
@@ -86,11 +90,11 @@ def test_create_name():
 
     responses.add_callback(
         responses.POST,
-        'https://api.mapbox.com/uploads/v1/{0}?access_token=pk.test'.format(username),
+        'https://api.mapbox.com/uploads/v1/{0}?access_token={1}'.format(username, access_token),
         match_querystring=True,
         callback=request_callback)
 
-    res = mapbox.Uploader(username, access_token='pk.test').create(
+    res = mapbox.Uploader(access_token=access_token).create(
         'http://example.com/test.json', 'testuser.test1', name="testname")
     assert res.status_code == 201
     job = res.json()
@@ -101,12 +105,12 @@ def test_create_name():
 def test_list():
     responses.add(
         responses.GET,
-        'https://api.mapbox.com/uploads/v1/{0}?access_token=pk.test'.format(username),
+        'https://api.mapbox.com/uploads/v1/{0}?access_token={1}'.format(username, access_token),
         match_querystring=True,
         body="[{0}]".format(upload_response_body), status=200,
         content_type='application/json')
 
-    res = mapbox.Uploader(username, access_token='pk.test').list()
+    res = mapbox.Uploader(access_token=access_token).list()
     assert res.status_code == 200
     uploads = res.json()
     assert len(uploads) == 1
@@ -118,12 +122,12 @@ def test_status():
     job = json.loads(upload_response_body)
     responses.add(
         responses.GET,
-        'https://api.mapbox.com/uploads/v1/{0}/{1}?access_token=pk.test'.format(username, job['id']),
+        'https://api.mapbox.com/uploads/v1/{0}/{1}?access_token={2}'.format(username, job['id'], access_token),
         match_querystring=True,
         body=upload_response_body, status=200,
         content_type='application/json')
 
-    res = mapbox.Uploader(username, access_token='pk.test').status(job)
+    res = mapbox.Uploader(access_token=access_token).status(job)
     assert res.status_code == 200
     status = res.json()
     assert job == status
@@ -134,10 +138,10 @@ def test_delete():
     job = json.loads(upload_response_body)
     responses.add(
         responses.DELETE,
-        'https://api.mapbox.com/uploads/v1/{0}/{1}?access_token=pk.test'.format(username, job['id']),
+        'https://api.mapbox.com/uploads/v1/{0}/{1}?access_token={2}'.format(username, job['id'], access_token),
         match_querystring=True,
         body=None, status=204,
         content_type='application/json')
 
-    res = mapbox.Uploader(username, access_token='pk.test').delete(job)
+    res = mapbox.Uploader(access_token=access_token).delete(job)
     assert res.status_code == 204

pip install -e .[test]
parse_log_pytest
pytest
pytest
3.4
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs @ file:///opt/conda/conda-bld/attrs_1642510447205/work
boto3==1.23.10
botocore==1.26.10
certifi==2021.5.30
charset-normalizer==2.0.12
click==8.0.4
click-plugins==1.1.1
cligj==0.7.2
coverage==6.2
coveralls==3.3.1
distlib==0.3.9
docopt==0.6.2
filelock==3.4.1
idna==3.10
importlib-metadata==4.8.3
importlib-resources==5.4.0
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
jmespath==0.10.0
-e git+https://github.com/mapbox/mapbox-sdk-py.git@06728ffc30fba83003e9c76645ecec3eec1c63de#egg=mapbox
more-itertools @ file:///tmp/build/80754af9/more-itertools_1637733554872/work
packaging @ file:///tmp/build/80754af9/packaging_1637314298585/work
platformdirs==2.4.0
pluggy @ file:///tmp/build/80754af9/pluggy_1615976315926/work
py @ file:///opt/conda/conda-bld/py_1644396412707/work
pyparsing @ file:///tmp/build/80754af9/pyparsing_1635766073266/work
pytest==6.2.4
pytest-cov==4.0.0
python-dateutil==2.9.0.post0
requests==2.27.1
responses==0.17.0
s3transfer==0.5.2
six==1.17.0
toml @ file:///tmp/build/80754af9/toml_1616166611790/work
tomli==1.2.3
tox==3.28.0
typing_extensions @ file:///opt/conda/conda-bld/typing_extensions_1647553014482/work
uritemplate==4.1.1
uritemplate.py==3.0.2
urllib3==1.26.20
virtualenv==20.17.1
zipp @ file:///tmp/build/80754af9/zipp_1633618647012/work

name: mapbox-sdk-py
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
  - attrs=21.4.0=pyhd3eb1b0_0
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - importlib_metadata=4.8.1=hd3eb1b0_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - more-itertools=8.12.0=pyhd3eb1b0_0
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - packaging=21.3=pyhd3eb1b0_0
  - pip=21.2.2=py36h06a4308_0
  - pluggy=0.13.1=py36h06a4308_0
  - py=1.11.0=pyhd3eb1b0_0
  - pyparsing=3.0.4=pyhd3eb1b0_0
  - pytest=6.2.4=py36h06a4308_2
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - toml=0.10.2=pyhd3eb1b0_0
  - typing_extensions=4.1.1=pyh06a4308_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zipp=3.6.0=pyhd3eb1b0_0
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - boto3==1.23.10
      - botocore==1.26.10
      - charset-normalizer==2.0.12
      - click==8.0.4
      - click-plugins==1.1.1
      - cligj==0.7.2
      - coverage==6.2
      - coveralls==3.3.1
      - distlib==0.3.9
      - docopt==0.6.2
      - filelock==3.4.1
      - idna==3.10
      - importlib-metadata==4.8.3
      - importlib-resources==5.4.0
      - jmespath==0.10.0
      - platformdirs==2.4.0
      - pytest-cov==4.0.0
      - python-dateutil==2.9.0.post0
      - requests==2.27.1
      - responses==0.17.0
      - s3transfer==0.5.2
      - six==1.17.0
      - tomli==1.2.3
      - tox==3.28.0
      - uritemplate==4.1.1
      - uritemplate-py==3.0.2
      - urllib3==1.26.20
      - virtualenv==20.17.1
prefix: /opt/conda/envs/mapbox-sdk-py

tests/test_base.py::test_username tests/test_base.py::test_username_failures tests/test_upload.py::test_get_credentials tests/test_upload.py::test_create tests/test_upload.py::test_create_name tests/test_upload.py::test_list tests/test_upload.py::test_status tests/test_upload.py::test_delete
tests/test_base.py::test_service_session tests/test_base.py::test_service_session_env tests/test_base.py::test_service_session_os_environ tests/test_base.py::test_service_session_os_environ_caps tests/test_base.py::test_product_token tests/test_base.py::test_user_agent tests/test_base.py::test_custom_messages
mapbox/services/base.py mapbox/services/uploads.py
mapbox/services/base.py mapbox/services/uploads.py