Repository: sec-edgar/sec-edgar

Issue:
NetworkClient throws 403s when saving filings due to misconfigured user agent
**Describe the bug**

The `NetworkClient` class is throwing 403s when filings are saved:

```
  File "/home/hkurtz/.asdf/installs/python/3.9.6/lib/python3.9/site-packages/secedgar-0.4.0a0-py3.9.egg/secedgar/core/_index.py", line 346, in _save_filings
    loop.run_until_complete(self.client.wait_for_download_async(inputs))
  File "/home/hkurtz/.asdf/installs/python/3.9.6/lib/python3.9/asyncio/base_events.py", line 642, in run_until_complete
    return future.result()
  File "/home/hkurtz/.asdf/installs/python/3.9.6/lib/python3.9/site-packages/secedgar-0.4.0a0-py3.9.egg/secedgar/client.py", line 252, in wait_for_download_async
    await asyncio.gather(
  File "/home/hkurtz/.asdf/installs/python/3.9.6/lib/python3.9/site-packages/secedgar-0.4.0a0-py3.9.egg/secedgar/client.py", line 221, in fetch_and_save
    contents = await self.fetch(link, session)
  File "/home/hkurtz/.asdf/installs/python/3.9.6/lib/python3.9/site-packages/secedgar-0.4.0a0-py3.9.egg/secedgar/client.py", line 208, in fetch
    async with await session.get(link) as response:
  File "/home/hkurtz/.asdf/installs/python/3.9.6/lib/python3.9/site-packages/aiohttp/client.py", line 625, in _request
    resp.raise_for_status()
  File "/home/hkurtz/.asdf/installs/python/3.9.6/lib/python3.9/site-packages/aiohttp/client_reqrep.py", line 1000, in raise_for_status
    raise ClientResponseError(
aiohttp.client_exceptions.ClientResponseError: 403, message='Forbidden', url=URL('http://www.sec.gov/Archives/edgar/data/1000275/0001140361-21-024396.txt')
```

**To Reproduce**

```
from datetime import date, timedelta

from secedgar.client import NetworkClient
from secedgar.core import DailyFilings

def base_edgar_client():
    return NetworkClient(
        retry_count=3,
        batch_size=1,
        backoff_factor=5,
        rate_limit=1,
        user_agent="Harrison Kurtz edgar@hkurtz.com",
    )

def get_filings_yesterday():
    """
    Get yesterday's filings
    """
    daily_filings = DailyFilings(
        client=base_edgar_client(), date=date.today() - timedelta(days=1)
    )
    daily_filings.save("./filings")

get_filings_yesterday()
```

**Resolution**

Add user agent header to `NetworkClient`


Discussion:

Patch:
diff --git a/README.rst b/README.rst
index 0a8597a..483bbd9 100644
--- a/README.rst
+++ b/README.rst
@@ -11,15 +11,15 @@ single command.
 Installation
 ------------
 
-You may have to install the package using pip:
+You can install the package using pip:
 
 .. code:: bash
 
    $ pip install secedgar
 
-or
+OR
 
-You can clone the project or download it as zip.
+You can clone the project:
 
 .. code:: bash
 
@@ -30,14 +30,47 @@ You can clone the project or download it as zip.
 Running
 -------
 
+Company Filings
+~~~~~~~~~~~~~~~
+
+Single Company
+^^^^^^^^^^^^^^
+
 .. code:: python
 
-    from secedgar.core import CompanyFilings, FilingType
+    from secedgar import filings, FilingType
 
     # 10Q filings for Apple (ticker "aapl")
-    my_filings = CompanyFilings(cik_lookup='aapl', filing_type=FilingType.FILING_10Q)
+    my_filings = filings(cik_lookup="aapl", filing_type=FilingType.FILING_10Q, user_agent="Your name (your email)")
+    my_filings.save('/path/to/dir')
+
+
+Multiple Companies
+^^^^^^^^^^^^^^^^^^
+
+.. code:: python
+
+    from secedgar import filings, FilingType
+
+    # 10Q filings for Apple and Facebook (tickers "aapl" and "fb")
+    my_filings = filings(cik_lookup=["aapl", "fb"], filing_type=FilingType.FILING_10Q, user_agent="Your name (your email)")
     my_filings.save('/path/to/dir')
 
+
+Daily Filings
+~~~~~~~~~~~~~
+
+
+.. code:: python
+
+    from secedgar import filings
+    from datetime import date
+
+    daily_filings = filings(start_date=date(2021, 6, 30), user_agent="Your name (your email)")
+    daily_urls = daily_filings.get_urls()
+
+
+
 Supported Methods
 -----------------
 
diff --git a/docs/source/ciklookup.rst b/docs/source/ciklookup.rst
index 133a6d3..b08cf00 100644
--- a/docs/source/ciklookup.rst
+++ b/docs/source/ciklookup.rst
@@ -14,7 +14,8 @@ Below is an example of how you can retrieve lookup CIKs by using the ``CIKLookup
 
    from secedgar.core.cik_lookup import CIKLookup
 
-   lookups = CIKLookup(['aapl', 'msft', 'Facebook'])
+   lookups = CIKLookup(['aapl', 'msft', 'Facebook'],
+                       user_agent="Name (email)")
 
 
 Accessing ``lookups.lookup_dict`` would then return
diff --git a/docs/source/filings.rst b/docs/source/filings.rst
index c2d8162..a7edd8f 100644
--- a/docs/source/filings.rst
+++ b/docs/source/filings.rst
@@ -3,9 +3,9 @@
 Filings
 =======
 
-There are multiple ways to download filings. The most direct way is to use the ``secedgar.core.filings.filings``
+There are multiple ways to download filings. The most direct way is to use the :meth:`secedgar.filings`
 function. This function will return a class which tries to best match your needs based on the arguments
-provided. For the more technical, the ``filings`` function is a factory.
+provided. For the more technical, the :meth:`secedgar.filings` function is a factory.
 
 If you find that this does not meet your needs for any reason or would like more direct control
 over what is being created, you can use the following classes:
@@ -17,71 +17,26 @@ over what is being created, you can use the following classes:
 
 Supported filing types can be found at :ref:`filingtypes`
 
-Filing
-------
-
-.. autoclass:: secedgar.CompanyFilings
-   :members:
-   :inherited-members:
-
-
-Examples
-^^^^^^^^
-
-Restrict the start and end dates by using the ``start_date`` and ``end_date`` arguments.
-
-.. code-block:: python
+Filings Function
+----------------
 
-   from secedgar import FilingType, CompanyFilings
-   from datetime import datetime
-
-   filing = CompanyFilings(cik_lookup='aapl',
-                   filing_type=FilingType.FILING_10K,
-                   start_date=datetime(2015, 1, 1),
-                   end_date=datetime(2019, 1, 1))
-
-If you would like to find all filings from some ``start_date`` until today, simply exclude ``end_date``.
-The end date defaults to today's date.
-
-.. code-block:: python
-
-   from secedgar import FilingType, CompanyFilings
-   from datetime import datetime
-
-   filing = CompanyFilings(cik_lookup='aapl',
-                   filing_type=FilingType.FILING_10K,
-                   start_date=datetime(2015, 1, 1)) # end date defaults to today
-
-You can also look up a specific filing type for multiple companies.
+.. autofunction:: secedgar.filings
 
-.. code-block:: python
-
-   from secedgar import FilingType, CompanyFilings
-   from datetime import datetime
 
-   filing = CompanyFilings(cik_lookup=['aapl', 'msft', 'fb'],
-                   filing_type=FilingType.FILING_10K,
-                   start_date=datetime(2015, 1, 1))
+Filings Classes
+---------------
 
-*For a full list of the available filing types, please see* :class:`secedgar.core.FilingType`.
-
-
-SEC requests that traffic identifies itself via a user agent string. You can
-customize this according to your preference using the ``user_agent`` argument.
-
-.. code-block:: python
+Filing
+~~~~~~
 
-   from secedgar.filings import FilingType, Filing
-   from datetime import datetime
+.. autoclass:: secedgar.CompanyFilings
+   :members:
+   :inherited-members:
 
-   filing = Filing(cik_lookup=['aapl', 'msft', 'fb'],
-                   filing_type=FilingType.FILING_10K,
-                   start_date=datetime(2015, 1, 1),
-                   user_agent='YOUR COMPANY NAME HERE')
 
 
 Daily Filings
--------------
+~~~~~~~~~~~~~
 
 The ``DailyFilings`` class can be used to fetch all the URLs for or download all filings from any given day.
 
@@ -104,27 +59,16 @@ Note that you should replace ``'/my_directory'`` with the desired directory of t
    daily_filings.save('/my_directory')
 
 Quarterly Filings
------------------
+~~~~~~~~~~~~~~~~~
 
 The ``QuarterlyFilings`` class can be used to fetch all the URLs for or download all filings from any given quarter.
 
 .. autoclass:: secedgar.QuarterlyFilings
    :members:
 
-Examples
-^^^^^^^^
-
-.. code-block:: python
-
-   from secedgar import QuarterlyFilings
-
-   quarterly_filings = QuarterlyFilings(year=2000, quarter=4)
-   urls = quarterly_filings.get_urls()  # gets all URLs for filings from quarter 4 of 2000
-   quarterly_filings.save('/my_directory')  # saves all filings from quarter 4 of 2000 in my_directory
-
 
 Combo Filings
--------------
+~~~~~~~~~~~~~
 
 The ``ComboFilings`` class can download all filings from a specified time frame.
 Internally, this class uses a mixture of ``DailyFilings`` and ``QuarterlyFilings`` to get all of the needed
@@ -134,17 +78,6 @@ filings.
    :members:
 
 
-Examples
-^^^^^^^^
-
-.. code-block:: python
-
-   from datetime import date
-   from secedgar import ComboFilings
-
-   combo_filings = ComboFilings(start_date=date(2020, 1, 6), end_date=date(2020, 11, 5)
-   combo_filings.save('/my_directory')
-
 Saving Filings
 --------------
 
@@ -156,8 +89,13 @@ Here are some examples of how you might use those arguments to create custom dir
 
    from secedgar import CompanyFilings, FilingType
 
-   f = CompanyFilings(cik_lookup=["aapl", "msft"], filing_type=FilingType.FILING_10Q, count=5)
-   f.save("./my_directory", dir_pattern="cik_{cik}/{type}", file_pattern="{accession_number}")
+   f = CompanyFilings(cik_lookup=["aapl", "msft"],
+                      filing_type=FilingType.FILING_10Q,
+                      count=5,
+                      user_agent="Name (email)")
+   f.save("./my_directory",
+          dir_pattern="cik_{cik}/{type}",
+          file_pattern="{accession_number}")
 
 The code above would create a directory structure that would look something like this:
 
@@ -180,4 +118,4 @@ The code above would create a directory structure that would look something like
          └── 0001564590-20-047996.txt
 
 
-This same sort of templating can be used for :class:`secedgar.core.DailyFilings` and :class:`secedgar.core.QuarterlyFilings`.
+This same sort of templating can be used for :class:`secedgar.DailyFilings` and :class:`secedgar.QuarterlyFilings`.
diff --git a/docs/source/usage.rst b/docs/source/usage.rst
index 448e04a..2d33ab4 100644
--- a/docs/source/usage.rst
+++ b/docs/source/usage.rst
@@ -21,8 +21,9 @@ The ``CompanyFilings`` class provides a simple API to fetch SEC filings.
    from secedgar import CompanyFilings, FilingType
 
    my_filings = CompanyFilings(cik_lookup='aapl',
-                       filing_type=FilingType.FILING_10Q,
-                       count=15)
+                               filing_type=FilingType.FILING_10Q,
+                               count=15,
+                               user_agent='Name (email)')
 
 The ``cik_lookup`` argument can also take multiple tickers and/or company names.
 
