diff --git a/catalogue/CatalogueTest.cpp b/catalogue/CatalogueTest.cpp
index d735f50ae84d11680437463886a5b9dc5d8da04d..c7b3f4452d254d24242bb0614512b3284652d508 100644
--- a/catalogue/CatalogueTest.cpp
+++ b/catalogue/CatalogueTest.cpp
@@ -2687,6 +2687,16 @@ TEST_P(cta_catalogue_CatalogueTest, createTape_many_tapes) {
     ASSERT_EQ(tapePoolName, vidToTape.begin()->second.tapePoolName);
   }
 
+  {
+    catalogue::TapeSearchCriteria searchCriteria;
+    searchCriteria.vo = vo;
+    const std::list<common::dataStructures::Tape> tapes = m_catalogue->getTapes(searchCriteria);
+    ASSERT_EQ(nbTapes, tapes.size());
+    const std::map<std::string, common::dataStructures::Tape> vidToTape = tapeListToMap(tapes);
+    ASSERT_EQ(nbTapes, vidToTape.size());
+    ASSERT_EQ(vo, vidToTape.begin()->second.vo);
+  }
+
   {
     catalogue::TapeSearchCriteria searchCriteria;
     searchCriteria.capacityInBytes = capacityInBytes;
diff --git a/catalogue/RdbmsCatalogue.cpp b/catalogue/RdbmsCatalogue.cpp
index f79411d8203e45391f1189b28c80af39389d56e0..972598b3e27a583e83e5dfd6100c2a40c3b5e3ae 100644
--- a/catalogue/RdbmsCatalogue.cpp
+++ b/catalogue/RdbmsCatalogue.cpp
@@ -1893,6 +1893,7 @@ std::list<common::dataStructures::Tape> RdbmsCatalogue::getTapes(rdbms::Conn &co
        searchCriteria.vendor ||
        searchCriteria.logicalLibrary ||
        searchCriteria.tapePool ||
+       searchCriteria.vo ||
        searchCriteria.capacityInBytes ||
        searchCriteria.disabled ||
        searchCriteria.full ||
@@ -1926,6 +1927,11 @@ std::list<common::dataStructures::Tape> RdbmsCatalogue::getTapes(rdbms::Conn &co
       sql += " TAPE.TAPE_POOL_NAME = :TAPE_POOL_NAME";
       addedAWhereConstraint = true;
     }
+    if(searchCriteria.vo) {
+      if(addedAWhereConstraint) sql += " AND ";
+      sql += " TAPE_POOL.VO = :VO";
+      addedAWhereConstraint = true;
+    }
     if(searchCriteria.capacityInBytes) {
       if(addedAWhereConstraint) sql += " AND ";
       sql += " TAPE.CAPACITY_IN_BYTES = :CAPACITY_IN_BYTES";
@@ -1955,6 +1961,7 @@ std::list<common::dataStructures::Tape> RdbmsCatalogue::getTapes(rdbms::Conn &co
     if(searchCriteria.vendor) stmt.bindString(":VENDOR", searchCriteria.vendor.value());
     if(searchCriteria.logicalLibrary) stmt.bindString(":LOGICAL_LIBRARY_NAME", searchCriteria.logicalLibrary.value());
     if(searchCriteria.tapePool) stmt.bindString(":TAPE_POOL_NAME", searchCriteria.tapePool.value());
+    if(searchCriteria.vo) stmt.bindString(":VO", searchCriteria.vo.value());
     if(searchCriteria.capacityInBytes) stmt.bindUint64(":CAPACITY_IN_BYTES", searchCriteria.capacityInBytes.value());
     if(searchCriteria.disabled) stmt.bindBool(":IS_DISABLED", searchCriteria.disabled.value());
     if(searchCriteria.full) stmt.bindBool(":IS_FULL", searchCriteria.full.value());
diff --git a/catalogue/TapeSearchCriteria.hpp b/catalogue/TapeSearchCriteria.hpp
index 65ec7e90bfebccb2118bab748feebf44aad6e86b..3a00bcbf1b0f128188304279f120de159180124b 100644
--- a/catalogue/TapeSearchCriteria.hpp
+++ b/catalogue/TapeSearchCriteria.hpp
@@ -61,6 +61,11 @@ struct TapeSearchCriteria {
    */
   optional<std::string> tapePool;
 
+  /**
+   * The virtual organisation that owns a tape.
+   */
+  optional<std::string> vo;
+
   /**
    * The capacity of a tape in bytes
    */
diff --git a/cmdline/CtaAdminCmdParse.hpp b/cmdline/CtaAdminCmdParse.hpp
index 5a3ac42e44e73c0d15e14b5a10f79bbe4fbc5ced..2173ed4e730c07c01d3d2a2b5d1bbbbe0da07e52 100644
--- a/cmdline/CtaAdminCmdParse.hpp
+++ b/cmdline/CtaAdminCmdParse.hpp
@@ -503,8 +503,8 @@ const std::map<cmd_key_t, cmd_val_t> cmdOptions = {
    {{ AdminCmd::CMD_TAPE,                 AdminCmd::SUBCMD_RECLAIM }, { opt_vid }},
    {{ AdminCmd::CMD_TAPE,                 AdminCmd::SUBCMD_LS    },
       { opt_header.optional(), opt_vid.optional(), opt_mediatype.optional(), opt_vendor.optional(),
-        opt_logicallibrary.optional(), opt_tapepool.optional(), opt_capacity.optional(), opt_lbp.optional(),
-        opt_disabled.optional(), opt_full.optional(), opt_all.optional() }},
+        opt_logicallibrary.optional(), opt_tapepool.optional(), opt_vo.optional(), opt_capacity.optional(),
+        opt_lbp.optional(), opt_disabled.optional(), opt_full.optional(), opt_all.optional() }},
    {{ AdminCmd::CMD_TAPE,                 AdminCmd::SUBCMD_LABEL },
       { opt_vid, opt_force.optional(), opt_lbp.optional() }},
    {{ AdminCmd::CMD_TAPEPOOL,             AdminCmd::SUBCMD_ADD   },
diff --git a/xroot_plugins/XrdSsiCtaRequestMessage.cpp b/xroot_plugins/XrdSsiCtaRequestMessage.cpp
index 4d8f607c56d19c0c7f20dae2a93f6c5e137d409d..00b324f9e9d522f117765127c04afa3ccfcbd3ea 100644
--- a/xroot_plugins/XrdSsiCtaRequestMessage.cpp
+++ b/xroot_plugins/XrdSsiCtaRequestMessage.cpp
@@ -1830,6 +1830,7 @@ void RequestMessage::processTape_Ls(const cta::admin::AdminCmd &admincmd, cta::x
       searchCriteria.capacityInBytes = getOptional(OptionUInt64::CAPACITY,        &has_any);
       searchCriteria.logicalLibrary  = getOptional(OptionString::LOGICAL_LIBRARY, &has_any);
       searchCriteria.tapePool        = getOptional(OptionString::TAPE_POOL,       &has_any);
+      searchCriteria.vo              = getOptional(OptionString::VO,              &has_any);
       searchCriteria.vid             = getOptional(OptionString::VID,             &has_any);
       searchCriteria.mediaType       = getOptional(OptionString::MEDIA_TYPE,      &has_any);
       searchCriteria.vendor          = getOptional(OptionString::VENDOR,          &has_any);