diff --git a/.gitignore b/.gitignore
index 82e7bbc2bb3e584a9ab48f7c5cc5fff1548241b4..b09fbf512c29d1135dd4c68158ed0ecddfb25a36 100644
--- a/.gitignore
+++ b/.gitignore
@@ -50,4 +50,5 @@ coverage.xml
 docs/_build/
 
 # enviroment variables
-.env
\ No newline at end of file
+.env
+venv/
\ No newline at end of file
diff --git a/bbcli/cli.py b/bbcli/cli.py
index 5c95cc5363b901648bdc567183b662bbf371eb5e..83865fbeb8be8136501c113b5669ef68e844032e 100644
--- a/bbcli/cli.py
+++ b/bbcli/cli.py
@@ -11,6 +11,8 @@ import click
 
 from bbcli.services import authorization_service
 from bbcli.services import announcement_service
+from bbcli.services import course_service
+from bbcli.utils.URL_builder import URLBuilder, URL
 
 @click.group()
 def entry_point():
@@ -19,8 +21,10 @@ def entry_point():
 
 @click.command(name='announcements')
 def get_announcements():
-    response = announcement_service.update_announcement(cookies, headers, '_33050_1', '_385022_1')
+    response = course_service.list_course(cookies, '_33050_1')
     click.echo(response)
+    
+    
 
 
 entry_point.add_command(get_announcements)
diff --git a/bbcli/services/__init__.py b/bbcli/services/__init__.py
index 540e4cbbe654d8299db8737108ad2ac292234b79..f30c4e42d3b55b2e31bb4a281935a9f0513de3f0 100644
--- a/bbcli/services/__init__.py
+++ b/bbcli/services/__init__.py
@@ -1 +1,2 @@
-from .authorization_service import *
\ No newline at end of file
+from .authorization_service import *
+from bbcli.utils.URL_builder import URLBuilder
\ No newline at end of file
diff --git a/bbcli/services/announcement_service.py b/bbcli/services/announcement_service.py
index 90177768b8a4ab591f54d25baec9d2e7c098df59..9fc38888dcedccef7869c3c1bec32671141d0682 100644
--- a/bbcli/services/announcement_service.py
+++ b/bbcli/services/announcement_service.py
@@ -5,6 +5,10 @@ import requests
 from bbcli.services.course_service import list_courses
 import click
 
+from bbcli.utils.URL_builder import URLBuilder
+
+url_builder = URLBuilder()
+
 def list_announcements(cookies: Dict, user_name: str):
     courses = list_courses(cookies=cookies, user_name=user_name)
 
@@ -12,7 +16,8 @@ def list_announcements(cookies: Dict, user_name: str):
     announcements = []
 
     for course in courses:
-        course_announcements = session.get('https://ntnu.blackboard.com/learn/api/public/v1/courses/{}/announcements'.format(course['id']), cookies=cookies)
+        url = url_builder.base_v1().add_courses().add_id(course['id']).add_announcements().create()
+        course_announcements = session.get(url, cookies=cookies)
         course_announcements = json.loads(course_announcements.text)
         
         # Adds the course name to each course announcement list to make it easier to display which course the announcement comes from
@@ -25,12 +30,14 @@ def list_announcements(cookies: Dict, user_name: str):
     return announcements
 
 def list_course_announcements(cookies: Dict, course_id: str):
-    course_announcements = requests.get('https://ntnu.blackboard.com/learn/api/public/v1/courses/{}/announcements'.format(course_id), cookies=cookies)
+    url = url_builder.base_v1().add_courses().add_id(course_id).add_announcements().create()
+    course_announcements = requests.get(url, cookies=cookies)
     course_announcements = json.loads(course_announcements.text)['results']
     return course_announcements
 
 def list_announcement(cookies: Dict, course_id: str, announcement_id: str):
-    announcement = requests.get('https://ntnu.blackboard.com/learn/api/public/v1/courses/{}/announcements/{}'.format(course_id, announcement_id), cookies=cookies)
+    url = url_builder.base_v1().add_courses().add_id(course_id).add_announcements().add_id(announcement_id).create()
+    announcement = requests.get(url, cookies=cookies)
     announcement = json.loads(announcement.text)
     return announcement
 