@@ -31,8 +32,9 @@ The ``cik_lookup`` argument can also take multiple tickers and/or company names.
    from secedgar import CompanyFilings, FilingType
 
    my_filings = CompanyFilings(cik_lookup=['aapl', 'msft', 'Facebook'],
-                       filing_type=FilingType.FILING_10Q,
-                       count=15)
+                               filing_type=FilingType.FILING_10Q,
+                               count=15,
+                               user_agent='Name (email)')
 
 
 Using a User Agent
@@ -53,12 +55,12 @@ A note from the SEC website:
 
 .. code-block:: python
 
-   from secedgar.filings import Filing, FilingType
+   from secedgar import CompanyFilings, FilingType
 
-   my_filings = Filing(cik_lookup=['aapl', 'msft', 'Facebook'],
-                       filing_type=FilingType.FILING_10Q,
-                       count=15,
-                       user_agent='YOUR COMPANY NAME HERE')
+   my_filings = CompanyFilings(cik_lookup=['aapl', 'msft', 'Facebook'],
+                               filing_type=FilingType.FILING_10Q,
+                               count=15,
+                               user_agent='Name (email)')
 
 
 Saving Filings
diff --git a/docs/source/whatsnew/v0.4.0.rst b/docs/source/whatsnew/v0.4.0.rst
index 0ada6a3..81f317a 100644
--- a/docs/source/whatsnew/v0.4.0.rst
+++ b/docs/source/whatsnew/v0.4.0.rst
@@ -13,6 +13,7 @@ Many breaking changes, as this update does a lot of restructuring.
 - Create ``filings`` factory that returns the correct class based on user inputs. Returns one of ``ComboFilings``, ``QuarterlyFilings``, ``DailyFilings``, ``CompanyFilings``.
 - Add many more types to ``FilingType`` enum. Switch to csv table for details.
 - If no filings found for ``DailyFilings`` or ``MasterFilings`` after finding that other filings that do not match the criteria exist, then raise a ``NoFilingsError`` instead of a ``ValueError``.
+- Fix use of ``user_agent`` by ``NetworkClient`` and require its use by all filings classes/functions
 
 Contributors
 ~~~~~~~~~~~~
diff --git a/secedgar/__init__.py b/secedgar/__init__.py
index 8de16c5..61a6fdf 100644
--- a/secedgar/__init__.py
+++ b/secedgar/__init__.py
@@ -1,4 +1,4 @@
-__version__ = '0.4.0-alpha'
+__version__ = '0.4.0-alpha.1'
 
 from secedgar.core.combo import ComboFilings  # noqa: F401
 from secedgar.core.company import CompanyFilings  # noqa: F401
diff --git a/secedgar/cik_lookup.py b/secedgar/cik_lookup.py
index 153e422..b299f0c 100644
--- a/secedgar/cik_lookup.py
+++ b/secedgar/cik_lookup.py
@@ -37,11 +37,13 @@ class CIKLookup:
 
     Args:
         lookup (Union[str, list]): Ticker, company name, or list of tickers and/or company names.
+        client (secedgar.client.NetworkClient): A network client object to use. See
+            :class:`secedgar.client.NetworkClient` for more details.
 
     .. versionadded:: 0.1.5
     """
 
-    def __init__(self, lookups, client=None, **kwargs):
+    def __init__(self, lookups, client, **kwargs):
         if lookups and isinstance(lookups, str):
             self._lookups = [lookups]  # make single string into list
         else:
@@ -50,7 +52,7 @@ class CIKLookup:
                 raise TypeError("CIKs must be given as string or iterable.")
             self._lookups = lookups
         self._params = {}
-        self._client = client if client is not None else NetworkClient(**kwargs)
+        self._client = client or NetworkClient(**kwargs)
         self._lookup_dict = None
         self._ciks = None
 
diff --git a/secedgar/client.py b/secedgar/client.py
index e8073b4..7b48c7a 100644
--- a/secedgar/client.py
+++ b/secedgar/client.py
@@ -18,30 +18,38 @@ class NetworkClient:
     """Class in charge of sending and handling requests to EDGAR database.
 
     Args:
-        retry_count (int): Number of times to retry connecting to URL if not successful.
+        user_agent (str): Value used for HTTP header "User-Agent" for all requests.
+            Must be given. See the SEC's statement on
+            `fair access <https://www.sec.gov/os/accessing-edgar-data>`_
+            for more information.
+        retry_count (int, optional): Number of times to retry connecting to URL if not successful.
             Defaults to 3.
-        batch_size (int): Number of filings to receive per request (helpful if pagination needed).
-            Defaults to 10.
-        backoff_factor (float): Backoff factor to use with ``urllib3.util.retry.Retry``.
+        batch_size (int, optional): Number of filings to receive per request
+            Helpful if pagination needed. Defaults to 10.
+        backoff_factor (float, optional): Backoff factor to use with ``urllib3.util.retry.Retry``.
             See urllib3 docs for more info. Defaults to 0.
-        rate_limit (int): Number of requests per second to limit to.
+        rate_limit (int, optional): Number of requests per second to limit to.
             Defaults to 10.
-        user_agent (str): Value used for HTTP header "User-Agent" for all requests.
-            Defaults to "github.com/sec-edgar/sec-edgar"
 
     .. note:
        It is highly suggested to keep rate_limit <= 10, as the SEC will block your IP
        temporarily if you exceed this rate.
+
+    Examples:
+        .. code-block:: python
+
+            from secedgar.client import NetworkClient
+            client = NetworkClient(user_agent="Name (email)")
     """
 
     _BASE = "http://www.sec.gov/"
 
     def __init__(self,
+                 user_agent,
                  retry_count=3,
                  batch_size=10,
                  backoff_factor=0,
-                 rate_limit=10,
-                 user_agent="github.com/sec-edgar/sec-edgar"):
+                 rate_limit=10):
         self.retry_count = retry_count
         self.batch_size = batch_size
         self.backoff_factor = backoff_factor
@@ -223,22 +231,17 @@ class NetworkClient:
             with open(path, "wb") as f:
                 f.write(contents)
 
-        conn = aiohttp.TCPConnector(limit=self.rate_limit)
-        headers = {
-            "Connection": "keep-alive",
-            "User-Agent": self.user_agent,
-        }
-        client = aiohttp.ClientSession(connector=conn, headers=headers,
-                                       raise_for_status=True)
-
         def batch(iterable, n):
             length = len(iterable)
             for ndx in range(0, length, n):
                 yield iterable[ndx:min(ndx + n, length)]
 
         conn = aiohttp.TCPConnector(limit=self.rate_limit)
-        client = aiohttp.ClientSession(connector=conn,
-                                       headers={'Connection': 'keep-alive'},
+        headers = {
+            "Connection": "keep-alive",
+            "User-Agent": self.user_agent,
+        }
+        client = aiohttp.ClientSession(connector=conn, headers=headers,
                                        raise_for_status=True)
 
         async with client:
diff --git a/secedgar/core/_base.py b/secedgar/core/_base.py
index 9bdb0d2..21fc4eb 100644
--- a/secedgar/core/_base.py
+++ b/secedgar/core/_base.py
@@ -29,7 +29,7 @@ class AbstractFiling(ABC):
     @property
     @abstractmethod
     def client(self):
-        """``secedgar.client._base``: Client to use to make requests."""
+        """``secedgar.client.NetworkClient``: Client to use to make requests."""
         pass  # pragma: no cover
 
     @property
diff --git a/secedgar/core/_index.py b/secedgar/core/_index.py
index e4380a5..be2cdcf 100644
--- a/secedgar/core/_index.py
+++ b/secedgar/core/_index.py
@@ -18,18 +18,24 @@ class IndexFilings(AbstractFiling):
     """Abstract Base Class for index filings.
 
     Args:
-        client (secedgar.client._base, optional): Client to use. Defaults to
-            ``secedgar.client.NetworkClient``.
-
+        user_agent (Union[str, NoneType]): Value used for HTTP header "User-Agent" for all requests.
+            If given None, a valid client with user_agent must be given.
+            See the SEC's statement on
+            `fair access <https://www.sec.gov/os/accessing-edgar-data>`_
+            for more information.
+        client (Union[NoneType, secedgar.client.NetworkClient], optional): Client to use for
+            fetching data. If None is given, a user_agent must be given to pass to
+            :class:`secedgar.client.NetworkClient`.
+            Defaults to ``secedgar.client.NetworkClient`` if none is given.
         entry_filter (function, optional): A boolean function to determine
             if the FilingEntry should be kept. E.g. `lambda l: l.form_type == "4"`.
             Defaults to `None`.
         kwargs: Any keyword arguments to pass to ``NetworkClient`` if no client is specified.
     """
 
-    def __init__(self, client=None, entry_filter=None, **kwargs):
+    def __init__(self, user_agent=None, client=None, entry_filter=None, **kwargs):
         super().__init__()
-        self._client = client if client is not None else NetworkClient(**kwargs)
+        self._client = client or NetworkClient(user_agent=user_agent, **kwargs)
         self._listings_directory = None
         self._master_idx_file = None
         self._filings_dict = None
@@ -54,7 +60,7 @@ class IndexFilings(AbstractFiling):
 
     @property
     def client(self):
-        """``secedgar.client._base``: Client to use to make requests."""
+        """``secedgar.client.NetworkClient``: Client to use to make requests."""
         return self._client
 
     @property
@@ -115,7 +121,7 @@ class IndexFilings(AbstractFiling):
             update_cache (bool, optional): Whether master index should be updated
                 method call. Defaults to False.
             kwargs: Keyword arguments to pass to
-                ``secedgar.client._base.AbstractClient.get_response``.
+                ``secedgar.client.NetworkClient.get_response``.
 
         Returns:
             text (str): Idx file text.
diff --git a/secedgar/core/combo.py b/secedgar/core/combo.py
index 9fc922a..604ba04 100644
--- a/secedgar/core/combo.py
+++ b/secedgar/core/combo.py
@@ -1,5 +1,6 @@
 from datetime import date, timedelta
 from functools import reduce
+from typing import Union
 
 from secedgar.core.daily import DailyFilings
 from secedgar.core.quarterly import QuarterlyFilings
@@ -17,7 +18,14 @@ class ComboFilings:
         end_date (Union[str, datetime.datetime, datetime.date], optional):
             Date after which not to fetch reports.
             Stands for "date before." Defaults to today.
-        client (secedgar.client._base.AbstractClient, optional): Client to use for fetching data.
+        user_agent (Union[str, NoneType]): Value used for HTTP header "User-Agent" for all requests.
+            If given None, a valid client with user_agent must be given.
+            See the SEC's statement on
+            `fair access <https://www.sec.gov/os/accessing-edgar-data>`_
+            for more information.
+        client (Union[NoneType, secedgar.client.NetworkClient], optional): Client to use for
+            fetching data. If None is given, a user_agent must be given to pass to
+            :class:`secedgar.client.NetworkClient`.
             Defaults to ``secedgar.client.NetworkClient`` if none is given.
         entry_filter (function, optional): A boolean function to determine
             if the FilingEntry should be kept. Defaults to `lambda _: True`.
@@ -28,26 +36,46 @@ class ComboFilings:
             ``DailyFilings`` to ``QuarterlyFilings``. If ``QuarterlyFilings`` is used, an
             additional filter will be added to limit which days are included.
             Defaults to 30.
+        kwargs: Any keyword arguments to pass to ``NetworkClient`` if no client is specified.
 
     .. versionadded:: 0.4.0
+
+    Examples:
+        To download all filings from January 6, 2020 until November 5, 2020, you could do following:
+
+        .. code-block:: python
+
+            from datetime import date
+            from secedgar import ComboFilings
+
+            combo_filings = ComboFilings(start_date=date(2020, 1, 6),
+                                         end_date=date(2020, 11, 5)
+            combo_filings.save('/my_directory')
     """
 
     def __init__(self,
                  start_date: date,
                  end_date: date,
+                 user_agent: Union[str, None] = None,
                  client=None,
                  entry_filter=lambda _: True,
-                 balancing_point=30):
+                 balancing_point=30,
+                 **kwargs):
         self.entry_filter = entry_filter
         self.start_date = start_date
         self.end_date = end_date
