diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2bc541d..3ffaafd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
 ## [Unreleased]
+### Changed
+- Simplify imports so that all commonly-used classes can be imported with `from apiron import <class>`
+
 ### Fixed
 - Error in calling a dynamic stub endpoint
 
diff --git a/README.md b/README.md
index 46c75b6..7cc25aa 100644
--- a/README.md
+++ b/README.md
@@ -26,8 +26,7 @@ A service definition requires a domain
 and one or more endpoints with which to interact:
 
 ```python
-from apiron.service.base import Service
-from apiron.endpoint import JsonEndpoint
+from apiron import JsonEndpoint, Service
 
 class GitHub(Service):
     domain = 'https://api.github.com'
diff --git a/apiron/__init__.py b/apiron/__init__.py
index e69de29..c42ec28 100644
--- a/apiron/__init__.py
+++ b/apiron/__init__.py
@@ -0,0 +1,4 @@
+from apiron.endpoint import *
+from apiron.service import *
+from apiron.client import ServiceCaller, Timeout
+from apiron.exceptions import APIException, NoHostsAvailableException, UnfulfilledParameterException
diff --git a/apiron/endpoint/__init__.py b/apiron/endpoint/__init__.py
index 6ade916..01d53b1 100644
--- a/apiron/endpoint/__init__.py
+++ b/apiron/endpoint/__init__.py
@@ -3,9 +3,10 @@ from apiron.endpoint.json import JsonEndpoint
 from apiron.endpoint.streaming import StreamingEndpoint
 from apiron.endpoint.stub import StubEndpoint
 
+
 __all__ = [
     'Endpoint',
     'JsonEndpoint',
     'StreamingEndpoint',
     'StubEndpoint',
-]
\ No newline at end of file
+]
diff --git a/apiron/service/__init__.py b/apiron/service/__init__.py
index e69de29..b119270 100644
--- a/apiron/service/__init__.py
+++ b/apiron/service/__init__.py
@@ -0,0 +1,8 @@
+from apiron.service.base import Service
+from apiron.service.discoverable import DiscoverableService
+
+
+__all__ = [
+    'Service',
+    'DiscoverableService',
+]
diff --git a/docs/advanced-usage.rst b/docs/advanced-usage.rst
index 0ca933a..d56ff28 100644
--- a/docs/advanced-usage.rst
+++ b/docs/advanced-usage.rst
@@ -14,8 +14,7 @@ Service and endpoints
 .. code-block:: python
 
     # test_service.py
-    from apiron.service.base import Service
-    from apiron.endpoint import Endpoint, JsonEndpoint, StreamingEndpoint
+    from apiron import Endpoint, JsonEndpoint, Service, StreamingEndpoint
 
     class HttpBin(Service):
         domain = 'https://httpbin.org'
@@ -36,7 +35,7 @@ Using all the features
 
     import requests
 
-    from apiron.client import Timeout
+    from apiron import Timeout
 
     from test_service import HttpBin
 
@@ -81,8 +80,7 @@ Here is an example where the resolver application always resolves to ``https://w
 
 .. code-block:: python
 
-    from apiron.client import ServiceCaller
-    from apiron.service.discoverable import DiscoverableService
+    from apiron import DiscoverableService
 
     class Eureka:
         @staticmethod
diff --git a/docs/getting-started.rst b/docs/getting-started.rst
index a2b7310..2b0c9f1 100644
--- a/docs/getting-started.rst
+++ b/docs/getting-started.rst
@@ -21,8 +21,7 @@ and one or more endpoints with which to interact:
 
 .. code-block:: python
 
-    from apiron.service.base import Service
-    from apiron.endpoint import JsonEndpoint
+    from apiron import JsonEndpoint, Service
 
     class GitHub(Service):
         domain = 'https://api.github.com'

Simpler import structure
**Is your feature request related to a problem? Please describe.**
It's tedious to import all the stuff you might need from `apiron`:

```python
from apiron.service.base import Service
from apiron.endpoint import Endpoint, JsonEndpoint
from apiron.client import ServiceCaller
```

**Describe the solution you'd like**
Given the fairly small footprint of the features of this package, the common needs should be available to import from the top-level of the package:

```python
from apiron import Endpoint, JsonEndpoint, Service, ServiceCaller
```

**Additional context**
Use `__all__` and `__init__.py` to make things available without moving them. Things to include are probably:

```
Endpoint
JsonEndpoint
StreamingEndpoint
StubEndpoint

Service
DiscoverableService

APIException
NoHostsAvailableException
UnfulfilledParameterException

