diff --git a/src/client/components/AdvancedSearch.vue b/src/client/components/AdvancedSearch.vue
index 2c752a148199dec103d129b0b4280d34faf6f397..232274196222dfc301993fc9c7d2688109fc6afc 100644
--- a/src/client/components/AdvancedSearch.vue
+++ b/src/client/components/AdvancedSearch.vue
@@ -35,7 +35,14 @@
       <h2>ADVANCED SEARCH</h2>
     </header>
 
-    <div class="options-group">
+    <section>
+      <h3>
+        SEQUENCE MOTIF
+      </h3>
+      <MotifInput v-model="filters.motif"></MotifInput>
+    </section>
+
+    <section>
       <h3>
         ORGANISM
         <button
@@ -61,9 +68,9 @@
           </AppCheckbox>
         </div>
       </div>
-    </div>
+    </section>
 
-    <div class="options-group">
+    <section>
       <h3>
         SOURCES
         <button
@@ -89,9 +96,9 @@
           </AppCheckbox>
         </div>
       </div>
-    </div>
+    </section>
 
-    <div class="options-group">
+    <section>
       <h3>
         HEAVY CHAIN GENE SEGMENTS
         <button
@@ -117,9 +124,9 @@
           </AppCheckbox>
         </div>
       </div>
-    </div>
+    </section>
 
-    <div class="options-group">
+    <section>
       <h3>
         LIGHT CHAIN GENE SEGMENTS
         <button
@@ -145,7 +152,7 @@
           </AppCheckbox>
         </div>
       </div>
-    </div>
+    </section>
 
     <footer>
       <button
@@ -165,6 +172,7 @@ import AppDialog from './AppDialog.vue'
 import { useStore } from '../store'
 import { type AdvancedFilters, type FilterChip } from '../types'
 import AppCheckbox from './AppCheckbox.vue';