+        self.user_agent = user_agent
         self.quarterly = QuarterlyFilings(year=self.start_date.year,
                                           quarter=get_quarter(self.start_date),
+                                          user_agent=user_agent,
                                           client=client,
-                                          entry_filter=self.entry_filter)
+                                          entry_filter=self.entry_filter,
+                                          **kwargs)
         self.daily = DailyFilings(date=self.start_date,
+                                  user_agent=user_agent,
                                   client=client,
-                                  entry_filter=self.entry_filter)
+                                  entry_filter=self.entry_filter,
+                                  **kwargs)
         self.balancing_point = balancing_point
         self._recompute()
 
diff --git a/secedgar/core/company.py b/secedgar/core/company.py
index 7908393..6df4f8d 100644
--- a/secedgar/core/company.py
+++ b/secedgar/core/company.py
@@ -18,6 +18,11 @@ class CompanyFilings(AbstractFiling):
         cik_lookup (str): Central Index Key (CIK) for company of interest.
         filing_type (Union[secedgar.core.filing_types.FilingType, None]): Valid filing type
             enum. Defaults to None. If None, then all filing types for CIKs will be returned.
+        user_agent (Union[str, NoneType]): Value used for HTTP header "User-Agent" for all requests.
+            If given None, a valid client with user_agent must be given.
+            See the SEC's statement on
+            `fair access <https://www.sec.gov/os/accessing-edgar-data>`_
+            for more information.
         start_date (Union[str, datetime.datetime, datetime.date], optional): Date before
             which not to fetch reports. Stands for "date after."
             Defaults to None (will fetch all filings before ``end_date``).
@@ -29,14 +34,72 @@ class CompanyFilings(AbstractFiling):
         ownership (str): Must be in {"include", "exclude"}. Whether or not to include ownership
             filings.
         match_format (str): Must be in {"EXACT", "AMEND", "ALL"}.
-        kwargs: See kwargs accepted for :class:`secedgar.client.network_client.NetworkClient`.
+        kwargs: See kwargs accepted for :class:`secedgar.client.NetworkClient`.
 
-    .. versionadded:: 0.1.5
+    Examples:
+        Restrict the start and end dates by using the ``start_date`` and ``end_date`` arguments.
+
+        .. code-block:: python
+
+            from secedgar import FilingType, CompanyFilings
+            from datetime import date
+
+            filing = CompanyFilings(cik_lookup="aapl",
+                                    filing_type=FilingType.FILING_10K,
+                                    start_date=date(2015, 1, 1),
+                                    end_date=date(2019, 1, 1),
+                                    user_agent="Name (email)")
+
+        If you would like to find all filings from some ``start_date`` until today,
+        simply exclude ``end_date``. The end date defaults to today's date.
+
+        .. code-block:: python
+
+            from secedgar import FilingType, CompanyFilings
+            from datetime import date
+
+            # end date defaults to today
+            filing = CompanyFilings(cik_lookup="aapl",
+                                    filing_type=FilingType.FILING_10K,
+                                    start_date=date(2015, 1, 1),
+                                    user_agent="Name (email)")
+
+        You can also look up a specific filing type for multiple companies.
+
+        .. code-block:: python
+
+            from secedgar import FilingType, CompanyFilings
+            from datetime import date
+
+            filing = CompanyFilings(cik_lookup=["aapl", "msft", "fb"],
+                                    filing_type=FilingType.FILING_10K,
+                                    start_date=date(2015, 1, 1),
+                                    user_agent="Name (email)")
+
+        *For a full list of the available filing types, please see*
+        :class:`secedgar.core.FilingType`.
+
+
+        SEC requests that traffic identifies itself via a user agent string. You can
+        customize this according to your preference using the ``user_agent`` argument.
+
+        .. code-block:: python
+
+            from secedgar import FilingType, CompanyFilings
+            from datetime import date
+
+            filing = CompanyFilings(cik_lookup=["aapl", "msft", "fb"],
+                                    filing_type=FilingType.FILING_10K,
+                                    start_date=date(2015, 1, 1),
+                                    user_agent="Name (email)")
+
+    .. versionadded:: 0.4.0
     """
 
     def __init__(self,
                  cik_lookup,
                  filing_type=None,
+                 user_agent=None,
                  start_date=None,
                  end_date=date.today(),
                  client=None,
@@ -57,7 +120,7 @@ class CompanyFilings(AbstractFiling):
         self.count = count
         self.match_format = match_format
         # Make default client NetworkClient and pass in kwargs
-        self._client = client if client is not None else NetworkClient(**kwargs)
+        self._client = client or NetworkClient(user_agent=user_agent, **kwargs)
         # make CIKLookup object for users if not given
         self.cik_lookup = cik_lookup
 
@@ -73,7 +136,7 @@ class CompanyFilings(AbstractFiling):
 
     @property
     def client(self):
-        """``secedgar.client._base``: Client to use to make requests."""
+        """``secedgar.client.NetworkClient``: Client to use to make requests."""
         return self._client
 
     @property
@@ -158,7 +221,7 @@ class CompanyFilings(AbstractFiling):
         Args:
             **kwargs: Anything to be passed to requests when making get request.
                 See keyword arguments accepted for
-                ``secedgar.client._base.AbstractClient.get_soup``.
+                ``secedgar.client.NetworkClient.get_soup``.
 
         Returns:
             urls (list): List of urls for txt files to download.
@@ -178,7 +241,7 @@ class CompanyFilings(AbstractFiling):
             cik (str): CIK for company.
             **kwargs: Anything to be passed to requests when making get request.
                 See keyword arguments accepted for
-                ``secedgar.client._base.AbstractClient.get_soup``.
+                ``secedgar.client.NetworkClient.get_soup``.
 
         Returns:
             txt_urls (list of str): Up to the desired number of URLs for that specific company
diff --git a/secedgar/core/daily.py b/secedgar/core/daily.py
index ff105e7..7bca0fe 100644
--- a/secedgar/core/daily.py
+++ b/secedgar/core/daily.py
@@ -10,8 +10,15 @@ class DailyFilings(IndexFilings):
 
     Attributes:
         date (datetime.date): Date of daily filing to fetch.
-        client (secedgar.client._base.AbstractClient, optional): Client to use for fetching data.
-            Defaults to ``secedgar.client.NetworkClient`` if none is given.
+        user_agent (Union[str, NoneType], optional): Value used for HTTP header
+            "User-Agent" for all requests. If given None, a valid client with
+            user_agent must be given. See the SEC's statement on
+            `fair access <https://www.sec.gov/os/accessing-edgar-data>`_
+            for more information. Defaults to None.
+        client (Union[NoneType, secedgar.client.NetworkClient], optional): Client to use for
+            fetching data. If None is given, a user_agent must be given to pass to
+            :class:`secedgar.client.NetworkClient`. Defaults to ``secedgar.client.NetworkClient``
+            if none is given.
         entry_filter (function, optional): A boolean function to determine
             if the FilingEntry should be kept. Defaults to `lambda _: True`.
             The ``FilingEntry`` object exposes 7 variables which can be
@@ -46,12 +53,17 @@ class DailyFilings(IndexFilings):
         from datetime import date
         from secedgar.core import DailyFilings
 
-        d = DailyFilings(date=date(2020, 12, 10), entry_filter=get_company_ab_10k)
+        d = DailyFilings(date=date(2020, 12, 10),
+                         entry_filter=get_company_ab_10k,
+                         user_agent="Name (email)")
 
     """
 
-    def __init__(self, date, client=None, entry_filter=lambda _: True):
-        super().__init__(client=client, entry_filter=entry_filter)
+    def __init__(self, date, user_agent=None, client=None, entry_filter=lambda _: True, **kwargs):
+        super().__init__(user_agent=user_agent,
+                         client=client,
+                         entry_filter=entry_filter,
+                         **kwargs)
         self.date = date
 
     @property
diff --git a/secedgar/core/filings.py b/secedgar/core/filings.py
index 61fefbd..e3fd481 100644
--- a/secedgar/core/filings.py
+++ b/secedgar/core/filings.py
@@ -5,18 +5,20 @@ from secedgar.core.company import CompanyFilings
 from secedgar.core.daily import DailyFilings
 from secedgar.core.filing_types import FilingType
 from secedgar.core.quarterly import QuarterlyFilings
-from secedgar.utils import get_month, get_quarter, add_quarter
 from secedgar.exceptions import FilingTypeError
