October 9, 2026

How to Build a Multi-Tenant Search API Layer with the You.com Web Search API

How to Build a Multi-Tenant Search API Layer with the You.com Web Search API

TLDR: A multi-tenant search layer serves many customer organizations from one integration, and the isolation is yours to build, because the upstream API key identifies your account, not your tenants. The standard shape is one backend service that owns the key, per-tenant option profiles with domain allowlists, usage metering per tenant, and a cache keyed by tenant so results never bleed across boundaries. This guide covers the design, working Python with the You.com Web Search API, and the failure modes that leak one tenant's world into another's.

If your product serves multiple organizations and any of them can ask it to search the live web, you have a multi-tenant search problem, whether you planned for it or not. The Web Search API from You.com supplies the retrieval primitive: a POST with a query, a count, and option filters, returning ranked results. The tenancy is the application layer you wrap around it, because the API answers for the account that holds the key. The OWASP cheat sheet series covers the general security framing, and this guide is the search-specific version of it.

What Does Multi-Tenancy Mean for a Search API?

Multi-tenancy means one deployment serves multiple customer organizations, and every request belongs to exactly one of them. For a search layer that adds three obligations the upstream API cannot see: a tenant's queries must not influence another tenant's results, a tenant's policies must apply only to that tenant, and a tenant's usage must be attributable, because spend and abuse are per-customer questions. The upstream API key is account-level, so none of those boundaries exist at the vendor. They exist in the service you build in front of it, which is why the layer is a product feature and not a configuration flag.

How Do You Isolate Tenants on a Shared Search Backend?

Key custody first: one backend service owns the vendor key, tenants authenticate to your service with their own credentials, and the vendor key never crosses the tenant boundary in either direction. Then per-tenant option profiles, which is where search tenancy gets concrete:

import hashlib
import json
import os
import time
import urllib.request


def call_search(options: dict) -> dict:
    req = urllib.request.Request(
        "https://ydc-index.io/v1/search",
        data=json.dumps(options).encode(),
        headers={
            "Content-Type": "application/json",
            "X-API-Key": os.environ["YOUCOM_API_KEY"],
        },
        method="POST",
    )
    with urllib.request.urlopen(req, timeout=30) as resp:
        return json.load(resp)


class Tenant:
    def __init__(self, tenant_id, allowed_domains=None, blocked_domains=None):
        self.tenant_id = tenant_id
        self.allowed_domains = allowed_domains or []
        self.blocked_domains = blocked_domains or []
        self.call_count = 0

    def options(self, query: str, count: int = 5) -> dict:
        opts = {"query": query, "count": count}
        if self.allowed_domains:
            opts["include_domains"] = sorted(self.allowed_domains)
        if self.blocked_domains:
            opts["exclude_domains"] = sorted(self.blocked_domains)
        return opts

The domain lists are the tenant's search policy as data: one tenant restricts research to a allowlist of trusted domains, another excludes a competitor's properties from results, and both are ordinary request options rather than code paths. Two details matter more than they look. The lists are sorted before they are sent, so identical policies in different storage orders produce identical requests, which is what makes caching and metering deterministic. And the options are assembled per request from the tenant's profile, never inherited from ambient state, so a policy change is a data edit rather than a deploy. The request surface these options ride on, including the domain and freshness filters, is documented in the Web Search API guide.

How Do You Meter and Cache per Tenant?

Metering is a counter on the request path, and caching is a map with a TTL whose key starts with the tenant identity. Both live in the same few lines:

CACHE_TTL = 300
_cache = {}


def cache_key(tenant: Tenant, options: dict) -> str:
    raw = json.dumps(
        {"tenant": tenant.tenant_id, "options": options},
        sort_keys=True,
    )
    return hashlib.sha256(raw.encode()).hexdigest()


def tenant_search(tenant: Tenant, query: str, count: int = 5) -> dict:
    options = tenant.options(query, count)
    key = cache_key(tenant, options)
    entry = _cache.get(key)
    if entry and time.time() - entry[0] < CACHE_TTL:
        return entry[1]
    payload = call_search(options)
    tenant.call_count += 1
    _cache[key] = (time.time(), payload)
    return payload

