Web search domain filtering

Domain filtering lets an Organization admin control which websites Mistral can reach on your Organization's behalf. You define one list of domains and a default action, and Mistral applies them to web search results, open_url requests and website library crawls.

Use it to keep an agent inside a set of trusted sources, or to keep specific sites out of your results.

There are two modes:

  • Allowlist: block every domain except the ones you list.
  • Blocklist: allow every domain except the ones you list.

Filtering only removes results. It never adds a result that web search would not otherwise have returned.

Before you begin

Before you begin

You need:

  • An Organization on the Mistral cloud platform (api.mistral.ai).
  • An API key that belongs to that Organization. If the key is tied to a user account, that user needs the Organization admin role. See Roles and permissions.
  • A terminal, or any HTTP client.

All endpoints on this page are under https://api.mistral.ai/v1/web-filters. There is no Admin Panel page for domain filtering yet, so configuration is API only. These endpoints are not in the Mistral client libraries yet, so the examples use curl.

Set your key once:

export MISTRAL_API_KEY="your-api-key"
Where filtering applies

Where filtering applies

Domain filtering is a governance control on the Mistral tools that reach the web. It is not a network-level egress control, and it does not cover every way a model can reach the web.

Covered

Covered

Your Organization's lists apply to:

  • web_search results on the Conversations API and the Agents API, including inside web_search_premium and during agent handoffs.
  • open_url, which fetches a page by URL. A request for a domain your lists block is refused instead of fetched.
  • Website libraries, which crawl a site you choose. A crawl of a blocked domain is refused when you save the library and again when a crawl starts.
Self-hosted deployments

Self-hosted deployments

Note

A self-hosted Mistral AI Suite deployment does not run web search inside your cluster. It sends searches to the Mistral cloud under the Organization its license key belongs to, so your lists apply to them when you configure them on that Organization. The deployment has no filter store or admin API of its own.

Quick start

Quick start

Two steps: choose a mode, then add domains.

Restrict search to a set of trusted domains

Restrict search to a set of trusted domains

Set the default action to block, then list the domains to allow.

curl --request PUT "https://api.mistral.ai/v1/web-filters/config" \
     --header "Authorization: Bearer $MISTRAL_API_KEY" \
     --header "Content-Type: application/json" \
     --data '{"block_by_default": true}'
curl --request POST "https://api.mistral.ai/v1/web-filters/domains/allowed" \
     --header "Authorization: Bearer $MISTRAL_API_KEY" \
     --header "Content-Type: application/json" \
     --data '{"domains": ["reuters.com", "arxiv.org", "docs.mistral.ai"]}'

Response:

{
  "domains": ["reuters.com", "arxiv.org", "docs.mistral.ai"]
}

Web search now returns results from those three domains and their subdomains, and nothing else.

Keep specific domains out of results

Keep specific domains out of results

Set the default action to allow, then list the domains to block.

curl --request PUT "https://api.mistral.ai/v1/web-filters/config" \
     --header "Authorization: Bearer $MISTRAL_API_KEY" \
     --header "Content-Type: application/json" \
     --data '{"block_by_default": false}'
curl --request POST "https://api.mistral.ai/v1/web-filters/domains/blocked" \
     --header "Authorization: Bearer $MISTRAL_API_KEY" \
     --header "Content-Type: application/json" \
     --data '{"domains": ["example-competitor.com", "low-quality-aggregator.net"]}'

Web search now returns results from anywhere except those two domains and their subdomains.

The default action and the two lists

The default action and the two lists

Your Organization has one setting and two lists.

ItemWhat it does
block_by_defaultThe action for a domain that matches no entry in either list.
allowed listDomains kept whatever the default action is, unless a more specific entry on the blocklist matches.
blocked listDomains dropped whatever the default action is, unless a more specific entry on the allowlist matches.

Until you call PUT /config, your Organization has no configuration and no filtering is applied. GET /config returns null in that state:

curl "https://api.mistral.ai/v1/web-filters/config" \
     --header "Authorization: Bearer $MISTRAL_API_KEY"
{
  "block_by_default": null
}

Both lists are always consulted, in both modes. This lets you carve exceptions out of a broad rule. For example, with block_by_default: false you can block example.com while allowing blog.example.com.

Manage domains

Manage domains

Each operation below exists once per list: use /domains/allowed for your allowlist and /domains/blocked for your blocklist. The examples show one list at a time, and the other works the same way.

List the domains in a list

List the domains in a list

curl "https://api.mistral.ai/v1/web-filters/domains/blocked" \
     --header "Authorization: Bearer $MISTRAL_API_KEY"
{
  "domains": ["example-competitor.com", "low-quality-aggregator.net"]
}
Check whether one domain is in a list

Check whether one domain is in a list

Returns 404 if the domain is not in that list.

curl "https://api.mistral.ai/v1/web-filters/domains/blocked/example-competitor.com" \
     --header "Authorization: Bearer $MISTRAL_API_KEY"
{
  "domain": "example-competitor.com"
}
Add domains

Add domains

Send between 1 and 1000 domains per request. A request outside that range returns 422. Adding a domain that is already in the list succeeds and changes nothing, so a retried request is safe.

curl --request POST "https://api.mistral.ai/v1/web-filters/domains/allowed" \
     --header "Authorization: Bearer $MISTRAL_API_KEY" \
     --header "Content-Type: application/json" \
     --data '{"domains": ["nature.com", "science.org"]}'

The response lists the domains the request left present in that list, in their stored form.

Remove domains