+from secedgar.utils import add_quarter, get_month, get_quarter
 
 
 def filings(
     cik_lookup=None,
     filing_type=None,
+    user_agent=None,
     start_date=None,
     end_date=date.today(),
     count=None,
     client=None,
     entry_filter=lambda _: True,
+    **kwargs
 ):
     """Utility method to get best filing object.
 
@@ -28,18 +30,107 @@ def filings(
             enum. Defaults to None. If None, then all filing types for CIKs will be returned.
         count (int, optional): Number of filings to fetch. Will fetch up to `count` if that
         many filings are available. Defaults to all filings available.
-        client (secedgar.client._base, optional): Client to use. Defaults to
+        client (secedgar.client.NetworkClient, optional): Client to use. Defaults to
                     ``secedgar.client.NetworkClient`` if None given.
         entry_filter (function, optional): A boolean function to determine
             if the FilingEntry should be kept. Defaults to ``lambda _: True``.
             See :class:`secedgar.core.DailyFilings` for more detail.
-    .. code-block:: python
+        kwargs: Any keyword arguments to pass to ``NetworkClient`` if no client is specified.
 
-        from datetime import date
-        from secedgar.core import filings, FilingType
+    Examples:
+        Using the ``filings`` function from secedgar is the easiest way to retrieve filings.
+
+        Depending on the arguments given, secedgar will return an object that will get you
+        the information you want from EDGAR.
+
+        There are 4 main classes which can be returned.
+
+            - :class:`secedgar.core.combo.ComboFilings` for fetching filings over multiple days
+                that does not fall exactly into a quarter
+            - :class:`secedgar.core.company.CompanyFilings` for fetching a particular
+                filing type for one or more companies
+            - :class:`secedgar.core.daily.DailyFilings` for fetching all filings
+                from a specific date
+            - :class:`secedgar.core.quarterly.QuarterlyFilings` for fetching all filings
+                from a specific quarter
+
+        To get all filings over a time span, you could use something like below.
+
+        .. code-block:: python
+
+            from datetime import date
+            from secedgar.core import filings, FilingType
+
+            # secedgar creates correct filing object for given arguments
+            # this will fetch the first 50 filings found over the time span
+            my_filings = filings(start_date=date(2020, 12, 10),
+                                 end_date=date(2020, 12, 15),
+                                 filing_type=FilingType.FILING_4,
+                                 user_agent="Name (email)",
+                                 count=50)
+
+            # easy access to methods shared across all 4 different filing classes
+            my_filings_urls = my_filings.get_urls()
+            my_filings.save("/path/to/directory")
+
+        To get a single filing type for one or more companies, you could use this:
+
+        .. code-block:: python
+
+            import secedgar as sec
+
+            # similar to above, but fetches filings for specific tickers
+            company_filings = filings(cik_lookup=["aapl", "fb"],
+                                      filing_type=sec.FilingType.FILING_10Q,
+                                      user_agent="Name (email)")
+            company_filings_urls = company_filings.get_urls()
+            company_filings.save("/path/to/directory")
+
+        To get filings for a single day, you could use something like this:
+
+        .. code-block:: python
+
+            from datetime import date
+            import secedgar as sec
+
+            # all filings for
+            daily_filings = filings(start_date=date(2020, 1 ,3),
+                                    end_date=date(2020, 1, 3),
+                                    user_agent="Name (email)")
+            daily_filings.save("/path/to/directory")
+
+            # limit which quarterly filings to use - saves only form 4 filings
+            limit_to_form4 = lambda f: f.form_type.lower() == "4"
+            daily_filings_limited = filings(start_date=date(2020, 1 ,3),
+                                            end_date=date(2020, 1, 3),
+                                            user_agent="Name (email)",
+                                            entry_filter=limit_to_form4)
+            daily_filings_limited.save("/path/to/other/directory")
+
+
+        For getting filings from a specific quarter, the function call would look like this:
+
+
+        .. code-block:: python
+
+            from datetime import date
+            import secedgar as sec
+
+            # all quarterly filings
+            quarterly_filings = filings(start_date=date(2020, 1 ,1),
+                                        end_date=date(2020, 3, 31),
+                                        user_agent="Name (email)")
+            quarterly_filings.save("/path/to/directory")
+
+            # limit which quarterly filings to use
+            # saves only 10-K and 10-Q filings from quarter
+            limit_to_10k_10q = lambda f: f.form_type.lower() in ("10-k", "10-q")
+            quarterly_filings_limited = filings(start_date=date(2020, 1 ,1),
+                                                end_date=date(2020, 3, 31),
+                                                user_agent="Name (email)",
+                                                entry_filter=limit_to_10k_10q)
+            quarterly_filings_limited.save("/path/to/other/directory")
 
-        engine = filings(start_date=date(2020, 12, 10), end_date=date(2020, 12, 10),
-            filing_type=FilingType.FILING_4, count=50)
     """
     if filing_type is not None and not isinstance(filing_type, FilingType):
         raise FilingTypeError
@@ -48,19 +139,20 @@ def filings(
         return CompanyFilings(
             cik_lookup,
             filing_type=filing_type,
+            user_agent=user_agent,
             start_date=start_date,
             end_date=end_date,
             count=count,
             client=client,
+            **kwargs
         )
+    # Define entry filter as original
+    _entry_filter = entry_filter
 
     if filing_type is not None:
-        original_entry_filter = entry_filter
-
-        def entry_filter(x):
-            return x.form_type == filing_type and original_entry_filter(x)
-
-        original_entry_filter = entry_filter
+        # If filing type also given, add filing types to existing entry filter
+        def _entry_filter(x):
+            return x.form_type == filing_type and entry_filter(x)
 
     if count is not None:
         raise NotImplementedError(
@@ -70,8 +162,10 @@ def filings(
     if (end_date is None or end_date == start_date) and isinstance(
             start_date, date):
         return DailyFilings(date=start_date,
+                            user_agent=user_agent,
                             client=client,
-                            entry_filter=entry_filter)
+                            entry_filter=_entry_filter,
+                            **kwargs)
 
     if isinstance(start_date, date) and isinstance(end_date, date):
         current_quarter = get_quarter(start_date)
@@ -81,14 +175,18 @@ def filings(
         end_quarter_date = date(next_year, get_month(next_quarter),
                                 1) - timedelta(days=1)
         if start_quarter_date == start_date and end_date == end_quarter_date:
-            return QuarterlyFilings(current_year,
-                                    current_quarter,
+            return QuarterlyFilings(year=current_year,
+                                    quarter=current_quarter,
                                     client=client,
-                                    entry_filter=entry_filter)
-        return ComboFilings(start_date,
-                            end_date,
+                                    user_agent=user_agent,
+                                    entry_filter=_entry_filter,
+                                    **kwargs)
+        return ComboFilings(start_date=start_date,
+                            end_date=end_date,
+                            user_agent=user_agent,
                             client=client,
-                            entry_filter=entry_filter)
+                            entry_filter=_entry_filter,
+                            **kwargs)
 
     raise ValueError(
         """Invalid arguments. You must provide 'cik_lookup' OR 'start_date' \
diff --git a/secedgar/core/quarterly.py b/secedgar/core/quarterly.py
index f3fa7fb..7e0ef7a 100644
--- a/secedgar/core/quarterly.py
+++ b/secedgar/core/quarterly.py
@@ -11,21 +11,42 @@ class QuarterlyFilings(IndexFilings):
     Args:
         year (int): Must be in between 1993 and the current year (inclusive).
         quarter (int): Must be 1, 2, 3, or 4. Quarter of filings to fetch.
-        client (secedgar.client._base, optional): Client to use. Defaults to
-            ``secedgar.client.NetworkClient`` if None given.
+        user_agent (Union[str, NoneType], optional): Value used for HTTP header
+            "User-Agent" for all requests. If given None, a valid client with
+            user_agent must be given. See the SEC's statement on
+            `fair access <https://www.sec.gov/os/accessing-edgar-data>`_
+            for more information. Defaults to None.
+        client (Union[NoneType, secedgar.client.NetworkClient], optional): Client to use for
+            fetching data. If None is given, a user_agent must be given to pass to
+            :class:`secedgar.client.NetworkClient`. Defaults to ``secedgar.client.NetworkClient``
+            if none is given.
         entry_filter (function, optional): A boolean function to determine
             if the FilingEntry should be kept. Defaults to ``lambda _: True``.
             See :class:`secedgar.core.DailyFilings` for more detail.
         kwargs: Keyword arguments to pass to ``secedgar.core._index.IndexFilings``.
+
+    Examples:
+        .. code-block:: python
+
+            import secedgar as sec
+            quarterly = sec.QuarterlyFilings(year=2021,
+                                             quarter=2,
+                                             user_agent="Name (email)")
+            quarterly.save("/path/to/dir")
+
     """
 
     def __init__(self,
                  year,
                  quarter,
+                 user_agent=None,
                  client=None,
                  entry_filter=lambda _: True,
                  **kwargs):
-        super().__init__(client=client, entry_filter=entry_filter, **kwargs)
+        super().__init__(user_agent=user_agent,
+                         client=client,
+                         entry_filter=entry_filter,
+                         **kwargs)
         self.year = year
         self.quarter = quarter
 


Test patch:
diff --git a/secedgar/tests/conftest.py b/secedgar/tests/conftest.py
index 3f4f70a..6ce7ec5 100644
--- a/secedgar/tests/conftest.py
+++ b/secedgar/tests/conftest.py
@@ -36,6 +36,11 @@ def no_http_requests(monkeysession):
         monkeysession.setattr(avoid, external_request_mock)
 
 
+@pytest.fixture(scope="session")
+def mock_user_agent():
+    return "Example (me@example.com)"
+
+
 @pytest.fixture(scope="session")
 def mock_filing_response(monkeysession):
     monkeysession.setattr(
diff --git a/secedgar/tests/core/test_combo.py b/secedgar/tests/core/test_combo.py
index 5a90ced..55ea6a2 100644
--- a/secedgar/tests/core/test_combo.py
+++ b/secedgar/tests/core/test_combo.py
@@ -1,6 +1,7 @@
 from datetime import date
 
 import pytest
+from secedgar.client import NetworkClient
 from secedgar.core.combo import ComboFilings
 
 
@@ -17,18 +18,34 @@ def quarterly_list_matches(l1, l2):
 
 
 class TestComboFilings:
+    def test_user_agent_client_none(self):
+        with pytest.raises(TypeError):
+            _ = ComboFilings(start_date=date(2020, 1, 1),
+                             end_date=date(2020, 12, 31),
+                             user_agent=None,
+                             client=None)
 
-    def test_combo_quarterly_only_one_year(self):
+    def test_client_passed_to_objects(self, mock_user_agent):
+        client = NetworkClient(user_agent=mock_user_agent)
         combo = ComboFilings(start_date=date(2020, 1, 1),
-                             end_date=date(2020, 12, 31))
+                             end_date=date(2020, 12, 31),
+                             client=client)
+        assert combo.quarterly.client == client
+        assert combo.daily.client == client
+
+    def test_combo_quarterly_only_one_year(self, mock_user_agent):
+        combo = ComboFilings(start_date=date(2020, 1, 1),
+                             end_date=date(2020, 12, 31),
+                             user_agent=mock_user_agent)
         expected = [(2020, 1, lambda _: True), (2020, 2, lambda _: True),
                     (2020, 3, lambda _: True), (2020, 4, lambda _: True)]
         assert quarterly_list_matches(combo.quarterly_date_list, expected)
         assert len(combo.daily_date_list) == 0
 
-    def test_combo_quarterly_only_multiple_years(self):
+    def test_combo_quarterly_only_multiple_years(self, mock_user_agent):
         combo = ComboFilings(start_date=date(2018, 10, 1),
-                             end_date=date(2020, 6, 30))
+                             end_date=date(2020, 6, 30),
+                             user_agent=mock_user_agent)
         expected = [
             (2018, 4, lambda _: True),
             (2019, 1, lambda _: True),
@@ -41,15 +58,17 @@ class TestComboFilings:
         assert quarterly_list_matches(combo.quarterly_date_list, expected)
         assert len(combo.daily_date_list) == 0
 
-    def test_combo_daily_only_single_day(self):
+    def test_combo_daily_only_single_day(self, mock_user_agent):
         combo = ComboFilings(start_date=date(2020, 12, 10),
-                             end_date=date(2020, 12, 10))
+                             end_date=date(2020, 12, 10),
+                             user_agent=mock_user_agent)
         assert [str(s) for s in combo.daily_date_list] == ["2020-12-10"]
         assert len(combo.quarterly_date_list) == 0
 
-    def test_combo_daily_only_multiple_days(self):
+    def test_combo_daily_only_multiple_days(self, mock_user_agent):
         combo = ComboFilings(start_date=date(2020, 12, 10),
-                             end_date=date(2020, 12, 12))
+                             end_date=date(2020, 12, 12),
+                             user_agent=mock_user_agent)
         expected = ["2020-12-10", "2020-12-11", "2020-12-12"]
         assert [str(s) for s in combo.daily_date_list] == expected
         assert len(combo.quarterly_date_list) == 0
@@ -73,8 +92,19 @@ class TestComboFilings:
             # (date(2020, 1, 1), date(2020, 6, 28), [], [(2020, 1, lambda _: True),
             #                                            (2020, 2, lambda x: date(x['date_filed']) <= date(2020, 6, 28))])  # noqa
         ])