The cache key hashes the tenant identity together with the full frozen options, which is the whole isolation argument in one line: two tenants asking the identical question get two cache entries, because a shared entry would mean tenant A's policy produced tenant B's results. Cache freshness semantics are the same discipline HTTP caching teaches, a TTL traded against staleness, applied here per tenant. The metering counter is deliberately boring, an integer on the request path, because the point is that per-tenant usage exists at all: attribution, quotas, and abuse detection all start with a per-tenant count that is cheap to keep and impossible to reconstruct later.

What Are the Failure Modes?

Four leaks account for most multi-tenant search incidents.

Cache bleed is the classic: a cache key that hashes the query but not the tenant identity, so the first tenant to ask a question sets the answer every other tenant receives under their policies. It is invisible until two tenants with different domain policies ask the same question and one notices results the other's allowlist should have excluded. Detection: assert the tenant identity is part of every cache key, and run a canary pair, two tenants with disjoint domain policies asking one identical query, expecting disjoint results. Option leakage is the subtler sibling: ambient state or a default profile applied to a tenant whose profile should differ, which is a correctness bug that looks like an odd ranking. Detection: freeze the options into the request and the cache key together, so a policy that did not apply cannot have been cached. Noisy-neighbor cost is the budget version: one tenant's nightly enrichment job floods the shared account's usage and everyone's costs and latencies drift together. Detection: per-tenant counters read on a schedule, with an alert when one tenant's share crosses your defined ceiling, which is only possible because the counter was there from day one. Residency is the quiet one: search traffic and cached results are tenant data moving through your infrastructure, and where it is allowed to live is a question each deployment answers for itself. Our guide to data residency for search APIs covers the considerations, and it belongs in the design review, not the incident review.

Related Guides

FAQ

What does multi-tenant mean for a search API? One deployment serves multiple customer organizations, and every search request belongs to exactly one tenant. The upstream API key is account-level, so isolation between tenants is built in your service layer, not inherited from the vendor.

Do I need a separate API key per tenant? Not for isolation. One backend-owned key plus per-tenant option profiles, metering, and tenant-keyed caching achieves the boundary. Separate keys matter when spend attribution or contractual separation requires them, which is an account-and-architecture decision to raise with your provider.

How do I keep one tenant's search policy from affecting another? Store each tenant's domain allowlists and blocklists as data, freeze them into the request options and the cache key together, and run a canary pair of tenants with disjoint policies on an identical query. Disjoint policies must produce disjoint results, every time.

How do I cache safely across tenants? Key every cache entry by tenant identity plus the frozen request options, give it a TTL, and treat two tenants asking the same question as two cache entries. A cache hit for tenant B that was written under tenant A's policy is a leak, not an optimization.

Frequently Asked Questions

One deployment serves multiple customer organizations, and every search request belongs to exactly one tenant. The upstream API key is account-level, so isolation between tenants is built in your service layer, not inherited from the vendor.

Not for isolation. One backend-owned key plus per-tenant option profiles, metering, and tenant-keyed caching achieves the boundary. Separate keys matter when spend attribution or contractual separation requires them, which is an account-and-architecture decision to raise with your provider.

Store each tenant's domain allowlists and blocklists as data, freeze them into the request options and the cache key together, and run a canary pair of tenants with disjoint policies on an identical query. Disjoint policies must produce disjoint results, every time.

Key every cache entry by tenant identity plus the frozen request options, give it a TTL, and treat two tenants asking the same question as two cache entries. A cache hit for tenant B that was written under tenant A's policy is a leak, not an optimization.

    Share Article:

  1. LI Test

  2. LI Test

Related resources.

What Is Agentic Resource Discovery? Machine-Readable Access for AI Agents

What Is Agentic Resource Discovery? Machine-Readable Access for AI Agents

October 9, 2026

Blog

How to Build a CVE Monitoring Pipeline with the You.com Web Search and Contents APIs

October 8, 2026

Blog

How to Build a Breach Monitoring Pipeline with the You.com Web Search and Contents APIs

October 8, 2026

Blog

What Is an AI Search API? A Practical Guide for Developers Building AI Applications

What Is an AI Search API? A Practical Guide for Developers Building AI Applications

September 30, 2026

Blog

How to Use the You.com Web Search API in TypeScript

How to Use the You.com Web Search API in TypeScript

September 22, 2026

Blog