ServiceCaller
Timeout
```
diff --git a/tests/service/test_base.py b/tests/service/test_base.py
index 5b48222..dc3a052 100644
--- a/tests/service/test_base.py
+++ b/tests/service/test_base.py
@@ -1,6 +1,6 @@
 import pytest
 
-from apiron.service.base import Service
+from apiron import Service
 
 
 @pytest.fixture(scope='class')
diff --git a/tests/service/test_discoverable.py b/tests/service/test_discoverable.py
index bffb9b8..dc4afa0 100644
--- a/tests/service/test_discoverable.py
+++ b/tests/service/test_discoverable.py
@@ -1,6 +1,6 @@
 import pytest
 
-from apiron.service.discoverable import DiscoverableService
+from apiron import DiscoverableService
 
 
 class FakeResolver:
diff --git a/tests/test_client.py b/tests/test_client.py
index 56105d3..b294f28 100644
--- a/tests/test_client.py
+++ b/tests/test_client.py
@@ -2,8 +2,7 @@ from unittest import mock
 
 import pytest
 
-from apiron.client import ServiceCaller
-from apiron.exceptions import NoHostsAvailableException
+from apiron import ServiceCaller, NoHostsAvailableException
 
 class TestClient:
     @mock.patch('requests.sessions.Session', autospec=True)
diff --git a/tests/test_endpoint.py b/tests/test_endpoint.py
index f37cbaf..3d6ed06 100644
--- a/tests/test_endpoint.py
+++ b/tests/test_endpoint.py
@@ -4,85 +4,85 @@ from unittest import mock
 
 import pytest
 
-from apiron import endpoint
-from apiron.exceptions import UnfulfilledParameterException
-from apiron.service.base import Service
+import apiron
+
 
 @pytest.fixture
 def service():
-    class SomeService(Service):
+    class SomeService(apiron.Service):
         domain = 'http://foo.com'
     return SomeService
 
+
 class TestEndpoint:
     def test_call(self, service):
-        service.foo = endpoint.Endpoint()
+        service.foo = apiron.Endpoint()
         service.foo()
 
     def test_call_without_service_raises_exception(self):
-        foo = endpoint.Endpoint()
+        foo = apiron.Endpoint()
         with pytest.raises(TypeError):
             foo()
 
     def test_default_attributes_from_constructor(self):
-        foo = endpoint.Endpoint()
+        foo = apiron.Endpoint()
         assert '/' == foo.path
         assert 'GET' == foo.default_method
 
     def test_constructor_stores_passed_attributes(self):
-        foo = endpoint.Endpoint(path='/foo/', default_method='POST')
+        foo = apiron.Endpoint(path='/foo/', default_method='POST')
         assert '/foo/' == foo.path
         assert 'POST' == foo.default_method
 
     def test_format_response(self):
-        foo = endpoint.Endpoint()
+        foo = apiron.Endpoint()
         mock_response = mock.Mock()
         mock_response.text = 'foobar'
         assert 'foobar' == foo.format_response(mock_response)
 
     def test_required_headers(self):
-        foo = endpoint.Endpoint()
+        foo = apiron.Endpoint()
         assert {} == foo.required_headers
 
     def test_str_method(self):
-        foo = endpoint.Endpoint(path='/foo/bar/')
+        foo = apiron.Endpoint(path='/foo/bar/')
         assert '/foo/bar/' == str(foo)
 
     def test_format_path_with_correct_kwargs(self):
-        foo = endpoint.Endpoint(path='/{one}/{two}/')
+        foo = apiron.Endpoint(path='/{one}/{two}/')
         path_kwargs = {'one': 'foo', 'two': 'bar'}
         assert '/foo/bar/' == foo.get_formatted_path(**path_kwargs)
 
     def test_format_path_with_incorrect_kwargs(self):
-        foo = endpoint.Endpoint(path='/{one}/{two}/')
+        foo = apiron.Endpoint(path='/{one}/{two}/')
         path_kwargs = {'foo': 'bar'}
         with pytest.raises(KeyError):
             foo.get_formatted_path(**path_kwargs)
 
     def test_format_path_with_extra_kwargs(self):
-        foo = endpoint.Endpoint(path='/{one}/{two}/')
+        foo = apiron.Endpoint(path='/{one}/{two}/')
         path_kwargs = {'one': 'foo', 'two': 'bar', 'three': 'not used'}
         assert '/foo/bar/' == foo.get_formatted_path(**path_kwargs)
 
     def test_query_parameter_in_path_generates_warning(self):
         with warnings.catch_warnings(record=True) as warning_records:
             warnings.simplefilter('always')
-            foo = endpoint.Endpoint(path='/?foo=bar')
+            foo = apiron.Endpoint(path='/?foo=bar')
             assert 1 == len(warning_records)
             assert issubclass(warning_records[-1].category, UserWarning)
 
     def test_get_merged_params(self):
-        foo = endpoint.JsonEndpoint(default_params={'foo': 'bar'}, required_params={'baz'})
+        foo = apiron.JsonEndpoint(default_params={'foo': 'bar'}, required_params={'baz'})
         assert {'foo': 'bar', 'baz': 'qux'} == foo.get_merged_params({'baz': 'qux'})
 
     def test_get_merged_params_with_unsupplied_param(self):
-        foo = endpoint.JsonEndpoint(default_params={'foo': 'bar'}, required_params={'baz'})
+        foo = apiron.JsonEndpoint(default_params={'foo': 'bar'}, required_params={'baz'})
 
-        with pytest.raises(UnfulfilledParameterException):
+        with pytest.raises(apiron.UnfulfilledParameterException):
             foo.get_merged_params(None)
 
     def test_get_merged_params_with_empty_param(self):
-        foo = endpoint.JsonEndpoint(default_params={'foo': 'bar'}, required_params={'baz'})
+        foo = apiron.JsonEndpoint(default_params={'foo': 'bar'}, required_params={'baz'})
 
         with warnings.catch_warnings(record=True) as warning_records:
             warnings.simplefilter('always')
@@ -91,13 +91,13 @@ class TestEndpoint:
             assert issubclass(warning_records[-1].category, RuntimeWarning)
 
     def test_get_merged_params_with_required_and_default_param(self):
-        foo = endpoint.JsonEndpoint(default_params={'foo': 'bar'}, required_params={'foo'})
+        foo = apiron.JsonEndpoint(default_params={'foo': 'bar'}, required_params={'foo'})
         assert {'foo': 'bar'} == foo.get_merged_params(None)
 
 
 class TestJsonEndpoint:
     def test_format_response_when_unordered(self):
-        foo = endpoint.JsonEndpoint()
+        foo = apiron.JsonEndpoint()
         mock_response = mock.Mock()
 
         with mock.patch.object(mock_response, 'json') as mock_json:
@@ -106,7 +106,7 @@ class TestJsonEndpoint:
             mock_json.assert_called_once_with(object_pairs_hook=None)
 
     def test_format_response_when_ordered(self):
-        foo = endpoint.JsonEndpoint(preserve_order=True)
+        foo = apiron.JsonEndpoint(preserve_order=True)
         mock_response = mock.Mock()
 
         with mock.patch.object(mock_response, 'json') as mock_json:
@@ -115,13 +115,13 @@ class TestJsonEndpoint:
             mock_json.assert_called_once_with(object_pairs_hook=collections.OrderedDict)
 
     def test_required_headers(self):
-        foo = endpoint.JsonEndpoint()
+        foo = apiron.JsonEndpoint()
         assert {'Accept': 'application/json'} == foo.required_headers
 
 
 class TestStreamingEndpoint:
     def test_format_response(self):
-        foo = endpoint.StreamingEndpoint()
+        foo = apiron.StreamingEndpoint()
         mock_response = mock.Mock()
         assert mock_response.iter_content(chunk_size=None) == foo.format_response(mock_response)
 
@@ -132,14 +132,14 @@ class TestStubEndpoint:
         """
         Test initializing a stub endpoint with a stub response
         """
-        stub_endpoint = endpoint.StubEndpoint(stub_response='stub response')
+        stub_endpoint = apiron.StubEndpoint(stub_response='stub response')
         assert 'stub response' == stub_endpoint.stub_response
 
     def test_extra_params(self):
         """
         Test initializing a stub endpoint with extra params
         """
-        stub_endpoint = endpoint.StubEndpoint(
+        stub_endpoint = apiron.StubEndpoint(
             stub_response='stub response',
             path='/some/path/',
             default_params={'param_name': 'param_val'},
@@ -156,7 +156,7 @@ class TestStubEndpoint:
         """
         Test calling a ``StubEndpoint`` with a static response
         """
-        service.stub_endpoint = endpoint.StubEndpoint(
+        service.stub_endpoint = apiron.StubEndpoint(
             stub_response='stub response',
         )
         actual_response = service.stub_endpoint()
@@ -174,7 +174,7 @@ class TestStubEndpoint:
                 else:
                     return {'default': 'response'}
 
-            service.stub_endpoint = endpoint.StubEndpoint(stub_response=stub_response)
+            service.stub_endpoint = apiron.StubEndpoint(stub_response=stub_response)
             actual_response = service.stub_endpoint(**call_kwargs)
             assert actual_response == expected_response
 

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest pytest-cov sphinx sphinx-autobuild twine wheel
apt-get update apt-get install -y gcc
3.7
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
alabaster==0.7.13
-e git+https://github.com/ithaka/apiron.git@a75eb697f27dcabaa7366f7859f192ad5b387b4f#egg=apiron
Babel==2.14.0
bleach==6.0.0
certifi @ file:///croot/certifi_1671487769961/work/certifi
cffi==1.15.1
charset-normalizer==3.4.1
colorama==0.4.6
coverage==7.2.7
cryptography==44.0.2
docutils==0.19
exceptiongroup==1.2.2
idna==3.10
imagesize==1.4.1
importlib-metadata==6.7.0
importlib-resources==5.12.0
iniconfig==2.0.0
jaraco.classes==3.2.3
jeepney==0.9.0
Jinja2==3.1.6
keyring==24.1.1
livereload==2.7.1
markdown-it-py==2.2.0
MarkupSafe==2.1.5
mdurl==0.1.2
more-itertools==9.1.0
packaging==24.0
pkginfo==1.10.0
pluggy==1.2.0
pycparser==2.21
Pygments==2.17.2
pytest==7.4.4
pytest-cov==4.1.0
pytz==2025.2
readme-renderer==37.3
requests==2.31.0
requests-toolbelt==1.0.0
rfc3986==2.0.0
rich==13.8.1
SecretStorage==3.3.3
six==1.17.0
snowballstemmer==2.2.0
Sphinx==5.3.0
sphinx-autobuild==2021.3.14
sphinxcontrib-applehelp==1.0.2
sphinxcontrib-devhelp==1.0.2
sphinxcontrib-htmlhelp==2.0.0
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-qthelp==1.0.3
sphinxcontrib-serializinghtml==1.1.5
tomli==2.0.1
tornado==6.2
twine==4.0.2
typing_extensions==4.7.1
urllib3==2.0.7
webencodings==0.5.1
zipp==3.15.0

name: apiron
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
  - certifi=2022.12.7=py37h06a4308_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=1.1.1w=h7f8727e_0
  - pip=22.3.1=py37h06a4308_0
  - python=3.7.16=h7a1cb2a_0
  - readline=8.2=h5eee18b_0
  - setuptools=65.6.3=py37h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.38.4=py37h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - alabaster==0.7.13
      - babel==2.14.0
      - bleach==6.0.0
      - cffi==1.15.1
      - charset-normalizer==3.4.1
      - colorama==0.4.6
      - coverage==7.2.7
      - cryptography==44.0.2
      - docutils==0.19
      - exceptiongroup==1.2.2
      - idna==3.10
      - imagesize==1.4.1
      - importlib-metadata==6.7.0
      - importlib-resources==5.12.0
      - iniconfig==2.0.0
      - jaraco-classes==3.2.3
      - jeepney==0.9.0
      - jinja2==3.1.6
      - keyring==24.1.1
      - livereload==2.7.1
      - markdown-it-py==2.2.0
      - markupsafe==2.1.5
      - mdurl==0.1.2
      - more-itertools==9.1.0
      - packaging==24.0
      - pkginfo==1.10.0
      - pluggy==1.2.0
      - pycparser==2.21
      - pygments==2.17.2
      - pytest==7.4.4
      - pytest-cov==4.1.0
      - pytz==2025.2
      - readme-renderer==37.3
      - requests==2.31.0
      - requests-toolbelt==1.0.0
      - rfc3986==2.0.0
      - rich==13.8.1
      - secretstorage==3.3.3
      - six==1.17.0
      - snowballstemmer==2.2.0
      - sphinx==5.3.0
      - sphinx-autobuild==2021.3.14
      - sphinxcontrib-applehelp==1.0.2
      - sphinxcontrib-devhelp==1.0.2
      - sphinxcontrib-htmlhelp==2.0.0
      - sphinxcontrib-jsmath==1.0.1
      - sphinxcontrib-qthelp==1.0.3
      - sphinxcontrib-serializinghtml==1.1.5
      - tomli==2.0.1
      - tornado==6.2
      - twine==4.0.2
      - typing-extensions==4.7.1
      - urllib3==2.0.7
      - webencodings==0.5.1
      - zipp==3.15.0
prefix: /opt/conda/envs/apiron

tests/service/test_base.py::TestService::test_get_hosts_returns_domain tests/service/test_base.py::TestService::test_str_method_on_class tests/service/test_base.py::TestService::test_str_method_on_instance tests/service/test_base.py::TestService::test_repr_method_on_class tests/service/test_base.py::TestService::test_repr_method_on_instance tests/service/test_base.py::TestService::test_required_hosts_returns_dictionary tests/service/test_discoverable.py::TestDiscoverableService::test_get_hosts_returns_hosts_from_resolver tests/service/test_discoverable.py::TestDiscoverableService::test_str_method_on_class tests/service/test_discoverable.py::TestDiscoverableService::test_str_method_on_instance tests/service/test_discoverable.py::TestDiscoverableService::test_repr_method_on_class tests/service/test_discoverable.py::TestDiscoverableService::test_repr_method_on_instance tests/test_client.py::TestClient::test_get_adapted_session tests/test_client.py::TestClient::test_get_required_headers tests/test_client.py::TestClient::test_build_request_object_passes_arguments_to_request_constructor tests/test_client.py::TestClient::test_call tests/test_client.py::TestClient::test_call_with_existing_session tests/test_client.py::TestClient::test_call_with_explicit_encoding tests/test_client.py::TestClient::test_build_request_object_raises_no_host_exception tests/test_client.py::TestClient::test_choose_host tests/test_client.py::TestClient::test_choose_host_raises_exception tests/test_client.py::TestClientStubCall::test_call_stub tests/test_client.py::TestClientStubCall::test_call_stub_dynamic tests/test_client.py::TestClientStubCall::test_call_stub_dynamic_params tests/test_client.py::test_build_url[http://biz.com-/endpoint-http://biz.com/endpoint] tests/test_client.py::test_build_url[http://biz.com/-endpoint-http://biz.com/endpoint] tests/test_client.py::test_build_url[http://biz.com/-/endpoint-http://biz.com/endpoint] tests/test_client.py::test_build_url[http://biz.com-endpoint-http://biz.com/endpoint] tests/test_client.py::test_build_url[http://biz.com/v2-/endpoint-http://biz.com/v2/endpoint] tests/test_client.py::test_build_url[http://biz.com/v2/-endpoint-http://biz.com/v2/endpoint] tests/test_client.py::test_build_url[http://biz.com/v2/-/endpoint-http://biz.com/v2/endpoint] tests/test_client.py::test_build_url[http://biz.com/v2-endpoint-http://biz.com/v2/endpoint] tests/test_endpoint.py::TestEndpoint::test_call tests/test_endpoint.py::TestEndpoint::test_call_without_service_raises_exception tests/test_endpoint.py::TestEndpoint::test_default_attributes_from_constructor tests/test_endpoint.py::TestEndpoint::test_constructor_stores_passed_attributes tests/test_endpoint.py::TestEndpoint::test_format_response tests/test_endpoint.py::TestEndpoint::test_required_headers tests/test_endpoint.py::TestEndpoint::test_str_method tests/test_endpoint.py::TestEndpoint::test_format_path_with_correct_kwargs tests/test_endpoint.py::TestEndpoint::test_format_path_with_incorrect_kwargs tests/test_endpoint.py::TestEndpoint::test_format_path_with_extra_kwargs tests/test_endpoint.py::TestEndpoint::test_query_parameter_in_path_generates_warning tests/test_endpoint.py::TestEndpoint::test_get_merged_params tests/test_endpoint.py::TestEndpoint::test_get_merged_params_with_unsupplied_param tests/test_endpoint.py::TestEndpoint::test_get_merged_params_with_empty_param tests/test_endpoint.py::TestEndpoint::test_get_merged_params_with_required_and_default_param tests/test_endpoint.py::TestJsonEndpoint::test_format_response_when_unordered tests/test_endpoint.py::TestJsonEndpoint::test_format_response_when_ordered tests/test_endpoint.py::TestJsonEndpoint::test_required_headers tests/test_endpoint.py::TestStreamingEndpoint::test_format_response tests/test_endpoint.py::TestStubEndpoint::test_stub_response tests/test_endpoint.py::TestStubEndpoint::test_extra_params tests/test_endpoint.py::TestStubEndpoint::test_call_static tests/test_endpoint.py::TestStubEndpoint::test_call_dynamic
apiron/endpoint/__init__.py apiron/service/__init__.py CHANGELOG.md docs/advanced-usage.rst apiron/__init__.py README.md docs/getting-started.rst
apiron/endpoint/__init__.py apiron/service/__init__.py CHANGELOG.md docs/advanced-usage.rst apiron/__init__.py README.md docs/getting-started.rst