-    def test_combo_daily_quarterly_mixed(self, start_date, end_date,
-                                         quarterly_expected, daily_expected):
-        combo = ComboFilings(start_date=start_date, end_date=end_date)
+    def test_combo_daily_quarterly_mixed(self, start_date,
+                                         end_date,
+
+                                         quarterly_expected,
+                                         daily_expected,
+                                         mock_user_agent):
+        combo = ComboFilings(start_date=start_date, end_date=end_date, user_agent=mock_user_agent)
         assert [str(s) for s in combo.daily_date_list] == daily_expected
         assert quarterly_list_matches(combo.quarterly_date_list, quarterly_expected)
+
+    def test_user_agent_passed_to_client(self, mock_user_agent):
+        combo = ComboFilings(start_date=date(2020, 1, 1),
+                             end_date=date(2021, 1, 1),
+                             user_agent=mock_user_agent)
+        assert combo.quarterly.client.user_agent == mock_user_agent
+        assert combo.daily.client.user_agent == mock_user_agent
diff --git a/secedgar/tests/core/test_company.py b/secedgar/tests/core/test_company.py
index af103be..d59cc2b 100644
--- a/secedgar/tests/core/test_company.py
+++ b/secedgar/tests/core/test_company.py
@@ -70,12 +70,33 @@ def mock_single_cik_filing_limited_responses(monkeypatch):
     monkeypatch.setattr(NetworkClient, "get_response", mock_limited_responses)
 
 
-class TestFiling(object):
-
-    def test_count_returns_exact(self, mock_cik_validator_get_single_cik,
+class TestCompanyFilings:
+    class TestCompanyFilingsClient:
+
+        def test_user_agent_client_none(self):
+            with pytest.raises(TypeError):
+                _ = CompanyFilings(cik_lookup="aapl",
+                                   filing_type=FilingType.FILING_10Q,
+                                   user_agent=None,
+                                   client=None)
+
+        def test_user_agent_set_to_client(self, mock_user_agent):
+            aapl = CompanyFilings(cik_lookup="aapl",
+                                  filing_type=FilingType.FILING_10Q,
+                                  user_agent=mock_user_agent)
+            assert aapl.client.user_agent == mock_user_agent
+
+        def test_client_property_set(self, mock_user_agent):
+            aapl = CompanyFilings(cik_lookup="aapl",
+                                  filing_type=FilingType.FILING_10Q,
+                                  client=NetworkClient(user_agent=mock_user_agent))
+            assert aapl.client.user_agent == mock_user_agent
+
+    def test_count_returns_exact(self, mock_user_agent, mock_cik_validator_get_single_cik,
                                  mock_single_cik_filing):
         count = 10
-        aapl = CompanyFilings(cik_lookup="aapl",
+        aapl = CompanyFilings(user_agent=mock_user_agent,
+                              cik_lookup="aapl",
                               filing_type=FilingType.FILING_10Q,
                               count=count)
         urls = aapl.get_urls()["aapl"]
@@ -85,8 +106,9 @@ class TestFiling(object):
                 urls, count))
 
     @pytest.mark.parametrize("count", [None, 5, 10, 15, 27, 33])
-    def test_count_setter_on_init(self, count):
-        filing = CompanyFilings(cik_lookup="aapl",
+    def test_count_setter_on_init(self, mock_user_agent, count):
+        filing = CompanyFilings(user_agent=mock_user_agent,
+                                cik_lookup="aapl",
                                 filing_type=FilingType.FILING_10Q,
                                 count=count)
         assert filing.count == count
@@ -98,17 +120,20 @@ class TestFiling(object):
         datetime.datetime(2020, 4, 1),
         datetime.datetime(2020, 5, 1), "20200101", 20200101, None
     ])
-    def test_good_start_date_setter_on_init(self, start_date):
-        filing = CompanyFilings(cik_lookup="aapl",
-                                filing_type=FilingType.FILING_10Q,
-                                start_date=start_date)
+    def test_good_start_date_setter_on_init(self, start_date, mock_user_agent):
+        filing = CompanyFilings(
+            cik_lookup="aapl",
+            filing_type=FilingType.FILING_10Q,
+            start_date=start_date,
+            user_agent=mock_user_agent)
         assert filing.start_date == start_date
 
     @pytest.mark.parametrize("bad_start_date",
                              [1, 2020010101, "2020010101", "2020", "0102"])
-    def test_bad_start_date_setter_on_init(self, bad_start_date):
+    def test_bad_start_date_setter_on_init(self, mock_user_agent, bad_start_date):
         with pytest.raises(TypeError):
-            CompanyFilings(cik_lookup="aapl",
+            CompanyFilings(user_agent=mock_user_agent,
+                           cik_lookup="aapl",
                            filing_type=FilingType.FILING_10Q,
                            start_date=bad_start_date)
 
@@ -117,16 +142,18 @@ class TestFiling(object):
                                                       (0.0, TypeError),
                                                       (1.0, TypeError),
                                                       ("1", TypeError)])
-    def test_count_setter_bad_values(self, count, expected_error):
+    def test_count_setter_bad_values(self, mock_user_agent, count, expected_error):
         with pytest.raises(expected_error):
-            CompanyFilings(cik_lookup="aapl",
+            CompanyFilings(user_agent=mock_user_agent,
+                           cik_lookup="aapl",
                            filing_type=FilingType.FILING_10Q,
                            count=count)
 
-    def test_date_is_sanitized(self):
+    def test_date_is_sanitized(self, mock_user_agent):
         start_date = datetime.datetime(2012, 3, 1)
         end_date = datetime.datetime(2015, 1, 1)
-        aapl = CompanyFilings(cik_lookup="aapl",
+        aapl = CompanyFilings(user_agent=mock_user_agent,
+                              cik_lookup="aapl",
                               filing_type=FilingType.FILING_10Q,
                               count=10,
                               start_date=start_date,
@@ -136,8 +163,9 @@ class TestFiling(object):
         assert aapl.start_date == datetime.datetime(2012, 3, 1)
         assert aapl.end_date == datetime.datetime(2015, 1, 1)
 
-    def test_date_is_sanitized_when_changed(self):
-        aapl = CompanyFilings(cik_lookup="aapl",
+    def test_date_is_sanitized_when_changed(self, mock_user_agent):
+        aapl = CompanyFilings(user_agent=mock_user_agent,
+                              cik_lookup="aapl",
                               filing_type=FilingType.FILING_10Q,
                               count=10,
                               start_date="20150101")
@@ -149,18 +177,20 @@ class TestFiling(object):
     @pytest.mark.parametrize("date,expected",
                              [("20120101", "20120101"), (20120101, 20120101),
                               (datetime.datetime(2012, 1, 1), "20120101")])
-    def test_end_date_setter(self, date, expected):
-        f = CompanyFilings("aapl",
-                           FilingType.FILING_10Q,
+    def test_end_date_setter(self, mock_user_agent, date, expected):
+        f = CompanyFilings(cik_lookup="aapl",
+                           user_agent=mock_user_agent,
+                           filing_type=FilingType.FILING_10Q,
                            start_date=datetime.datetime(2010, 1, 1),
                            end_date=datetime.datetime(2015, 1, 1))
         f.end_date = date
         assert f.end_date == date and f.params.get("dateb") == expected
 
     @pytest.mark.slow
-    def test_txt_urls(self, mock_cik_validator_get_single_cik,
+    def test_txt_urls(self, mock_user_agent, mock_cik_validator_get_single_cik,
                       mock_single_cik_filing):
-        aapl = CompanyFilings(cik_lookup="aapl",
+        aapl = CompanyFilings(user_agent=mock_user_agent,
+                              cik_lookup="aapl",
                               filing_type=FilingType.FILING_10Q,
                               count=10)
         first_txt_url = aapl.get_urls()["aapl"][0]
@@ -169,28 +199,35 @@ class TestFiling(object):
     @pytest.mark.parametrize("new_filing_type",
                              (FilingType.FILING_10K, FilingType.FILING_8K,
                               FilingType.FILING_13FHR, FilingType.FILING_SD))
-    def test_filing_type_setter(self, new_filing_type):
-        f = CompanyFilings(cik_lookup="aapl", filing_type=FilingType.FILING_10Q)
+    def test_filing_type_setter(self, mock_user_agent, new_filing_type):
+        f = CompanyFilings(user_agent=mock_user_agent,
+                           cik_lookup="aapl",
+                           filing_type=FilingType.FILING_10Q)
         f.filing_type = new_filing_type
         assert f.filing_type == new_filing_type
 
     @pytest.mark.parametrize("bad_filing_type",
                              ("10-k", "10k", "10-q", "10q", 123))
-    def test_bad_filing_type_setter(self, bad_filing_type):
-        f = CompanyFilings(cik_lookup="aapl", filing_type=FilingType.FILING_10Q)
+    def test_bad_filing_type_setter(self, mock_user_agent, bad_filing_type):
+        f = CompanyFilings(user_agent=mock_user_agent,
+                           cik_lookup="aapl",
+                           filing_type=FilingType.FILING_10Q)
         with pytest.raises(FilingTypeError):
             f.filing_type = bad_filing_type
 
     @pytest.mark.parametrize("bad_filing_type",
                              ("10-j", "10-k", "ssd", "invalid", 1))
-    def test_invalid_filing_type_types(self, bad_filing_type):
+    def test_invalid_filing_type_types(self, mock_user_agent, bad_filing_type):
         with pytest.raises(FilingTypeError):
-            CompanyFilings(cik_lookup="0000320193", filing_type=bad_filing_type)
+            CompanyFilings(user_agent=mock_user_agent,
+                           cik_lookup="0000320193",
+                           filing_type=bad_filing_type)
 
     @pytest.mark.parametrize("bad_cik_lookup", (1234567891011, 12345, 123.0))
-    def test_validate_cik_type_inside_filing(self, bad_cik_lookup):
+    def test_validate_cik_type_inside_filing(self, mock_user_agent, bad_cik_lookup):
         with pytest.raises(TypeError):
-            CompanyFilings(cik_lookup=bad_cik_lookup,
+            CompanyFilings(user_agent=mock_user_agent,
+                           cik_lookup=bad_cik_lookup,
                            filing_type=FilingType.FILING_10K)
 
     @pytest.mark.parametrize("no_urls", ({}, {
@@ -199,34 +236,40 @@ class TestFiling(object):
         "msft": []
     }))
     def test_save_no_filings_raises_error(self, tmp_data_directory, monkeypatch,
-                                          no_urls):
+                                          mock_user_agent, no_urls):
         monkeypatch.setattr(CompanyFilings, "get_urls", lambda x: no_urls)
-        f = CompanyFilings(cik_lookup="aapl", filing_type=FilingType.FILING_10K)
+        f = CompanyFilings(user_agent=mock_user_agent,
+                           cik_lookup="aapl", filing_type=FilingType.FILING_10K)
         with pytest.raises(NoFilingsError):
             f.save(tmp_data_directory)
 
     @pytest.mark.smoke
     def test_filing_save_multiple_ciks(self, tmp_data_directory,
+                                       mock_user_agent,
                                        mock_cik_validator_get_multiple_ciks,
                                        mock_single_cik_filing,
                                        mock_filing_response):
         f = CompanyFilings(["aapl", "amzn", "msft"],
                            FilingType.FILING_10Q,
+                           user_agent=mock_user_agent,
                            count=3)
         f.save(tmp_data_directory)
 
     @pytest.mark.smoke
     def test_filing_save_single_cik(self, tmp_data_directory,
+                                    mock_user_agent,
                                     mock_cik_validator_get_single_cik,
                                     mock_single_cik_filing,
                                     mock_filing_response):
-        f = CompanyFilings("aapl", FilingType.FILING_10Q, count=3)
+        f = CompanyFilings("aapl", FilingType.FILING_10Q, user_agent=mock_user_agent, count=3)
         f.save(tmp_data_directory)
 
     def test_filing_get_urls_returns_single_list_of_urls(
-            self, mock_cik_validator_get_multiple_ciks, mock_single_cik_filing):
+            self, mock_user_agent,
+            mock_cik_validator_get_multiple_ciks, mock_single_cik_filing):
         # Uses same response for filing links (will all be filings for aapl)
         f = CompanyFilings(cik_lookup=["aapl", "msft", "amzn"],
+                           user_agent=mock_user_agent,
                            filing_type=FilingType.FILING_10Q,
                            count=5)
         assert all(
@@ -234,13 +277,14 @@ class TestFiling(object):
 
     @pytest.mark.parametrize("count", [10, 25, 30])
     def test_filing_returns_correct_number_of_urls(
-            self, count, mock_cik_validator_get_multiple_ciks,
+            self, count, mock_user_agent, mock_cik_validator_get_multiple_ciks,
             mock_single_cik_filing):
         # Uses same response for filing links (will all be filings for aapl)
         f = CompanyFilings(cik_lookup=["aapl", "msft", "amzn"],
                            filing_type=FilingType.FILING_10Q,
+                           user_agent=mock_user_agent,
                            count=count,
-                           client=NetworkClient(batch_size=10))
+                           client=NetworkClient(user_agent=mock_user_agent, batch_size=10))
         assert all(
             len(f.get_urls().get(key)) == count for key in f.get_urls().keys())
 
@@ -251,13 +295,14 @@ class TestFiling(object):
     # For collections.abc warning 3.8+
     def test_filings_warning_lt_count(self, recwarn, count, raises_error,
                                       tmp_data_directory,
+                                      mock_user_agent,
                                       mock_cik_validator_get_single_cik,
                                       mock_single_cik_filing_limited_responses,
                                       mock_filing_response):  # noqa:E501
         f = CompanyFilings(cik_lookup="aapl",
                            filing_type=FilingType.FILING_10Q,
                            count=count,
-                           client=NetworkClient(batch_size=10))
+                           client=NetworkClient(batch_size=10, user_agent=mock_user_agent))
         f.save(tmp_data_directory)
         if raises_error:
             w = recwarn.pop(UserWarning)
@@ -272,7 +317,8 @@ class TestFiling(object):
 
     @pytest.mark.skip
     @pytest.mark.smoke
-    def test_filing_simple_example(self, tmp_data_directory):
+    def test_filing_simple_example(self, tmp_data_directory, mock_user_agent):
         my_filings = CompanyFilings(cik_lookup="IBM",
-                                    filing_type=FilingType.FILING_10Q)
+                                    filing_type=FilingType.FILING_10Q,
+                                    user_agent=mock_user_agent)
         my_filings.save(tmp_data_directory)
diff --git a/secedgar/tests/core/test_daily.py b/secedgar/tests/core/test_daily.py
index ac3dd6f..14ae94b 100644
--- a/secedgar/tests/core/test_daily.py
+++ b/secedgar/tests/core/test_daily.py
@@ -3,6 +3,7 @@ from datetime import date, datetime
 
 import pytest
 import secedgar.utils as utils
+from secedgar.client import NetworkClient
 from secedgar.core.daily import DailyFilings
 from secedgar.tests.utils import MockResponse, datapath
 
@@ -44,8 +45,8 @@ class TestDaily:
                                                (date(2020, 9, 30), 3),
                                                (date(2020, 10, 1), 4),
                                                (date(2020, 12, 31), 4)])
-    def test_quarter(self, date, expected):
-        assert DailyFilings(date=date).quarter == expected
+    def test_quarter(self, date, expected, mock_user_agent):
+        assert DailyFilings(date=date, user_agent=mock_user_agent).quarter == expected
 
     @pytest.mark.parametrize("date,expected_filename", [
         (date(2020, 1, 1), "master.20200101.idx"),
@@ -53,8 +54,8 @@ class TestDaily:
         (date(2020, 4, 1), "master.20200401.idx"),
         (date(2020, 6, 30), "master.20200630.idx"),
     ])
-    def test_idx_filename(self, date, expected_filename):
-        assert DailyFilings(date=date).idx_filename == expected_filename
+    def test_idx_filename(self, date, expected_filename, mock_user_agent):
+        assert DailyFilings(date=date, user_agent=mock_user_agent).idx_filename == expected_filename
 
     @pytest.mark.parametrize("bad_date", [1.0, 12, "12/31/2018"])
     def test_bad_date_format_fails(self, bad_date):
@@ -65,14 +66,15 @@ class TestDaily:
                                           "http://www.sec.gov/Archives/edgar/data/{cik}/{f}".format(
                                               cik=cik, f=f))
                                          for cik, f in cik_file_pairs])