Remove domains

Remove one domain. The response is 204 No Content, and removing a domain that is not in the list also returns 204.

curl --request DELETE "https://api.mistral.ai/v1/web-filters/domains/allowed/nature.com" \
     --header "Authorization: Bearer $MISTRAL_API_KEY"

Remove several at once with batch-delete, which also returns 204:

curl --request POST "https://api.mistral.ai/v1/web-filters/domains/allowed/batch-delete" \
     --header "Authorization: Bearer $MISTRAL_API_KEY" \
     --header "Content-Type: application/json" \
     --data '{"domains": ["nature.com", "science.org"]}'
Note

batch-delete is a POST because it carries a request body. Some proxies drop the body of a DELETE request.

Turn filtering off

Turn filtering off

Entries on both lists are matched whatever the default action is, so switching mode is not enough on its own. A domain left on the blocklist keeps being dropped. To stop filtering without deleting your allowlist, take both steps.

Set the default action to allow:

curl --request PUT "https://api.mistral.ai/v1/web-filters/config" \
     --header "Authorization: Bearer $MISTRAL_API_KEY" \
     --header "Content-Type: application/json" \
     --data '{"block_by_default": false}'

Read the blocklist, then empty it:

curl "https://api.mistral.ai/v1/web-filters/domains/blocked" \
     --header "Authorization: Bearer $MISTRAL_API_KEY"
curl --request POST "https://api.mistral.ai/v1/web-filters/domains/blocked/batch-delete" \
     --header "Authorization: Bearer $MISTRAL_API_KEY" \
     --header "Content-Type: application/json" \
     --data '{"domains": ["example-competitor.com", "low-quality-aggregator.net"]}'
How domains are matched

How domains are matched

What you can send

What you can send

Send a bare hostname. Mistral normalizes what you send before storing it, so a full URL also works.

You sendMistral stores
Example.comexample.com
https://example.com/path?q=1example.com
www.example.comwww.example.com
ß.dexn--zca.de

Two rules follow from this:

  • A leading www. is kept. example.com and www.example.com are two different entries.
  • Internationalized domains are converted to their punycode form, so entries match the hosts a browser resolves.

The API returns 422 for anything that is not a valid hostname, including bare IP addresses, hosts with no top-level domain, and hosts with an invalid label.

Subdomains are included

Subdomains are included

An entry matches the domain itself and everything below it. example.com matches example.com, news.example.com, and a.b.example.com.

To target one subdomain only, add that subdomain as its own entry.

The most specific entry wins

The most specific entry wins

When a result's host matches entries on both lists, the longest match decides. The default action applies only when nothing matches.

Example, with block_by_default: false, example.com blocked, and blog.example.com allowed:

Result hostOutcomeWhy
blog.example.comKeptMatches blog.example.com on the allowlist, which is more specific.
shop.example.comDroppedClosest match is example.com on the blocklist.
example.orgKeptNo match, so the default action applies.
Tip

A domain cannot be in both lists at once. Adding one that is already on the other list returns 409. Remove it from the other list first.

What happens to filtered results

What happens to filtered results

A filtered result is dropped before the response is built. It is not returned, not cited, and not visible to the model. There is no placeholder and no count of what was removed.

Three details worth knowing:

  • Filtering runs before your result limit applies. A dropped result does not consume your requested number of results, so a search can still return a full page after filtering.
  • A result with an unusable URL follows the default action. If a result's host cannot be read, it is dropped in allowlist mode and kept in blocklist mode.
  • Filtering never widens results. It can only remove what web search already found.
Filtering can leave a search with no results

Filtering can leave a search with no results

Warning

If every result is dropped, the search returns nothing. This is most likely in allowlist mode, when a query has no coverage on your allowed domains.

open_url and website libraries are refused, not silently dropped

open_url and website libraries are refused, not silently dropped

A search returns many results, so filtered ones are removed quietly. A request that names a single domain is refused instead, so the caller is told why.

Both return 451 Unavailable For Legal Reasons:

  • open_url reports that your Organization's filters blocked the page, which is distinct from the site being unreachable.
  • Website libraries reject the domain when you save the library, and again when a crawl starts. A library saved before you blocked its domain stops crawling at its next run.
Troubleshooting

Troubleshooting

ProblemCauseSolution
Web search returns no resultsEvery result was dropped. Usually allowlist mode with too few domains, or a query with no coverage on them.Add domains, or switch to block_by_default: false while you test.
Nothing is filtered at allYour Organization has no configuration.Call GET /v1/web-filters/config. If it returns null, call PUT /config to set a default action.
401 UnauthorizedThe credential is missing, malformed, or not scoped to an Organization.Use an Organization-scoped API key.
403 ForbiddenThe user behind the key is not an Organization admin.Ask an Organization admin to make the change.
409 ConflictThe domain is already in the other list.Remove it from the other list, then add it.
422 Unprocessable EntityThe value is not a valid hostname, for example an IP address or a host with no top-level domain. The batch is also rejected when it is empty or holds more than 1000 domains.Send hostnames such as example.com, between 1 and 1000 per request.
451 Unavailable For Legal Reasons from open_url or a website libraryThe domain is on your blocklist, or outside your allowlist.Remove it from your blocklist, or add it to your allowlist.
503 Service UnavailableMistral could not check your permissions, so the request was refused rather than served unchecked.Retry. If it persists, contact support.
A subdomain is filtered when you did not expect itEntries match subdomains.Add the subdomain as its own entry on the other list to carve out an exception.
Next steps

Next steps