diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,12 @@
-## [_Unreleased_](https://github.com/freckle/freckle-app/compare/v1.15.5.0...main)
+## [_Unreleased_](https://github.com/freckle/freckle-app/compare/v1.16.0.0...main)
+
+## [v1.16.0.0](https://github.com/freckle/freckle-app/compare/v1.15.5.0...v1.16.0.0)
+
+- Add tracing to HTTP requests made within `AppT`
+
+  This is a potentially breaking change for users of `AppT app` because it now
+  requires `HasTracer app`. This instance most likely already exists, since it's
+  required to use `runDB` too.
 
 ## [v1.15.5.0](https://github.com/freckle/freckle-app/compare/v1.15.4.0...v1.15.5.0)
 
diff --git a/freckle-app.cabal b/freckle-app.cabal
--- a/freckle-app.cabal
+++ b/freckle-app.cabal
@@ -1,6 +1,6 @@
 cabal-version:      1.18
 name:               freckle-app
-version:            1.15.5.0
+version:            1.16.0.0
 license:            MIT
 license-file:       LICENSE
 maintainer:         Freckle Education
@@ -60,6 +60,7 @@
         Freckle.App.Memcached.Servers
         Freckle.App.OpenTelemetry
         Freckle.App.OpenTelemetry.Context
+        Freckle.App.OpenTelemetry.Http
         Freckle.App.OpenTelemetry.ThreadContext
         Freckle.App.Prelude
         Freckle.App.Random
diff --git a/library/Freckle/App.hs b/library/Freckle/App.hs
--- a/library/Freckle/App.hs
+++ b/library/Freckle/App.hs
@@ -191,6 +191,8 @@
 import qualified Freckle.App.Database.XRay as XRay
 import Freckle.App.Http (MonadHttp (..))
 import Freckle.App.OpenTelemetry
+import Freckle.App.OpenTelemetry.Context
+import Freckle.App.OpenTelemetry.Http
 import System.IO (BufferMode (..), hSetBuffering, stderr, stdout)
 
 runApp
@@ -240,8 +242,10 @@
   primitive = AppT . lift . lift . lift . primitive
   {-# INLINE primitive #-}
 
-instance MonadIO m => MonadHttp (AppT app m) where
-  httpLbs = liftIO . httpLbs
+instance (MonadUnliftIO m, HasTracer app) => MonadHttp (AppT app m) where
+  httpLbs req = inSpan (httpSpanName req) (httpSpanArguments req) $ do
+    resp <- liftIO . httpLbs =<< injectContext req
+    resp <$ addCurrentSpanAttributes (httpResponseAttributes resp)
 
 instance (Monad m, HasTracer app) => MonadTracer (AppT app m) where
   getTracer = view tracerL
diff --git a/library/Freckle/App/OpenTelemetry/Http.hs b/library/Freckle/App/OpenTelemetry/Http.hs
new file mode 100644
--- /dev/null
+++ b/library/Freckle/App/OpenTelemetry/Http.hs
@@ -0,0 +1,58 @@
+module Freckle.App.OpenTelemetry.Http
+  ( httpSpanName
+  , httpSpanArguments
+  , httpAttributes
+  , httpResponseAttributes
+  ) where
+
+import Freckle.App.Prelude
+
+import qualified Data.CaseInsensitive as CI
+import qualified Data.HashMap.Strict as HashMap
+import qualified Data.Text as T
+import Data.Text.Encoding (decodeUtf8With)
+import Data.Text.Encoding.Error (lenientDecode)
+import Freckle.App.OpenTelemetry
+  ( SpanArguments (..)
+  , byteStringToAttribute
+  , clientSpanArguments
+  )
+import Network.HTTP.Client (Request, Response)
+import qualified Network.HTTP.Client as HTTP
+import Network.HTTP.Types.Status (statusCode)
+import OpenTelemetry.Attributes (Attribute, ToAttribute (..))
+
+httpSpanName :: Request -> Text
+httpSpanName req =
+  decodeUtf8With lenientDecode $ HTTP.method req <> " " <> HTTP.path req
+
+httpSpanArguments :: Request -> SpanArguments
+httpSpanArguments req = clientSpanArguments {attributes = httpAttributes req}
+
+httpAttributes :: Request -> HashMap Text Attribute
+httpAttributes req =
+  HashMap.fromList
+    [ ("service.name", byteStringToAttribute $ HTTP.host req)
+    , ("resource.name", toAttribute $ httpSpanName req)
+    , ("http.host", byteStringToAttribute $ HTTP.host req)
+    , ("http.method", byteStringToAttribute $ HTTP.method req)
+    , ("http.path", byteStringToAttribute $ HTTP.path req)
+    , ("http.query", byteStringToAttribute $ HTTP.queryString req)
+    ]
+
+httpResponseAttributes :: Response body -> HashMap Text Attribute
+httpResponseAttributes resp = statusAttr <> foldMap (uncurry headerAttr) (HTTP.responseHeaders resp)
+ where
+  statusAttr =
+    HashMap.singleton "http.status_code"
+      . toAttribute
+      . statusCode
+      $ HTTP.responseStatus resp
+
+  headerAttr k = HashMap.singleton (headerAttrKey k) . byteStringToAttribute
+
+  headerAttrKey =
+    ("http.response.headers." <>)
+      . T.toLower
+      . decodeUtf8With lenientDecode
+      . CI.original
diff --git a/package.yaml b/package.yaml
--- a/package.yaml
+++ b/package.yaml
@@ -1,5 +1,5 @@
 name: freckle-app
-version: 1.15.5.0
+version: 1.16.0.0
 maintainer: Freckle Education
 category: Utils
 github: freckle/freckle-app