-    def test_get_urls(self, mock_daily_quarter_directory, mock_daily_idx_file,
+    def test_get_urls(self, mock_user_agent, mock_daily_quarter_directory, mock_daily_idx_file,
                       key, url):
-        daily_filing = DailyFilings(date(2018, 12, 31))
+        daily_filing = DailyFilings(date(2018, 12, 31), user_agent=mock_user_agent)
         assert url in daily_filing.get_urls()[key]
 
-    def test_get_listings_directory(self, mock_daily_quarter_directory):
-        daily_filing_listing_directory = DailyFilings(date(
-            2018, 12, 31))._get_listings_directory()
+    def test_get_listings_directory(self, mock_user_agent, mock_daily_quarter_directory):
+        daily_filings = DailyFilings(date(2018, 12, 31),
+                                     user_agent=mock_user_agent)
+        daily_filing_listing_directory = daily_filings._get_listings_directory()
         assert daily_filing_listing_directory.status_code == 200
         assert "master.20181231.idx" in daily_filing_listing_directory.text
 
@@ -81,9 +83,10 @@ class TestDaily:
         "BROOKFIELD ASSET MANAGEMENT INC.",
         "PERUSAHAAN PERSEROAN PERSERO PT TELEKOMUNIKASI INDONESIA TBK"
     ])
-    def test_get_master_idx_file(self, mock_daily_quarter_directory,
+    def test_get_master_idx_file(self, mock_user_agent, mock_daily_quarter_directory,
                                  mock_daily_idx_file, company_name):
-        daily_filing = DailyFilings(date(2018, 12, 31))
+        daily_filing = DailyFilings(date(2018, 12, 31),
+                                    user_agent=mock_user_agent)
         assert company_name in daily_filing._get_master_idx_file()
 
     @pytest.mark.parametrize("year,month,day,quarter", [(2018, 1, 1, 1),
@@ -94,8 +97,8 @@ class TestDaily:
                                                         (2013, 10, 1, 4),
                                                         (2012, 11, 20, 4),
                                                         (2011, 12, 31, 4)])
-    def test_path_property(self, year, month, day, quarter):
-        daily_filing = DailyFilings(date(year, month, day))
+    def test_path_property(self, year, month, day, quarter, mock_user_agent):
+        daily_filing = DailyFilings(date(year, month, day), user_agent=mock_user_agent)
         assert daily_filing.path == "Archives/edgar/daily-index/{year}/QTR{quarter}/".format(
             year=year, quarter=quarter)
 
@@ -105,8 +108,8 @@ class TestDaily:
         date(1993, 12, 31),
         datetime(2020, 1, 1)
     ])
-    def test_good_date_setter(self, date):
-        daily_filing = DailyFilings(date=date)
+    def test_good_date_setter(self, date, mock_user_agent):
+        daily_filing = DailyFilings(date=date, user_agent=mock_user_agent)
         assert daily_filing.date == date
 
     @pytest.mark.parametrize("bad_date", [20201231, "20201231", "2020/12/31"])
@@ -115,14 +118,14 @@ class TestDaily:
             _ = DailyFilings(date=bad_date)
 
     @pytest.mark.parametrize("bad_date", [20201231, "20201231", "2020/12/31"])
-    def test_bad_date_setter_after_init(self, bad_date):
-        daily_filing = DailyFilings(date=date(2020, 1, 1))
+    def test_bad_date_setter_after_init(self, bad_date, mock_user_agent):
+        daily_filing = DailyFilings(date=date(2020, 1, 1), user_agent=mock_user_agent)
         with pytest.raises(TypeError):
             daily_filing.date = bad_date
 
-    def test_no_params(self):
+    def test_no_params(self, mock_user_agent):
         """Params should always be empty."""
-        daily_filing = DailyFilings(date(2020, 1, 1))
+        daily_filing = DailyFilings(date(2020, 1, 1), user_agent=mock_user_agent)
         assert not daily_filing.params
 
     @pytest.mark.parametrize("date_tuple,formatted", [
@@ -136,15 +139,16 @@ class TestDaily:
         ((1998, 4, 1), "19980401"),
         ((1999, 1, 1), "19990101"),
     ])
-    def test_master_idx_date_format(self, date_tuple, formatted):
-        daily_filing = DailyFilings(date(*date_tuple))
+    def test_master_idx_date_format(self, date_tuple, formatted, mock_user_agent):
+        daily_filing = DailyFilings(date(*date_tuple), user_agent=mock_user_agent)
         assert daily_filing._get_idx_formatted_date() == formatted
 
     @pytest.mark.parametrize("cik,file", cik_file_pairs)
     def test_save_default(self, tmp_data_directory,
                           mock_daily_quarter_directory, mock_daily_idx_file,
-                          mock_filing_response, cik, file):
-        daily_filing = DailyFilings(date(2018, 12, 31))
+                          mock_filing_response,
+                          mock_user_agent, cik, file):
+        daily_filing = DailyFilings(date(2018, 12, 31), user_agent=mock_user_agent)
         daily_filing.save(tmp_data_directory)
         subdir = os.path.join("20181231", cik)
         path_to_check = os.path.join(tmp_data_directory, subdir, file)
@@ -152,9 +156,9 @@ class TestDaily:
 
     @pytest.mark.parametrize("file", [cf[1] for cf in cik_file_pairs])
     def test_save_with_single_level_date_dir_pattern(
-            self, tmp_data_directory, mock_daily_quarter_directory,
+            self, tmp_data_directory, mock_user_agent, mock_daily_quarter_directory,
             mock_daily_idx_file, mock_filing_response, file):
-        daily_filing = DailyFilings(date(2018, 12, 31))
+        daily_filing = DailyFilings(date(2018, 12, 31), user_agent=mock_user_agent)
         daily_filing.save(tmp_data_directory,
                           dir_pattern="{date}",
                           date_format="%Y-%m-%d")
@@ -163,19 +167,20 @@ class TestDaily:
 
     @pytest.mark.parametrize("cik,file", cik_file_pairs)
     def test_save_with_single_level_cik_dir_pattern(
-            self, tmp_data_directory, mock_daily_quarter_directory,
+            self, tmp_data_directory, mock_user_agent, mock_daily_quarter_directory,
             mock_daily_idx_file, mock_filing_response, cik, file):
-        daily_filing = DailyFilings(date(2018, 12, 31))
+        daily_filing = DailyFilings(date(2018, 12, 31), user_agent=mock_user_agent)
         daily_filing.save(tmp_data_directory, dir_pattern="{cik}")
         path_to_check = os.path.join(tmp_data_directory, cik, file)
         assert os.path.exists(path_to_check)
 
     @pytest.mark.parametrize("cik,file", cik_file_pairs)
     def test_save_with_multi_level_dir_pattern(self, tmp_data_directory,
+                                               mock_user_agent,
                                                mock_daily_quarter_directory,
                                                mock_daily_idx_file,
                                                mock_filing_response, cik, file):
