diff --git a/docs/changelog.md b/docs/changelog.md
index dd448749..7e87f6b1 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -2,6 +2,17 @@
 
 This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) and [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) format.
 
+## [0.10.5] - 12-04-2023
+
+### Changed
+
+- optimized web interface fetching of PEP annotation data. 
+
+### Added
+
+- project annotation endpoint  (#234)
+
+
 # [0.10.4] - 10-02-2023
 
 ### Fixed
diff --git a/pephub/_version.py b/pephub/_version.py
index d9b054ab..a67aac09 100644
--- a/pephub/_version.py
+++ b/pephub/_version.py
@@ -1 +1 @@
-__version__ = "0.10.4"
+__version__ = "0.10.5"
diff --git a/pephub/const.py b/pephub/const.py
index 1f2bba5e..7cb3566c 100644
--- a/pephub/const.py
+++ b/pephub/const.py
@@ -7,7 +7,6 @@
 from platform import python_version
 from fastapi import __version__ as fastapi_version
 from pepdbagent import __version__ as pepdbagent_version
-from pepdbagent.const import DEFAULT_TAG
 
 from secrets import token_hex
 
@@ -131,3 +130,4 @@
 CALLBACK_ENDPOINT = "/auth/callback"
 
 DEFAULT_PEP_SCHEMA = "pep/2.1.0"
+DEFAULT_TAG = "default"
diff --git a/pephub/dependencies.py b/pephub/dependencies.py
index 6628784f..1effc478 100644
--- a/pephub/dependencies.py
+++ b/pephub/dependencies.py
@@ -12,18 +12,19 @@
 from typing import Union, List, Optional, Dict, Any
 from datetime import datetime, timedelta
 
-from fastapi import Depends, Header, Form
+from fastapi import Depends, Header
 from fastapi.exceptions import HTTPException
 from fastapi.security import HTTPBearer
 from pydantic import BaseModel
 from pepdbagent import PEPDatabaseAgent
 from pepdbagent.const import DEFAULT_TAG
 from pepdbagent.exceptions import ProjectNotFoundError
+from pepdbagent.models import AnnotationModel, Namespace
 from qdrant_client import QdrantClient
 from qdrant_client.http.exceptions import ResponseHandlingException
 from sentence_transformers import SentenceTransformer
 
-from .routers.models import AnnotationModel, NamespaceList, Namespace, ForkRequest
+from .routers.models import ForkRequest
 from .const import (
     DEFAULT_POSTGRES_HOST,
     DEFAULT_POSTGRES_PASSWORD,
@@ -213,7 +214,6 @@ def get_project_annotation(
     agent: PEPDatabaseAgent = Depends(get_db),
     namespace_access_list: List[str] = Depends(get_namespace_access_list),
 ) -> AnnotationModel:
-    # TODO: Is just grabbing the first annotation the right thing to do?
     try:
         anno = agent.annotation.get(
             namespace, project, tag, admin=namespace_access_list
@@ -226,14 +226,6 @@ def get_project_annotation(
         )
 
 
-# TODO: This isn't used; do we still need it?
-def get_namespaces(
-    agent: PEPDatabaseAgent = Depends(get_db),
-    user: str = Depends(get_user_from_session_info),
-) -> List[NamespaceList]:
-    yield agent.namespace.get(admin=user)
-
-
 def verify_user_can_write_namespace(
     namespace: str,
     session_info: Union[dict, None] = Depends(read_authorization_header),
@@ -257,8 +249,8 @@ def verify_user_can_write_namespace(
 
 
 def verify_user_can_read_project(
-    project: str,
     namespace: str,
+    project: str,
     tag: Optional[str] = DEFAULT_TAG,
     project_annotation: AnnotationModel = Depends(get_project_annotation),
     session_info: Union[dict, None] = Depends(read_authorization_header),
@@ -323,13 +315,13 @@ def verify_user_can_write_project(
         if session_info is None:
             raise HTTPException(
                 401,
-                f"Please authenticate before editing project.",
+                "Please authenticate before editing project.",
             )
         # AUTHORIZATION REQUIRED
         if session_info["login"] != namespace and namespace not in orgs:
             raise HTTPException(
                 403,
-                f"The current authenticated user does not have permission to edit this project.",
+                "The current authenticated user does not have permission to edit this project.",
             )
 
 
diff --git a/pephub/helpers.py b/pephub/helpers.py
index 053a2a92..58ef3183 100644
--- a/pephub/helpers.py
+++ b/pephub/helpers.py
@@ -1,7 +1,6 @@
-import json
 from datetime import date
 from typing import List, Union, Tuple
-from fastapi import Response, Depends, UploadFile
+from fastapi import Response, UploadFile
 from fastapi.exceptions import HTTPException
 from ubiquerg import VersionInHelpParser
 
@@ -54,7 +53,7 @@ def add_subparser(cmd, description):
             "--config",
             required=False,
             dest="config",
-            help=f"A path to the pepserver config file",
+            help="A path to the pepserver config file",
         )
 
     sps["serve"].add_argument(
diff --git a/pephub/main.py b/pephub/main.py
index fbd37c50..cf236b6c 100644
--- a/pephub/main.py
+++ b/pephub/main.py
@@ -1,16 +1,12 @@
 import logging
-import sys
-import uvicorn
 import coloredlogs
 
 from fastapi import FastAPI
-from fastapi.middleware import Middleware
 from fastapi.middleware.cors import CORSMiddleware
 from fastapi.staticfiles import StaticFiles
 
 from ._version import __version__ as server_v
 from .const import PKG_NAME, TAGS_METADATA, SPA_PATH
-from .helpers import build_parser
 from .routers.api.v1.base import api as api_base
 from .routers.api.v1.namespace import namespace as api_namespace
 from .routers.api.v1.project import project as api_project
@@ -18,13 +14,12 @@
 from .routers.api.v1.search import search as api_search
 from .routers.auth.base import auth as auth_router
 from .routers.eido.eido import router as eido_router
-from .middleware import SPA, EnvironmentMiddleware
-from .const import LOG_LEVEL_MAP
+from .middleware import SPA
 
 _LOGGER_PEPDBAGENT = logging.getLogger("pepdbagent")
 coloredlogs.install(
     logger=_LOGGER_PEPDBAGENT,
-    level=logging.WARNING,
+    level=logging.INFO,
     datefmt="%b %d %Y %H:%M:%S",
     fmt="[%(levelname)s] [%(asctime)s] [PEPDBAGENT] %(message)s",
 )
@@ -32,7 +27,7 @@
 _LOGGER_PEPPY = logging.getLogger("peppy")
 coloredlogs.install(
     logger=_LOGGER_PEPPY,
-    level=logging.WARNING,
+    level=logging.ERROR,
     datefmt="%b %d %Y %H:%M:%S",
     fmt="[%(levelname)s] [%(asctime)s] [PEPPY] %(message)s",
 )
diff --git a/pephub/middleware.py b/pephub/middleware.py
index 3f6dcf22..9596ccd6 100644
--- a/pephub/middleware.py
+++ b/pephub/middleware.py
@@ -4,7 +4,7 @@
 from fastapi.responses import FileResponse
 from starlette.middleware.base import BaseHTTPMiddleware
 from starlette.responses import Response
-from starlette.types import ASGIApp, Receive, Scope, Send
+from starlette.types import Send
 
 from .const import SPA_PATH
 
diff --git a/pephub/routers/api/v1/base.py b/pephub/routers/api/v1/base.py
index 97ef97a2..2745c861 100644
--- a/pephub/routers/api/v1/base.py
+++ b/pephub/routers/api/v1/base.py
@@ -1,6 +1,5 @@
 from fastapi import APIRouter
 
-from ....dependencies import *
 from ....const import ALL_VERSIONS
 
 
diff --git a/pephub/routers/api/v1/namespace.py b/pephub/routers/api/v1/namespace.py
index 7002810f..24ac00a1 100644
--- a/pephub/routers/api/v1/namespace.py
+++ b/pephub/routers/api/v1/namespace.py
@@ -1,17 +1,30 @@
 import tempfile
 import shutil
+import json
+from typing import List, Optional, Union
 
-from fastapi import APIRouter, File, UploadFile, Request, Depends, Form, Body
+import peppy
+
+from fastapi import APIRouter, File, UploadFile, Request, Depends, Form
 from fastapi.responses import JSONResponse
 from peppy import Project
 from peppy.const import DESC_KEY, NAME_KEY
+from pepdbagent import PEPDatabaseAgent
 from pepdbagent.exceptions import ProjectUniqueNameError
 from pepdbagent.const import DEFAULT_LIMIT_INFO
-from pepdbagent.models import ListOfNamespaceInfo
+from pepdbagent.models import ListOfNamespaceInfo, Namespace
 from typing import Literal
 from typing_extensions import Annotated
 
-from ....dependencies import *
+from ....dependencies import (
+    get_db,
+    get_namespace_info,
+    get_user_from_session_info,
+    read_authorization_header,
+    get_organizations_from_session_info,
+    get_namespace_access_list,
+    verify_user_can_write_namespace,
+)
 from ....helpers import parse_user_file_upload, split_upload_files_on_init_file
 from ....const import (
     DEFAULT_TAG,
@@ -292,7 +305,7 @@ async def upload_raw_pep(
     except Exception as e:
         return JSONResponse(
             content={
-                "message": f"Incorrect raw project was provided. Couldn't initiate peppy object.",
+                "message": "Incorrect raw project was provided. Couldn't initiate peppy object.",
                 "error": f"{e}",
                 "status_code": 417,
             },
@@ -313,7 +326,7 @@ async def upload_raw_pep(
         return JSONResponse(
             content={
                 "message": f"Project '{namespace}/{p_project.name}:{tag}' already exists in namespace",
-                "error": f"Set overwrite=True to overwrite or update project",
+                "error": "Set overwrite=True to overwrite or update project",
                 "status_code": 409,
             },
             status_code=409,
diff --git a/pephub/routers/api/v1/project.py b/pephub/routers/api/v1/project.py
index e3fe1231..9b118462 100644
--- a/pephub/routers/api/v1/project.py
+++ b/pephub/routers/api/v1/project.py
@@ -1,9 +1,10 @@
 import eido
 import yaml
 import pandas as pd
-from io import StringIO
-from typing import Callable, Literal, Union
-from fastapi import APIRouter
+import peppy
+from typing import Callable, Literal, Union, Optional
+from fastapi import APIRouter, Depends
+from fastapi.exceptions import HTTPException
 from fastapi.responses import JSONResponse, PlainTextResponse
 from peppy import Project
 from peppy.const import (
@@ -13,15 +14,27 @@
     SUBSAMPLE_RAW_LIST_KEY,
 )
 
-from ...models import ProjectOptional, ProjectRawModel
-from ....helpers import zip_conv_result, get_project_sample_names, zip_pep
-from ....dependencies import *
-from ....const import SAMPLE_CONVERSION_FUNCTIONS, VALID_UPDATE_KEYS
-
+from pepdbagent import PEPDatabaseAgent
 from pepdbagent.exceptions import ProjectUniqueNameError
+from pepdbagent.models import AnnotationModel
 
 from dotenv import load_dotenv
 
+
+from ...models import ProjectOptional, ProjectRawModel, ForkRequest
+from ....helpers import zip_conv_result, get_project_sample_names, zip_pep
+from ....dependencies import (
+    get_db,
+    get_project,
+    get_project_annotation,
+    get_namespace_access_list,
+    verify_user_can_fork,
+    verify_user_can_read_project,
+    DEFAULT_TAG,
+)
+from ....const import SAMPLE_CONVERSION_FUNCTIONS, VALID_UPDATE_KEYS
+
+
 load_dotenv()
 
 project = APIRouter(
@@ -51,7 +64,7 @@ async def get_a_pep(
         try:
             raw_project = ProjectRawModel(**proj)
         except Exception:
-            raise HTTPException(500, f"Unexpected project error!")
+            raise HTTPException(500, "Unexpected project error!")
         return raw_project.dict(by_alias=False)
     samples = [s.to_dict() for s in proj.samples]
     sample_table_index = proj.sample_table_index
@@ -92,8 +105,8 @@ async def get_a_pep(
     summary="Update a PEP",
 )
 async def update_a_pep(
-    project: str,
     namespace: str,
+    project: str,
     updated_project: ProjectOptional,
     tag: Optional[str] = DEFAULT_TAG,
     agent: PEPDatabaseAgent = Depends(get_db),
@@ -160,10 +173,10 @@ async def update_a_pep(
             eido.validate_project(
                 new_project, "http://schema.databio.org/pep/2.1.0.yaml"
             )
-        except Exception as e:
+        except Exception as _:
             raise HTTPException(
                 status_code=400,
-                detail=f"",
+                detail="Could not validate PEP. Please check your PEP and try again.",
             )
 
         # if we get through all samples, then update project in the database
@@ -308,7 +321,7 @@ async def get_pep_samples(
 
 
 @project.get("/config", summary="Get project configuration file")
-async def get_pep_samples(
+async def get_pep_config(
     proj: Union[peppy.Project, dict] = Depends(get_project),
     format: Optional[Literal["JSON", "String"]] = "JSON",
     raw: Optional[bool] = False,
@@ -507,3 +520,13 @@ async def fork_pep_to_namespace(
         },
         status_code=202,
     )
+
+
+@project.get("/annotation")
+async def get_project_annotation(
+    proj_annotation: AnnotationModel = Depends(get_project_annotation),
+):
+    """
+    Get project annotation from a certain project and namespace
+    """
+    return proj_annotation
diff --git a/pephub/routers/api/v1/search.py b/pephub/routers/api/v1/search.py
index dea1f069..42198767 100644
--- a/pephub/routers/api/v1/search.py
+++ b/pephub/routers/api/v1/search.py
@@ -1,14 +1,20 @@
-from enum import Enum
-from fastapi import APIRouter, __version__ as fastapi_version
+from typing import List
+
+from fastapi import APIRouter, Depends
 from fastapi.responses import JSONResponse
-from peppy import __version__ as peppy_version
-from platform import python_version
 from pepdbagent import PEPDatabaseAgent
 
-from ...._version import __version__ as pephub_version
-from ....dependencies import *
+from sentence_transformers import SentenceTransformer
+from qdrant_client import QdrantClient
+
+from ....dependencies import (
+    get_db,
+    get_qdrant,
+    get_sentence_transformer,
+    get_namespace_access_list,
+)
 from ...models import SearchQuery
-from ....const import DEFAULT_QDRANT_COLLECTION_NAME, ALL_VERSIONS
+from ....const import DEFAULT_QDRANT_COLLECTION_NAME
 
 
 from dotenv import load_dotenv
diff --git a/pephub/routers/api/v1/user.py b/pephub/routers/api/v1/user.py
index a44da8e8..4c7c68cf 100644
--- a/pephub/routers/api/v1/user.py
+++ b/pephub/routers/api/v1/user.py
@@ -1,10 +1,15 @@
-from fastapi import APIRouter, Request
+from fastapi import APIRouter, Request, Depends
 from fastapi.templating import Jinja2Templates
 from fastapi.responses import RedirectResponse, JSONResponse
 from platform import python_version
 
+from pepdbagent import PEPDatabaseAgent
+
 from ...._version import __version__ as pephub_version
-from ....dependencies import *
+from ....dependencies import (
+    get_db,
+    read_authorization_header,
+)
 from ....const import BASE_TEMPLATES_PATH
 
 user = APIRouter(prefix="/api/v1/me", tags=["profile"])
@@ -36,7 +41,7 @@ def profile_data(
 
 
 @user.get("/data")
-def profile_data(
+def profile_data2(
     request: Request,
     session_info=Depends(read_authorization_header),
     agent: PEPDatabaseAgent = Depends(get_db),
diff --git a/pephub/routers/auth/base.py b/pephub/routers/auth/base.py
index 4b2d6e16..b0fde5a7 100644
--- a/pephub/routers/auth/base.py
+++ b/pephub/routers/auth/base.py
@@ -5,7 +5,7 @@
 import time
 from typing import Union
 from fastapi import APIRouter, Request, Header, BackgroundTasks, Depends
-from fastapi.responses import RedirectResponse, Response
+from fastapi.responses import RedirectResponse
 from fastapi.exceptions import HTTPException
 from fastapi.templating import Jinja2Templates
 from dotenv import load_dotenv
@@ -136,7 +136,7 @@ def callback(
 
     if state.get("device"):
         DEVICE_CODES[state["device_code"]]["token"] = token
-        return f"/login/device/success"
+        return "/login/device/success"
 
     else:
         # create random auth code
diff --git a/pephub/routers/eido/eido.py b/pephub/routers/eido/eido.py
index 9e787ab7..09fcf8ee 100644
--- a/pephub/routers/eido/eido.py
+++ b/pephub/routers/eido/eido.py
@@ -5,14 +5,16 @@
 import shutil
 import yaml
 
-from fastapi import UploadFile, Form, APIRouter
+from fastapi import UploadFile, Form, APIRouter, Depends
+from fastapi.exceptions import HTTPException
 from starlette.requests import Request
 from starlette.responses import JSONResponse
-from typing import List, Tuple
+from typing import List, Tuple, Optional
+from pepdbagent import PEPDatabaseAgent
 from pepdbagent.utils import registry_path_converter
 
 from ...helpers import parse_user_file_upload, split_upload_files_on_init_file
-from ...dependencies import *
+from ...dependencies import get_db, DEFAULT_TAG
 
 
 schemas_url = "https://schema.databio.org/list.json"
diff --git a/pephub/routers/models.py b/pephub/routers/models.py
index 70ced651..f0ac6f37 100644
--- a/pephub/routers/models.py
+++ b/pephub/routers/models.py
@@ -1,6 +1,6 @@
-from typing import Optional
-from pydantic import BaseModel
-from pepdbagent.models import *
+from typing import Optional, List
+from pydantic import BaseModel, Field, Extra
+from pepdbagent.models import UpdateItems
 from pepdbagent.const import DEFAULT_TAG
 
 from ..const import DEFAULT_PEP_SCHEMA
diff --git a/pephub/routers/profile.py b/pephub/routers/profile.py
deleted file mode 100644
index e998d1dc..00000000
--- a/pephub/routers/profile.py
+++ /dev/null
@@ -1,43 +0,0 @@
-from fastapi import APIRouter, Request
-from fastapi.templating import Jinja2Templates
-from fastapi.responses import RedirectResponse
-from platform import python_version
-
-from .._version import __version__ as pephub_version
-
-from ..dependencies import *
-from ..view_dependencies import *
-from ..const import BASE_TEMPLATES_PATH
-
-router = APIRouter(prefix="/profile", tags=["profile"])
-
-templates = Jinja2Templates(directory=BASE_TEMPLATES_PATH)
-
-
-@router.get("/")
-def profile(
-    request: Request,
-    session_info=Depends(read_session_cookie),
-    agent: PEPDatabaseAgent = Depends(get_db),
-):
-    """
-    Display the user's profile page.
-    """
-    if session_info is None:
-        return RedirectResponse(url="/auth/login")
-    else:
-        namespace_info = agent.namespace.get(
-            namespace=session_info["login"], admin=session_info["login"]
-        )
-        return templates.TemplateResponse(
-            "profile.html",
-            {
-                "request": request,
-                "session_info": session_info,
-                "python_version": python_version(),
-                "pephub_version": pephub_version,
-                "logged_in": session_info is not None,
-                "namespace_info": namespace_info,
-                "projects": namespace_info.projects,
-            },
-        )
diff --git a/pephub/routers/views/eido.py b/pephub/routers/views/eido.py
index f62f5956..0154c844 100644
--- a/pephub/routers/views/eido.py
+++ b/pephub/routers/views/eido.py
@@ -1,18 +1,14 @@
 import jinja2
 import eido
-import peppy
 
 from fastapi import APIRouter, Request
-from fastapi.responses import RedirectResponse, HTMLResponse
+from fastapi.responses import HTMLResponse
 from starlette.templating import Jinja2Templates
 from peppy import __version__ as peppy_version
-from peppy.const import SAMPLE_RAW_DICT_KEY, CONFIG_KEY
 from platform import python_version
 from dotenv import load_dotenv
 
 from ..._version import __version__ as pephub_version
-from ...dependencies import *
-from ...view_dependencies import *
 from ...const import EIDO_TEMPLATES_PATH
 
 
diff --git a/requirements/requirements-all.txt b/requirements/requirements-all.txt
index 33d32f40..415a032c 100644
--- a/requirements/requirements-all.txt
+++ b/requirements/requirements-all.txt
@@ -9,7 +9,7 @@ tqdm
 uvicorn
 python-dotenv
 pepdbagent>=0.6.0
-peppy>=0.40.0a4
+peppy>=0.40.0a5
 qdrant-client
 requests
 aiofiles
diff --git a/web/src/api/project.ts b/web/src/api/project.ts
index 014d7e22..61e328d1 100644
--- a/web/src/api/project.ts
+++ b/web/src/api/project.ts
@@ -1,6 +1,6 @@
 import axios from 'axios';
 
-import { Project, ProjectConfigResponse, Sample } from '../../types';
+import { Project, ProjectAnnotation, ProjectConfigResponse, Sample } from '../../types';
 
 const API_HOST = import.meta.env.VITE_API_HOST || '';
 const API_BASE = `${API_HOST}/api/v1`;
@@ -43,6 +43,20 @@ export const getProject = (
   }
 };
 
+export const getProjectAnnotation = (
+  namespace: string,
+  projectName: string,
+  tag: string = 'default',
+  token: string | null = null,
+) => {
+  const url = `${API_BASE}/projects/${namespace}/${projectName}/annotation?tag=${tag}`;
+  if (!token) {
+    return axios.get<ProjectAnnotation>(url).then((res) => res.data);
+  } else {
+    return axios.get<ProjectAnnotation>(url, { headers: { Authorization: `Bearer ${token}` } }).then((res) => res.data);
+  }
+};
+
 export const getSampleTable = (
   namespace: string,
   projectName: string,
diff --git a/web/src/hooks/queries/useProjectAnnotation.ts b/web/src/hooks/queries/useProjectAnnotation.ts
new file mode 100644
index 00000000..0092c953
--- /dev/null
+++ b/web/src/hooks/queries/useProjectAnnotation.ts
@@ -0,0 +1,17 @@
+import { useQuery } from '@tanstack/react-query';
+
+import { getProjectAnnotation } from '../../api/project';
+
+export const useProjectAnnotation = (
+  namespace: string | undefined,
+  project: string | undefined,
+  tag: string | undefined,
+  token: string | null,
+) => {
+  const query = useQuery({
+    queryKey: [namespace, project, tag, 'annotation'],
+    queryFn: () => getProjectAnnotation(namespace || '', project || '', tag, token),
+    enabled: namespace !== undefined || project !== undefined,
+  });
+  return query;
+};
diff --git a/web/src/pages/Project.tsx b/web/src/pages/Project.tsx
index 0fabbf37..4e9ff5b9 100644
--- a/web/src/pages/Project.tsx
+++ b/web/src/pages/Project.tsx
@@ -19,7 +19,7 @@ import { useConfigMutation } from '../hooks/mutations/useConfigMutation';
 import { useSampleTableMutation } from '../hooks/mutations/useSampleTableMutation';
 import { useSubsampleTableMutation } from '../hooks/mutations/useSubsampleTableMutation';
 import { useTotalProjectChangeMutation } from '../hooks/mutations/useTotalProjectChangeMutation';
-import { useProject } from '../hooks/queries/useProject';
+import { useProjectAnnotation } from '../hooks/queries/useProjectAnnotation';
 import { useProjectConfig } from '../hooks/queries/useProjectConfig';
 import { useSampleTable } from '../hooks/queries/useSampleTable';
 import { useSubsampleTable } from '../hooks/queries/useSubsampleTable';
@@ -65,7 +65,11 @@ export const ProjectPage: FC = () => {
   const fork = searchParams.get('fork');
 
   // fetch data
-  const { data: projectInfo, isLoading: projectInfoIsLoading, error } = useProject(namespace, project || '', tag, jwt);
+  const {
+    data: projectInfo,
+    isLoading: projectInfoIsLoading,
+    error,
+  } = useProjectAnnotation(namespace, project || '', tag, jwt);
   const { data: projectSamples } = useSampleTable(namespace, project, tag, jwt);
   const { data: projectSubsamples } = useSubsampleTable(namespace, project, tag, jwt);
   const { data: projectConfig, isLoading: projectConfigIsLoading } = useProjectConfig(