diff --git a/src/dto.rs b/src/dto.rs
index fbdc64f546ba542fe08363a4b200c1f615d8d4d7..7947f0e3c509831a7f49d2fc5e76a380ab9d72ed 100644
--- a/src/dto.rs
+++ b/src/dto.rs
@@ -2,12 +2,13 @@ use serde::{Deserialize, Serialize};
 
 use crate::config::Config;
 use crate::models::*;
-use crate::dto_helpers::absolute_url;
+use crate::dto_helpers::{absolute_url, UrlKind};
 
 #[derive(Serialize, Deserialize, PartialEq, Debug)]
 #[serde(rename_all = "camelCase")]
 pub struct TitleDto {
     pub ids: TitleIdDto,
+    pub kind: String,
     pub original_language: Option<String>,
     pub runtime: Option<i32>,
     pub year_start: Option<i32>,
@@ -45,6 +46,7 @@ impl TitleDto {
                 tmdb: src.tmdb_id,
                 tvdb: src.tvdb_id,
             },
+            kind: src.kind,
             original_language: src.original_language,
             runtime: src.runtime,
             year_start: src.year_start,
@@ -204,7 +206,7 @@ impl ImageDto {
         Ok(ImageDto {
             kind: src.kind,
             mime: src.mime,
-            src: absolute_url(config, src.src)?,
+            src: absolute_url(config, UrlKind::Static,src.src)?,
         })
     }
 }
@@ -224,7 +226,7 @@ impl MediaDto {
             mime: src.mime,
             codecs: src.codecs,
             languages: src.languages,
-            src: absolute_url(config, src.src)?,
+            src: absolute_url(config, UrlKind::Static, src.src)?,
         })
     }
 }
@@ -262,7 +264,7 @@ impl SubtitleDto {
             language: src.language,
             region: src.region,
             specifier: src.specifier,
-            src: absolute_url(config, src.src)?,
+            src: absolute_url(config, UrlKind::Static,src.src)?,
         })
     }
 }
@@ -272,4 +274,12 @@ impl SubtitleDto {
 pub struct GenreWithTitlesDto {
     pub genre: GenreDto,
     pub titles: Vec<TitleDto>,
+}
+
+#[derive(Serialize, Deserialize, PartialEq, Debug)]
+#[serde(rename_all = "camelCase")]
+pub struct TitleMetaDto {
+    pub title: TitleDto,
+    pub show: Option<TitleDto>,
+    pub episodes: Vec<EpisodeDto>,
 }
\ No newline at end of file
diff --git a/src/dto_helpers.rs b/src/dto_helpers.rs
index 43090bb15660d4d143c4d95473a772e43034c35d..bc51807a241475387d5161ae902e8b3bb5ef3788 100644
--- a/src/dto_helpers.rs
+++ b/src/dto_helpers.rs
@@ -1,10 +1,32 @@
 use diesel::*;
+use url::ParseError;
+use uuid::Uuid;
 
 use crate::config::Config;
 use crate::dto::*;
 use crate::models::*;
 use crate::schema::*;
-use url::ParseError;
+
+pub fn load_episodes(db: &PgConnection, config: &Config, title_id: Uuid) -> QueryResult<Vec<EpisodeDto>> {
+    let titles: Vec<(TitleEpisode, Title)> = title_episodes::table
+        .filter(title_episodes::show_id.eq(title_id))
+        .inner_join(titles::table.on(titles::id.eq(title_episodes::episode_id)))
+        .load::<(TitleEpisode, Title)>(db)?;
+    let (episodes, titles): (Vec<TitleEpisode>, Vec<Title>) = titles.into_iter().unzip();
+    let titles = load_titles(db, config, titles)?;
+    Ok(episodes.into_iter().zip(titles)
+        .map(|(episode, title)| EpisodeDto::of(episode, title))
+        .collect::<Vec<_>>())
+}
+
+pub fn load_show(db: &PgConnection, config: &Config, title_id: Uuid) -> QueryResult<TitleDto> {
+    let show = title_episodes::table
+        .filter(title_episodes::episode_id.eq(title_id))
+        .inner_join(titles::table.on(titles::id.eq(title_episodes::show_id)))
+        .select(titles::all_columns)
+        .first::<Title>(db)?;
+    load_title(db, config, show)
+}
 
 pub fn load_title(db: &PgConnection, config: &Config, title: Title) -> QueryResult<TitleDto> {
     let title_names: Vec<TitleName> = TitleName::belonging_to(&title)
@@ -129,10 +151,19 @@ fn process_title(
         }).collect::<Vec<_>>(),
         preview
             .and_then(|preview| preview.src)
-            .and_then(|preview| absolute_url(config, preview).ok())
+            .and_then(|preview| absolute_url(config, UrlKind::Static, preview).ok()),
     )
 }
 