+import MotifInput from './MotifInput.vue';
 
 onMounted(() => {
   if (store.antibodiesSources.length === 0) {
@@ -184,7 +192,8 @@ const filters: Ref<AdvancedFilters> = ref({
   species: [],
   sources: [],
   heavySegments: [],
-  lightSegments: []
+  lightSegments: [],
+  motif: ""
 })
 const sourcesList = computed(() => store.antibodiesSources)
 const speciesList = computed(() => store.antibodiesSpecies)
@@ -240,7 +249,7 @@ function updateFilters(): void {
  * @param filter - The name of the filter group
  * @param list - The list of values to select
  */
-function selectAll(filter: keyof AdvancedFilters, list: Readonly<Array<string>>): void {
+function selectAll(filter: keyof Omit<AdvancedFilters, "motif">, list: Readonly<Array<string>>): void {
   if (filters.value[filter].length === list.length) {
     filters.value[filter] = []
   } else {
@@ -308,7 +317,7 @@ function removeFilter(filterChip: FilterChip): void {
   margin-top: 0;
 }
 
-.options-group h3 {
+.advanced-search-dialog section h3 {
   align-items: center;
   display: flex;
   gap: var(--spacing);
diff --git a/src/client/components/AntibodiesTable.vue b/src/client/components/AntibodiesTable.vue
index 6cb2ce93e7c3dac1997c371c18f631d233262081..497930b1422dc99aea1922ad48bde2a4a81d05b9 100644
--- a/src/client/components/AntibodiesTable.vue
+++ b/src/client/components/AntibodiesTable.vue
@@ -279,6 +279,7 @@ function download(): void {
     sources: request.value.sources,
     heavySegments: request.value.heavySegments,
     lightSegments: request.value.lightSegments,
+    motif: request.value.motif,
     format: downloadFormat.value
   }
 
diff --git a/src/client/components/MotifInput.vue b/src/client/components/MotifInput.vue
new file mode 100644
index 0000000000000000000000000000000000000000..f3030de68bfe2a9c762a8833522ac9b8f1108916
--- /dev/null
+++ b/src/client/components/MotifInput.vue
@@ -0,0 +1,61 @@
+<template>
+  <div class="motif">
+    <input
+      id="motif-input"
+      v-model="model"
+      type="text"
+      placeholder="e.g., GLLF, ACKC.."
+      spellcheck="false"
+      pattern="[A-Z,]*">
+    <button
+      type="button"
+      class="clear-input"
+      @click="clearInput()">
+      CLEAR
+    </button>
+  </div>
+</template>
+
+<script lang="ts" setup>
+const model = defineModel<string>()
+
+function clearInput(): void {
+  model.value = ''
+}
+</script>
+
+<style scoped>
+.motif {
+  display: flex;
+  flex-flow: row nowrap;
+  gap: var(--spacing);
+  padding-bottom: var(--spacing);
+}
+
+#motif-input {
+  background: none;
+  border: none;
+  border-bottom: 1px var(--white) solid;
+  border-radius: var(--radius) var(--radius) 0 0;
+  color: var(--primary);
+  font-size: var(--spacing);
+  padding: var(--half-spacing);
+  transition: background-color 0.2s;
+  width: 100%;
+
+  &:focus {
+    background-color: var(--primary-translucent);
+    outline: none;
+    border-bottom-color: var(--primary);
+  }
+
+  &:not(:valid) {
+    color: var(--red);
+    border-bottom-color: var(--red);
+  }
+
+  &:not(:valid):focus {
+    background-color: var(--red-translucent);
+  }
+}
+</style>
diff --git a/src/client/components/SearchBar.vue b/src/client/components/SearchBar.vue
index ad8a350efe76b96d4fd19272c30f8d53d3e6c13b..10c426c3fbba3abdf2c5faa56a8bf6d23b490ef8 100644
--- a/src/client/components/SearchBar.vue
+++ b/src/client/components/SearchBar.vue
@@ -41,7 +41,8 @@ const filters: Ref<AdvancedFilters> = ref({
   species: [],
   sources: [],
   heavySegments: [],
-  lightSegments: []
+  lightSegments: [],
+  motif: ""
 })
 
 /**
@@ -79,13 +80,18 @@ function fetchAntibodies (): void {
     store.request.lightSegments = filters.value.lightSegments.join(',')
   } else delete store.request.lightSegments
 
+  if (filters.value.motif.length > 0) {
+    store.request.motif = filters.value.motif
+  } else delete store.request.motif
+
   store.fetchAntibodies(store.request)
   store.countAntibodies({
     keywords: store.request.keywords,
     species: store.request.species,
     sources: store.request.sources,
     heavySegments: store.request.heavySegments,
-    lightSegments: store.request.lightSegments
+    lightSegments: store.request.lightSegments,
+    motif: store.request.motif
   })
 }
 </script>
@@ -104,7 +110,7 @@ function fetchAntibodies (): void {
 .search-bar {
   border-bottom: 1px var(--white) solid;
   display: flex;
-  gap: var(--spacing);
+  gap: var(--half-spacing);
   padding-bottom: var(--half-spacing);
   transition: border-bottom-color .5s;
 }
@@ -126,7 +132,7 @@ function fetchAntibodies (): void {
   font-size: var(--spacing);
   padding: 0 var(--half-spacing);
   min-width: 0;
-  transition: background-color 0.1s;
+  transition: background-color 0.2s;
 
   &:focus {
     background-color: var(--primary-translucent);
diff --git a/src/client/globals.css b/src/client/globals.css
index bf666502b8e7c10dd73626b068e672de4372ef0c..b6c4501f9de9e99ed0a8799facc353a0387ea62d 100644
--- a/src/client/globals.css
+++ b/src/client/globals.css
@@ -11,6 +11,7 @@
   --primary: hsl(252, 100%, 86%);
   --primary-translucent: hsl(252, 100%, 86%, 0.3);
   --red: hsl(0, 69%, 50%);
+  --red-translucent: hsl(0, 69%, 50%, 0.3);
   --purple: hsl(252, 70%, 48%);
   --black: hsl(0, 0%, 4%);
   --black-translucent: hsla(0, 0%, 4%, 0.5);
diff --git a/src/client/types.ts b/src/client/types.ts
index 887b560d6c990d0b832112776673081f2b9735cc..ecb980cca75a387edbf3eebc2ac27d6bae3e6eb9 100644
--- a/src/client/types.ts
+++ b/src/client/types.ts
@@ -47,7 +47,8 @@ interface APIFiltersParams {
   species?: string
   sources?: string
   heavySegments?: string,
-  lightSegments?: string
+  lightSegments?: string,
+  motif?: string
 }
 
 /**
@@ -118,7 +119,8 @@ export interface AdvancedFilters {
   species: Array<Antibody['species']>
   sources: Array<FastaHeader['source']>
   heavySegments: Array<FastaHeader['vGeneSegment']>
-  lightSegments: Array<FastaHeader['vGeneSegment']>
+  lightSegments: Array<FastaHeader['vGeneSegment']>,
+  motif: string
 }
 
 /**
diff --git a/src/server/hooks/buildDBQuery.js b/src/server/hooks/buildDBQuery.js
index 16335e1d78eac91cfe092c3eeafffa83c607572e..027f4d42d53898a122c9df9b685ffe843fcafb9a 100644
--- a/src/server/hooks/buildDBQuery.js
+++ b/src/server/hooks/buildDBQuery.js
@@ -72,6 +72,19 @@ export default function buildDBQuery(request, reply, done) {
     })
   }
 
+  if (request.query.motif) {
+    const $or = []
+
+    request.query.motif
+      .forEach(motif => {
+        // TODO: test if removing the 'i' options is faster
+        $or.push({ 'heavyChain.sequence': { $regex: motif.trim(), $options: 'i' } })
+        $or.push({ 'ligthChain.sequence': { $regex: motif.trim(), $options: 'i' } })
+      })
+
+    where.$and.push({ $or })
+  }
+
   // Empty $and operators raise an error in MongoDB
   // so where needs to remain empty as well.
   if (where.$and.length !== 0) request.where = where
diff --git a/src/server/hooks/parseRequest.js b/src/server/hooks/parseRequest.js
index 379c88ee7e73ace82c0d8e713cbf228936af6cf7..a73fc7ec6968c4cc30d57c05babb9d0a7c8d261b 100644
--- a/src/server/hooks/parseRequest.js
+++ b/src/server/hooks/parseRequest.js
@@ -20,6 +20,11 @@
  * for the ones that are not simple strings.
  */
 export default function parseRequest(request, reply, done) {
+  console.log(JSON.stringify(request.query, null, 2))
+  if (request.query.motif) {
+    request.query.motif = request.query.motif.trim().split(',')
+  }
+
   if (request.query.keywords) {
     request.query.keywords = request.query.keywords.trim().split(',')
   }
diff --git a/src/server/routes/antibodiesCountRoute.js b/src/server/routes/antibodiesCountRoute.js
index 6c0463a28409eb6e69aa5733c543404f469daa36..5c254efd8c39fd22e32b38bea208c2a1b6e8cba6 100644
--- a/src/server/routes/antibodiesCountRoute.js
+++ b/src/server/routes/antibodiesCountRoute.js
@@ -88,6 +88,13 @@ export default {
           items: {
             type: 'string',
           }
+        },
+        motif: {
+          description: 'The motif to search for in the sequences',
+          type: 'array',
+          items: {
+            type: 'string',
+          }
         }
       }
     },
diff --git a/src/server/routes/antibodiesDownloadRoute.js b/src/server/routes/antibodiesDownloadRoute.js
index d765e012a4c77e302d92dfd1dab3a683b36725a1..3632d34b2d2516cfd8f2f9debdfd2723bf009bb7 100644
--- a/src/server/routes/antibodiesDownloadRoute.js
+++ b/src/server/routes/antibodiesDownloadRoute.js
@@ -155,6 +155,13 @@ export default {
           items: {
             type: 'string',
           }
+        },
+        motif: {
+          description: 'The motif to search for in the sequences',
+          type: 'array',
+          items: {
+            type: 'string',
+          }
         }
       }
     }
diff --git a/src/server/routes/antibodiesFindRoute.js b/src/server/routes/antibodiesFindRoute.js
index ee9228730e6cb5cd3f13e16fae944f23e21d1f6e..0f6ec7a04359eff0e3969ffb94d64ffb12a8e1ed 100644
--- a/src/server/routes/antibodiesFindRoute.js
+++ b/src/server/routes/antibodiesFindRoute.js
@@ -100,6 +100,13 @@ export default {
             type: 'string',
           }
         },
+        motif: {
+          description: 'The motif to search for in the sequences',
+          type: 'array',
+          items: {
+            type: 'string',
+          }
+        },
         limit: {
           description: 'The maximum number of antibodies to return. Used mainly for pagination.',
           type: 'integer',