-        daily_filing = DailyFilings(date(2018, 12, 31))
+        daily_filing = DailyFilings(date(2018, 12, 31), user_agent=mock_user_agent)
         daily_filing.save(tmp_data_directory,
                           dir_pattern="{date}/{cik}",
                           date_format="%Y-%m-%d")
@@ -185,9 +190,9 @@ class TestDaily:
 
     @pytest.mark.parametrize("cik,file", cik_file_pairs)
     def test_save_with_multi_level_dir_pattern_date_not_first(
-            self, tmp_data_directory, mock_daily_quarter_directory,
+            self, tmp_data_directory, mock_user_agent, mock_daily_quarter_directory,
             mock_daily_idx_file, mock_filing_response, cik, file):
-        daily_filing = DailyFilings(date(2018, 12, 31))
+        daily_filing = DailyFilings(date(2018, 12, 31), user_agent=mock_user_agent)
         daily_filing.save(tmp_data_directory,
                           dir_pattern="{cik}/{date}",
                           date_format="%Y-%m-%d")
@@ -196,13 +201,14 @@ class TestDaily:
         assert os.path.exists(path_to_check)
 
     @pytest.mark.parametrize("year,quarter", [(1995, 2), (1995, 1), (1994, 4)])
-    def test__get_tar_bad_year_quarter(self, year, quarter):
+    def test__get_tar_bad_year_quarter(self, year, quarter, mock_user_agent):
         with pytest.raises(ValueError):
-            d = DailyFilings(date=date(year, utils.get_month(quarter), 1))
+            d = DailyFilings(date=date(year, utils.get_month(quarter), 1),
+                             user_agent=mock_user_agent)
             d._get_tar_urls()
 
-    def test__get_tar_valid(self):
-        d = DailyFilings(date=date(2020, 1, 2))
+    def test__get_tar_valid(self, mock_user_agent):
+        d = DailyFilings(date=date(2020, 1, 2), user_agent=mock_user_agent)
         urls = d._get_tar_urls()
         assert urls[0].endswith('20200102.nc.tar.gz')
         assert urls[0].startswith('http')
@@ -217,9 +223,9 @@ class TestDaily:
             1.0
         ]
     )
-    def test_set_bad_entry_filter(self, bad_entry_filter):
+    def test_set_bad_entry_filter(self, bad_entry_filter, mock_user_agent):
         with pytest.raises(ValueError):
-            d = DailyFilings(date=date(2020, 1, 2))
+            d = DailyFilings(date=date(2020, 1, 2), user_agent=mock_user_agent)
             d.entry_filter = bad_entry_filter
 
     @pytest.mark.parametrize(
@@ -230,10 +236,25 @@ class TestDaily:
             lambda f: f.name.lower().startswith("a")
         ]
     )
-    def test_set_good_entry_filter(self, good_entry_filter):
-        d = DailyFilings(date=date(2020, 1, 2))
+    def test_set_good_entry_filter(self, good_entry_filter, mock_user_agent):
+        d = DailyFilings(date=date(2020, 1, 2), user_agent=mock_user_agent)
         d.entry_filter = good_entry_filter
         assert callable(d.entry_filter)
 
     def test_download_all(self, monkeypatch):
         pass
+
+    def test_user_agent_passed_to_client(self, mock_user_agent):
+        daily = DailyFilings(date=date(2021, 1, 1), user_agent=mock_user_agent)
+        assert daily.client.user_agent == mock_user_agent
+
+    def test_user_agent_client_none(self):
+        with pytest.raises(TypeError):
+            _ = DailyFilings(date=date(2020, 1, 1),
+                             user_agent=None,
+                             client=None)
+
+    def test_set_good_client(self, mock_user_agent):
+        client = NetworkClient(user_agent=mock_user_agent)
+        daily = DailyFilings(date=date(2021, 1, 1), client=client)
+        assert daily.client == client
diff --git a/secedgar/tests/core/test_filings.py b/secedgar/tests/core/test_filings.py
index 684f3a8..5f48a00 100644
--- a/secedgar/tests/core/test_filings.py
+++ b/secedgar/tests/core/test_filings.py
@@ -2,7 +2,7 @@ from datetime import date
 
 import pytest
 from secedgar.client import NetworkClient
-from secedgar.core import DailyFilings, QuarterlyFilings
+from secedgar.core import DailyFilings, FilingType, QuarterlyFilings
 from secedgar.core.combo import ComboFilings
 from secedgar.core.filings import filings
 from secedgar.exceptions import FilingTypeError
@@ -14,34 +14,51 @@ class TestFilings:
         with pytest.raises(FilingTypeError):
             filings(cik_lookup='aapl', filing_type='10-k')
 
+    def test_user_agent_passed_to_objects(self, mock_user_agent):
+        f = filings(cik_lookup="aapl",
+                    filing_type=FilingType.FILING_10Q,
+                    user_agent=mock_user_agent)
+        assert f.client.user_agent == mock_user_agent
+
+    def test_client_passed_to_objects(self, mock_user_agent):
+        client = NetworkClient(user_agent=mock_user_agent)
+        company = filings(cik_lookup="aapl", filing_type=FilingType.FILING_10Q, client=client)
+        daily = filings(start_date=date(2021, 1, 1), end_date=date(2021, 1, 1), client=client)
+        quarterly = filings(start_date=date(2020, 1, 1), end_date=date(2020, 3, 31), client=client)
+        assert company.client == client
+        assert daily.client == client
+        assert quarterly.client == client
+
     def test_filing_type_plus_entry_filter_filters_both(self):
         pass
 
-    def test_count_not_implemented(self):
+    def test_count_not_implemented(self, mock_user_agent):
         with pytest.raises(NotImplementedError):
             filings(start_date=date(2010, 1, 1),
                     end_date=date(2020, 1, 1),
+                    user_agent=mock_user_agent,
                     count=10)
 
-    def test_no_end_date_no_cik_lookp_returns_daily_filings(self):
+    def test_same_date_returns_daily_filings(self, mock_user_agent):
         day = date(2020, 1, 1)
 
-        f = filings(start_date=day, end_date=date(2020, 1, 1))
+        f = filings(start_date=day, end_date=day, user_agent=mock_user_agent)
         assert isinstance(f, DailyFilings)
         assert f.date == day
         assert f.year == day.year
         assert f.quarter == day.month // 3 + 1
 
-    def test_exact_quarter_returns_quarterly_filings(self):
-        f = filings(start_date=date(2020, 1, 1), end_date=date(2020, 3, 31))
+    def test_exact_quarter_returns_quarterly_filings(self, mock_user_agent):
+        f = filings(start_date=date(2020, 1, 1), end_date=date(
+            2020, 3, 31), user_agent=mock_user_agent)
         assert isinstance(f, QuarterlyFilings)
         assert f.year == 2020
         assert f.quarter == 1
 
-    def test_mismatch_of_quarters_returns_combo_filings(self):
+    def test_mismatch_of_quarters_returns_combo_filings(self, mock_user_agent):
         start_date = date(2020, 1, 1)
         end_date = date(2020, 5, 1)
-        f = filings(start_date=start_date, end_date=end_date)
+        f = filings(start_date=start_date, end_date=end_date, user_agent=mock_user_agent)
         assert isinstance(f, ComboFilings)
         assert f.start_date == start_date
         assert f.end_date == end_date
@@ -51,12 +68,12 @@ class TestFilings:
         [
             (dict(), ValueError),
             (dict(end_date=date(2020, 1, 1)), ValueError),
-            (dict(client=NetworkClient()), ValueError),
+            (dict(client=NetworkClient(user_agent="Test")), ValueError),
             (dict(entry_filter=lambda f: f.form_type == '10-k'), ValueError),
 
             # end_date assumed to be today - giving count tries to
             # make daily or quarterly filing, but will not be recognized
-            (dict(count=10, client=NetworkClient()), NotImplementedError),
+            (dict(count=10, client=NetworkClient(user_agent="Test")), NotImplementedError),
             (dict(count=10), NotImplementedError),
         ])
     def test_bad_args_combination_raises_error(self, kwargs, error):
diff --git a/secedgar/tests/core/test_quarterly.py b/secedgar/tests/core/test_quarterly.py
index ea16770..1853237 100644
--- a/secedgar/tests/core/test_quarterly.py
+++ b/secedgar/tests/core/test_quarterly.py
@@ -36,13 +36,13 @@ class TestQuarterly:
         ("1993", TypeError),
         ("1993.0", TypeError),
     ])
-    def test_bad_year(self, bad_year, expected_error):
+    def test_bad_year(self, mock_user_agent, bad_year, expected_error):
         with pytest.raises(expected_error):
-            _ = QuarterlyFilings(year=bad_year, quarter=1)
+            _ = QuarterlyFilings(year=bad_year, quarter=1, user_agent=mock_user_agent)
 
-    def test_good_year(self):
+    def test_good_year(self, mock_user_agent):
         for year in range(1993, date.today().year + 1):
-            mf = QuarterlyFilings(year=year, quarter=1)
+            mf = QuarterlyFilings(year=year, quarter=1, user_agent=mock_user_agent)
             assert mf.year == year
 
     @pytest.mark.parametrize("bad_quarter,expected_error", [(0.0, TypeError),
@@ -53,24 +53,25 @@ class TestQuarterly:
                                                             (5, ValueError),
                                                             (6, ValueError),
                                                             (2020, ValueError)])
-    def test_bad_quarter(self, bad_quarter, expected_error):
+    def test_bad_quarter(self, mock_user_agent, bad_quarter, expected_error):
         with pytest.raises(expected_error):
-            _ = QuarterlyFilings(year=2020, quarter=bad_quarter)
+            _ = QuarterlyFilings(year=2020, quarter=bad_quarter, user_agent=mock_user_agent)
 
-    def test_good_quarters(self):
+    def test_good_quarters(self, mock_user_agent):
         for quarter in range(1, 5):
-            mf = QuarterlyFilings(year=2019, quarter=quarter)
+            mf = QuarterlyFilings(year=2019, quarter=quarter, user_agent=mock_user_agent)
             assert mf.quarter == quarter
 
     @pytest.mark.parametrize("year,quarter", [(2018, 1), (2019, 2), (2020, 3)])
-    def test_idx_filename_is_always_the_same(self, year, quarter):
-        mf = QuarterlyFilings(year=year, quarter=quarter)
+    def test_idx_filename_is_always_the_same(self, mock_user_agent, year, quarter):
+        mf = QuarterlyFilings(year=year, quarter=quarter, user_agent=mock_user_agent)
         assert mf.idx_filename == "master.idx"
 
-    def test_always_false_entry_filter(self, mock_master_idx_file):
+    def test_always_false_entry_filter(self, mock_user_agent, mock_master_idx_file):
         quarterly_filing = QuarterlyFilings(year=1993,
                                             quarter=4,
-                                            entry_filter=lambda _: False)
+                                            entry_filter=lambda _: False,
+                                            user_agent=mock_user_agent)
         urls = quarterly_filing.get_urls()
         assert len(urls) == 0
 
@@ -81,10 +82,10 @@ class TestQuarterly:
         ("205239", "0000205239-94-000003.txt"),
         ("20762", "0000950131-94-000025.txt"),
     ])
