Skip to content
Back to Blog
TechnicalHTMLAI Agents

Making your search function work for AI agents

Agent Checker4 min read

When an AI agent arrives at your site with a task, the first thing it often does is look for the search function. Finding a specific product, locating a support article, checking stock on a particular item: search is the fastest path to most tasks, which is why agents navigating product catalogues depend on it so heavily. If your search does not work for agents, they fall back to clicking through navigation, which is slower and less reliable.

Why agents struggle with search

Most search implementations were built for humans who can see the search box, type into it, and visually scan results. Agents face several problems:

The search box is hard to find. It might be hidden behind an icon click, rendered only after JavaScript loads, or buried in a hamburger menu. Agents look for <input type="search">, role="search", or an input with a recognisable label. If none of these exist, the agent has to guess.

Results load dynamically. Many search implementations use JavaScript to fetch and render results without a full page load. Agents that do not execute JavaScript (or wait long enough for it) see an empty results container.

Result structure is ambiguous. Search results rendered as a list of styled <div> elements give agents no way to distinguish the result title from the description from the URL from the price.

Make the search input discoverable

Start with the markup. A search form that agents can find immediately:

<form role="search" action="/search" method="get" aria-label="Site search">
  <label for="site-search" class="sr-only">Search</label>
  <input
    type="search"
    id="site-search"
    name="q"
    placeholder="Search products..."
    aria-label="Search products"
  />
  <button type="submit" aria-label="Submit search">
    <svg aria-hidden="true"><!-- search icon --></svg>
  </button>
</form>

Key points: the role="search" tells agents this is the search form (not a login form or filter). The type="search" identifies the input. The aria-label provides a clear purpose. The submit button has its own label so agents know how to trigger the search.

Support URL-based search

Agents can trigger searches much more efficiently through URLs than through form interactions. Make sure your search works via a query parameter:

GET /search?q=wireless+keyboard

This should return a full HTML page with results. If your search only works through an AJAX endpoint that returns JSON fragments, agents that browse your site (rather than use your API) will not get results.

Even better, support both:

GET /search?q=wireless+keyboard           # returns HTML page
GET /api/search?q=wireless+keyboard        # returns JSON

The HTML version works for browsing agents. The JSON version works for agents with API access. An API-first design makes this dual approach straightforward.

Structure your results

Search results need markup that agents can parse. Here is a before and after:

<!-- Bad: results are just styled divs -->
<div class="result">
  <div class="title">Wireless Keyboard</div>
  <div class="info">GBP 49.99 - In Stock</div>
  <div class="link">example.com/products/wireless-keyboard</div>
</div>

<!-- Good: semantic, structured results -->
<ol aria-label="Search results">
  <li class="result">
    <article>
      <h2><a href="/products/wireless-keyboard">Wireless Keyboard</a></h2>
      <p class="description">Compact wireless keyboard with UK layout</p>
      <p class="price">
        <span data-field="price" data-currency="GBP">49.99</span>
      </p>
      <p class="availability" data-field="stock-status">In Stock</p>
    </article>
  </li>
</ol>

The second version uses an ordered list (ranking matters in search), wraps each result in an <article>, uses a heading with a link for the title, and separates distinct data fields. An agent can extract the product name, price, stock status, and URL from each result independently.

Handle empty results well

When a search returns no results, agents need a clear signal:

<div role="status" aria-live="polite">
  <p>No results found for "wireles keybord".</p>
  <p>Did you mean: <a href="/search?q=wireless+keyboard">wireless keyboard</a>?</p>
</div>

The role="status" tells agents this is a status message, not regular content. The suggestion link gives the agent a way to correct the search automatically.

Without a clear empty-results message, an agent might interpret a blank results area as "still loading" and wait indefinitely.

Add search-related structured data

You can add structured data to your search results page to make it even clearer:

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "SearchResultsPage",
  "mainEntity": {
    "@type": "ItemList",
    "numberOfItems": 24,
    "itemListElement": [
      {
        "@type": "ListItem",
        "position": 1,
        "url": "/products/wireless-keyboard"
      }
    ]
  }
}
</script>

This tells agents exactly how many results there are and provides a structured list they can iterate through.

Pagination that agents can follow

If your results span multiple pages, agents need clear pagination controls:

<nav aria-label="Search results pagination">
  <a href="/search?q=keyboard&page=1" aria-current="page">1</a>
  <a href="/search?q=keyboard&page=2">2</a>
  <a href="/search?q=keyboard&page=3">3</a>
  <a href="/search?q=keyboard&page=2" rel="next" aria-label="Next page">Next</a>
</nav>

The rel="next" attribute and aria-label help agents move through pages. Without these, an agent has to guess which link means "next page", and numbered pagination looks like any other set of links.

Test your search with an agent

The best test is simple. Give an agent a task: "Find the cheapest wireless keyboard on [your site]." Watch what happens. Does it find the search box? Can it enter a query? Can it read the results? Can it compare prices across results? The answers tell you exactly what needs fixing.