> ## Documentation Index
> Fetch the complete documentation index at: https://wiki.darknetsearch.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Set up a brand library for domain monitoring

> Upload logos, brand keywords, word lists and trusted domains, then attach them to domain scans so look-alike detection checks for your brand.

The brand library holds what a domain scan compares look-alike domains against. A scan without it still finds look-alikes of `acme.com`. With it, the scan also flags websites that show your logo or brand names, tries word combinations such as `acme-login.com`, and leaves out domains you own.

Domain monitoring appears as the **SpoofGuard** tag in the API reference. In the dashboard, the same library is **Lookalike Domains → Brand library**.

| Item | Dashboard tab | What it adds to a scan |
| - | - | - |
| [Logo](#upload-a-logo) | Logos | Logo-spoofing checks on look-alike websites, plus a Google Lens reverse-image search |
| [Branding keyword set](#create-a-branding-keyword-set) | Branding keywords | Brand-spoofing checks: a website that shows your keywords is flagged |
| [Word list](#create-a-word-list) | Word lists | Word-combination variations of your domain, such as `acme-login.com` |
| [Trusted domains](#create-a-trusted-domain-list) | Trusted domains | Removes the domains you list from scan results |

Every item belongs to your organization. All API keys and dashboard users of the organization share one library, and the same item can be attached to many scans.

**Availability:** Domain Monitoring, the plan feature that also opens [domain scans](https://wiki.darknetsearch.com/api/guides/domain-monitoring-scans.md).

**Cost:** no metered allowance. Library items use no Monitored Assets slots. Each call counts toward the per-second [rate limit](https://wiki.darknetsearch.com/api/guides/rate-limits.md) and, when it succeeds, uses one unit of your monthly API Calls allowance.

Without Domain Monitoring, or when your license is not active, every endpoint on this page returns `403`:

```json theme={"dark"}
{
  "detail": "This feature is not included in your plan."
}
```

The library endpoints, `analysis_update` and `analysis_rescan` have no allowance or capacity of their own. They never return the allowance `429` or the capacity `409` described in [Plans and quotas](https://wiki.darknetsearch.com/api/guides/plans-and-quotas).

[`start_domain_analysis`](#attach-items-to-a-scan) is different. Each scanned domain holds one Monitored Assets slot, from the same pool as your monitors. When every slot is in use, the call returns `409` and nothing is created:

```json theme={"dark"}
{
  "detail": "You have reached your Monitored Assets limit of 10.",
  "resource": "monitored_assets",
  "limit": 10
}
```

[Monitored Assets slots](https://wiki.darknetsearch.com/api/guides/domain-monitoring.md#monitored-assets-slots) explains how to free a slot.

Two kinds of `429` can come back from any endpoint on this page:

* **`"message": "Rate limit exceeded"`** is the per-second rate limit or your API Calls allowance. See [Rate limits](https://wiki.darknetsearch.com/api/guides/rate-limits.md).
* **`"detail": "Request was throttled. Expected available in 12 seconds."`** is the domain monitoring service's own limit: 10 scan starts an hour and 180 other requests a minute. Every API key and dashboard user of your organization shares it. Wait the seconds the message gives. The response has no `Retry-After` header.

### Upload a logo

[`spoofguard_logos_create`](https://client-api.leak.center/scalar-docs/#tag/spoofguard/POST/api/spoofguard/logos/) takes `multipart/form-data` with two fields:

* `name`: up to 100 characters, unique within your organization.
* `logo`: a JPEG, PNG or GIF image of up to 20 MB.

```bash theme={"dark"}
curl https://client-api.leak.center/api/spoofguard/logos/ \
  -X POST \
  -H "X-API-Key: YOUR_API_KEY" \
  -F "name=Acme logo" \
  -F "logo=@acme-logo.png"
```

The call returns `201` with the logo's `id`, `name`, `logo_link` (the URL of the stored image) and timestamps. Keep the `id`: you attach the logo to a scan with it.

A logo attached to a scan does two things. The scan compares look-alike websites with your logo, and the result counts them in `total_logo_spoofing`. The scan also runs a Google Lens reverse-image search for the logo. Without a logo, the scan's `module_statuses` reports `logo_spoofing` with the reason `logo_not_supplied`.

To rename a logo or replace its image, send [`spoofguard_logos_update`](https://client-api.leak.center/scalar-docs/#tag/spoofguard/PATCH/api/spoofguard/logos/\{logo_id}/) as `multipart/form-data` with `name`, `logo` or both. Both upload calls accept only `multipart/form-data`. A JSON body returns `415`.

### Create a branding keyword set

[`spoofguard_branding_create`](https://client-api.leak.center/scalar-docs/#tag/spoofguard/POST/api/spoofguard/branding/) takes a JSON body:

* `name`: up to 100 characters, unique within your organization.
* `keywords`: 1 to 3 keywords. A keyword can be a phrase.

```bash theme={"dark"}
curl https://client-api.leak.center/api/spoofguard/branding/ \
  -X POST \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "Acme brand", "keywords": ["acme", "acme pay"]}'
```

```json theme={"dark"}
{
  "id": 4,
  "name": "Acme brand",
  "keywords": ["acme", "acme pay"],
  "created_at": "2026-10-08T09:12:44.512345Z",
  "updated_at": "2026-10-08T09:12:44.512345Z"
}
```

A look-alike website counts as brand spoofing only when **every** keyword in the set appears in its page content. Matching ignores case and needs whole words: `acme` matches "Acme Login" but not "acmesoft". Each keyword you add narrows the match, so a set with one strong keyword catches the most pages. Matches are counted in the scan's `total_brand_spoofing`. Without a keyword set, `module_statuses` reports `brand_spoofing` with the reason `branding_list_not_supplied`.

[`spoofguard_branding_update`](https://client-api.leak.center/scalar-docs/#tag/spoofguard/PATCH/api/spoofguard/branding/\{branding_id}/) takes `name`, `keywords` or both. `keywords` replaces the whole set.

### Create a word list

A word list feeds the word-combination check, which joins your domain with each word to build candidates such as `acme-login.com`. The check runs only on a scan that has a word list attached.

[`spoofguard_wordlists_create`](https://client-api.leak.center/scalar-docs/#tag/spoofguard/POST/api/spoofguard/wordlists/) takes three required fields:

* `name`: up to 512 characters.
* `code`: a slug of letters, digits, hyphens and underscores. You attach the list to a scan by its code, not its `id`.
* `words`: a non-empty list. Each word may contain letters and digits only, with no spaces, hyphens or dots.

```bash theme={"dark"}
curl https://client-api.leak.center/api/spoofguard/wordlists/ \
  -X POST \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "Acme services", "code": "acme-services", "words": ["login", "secure", "pay"]}'
```

```json theme={"dark"}
{
  "id": 9,
  "name": "Acme services",
  "code": "acme-services",
  "words": ["login", "pay", "secure"],
  "created_at": "2026-10-08T09:20:03.118274Z",
  "updated_at": "2026-10-08T09:20:03.118274Z",
  "details": "",
  "existing_wordlist_code": null
}
```

The call returns `201` in every successful case, but it does not always create a list:

* **Words are stored lowercase and sorted.**
* **Only the first 10 words are kept.** `details` names the words that were dropped.
* **A code you already use returns that list unchanged.** `existing_wordlist_code` holds the code, and your new words are not saved.
* **The same set of words returns the existing list.** No new list is created. `details` says so, and `code` and `existing_wordlist_code` hold the existing list's code.

Read `code` from the response and use that value when you attach the list.

[`spoofguard_wordlists_update`](https://client-api.leak.center/scalar-docs/#tag/spoofguard/PUT/api/spoofguard/wordlists/\{wordlist_id}/) is a `PUT`: send `name`, `code` and `words` every time. It returns the stored `name`, `code` and `words`. A code that another of your lists already uses returns `400`.

### Set the default word lists

The other checks in a scan, such as TLD swaps and common-word additions, use built-in word lists. Each check has a default list and a few alternatives. [`spoofguard_wordlists_defaults_list`](https://client-api.leak.center/scalar-docs/#tag/spoofguard/GET/api/spoofguard/wordlists/defaults/) returns one entry per check:

| Field | Meaning |
| - | - |
| `code` | The check, for example `module_tld` |
| `name`, `category`, `description` | What the check does |
| `default_wordlist` | The built-in list the check uses now |
| `wordlist_options` | The built-in lists it can use instead |

To change a default, send the check's `code` as `module_name` and one of its `wordlist_options` as `default_wordlist` to [`spoofguard_wordlists_defaults_update`](https://client-api.leak.center/scalar-docs/#tag/spoofguard/PATCH/api/spoofguard/wordlists/defaults/):

```bash theme={"dark"}
curl https://client-api.leak.center/api/spoofguard/wordlists/defaults/ \
  -X PATCH \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"module_name": "module_tld", "default_wordlist": "most-abused-tlds"}'
```

The call returns the defaults of every check. A list outside the check's `wordlist_options` returns `400`, and the message names the allowed options. Your own word lists cannot be defaults: attach them to a scan instead.

Defaults apply to every scan your organization starts or rescans after the change. Scans already finished keep their results.

### Create a trusted-domain list

A trusted-domain list removes domains you own or trust from scan results, so they are not reported as look-alikes. The API calls it a whitelist.

[`spoofguard_whitelists_create`](https://client-api.leak.center/scalar-docs/#tag/spoofguard/POST/api/spoofguard/whitelists/) takes `name` (up to 512 characters, unique within your organization) and `domains` (1 to 1,000 entries):

```bash theme={"dark"}
curl https://client-api.leak.center/api/spoofguard/whitelists/ \
  -X POST \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "Acme owned domains", "domains": ["acme.com", "*.acme.*"]}'
```

The response carries the list's `id`, which you attach to scans, plus a `uid`, `name`, `domains` and timestamps.

Each entry matches in one of three ways:

| Entry | Matches | Does not match |
| - | - | - |
| `acme.com` | `acme.com` and every subdomain, such as `shop.acme.com` | `acme.com.acme-login.com` |
| `*.cdn.acme.com` | Every subdomain, such as `eu.cdn.acme.com` | `cdn.acme.com` itself |
| `*.acme.*` | Shell-style patterns after `*.`, such as `www.acme.com` | `acme.com` |

Write entries in lowercase. A matching domain is dropped from the scan's results and counts and listed in the scan's `whitelisted_domains` instead. Matching runs each time you read the scan, so an edit to the list changes every scan that uses it straight away.

[`spoofguard_whitelists_update`](https://client-api.leak.center/scalar-docs/#tag/spoofguard/PATCH/api/spoofguard/whitelists/\{whitelist_id}/) takes `name`, `domains` or both. `domains` replaces the whole list.

### Attach items to a scan

Attach items when you start a scan. [`start_domain_analysis`](https://client-api.leak.center/scalar-docs/#tag/spoofguard/POST/api/service/start_domain_analysis/) takes the IDs and the word-list code next to `domain`:

```bash theme={"dark"}
curl https://client-api.leak.center/api/service/start_domain_analysis/ \
  -X POST \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "domain": "acme.com",
    "logo_id": 12,
    "branding_id": 4,
    "whitelist_id": 7,
    "wordlist_code": "acme-services"
  }'
```

All four are optional. [Scan an organization's domain for look-alikes](https://wiki.darknetsearch.com/api/guides/domain-monitoring-scans.md) covers the rest of the call.

> [!WARNING]
>
> **A domain with an existing scan ignores these fields.** If `acme.com` already has a scan, `start_domain_analysis` returns that scan, or a `400` while it is still running, and attaches nothing. When every Monitored Assets slot is in use, it returns the capacity `409` instead, even for a domain you already scan. Attach items to the existing scan as shown below.

To change an existing scan:

**1. Attach the logo, keywords and trusted domains**

Send `logo_id`, `branding_id`, `whitelist_id` or any mix of them to [`analysis_update`](https://client-api.leak.center/scalar-docs/#tag/spoofguard/PATCH/api/service/analysis_detail/\{analysis_id}/). Only the fields you send change.

```bash theme={"dark"}
curl https://client-api.leak.center/api/service/analysis_detail/7c1e2d3f-5a6b-4c8d-9e0f-1a2b3c4d5e6f/ \
  -X PATCH \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"logo_id": 12, "branding_id": 4}'
```

A trusted-domain list applies straight away. The next read of the scan leaves out the domains it matches.

**2. Rescan for the logo, keywords and word list**

Call [`analysis_rescan`](https://client-api.leak.center/scalar-docs/#tag/spoofguard/POST/api/service/analysis_rescan/\{analysis_id}/). Pass `wordlist_code` in its body to add a word list. The rescan adds the Google Lens search when a logo is attached, and runs the logo and keyword checks over the regenerated candidates.

Rules for attaching:

* **You can swap an item, not detach it.** Send another ID to replace an attached item. Leave a field out to keep the attached item. `null` or an empty value returns `400`.
* **Deleting a logo, branding keyword set or trusted-domain list detaches it everywhere.** Every scan that used it loses it.
* **Deleting a word list does not detach it.** A scan that used it returns `400` on rescan until you pass another `wordlist_code`.
* **`whitelist_domains` adds single domains.** Send a list of domains to `analysis_update` to add them to the scan's trusted-domain list. If the scan has a list, the domains are added to it, which changes every scan that shares it. If not, the domains go to your list named after the domain's name, `acme` for `acme.com`, which is created if needed and attached.
* **Unknown IDs are refused.** An ID or code that is not in your library returns `400`.

[`analysis_detail`](https://client-api.leak.center/scalar-docs/#tag/spoofguard/GET/api/service/analysis_detail/\{analysis_id}/) shows what a scan uses: `logo`, `branding` and `whitelist` hold the attached items, `whitelisted_domains` the domains they removed, and `module_statuses` the checks that did not run for lack of a logo or keywords.

### Errors

Errors from the API itself carry `detail`. Errors from the domain monitoring service carry `details`, except its own `429`, which carries `detail`. The per-second rate limit and the API Calls allowance carry `message`. Read the first of `detail`, `details` and `message` that the body contains. Every status is explained once in [Handle API errors](https://wiki.darknetsearch.com/api/guides/errors.md).

| Status | When | Body |
| - | - | - |
| `400` | A field fails validation: missing `name`, more than 3 keywords, more than 1,000 domains, a `code` that is not a slug | The field name with its messages, for example `{"keywords": ["Ensure this field has no more than 3 elements."]}` |
| `400` | An update with no fields | `{"non_field_errors": ["At least one of 'name' or 'keywords' must be provided."]}`, naming the fields of that item |
| `400` | The name is already used by an item of the same kind | `{"details": ["A branding list with the name 'Acme brand' already exists."]}` |
| `400` | The logo is not a JPEG, PNG or GIF image, or is over 20 MB | `{"logo": [...]}` |
| `400` | A word contains characters other than letters and digits | `details`, with the rejected words in `words` |
| `400` | A word-list `PUT` uses a code another list already has | `{"details": "Wordlist with this code already exists."}` |
| `400` | A default word list outside the check's `wordlist_options` | `details`, naming the allowed options |
| `400` | `logo_id`, `branding_id` or `whitelist_id` on `analysis_update` is `null`, empty or not a number | `{"logo_id": ["This field may not be null."]}` or `{"logo_id": ["A valid integer is required."]}` |
| `400` | Attaching an ID or code that is not in your library, or rescanning a scan whose word list you deleted | For example `{"details": "Logo with this ID does not exist."}` or `{"details": "The supplied wordlist does not belong to the authenticated account."}` |
| `403` | Your plan lacks Domain Monitoring, or your license is not active | `{"detail": "This feature is not included in your plan."}` |
| `404` | The ID is not in your organization's library | — |
| `409` | `start_domain_analysis` while every Monitored Assets slot is in use | `{"detail": "You have reached your Monitored Assets limit of 10.", "resource": "monitored_assets", "limit": 10}`. See [Monitored Assets slots](https://wiki.darknetsearch.com/api/guides/domain-monitoring.md#monitored-assets-slots). |
| `415` | A logo upload sent as JSON | `{"detail": "Unsupported media type \"application/json\" in request."}` |
| `429` | The domain monitoring service's limit for your organization: 10 scan starts an hour, or 180 other requests a minute | `detail` starts with `Request was throttled. Expected available in`, then the wait in seconds |
| `429` | The per-second rate limit or the API Calls allowance | `{"message": "Rate limit exceeded", ...}`. See [Rate limits](https://wiki.darknetsearch.com/api/guides/rate-limits.md). |
| `502` | The domain monitoring service failed | `{"detail": "The requested service is temporarily unavailable."}` |
| `504` | The domain monitoring service timed out | `{"detail": "The requested service timed out."}` |

### Endpoints

| Action | Endpoint |
| - | - |
| List logos | [`GET /api/spoofguard/logos/`](https://client-api.leak.center/scalar-docs/#tag/spoofguard/GET/api/spoofguard/logos/) |
| Upload a logo | [`POST /api/spoofguard/logos/`](https://client-api.leak.center/scalar-docs/#tag/spoofguard/POST/api/spoofguard/logos/) |
| Get a logo | [`GET /api/spoofguard/logos/{logo_id}/`](https://client-api.leak.center/scalar-docs/#tag/spoofguard/GET/api/spoofguard/logos/\{logo_id}/) |
| Rename a logo or replace its image | [`PATCH /api/spoofguard/logos/{logo_id}/`](https://client-api.leak.center/scalar-docs/#tag/spoofguard/PATCH/api/spoofguard/logos/\{logo_id}/) |
| Delete a logo | [`DELETE /api/spoofguard/logos/{logo_id}/`](https://client-api.leak.center/scalar-docs/#tag/spoofguard/DELETE/api/spoofguard/logos/\{logo_id}/) |
| List branding keyword sets | [`GET /api/spoofguard/branding/`](https://client-api.leak.center/scalar-docs/#tag/spoofguard/GET/api/spoofguard/branding/) |
| Create a branding keyword set | [`POST /api/spoofguard/branding/`](https://client-api.leak.center/scalar-docs/#tag/spoofguard/POST/api/spoofguard/branding/) |
| Get a branding keyword set | [`GET /api/spoofguard/branding/{branding_id}/`](https://client-api.leak.center/scalar-docs/#tag/spoofguard/GET/api/spoofguard/branding/\{branding_id}/) |
| Rename a set or replace its keywords | [`PATCH /api/spoofguard/branding/{branding_id}/`](https://client-api.leak.center/scalar-docs/#tag/spoofguard/PATCH/api/spoofguard/branding/\{branding_id}/) |
| Delete a branding keyword set | [`DELETE /api/spoofguard/branding/{branding_id}/`](https://client-api.leak.center/scalar-docs/#tag/spoofguard/DELETE/api/spoofguard/branding/\{branding_id}/) |
| List word lists | [`GET /api/spoofguard/wordlists/`](https://client-api.leak.center/scalar-docs/#tag/spoofguard/GET/api/spoofguard/wordlists/) |
| Create a word list | [`POST /api/spoofguard/wordlists/`](https://client-api.leak.center/scalar-docs/#tag/spoofguard/POST/api/spoofguard/wordlists/) |
| Get a word list | [`GET /api/spoofguard/wordlists/{wordlist_id}/`](https://client-api.leak.center/scalar-docs/#tag/spoofguard/GET/api/spoofguard/wordlists/\{wordlist_id}/) |
| Replace a word list | [`PUT /api/spoofguard/wordlists/{wordlist_id}/`](https://client-api.leak.center/scalar-docs/#tag/spoofguard/PUT/api/spoofguard/wordlists/\{wordlist_id}/) |
| Delete a word list | [`DELETE /api/spoofguard/wordlists/{wordlist_id}/`](https://client-api.leak.center/scalar-docs/#tag/spoofguard/DELETE/api/spoofguard/wordlists/\{wordlist_id}/) |
| List default word lists | [`GET /api/spoofguard/wordlists/defaults/`](https://client-api.leak.center/scalar-docs/#tag/spoofguard/GET/api/spoofguard/wordlists/defaults/) |
| Change a check's default word list | [`PATCH /api/spoofguard/wordlists/defaults/`](https://client-api.leak.center/scalar-docs/#tag/spoofguard/PATCH/api/spoofguard/wordlists/defaults/) |
| List trusted-domain lists | [`GET /api/spoofguard/whitelists/`](https://client-api.leak.center/scalar-docs/#tag/spoofguard/GET/api/spoofguard/whitelists/) |
| Create a trusted-domain list | [`POST /api/spoofguard/whitelists/`](https://client-api.leak.center/scalar-docs/#tag/spoofguard/POST/api/spoofguard/whitelists/) |
| Get a trusted-domain list | [`GET /api/spoofguard/whitelists/{whitelist_id}/`](https://client-api.leak.center/scalar-docs/#tag/spoofguard/GET/api/spoofguard/whitelists/\{whitelist_id}/) |
| Rename a list or replace its domains | [`PATCH /api/spoofguard/whitelists/{whitelist_id}/`](https://client-api.leak.center/scalar-docs/#tag/spoofguard/PATCH/api/spoofguard/whitelists/\{whitelist_id}/) |
| Delete a trusted-domain list | [`DELETE /api/spoofguard/whitelists/{whitelist_id}/`](https://client-api.leak.center/scalar-docs/#tag/spoofguard/DELETE/api/spoofguard/whitelists/\{whitelist_id}/) |
| Start a scan with library items | [`POST /api/service/start_domain_analysis/`](https://client-api.leak.center/scalar-docs/#tag/spoofguard/POST/api/service/start_domain_analysis/) |
| Attach items to an existing scan | [`PATCH /api/service/analysis_detail/{analysis_id}/`](https://client-api.leak.center/scalar-docs/#tag/spoofguard/PATCH/api/service/analysis_detail/\{analysis_id}/) |
| Rescan, optionally with a word list | [`POST /api/service/analysis_rescan/{analysis_id}/`](https://client-api.leak.center/scalar-docs/#tag/spoofguard/POST/api/service/analysis_rescan/\{analysis_id}/) |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.