-    def test_save(self, tmp_data_directory, mock_filing_data,
+    def test_save(self, tmp_data_directory, mock_user_agent, mock_filing_data,
                   mock_quarterly_quarter_directory, mock_master_idx_file,
                   mock_filing_response, subdir, file):
-        quarterly_filing = QuarterlyFilings(year=1993, quarter=4)
+        quarterly_filing = QuarterlyFilings(year=1993, quarter=4, user_agent=mock_user_agent)
         quarterly_filing.save(tmp_data_directory)
         subdir = os.path.join("1993", "QTR4", subdir)
         path_to_check = os.path.join(tmp_data_directory, subdir, file)
@@ -96,6 +97,10 @@ class TestQuarterly:
         ("Bed, Bath, & Beyond", "Bed_Bath__Beyond"),
         ("Company with \\lots\\ of /slashes/", "Company_with_lots_of_slashes")
     ])
-    def test_clean_path(self, original_path, clean_path):
-        quarterly_filing = QuarterlyFilings(year=2000, quarter=1)
+    def test_clean_path(self, mock_user_agent, original_path, clean_path):
+        quarterly_filing = QuarterlyFilings(year=2000, quarter=1, user_agent=mock_user_agent)
         assert quarterly_filing.clean_directory_path(original_path) == clean_path
+
+    def test_user_agent_passed_to_client(self, mock_user_agent):
+        quarterly = QuarterlyFilings(year=2020, quarter=1, user_agent=mock_user_agent)
+        assert quarterly.client.user_agent == mock_user_agent
diff --git a/secedgar/tests/test_cik_lookup.py b/secedgar/tests/test_cik_lookup.py
index 46e08e6..02d2da3 100644
--- a/secedgar/tests/test_cik_lookup.py
+++ b/secedgar/tests/test_cik_lookup.py
@@ -1,24 +1,24 @@
 import json
+from unittest.mock import patch
 
 import pytest
 import requests
-from unittest.mock import patch
 from secedgar.cik_lookup import CIKLookup, get_cik_map
 from secedgar.client import NetworkClient
 from secedgar.exceptions import CIKError, EDGARQueryError
 from secedgar.tests.conftest import MockResponse
 
 
-@pytest.fixture
-def client():
-    return NetworkClient()
-
-
 @pytest.fixture
 def ticker_lookups():
     return ["AAPL", "FB", "GOOGL", "NFLX", "MSFT"]
 
 
+@pytest.fixture(scope="class")
+def mock_client_cik_lookup(mock_user_agent):
+    return NetworkClient(user_agent=mock_user_agent)
+
+
 @pytest.fixture
 def mock_single_cik_lookup_outside_map(monkeypatch):
     monkeypatch.setattr(NetworkClient, "get_response",
@@ -70,32 +70,37 @@ class TestCIKLookup(object):
             (['AAPL', '1018724'], {'AAPL': '320193', '1018724': '1018724'}),
         ]
     )
-    def test_cik_lookup_returns_correct_values(self, lookup, expected, mock_get_cik_map):
-        look = CIKLookup(lookup)
+    def test_cik_lookup_returns_correct_values(self, mock_client_cik_lookup,
+                                               lookup,
+                                               expected,
+                                               mock_get_cik_map):
+        look = CIKLookup(lookup, client=mock_client_cik_lookup)
         assert look.lookup_dict == expected
 
-    def test_cik_lookup_lookups_property(self):
-        multiple_company_lookup = CIKLookup(['aapl', 'msft', 'fb'])
+    def test_cik_lookup_lookups_property(self, mock_client_cik_lookup):
+        multiple_company_lookup = CIKLookup(['aapl', 'msft', 'fb'], client=mock_client_cik_lookup)
         assert multiple_company_lookup.lookups == ['aapl', 'msft', 'fb']
 
-    def test_multiple_results_company_name_search(self, mock_single_cik_multiple_results_response):
-        multiple_results_cik = CIKLookup('paper')
+    def test_multiple_results_company_name_search(self, mock_client_cik_lookup,
+                                                  mock_single_cik_multiple_results_response):
+        multiple_results_cik = CIKLookup('paper', client=mock_client_cik_lookup)
         with pytest.warns(UserWarning):
             assert len(multiple_results_cik.ciks) == 0
 
-    def test_multiple_results_raises_warnings(self, mock_single_cik_multiple_results_response):
-        multiple_results_cik = CIKLookup('paper')
+    def test_multiple_results_raises_warnings(self, mock_client_cik_lookup,
+                                              mock_single_cik_multiple_results_response):
+        multiple_results_cik = CIKLookup('paper', client=mock_client_cik_lookup)
         with pytest.warns(UserWarning):
             _ = multiple_results_cik.ciks
 
-    def test_cik_lookup_cik_hits_request(self):
+    def test_cik_lookup_cik_hits_request(self, mock_client_cik_lookup):
         with patch.object(CIKLookup, '_get_cik_from_html') as mock:
-            CIKLookup(['Apple']).get_ciks()
+            CIKLookup(['Apple'], client=mock_client_cik_lookup).get_ciks()
             mock.assert_called()
 
-    def test_cik_lookup_cik_bypasses_request(self):
+    def test_cik_lookup_cik_bypasses_request(self, mock_client_cik_lookup):
         with patch.object(CIKLookup, '_get_cik_from_html') as mock:
-            CIKLookup(['1018724']).get_ciks()
+            CIKLookup(['1018724'], client=mock_client_cik_lookup).get_ciks()
             mock.assert_not_called()
 
     @pytest.mark.parametrize(
@@ -105,19 +110,20 @@ class TestCIKLookup(object):
             123.0,
         ]
     )
-    def test_validate_cik__is_string(self, bad_cik):
+    def test_validate_cik__is_string(self, mock_client_cik_lookup, bad_cik):
         with pytest.raises(TypeError):
-            CIKLookup(bad_cik)
+            CIKLookup(bad_cik, client=mock_client_cik_lookup)
 
-    def test_validate_cik_after_cik_lookup(self, mock_single_cik_not_found):
+    def test_validate_cik_after_cik_lookup(self, mock_client_cik_lookup,
+                                           mock_single_cik_not_found):
         # string remains unchecked until query to allow for possibility of
         # using company name, ticker, or CIK as string
         with pytest.raises(EDGARQueryError):
-            _ = CIKLookup('0notvalid0').ciks
+            _ = CIKLookup('0notvalid0', client=mock_client_cik_lookup).ciks
 
-    def test_client_property(self, client, ticker_lookups):
-        lookup = CIKLookup(ticker_lookups, client=client)
-        assert lookup.client == client
+    def test_client_property(self, mock_client_cik_lookup, ticker_lookups):
+        lookup = CIKLookup(ticker_lookups, client=mock_client_cik_lookup)
+        assert lookup.client == mock_client_cik_lookup
 
     @pytest.mark.parametrize(
         "bad_lookups",
@@ -127,12 +133,12 @@ class TestCIKLookup(object):
             ["AAPL", 4, "FB"]
         ]
     )
-    def test_empty_lookups_raises_type_error(self, bad_lookups):
+    def test_bad_lookups_raises_type_error(self, bad_lookups, mock_client_cik_lookup):
         with pytest.raises(TypeError):
-            CIKLookup(lookups=bad_lookups)
+            CIKLookup(lookups=bad_lookups, client=mock_client_cik_lookup)
 
-    def test_lookups_property(self, ticker_lookups):
-        lookup = CIKLookup(lookups=ticker_lookups)
+    def test_lookups_property(self, mock_client_cik_lookup, ticker_lookups):
+        lookup = CIKLookup(lookups=ticker_lookups, client=mock_client_cik_lookup)
         assert lookup.lookups == ticker_lookups
 
     @pytest.mark.parametrize(
@@ -161,9 +167,9 @@ class TestCIKLookup(object):
         with pytest.raises(CIKError):
             CIKLookup._validate_cik(bad_cik)
 
-    def test_params_reset_after_get_cik(self, ticker_lookups, client,
+    def test_params_reset_after_get_cik(self, ticker_lookups, mock_client_cik_lookup,
                                         mock_single_cik_lookup_outside_map):
-        lookup = CIKLookup(lookups=ticker_lookups, client=client)
+        lookup = CIKLookup(lookups=ticker_lookups, client=mock_client_cik_lookup)
         lookup._get_cik_from_html(ticker_lookups[0])
         assert lookup.params.get("CIK") is None and lookup.params.get("company") is None
 
diff --git a/secedgar/tests/test_client.py b/secedgar/tests/test_client.py
index 3b1a5e0..4bfa70c 100644
--- a/secedgar/tests/test_client.py
+++ b/secedgar/tests/test_client.py
@@ -11,8 +11,8 @@ from secedgar.tests.utils import MockResponse
 
 
 @pytest.fixture
-def client():
-    return NetworkClient()
+def client(mock_user_agent):
+    return NetworkClient(user_agent=mock_user_agent)
 
 
 @pytest.fixture
@@ -35,10 +35,23 @@ def mock_single_filing_page_good_response(monkeypatch):
 
 
 class TestNetworkClient:
+    @pytest.mark.parametrize(
+        "user_agent",
+        [
+            None,
+            1,
+            True,
+            False,
+        ]
+    )
+    def test_client_bad_user_agent(self, user_agent):
+        with pytest.raises(TypeError):
+            _ = NetworkClient(user_agent=user_agent)
+
     def test_client_bad_response_raises_error(self, client):
         no_cik_response = MockResponse(datapath_args=["CIK", "cik_not_found.html"])
         with pytest.raises(EDGARQueryError):
-            NetworkClient()._validate_response(no_cik_response)
+            client._validate_response(no_cik_response)
 
     def test_client_good_response_single_filing_type_passes(self,
                                                             mock_single_filing_type_good_response,
@@ -91,9 +104,9 @@ class TestNetworkClient:
             [1, 2, 3],
         ]
     )
-    def test_bad_backoff_factor_setter(self, bad_backoff_factor):
+    def test_bad_backoff_factor_setter(self, bad_backoff_factor, mock_user_agent):
         with pytest.raises(TypeError):
-            _ = NetworkClient(backoff_factor=bad_backoff_factor)
+            _ = NetworkClient(user_agent=mock_user_agent, backoff_factor=bad_backoff_factor)
 
     @pytest.mark.parametrize(
         "good_backoff_factor",
@@ -106,8 +119,8 @@ class TestNetworkClient:
             10
         ]
     )
-    def test_good_backoff_factor_setter(self, good_backoff_factor):
-        client = NetworkClient()
+    def test_good_backoff_factor_setter(self, mock_user_agent, good_backoff_factor):
+        client = NetworkClient(user_agent=mock_user_agent)
         client.backoff_factor = good_backoff_factor
         assert client.backoff_factor == good_backoff_factor
 
@@ -166,9 +179,10 @@ class TestNetworkClient:
         "rate_limit",
         range(1, 10)
     )
-    def test_rate_limit_requests_per_second(self, tmp_data_directory, rate_limit,
+    def test_rate_limit_requests_per_second(self, mock_user_agent,
+                                            tmp_data_directory, rate_limit,
                                             mock_filing_response):
-        client = NetworkClient(rate_limit=rate_limit)
+        client = NetworkClient(user_agent=mock_user_agent, rate_limit=rate_limit)
         min_seconds = 3
         num_requests = rate_limit * min_seconds
         inputs = [("https://google.com", os.path.join(tmp_data_directory, str(i)))