@@ -49,13 +56,15 @@ def create_announcement(cookies: Dict, headers: Dict, course_id: str, title: str
 
     data = json.dumps(data)
     headers['Content-Type'] = 'application/json'
-
-    response = requests.post('https://ntnu.blackboard.com/learn/api/public/v1/courses/{}/announcements'.format(course_id), cookies=cookies, headers=headers, data=data)
+    
+    url = url_builder.base_v1().add_courses().add_id(course_id).add_announcements().create()
+    response = requests.post(url, cookies=cookies, headers=headers, data=data)
 
     return response.text
 
 def delete_announcement(cookies: Dict, headers: Dict, course_id: str, announcement_id: str):
-    response = requests.delete('https://ntnu.blackboard.com/learn/api/public/v1/courses/{}/announcements/{}'.format(course_id, announcement_id), cookies=cookies, headers=headers)
+    url = url_builder.base_v1().add_courses().add_id(course_id).add_announcements().add_id(announcement_id).create()
+    response = requests.delete(url, cookies=cookies, headers=headers)
     if response.text == '':
         return 'Sucessfully deleted announcement!'
     else:
@@ -76,6 +85,8 @@ def update_announcement(cookies: Dict, headers: Dict, course_id: str, announceme
     new_data = click.edit(announcement + '\n\n' + MARKER)
 
     headers['Content-Type'] = 'application/json'
-    response = requests.patch('https://ntnu.blackboard.com/learn/api/public/v1/courses/{}/announcements/{}'.format(course_id, announcement_id), cookies=cookies, headers=headers, data=new_data)
+    url = url_builder.base_v1().add_courses().add_id(course_id).add_announcements().add_id(announcement_id).create()
+    response = requests.patch(url, cookies=cookies, headers=headers, data=new_data)
+    response.raise_for_status()
 
     return response.text
\ No newline at end of file
diff --git a/bbcli/services/ContentService.py b/bbcli/services/content_service.py
similarity index 100%
rename from bbcli/services/ContentService.py
rename to bbcli/services/content_service.py
diff --git a/bbcli/services/course_service.py b/bbcli/services/course_service.py
index 6b893e8b0e4e4b688237bc9a8bf9c2586ddc37f7..8d65278c031da654ba4615458ec9a3b490ec1802 100644
--- a/bbcli/services/course_service.py
+++ b/bbcli/services/course_service.py
@@ -3,6 +3,10 @@ from typing import Dict, Any
 import requests
 from datetime import date
 
+from bbcli.utils.URL_builder import URLBuilder
+
+url_builder = URLBuilder()
+
 def take_start_date(elem):
     return date.fromisoformat(elem['availability']['duration']['start'].split('T')[0])
 
@@ -10,7 +14,8 @@ def take_start_date(elem):
 def list_courses(cookies: Dict, user_name: str) -> Any:
     session = requests.Session()
 
-    terms = session.get('https://ntnu.blackboard.com/learn/api/public/v1/terms', cookies=cookies)
+    url = url_builder.base_v1().add_terms().create()
+    terms = session.get(url, cookies=cookies)
     terms = json.loads(terms.text)['results']
     
     # Sort terms by start date to get the two most recent semesters to determine which courses to show
@@ -22,14 +27,16 @@ def list_courses(cookies: Dict, user_name: str) -> Any:
     term_1 = terms[len(terms) - 1]
     term_2 = terms[len(terms) - 2]
 
-    course_memberships = session.get('https://ntnu.blackboard.com/learn/api/public/v1/users/userName:{}/courses'.format(user_name), cookies=cookies)
+    url = url_builder.base_v1().add_users().add_id(id=user_name, id_type='userName').create()
+    course_memberships = session.get(url, cookies=cookies)
     course_memberships = json.loads(course_memberships.text)['results']
 
     course_list = []
 
     # Get courses from the correct terms
     for course in course_memberships:
-        response = session.get('https://ntnu.blackboard.com/learn/api/public/v3/courses/{}'.format(course['courseId']), cookies=cookies, params={'fields': 'id, name, termId'})
+        url = url_builder.base_v3().add_courses().add_id(course['courseId']).create()
+        response = session.get(url, cookies=cookies, params={'fields': 'id, name, termId'})
         response = json.loads(response.text)
         if response['termId'] == term_1['id'] or response['termId'] == term_2['id']:
             course_list.append({
@@ -42,7 +49,8 @@ def list_courses(cookies: Dict, user_name: str) -> Any:
     return course_list
 
 def list_course(cookies: Dict, course_id:str) -> Any:
-    response = requests.get('https://ntnu.blackboard.com/learn/api/public/v3/courses/{}'.format(course_id), cookies=cookies)
+    url = url_builder.base_v3().add_courses().add_id(course_id).create()
+    response = requests.get(url, cookies=cookies)
     return json.loads(response.text)
 
 def list_favorite_courses(cookies: Dict, user_name: str) -> Any:
diff --git a/bbcli/utils/URL_builder.py b/bbcli/utils/URL_builder.py
new file mode 100644
index 0000000000000000000000000000000000000000..126e55cde999460ab73e36097d918b248143cbcb
--- /dev/null
+++ b/bbcli/utils/URL_builder.py
@@ -0,0 +1,129 @@
+from __future__ import annotations
+from typing import Any
+from abc import ABC, abstractmethod
+
+DOMAIN = 'https://ntnu.blackboard.com'
+API_BASE = '/learn/api/public'
+
+
+class Builder(ABC):
+
+    @property
+    @abstractmethod
+    def product(self) -> None:
+        pass
+
+
+    """
+    Returns the base URL which includes the domain and first part of all the endpoints: domain/learn/api/public/vX,
+    where X is the version from 1 to 3.
+    """
+
+    @abstractmethod
+    def base_v1(self) -> Builder:
+        pass
+
+    @abstractmethod
+    def base_v2(self) -> Builder:
+        pass
+
+    @abstractmethod
+    def base_v3(self) -> Builder:
+        pass
+
+    @abstractmethod
+    def add_courses(self) -> Builder:
+        pass
+
+    @abstractmethod
+    def add_users(self) -> Builder:
+        pass
+
+    @abstractmethod
+    def add_announcements(self) -> Builder:
+        pass
+
+    @abstractmethod
+    def add_contents(self) -> Builder:
+        pass
+
+    @abstractmethod
+    def add_terms(slef) -> Builder:
+        pass
+
+    @abstractmethod
+    def add_id(self, id: str, id_type: str = None) -> Builder:
+        pass
+
+
+class URLBuilder(Builder):
+
+    def __init__(self) -> None:
+        self.reset()
+
+    def reset(self) -> None:
+        self._product = URL()
+
+    @property
+    def product(self) -> URL:
+
+        product = self._product
+        self.reset()
+        return product
+
+
+
+    def base_v1(self) -> URLBuilder:
+        self._product.add(f'{DOMAIN}{API_BASE}/v1')
+        return self
+    
+    def base_v2(self) -> URLBuilder:
+        self._product.add(f'{DOMAIN}{API_BASE}/v2')
+        return self
+
+    def base_v3(self) -> URLBuilder:
+        self._product.add(f'{DOMAIN}{API_BASE}/v3')
+        return self
+
+    def add_courses(self) -> URLBuilder:
+        self._product.add('/courses')
+        return self
+
+    def add_users(self) -> URLBuilder:
+        self._product.add('/users')
+        return self
+    
+    def add_announcements(self) -> URLBuilder:
+        self._product.add('/announcements')
+        return self
+
+    def add_contents(self) -> URLBuilder:
+        self._product.add('/contents')
+        return self
+
+    def add_terms(self) -> URLBuilder:
+        self._product.add('/terms')
+        return self
+
+    def add_id(self, id:str, id_type:str=None) -> URLBuilder:
+        if id_type:
+            self._product.add(f'/{id_type}:{id}')
+        else:
+            self._product.add(f'/{id}')
+        return self
+
+    def create(self) -> str:
+        url = self._product.get_url()
+        self._product = URL()
+        return url
+
+class URL():
+
+    def __init__(self) -> None:
+        self.URL = ''
+
+    def add(self, url_part: str) -> None:
+        self.URL += url_part
+
+    def get_url(self) -> None:
+        return self.URL
\ No newline at end of file
diff --git a/bbcli/utils/error_handler.py b/bbcli/utils/error_handler.py
new file mode 100644
index 0000000000000000000000000000000000000000..3ef18c457e14b69fb63848806eca035cc32ac56f
--- /dev/null
+++ b/bbcli/utils/error_handler.py
@@ -0,0 +1,13 @@
+import requests
+import click
+
+# ERROR HANDLER SHOULD BE USED IN VIEW??
+
+def HTTP_exception_handler(func):
+    def inner_function(*args, **kwargs):
+        try:
+            func(*args, **kwargs)
+        except requests.exceptions.HTTPError as err:
+            click.echo(err)
+            click.Abort()
+    return inner_function     
\ No newline at end of file