-pub fn absolute_url(config: &Config, url: impl Into<String>) -> Result<String, ParseError> {
-    config.static_url.join(url.into().as_str()).map(url::Url::into_string)
+pub enum UrlKind {
+    Static,
+    Api,
+}
+
+pub fn absolute_url(config: &Config, url_kind: UrlKind, url: impl Into<String>) -> Result<String, ParseError> {
+    let base_url = match url_kind {
+        UrlKind::Static => &config.static_url,
+        UrlKind::Api => &config.api_url
+    };
+    base_url.join(url.into().as_str()).map(url::Url::into_string)
 }
\ No newline at end of file
diff --git a/src/main.rs b/src/main.rs
index c99a27964bc67ab679b2de89285a9e8530a0423e..760a2fb0dc20a045530cb968b059729010d0190c 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -19,7 +19,7 @@ use url::Url;
 
 use media_backend::config::Config;
 use media_backend::dto::*;
-use media_backend::dto_helpers::{load_title, load_titles};
+use media_backend::dto_helpers::{load_title, load_titles, load_episodes, load_show};
 use media_backend::models::*;
 use media_backend::param_helpers::ParamUuid;
 
@@ -62,7 +62,7 @@ fn list_titles(db: MediaflixConnection, config: State<Config>) -> QueryResult<Js
 }
 
 #[get("/api/v1/titles/<title_id>")]
-fn get_title(db: MediaflixConnection, config: State<Config>, title_id: ParamUuid) -> QueryResult<Json<TitleDto>> {
+fn get_title(db: MediaflixConnection, config: State<Config>, title_id: ParamUuid) -> QueryResult<Json<TitleMetaDto>> {
     use media_backend::schema::*;
     let title = load_title(
         &db.0,
@@ -71,21 +71,25 @@ fn get_title(db: MediaflixConnection, config: State<Config>, title_id: ParamUuid
             .find(title_id.uuid())
             .first::<Title>(&db.0)?,
     )?;
-    Ok(Json(title))
+    match title.kind.as_str() {
+        "episode" => {
+            let show = load_show(&db.0, &config, title.ids.uuid)?;
+            let episodes = load_episodes(&db.0, &config, show.ids.uuid)?;
+            Ok(Json(TitleMetaDto { title, show: Some(show), episodes }))
+        }
+        "show" => {
+            let episodes = load_episodes(&db.0, &config, title.ids.uuid)?;
+            Ok(Json(TitleMetaDto { title, show: None, episodes }))
+        }
+        _ => {
+            Ok(Json(TitleMetaDto { title, show: None, episodes: vec![] }))
+        }
+    }
 }
 
 #[get("/api/v1/titles/<title_id>/episodes")]
 fn list_episodes(db: MediaflixConnection, config: State<Config>, title_id: ParamUuid) -> QueryResult<Json<Vec<EpisodeDto>>> {
-    use media_backend::schema::*;
-    let titles: Vec<(TitleEpisode, Title)> = title_episodes::table
-        .filter(title_episodes::show_id.eq(title_id.uuid()))
-        .inner_join(titles::table.on(titles::id.eq(title_episodes::episode_id)))
-        .load::<(TitleEpisode, Title)>(&db.0)?;
-    let (episodes, titles): (Vec<TitleEpisode>, Vec<Title>) = titles.into_iter().unzip();
-    let titles = load_titles(&db.0, &config, titles)?;
-    Ok(Json(episodes.into_iter().zip(titles)
-        .map(|(episode, title)| EpisodeDto::of(episode, title))
-        .collect::<Vec<_>>()))
+    Ok(Json(load_episodes(&db.0, &config, title_id.uuid())?))
 }
 
 fn main() -> Result<(), Box<dyn std::error::Error>> {
@@ -96,12 +100,6 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
             api_url: Url::parse(env::var("MEDIAFLIX_API_URL")?.as_str())?,
             static_url: Url::parse(env::var("MEDIAFLIX_STATIC_URL")?.as_str())?,
         })
-        .attach(MediaflixConnection::fairing())
-        .attach(rocket_cors::CorsOptions {
-            allowed_headers: AllowedHeaders::some(&["Authorization", "Accept", "Accept-Language"]),
-            allow_credentials: true,
-            ..Default::default()
-        }.to_cors()?)
         .mount(
             "/",
             rocket::routes![
@@ -112,6 +110,12 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
             ],
         )
         .mount("/static", StaticFiles::from(env::var("MEDIAFLIX_PATH")?))
+        .attach(MediaflixConnection::fairing())
+        .attach(rocket_cors::CorsOptions {
+            allowed_headers: AllowedHeaders::some(&["Authorization", "Accept", "Accept-Language"]),
+            allow_credentials: true,
+            ..Default::default()
+        }.to_cors()?)
         .launch();
 
     Ok(())