# Brand Kit: Logos, Colors & Fonts from Any Website (`urlwright/brand-kit`) Actor

Get the brand kit of any website: logo variants, favicons and touch icons, colors with roles and contrast, fonts, design tokens and brand checks. Files as ZIP. Failed, blocked and duplicate domains are free.

- **URL**: https://apify.com/urlwright/brand-kit.md
- **Developed by:** [Urlwright](https://apify.com/urlwright) (community)
- **Categories:** Developer tools, Marketing, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.50 / 1,000 domain analyzeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Brand Kit: Logos, Colors & Fonts from Any Website

**Urlwright**: *Little tools for the web.*

Get the brand kit of any website: logo variants, favicons and touch icons, colors with roles and contrast, fonts, design tokens and brand checks. Files as ZIP. Failed, blocked and duplicate domains are free.

Default User-Agent: `UrlwrightBot/1.0 (brand-kit; +https://urlwright.com/bot)` (`UrlwrightBot` for robots.txt matching).

### What it does

Give it a list of domains (or any URL of a site), a dataset or a CSV. For each domain you get one dataset row, the brand kit:

1. **Name** from JSON-LD, `og:site_name`, the web app manifest, `application-name` and the page title, with source and confidence.
2. **Logo** ranked from many signals: the header home link (image or inline SVG), JSON-LD `logo`, elements named logo or brand, `<picture>` dark-mode sources, mask icon and touch icon. Logos of customers, partners and payment providers ("Trusted by" strips, footers) are left out. Every logo file is fetched and checked (format, size, transparency, main color). Kind (`symbol`, `wordmark`, `combination`) and color (`light`, `dark`) are reported.
3. **Official logos** from linked brand, press and media kit pages on the same site (`scanBrandPages`), marked `official: true`.
4. **Favicons, touch icons and mask icon**, with declared and real sizes. ICO files are split into their sizes.
5. **Web app manifest**: name, colors, icons with `purpose`.
6. **Colors with roles**: `primary`, `secondary`, `accent`, `background`, `text`, `link`, plus dark mode values. Sources are CSS custom properties, `theme-color`, the manifest, buttons, links, the header and the logo. Output in HEX, RGB, HSL and OKLCH, deduplicated by CIEDE2000.
7. **Contrast pairs** after WCAG 2.2 (text on background, button text on button) with AA and AAA results.
8. **Fonts with roles** (`heading`, `body`, `mono`), source (`google`, `adobe`, `self-hosted`, `system`) and a license hint. **Font files are never downloaded.**
9. **Design tokens** as CSS variables, a Tailwind v4 `@theme` block and W3C design tokens (DTCG JSON), in the row and as files.
10. **Checks** with `pass`, `warn` or `fail`: favicon, SVG favicon, 180 px touch icon, manifest and its 192/512 px and maskable icons, `theme-color`, OG image (reachable, 1200×630), Twitter card, SVG logo, brand color contrast, declared vs. real icon sizes.
11. **Files** (`downloadAssets`): logo, favicons, touch icon, manifest icon and OG image as files and in one ZIP, plus `logo.png` from SVG logos, a square `icon-512.png`, favicon PNGs in 32 and 180 px, a self-contained `brand-sheet.html`, `tokens.css`, `tailwind.css` and `tokens.json`. SVGs are cleaned (no scripts, no event handlers, no external references). Metadata such as EXIF is removed.
12. **Monitor mode**: compares each domain with the last run and lists changes to logo, primary color, name, favicon and fonts. Unchanged files are not stored again and are free.

Static HTML first. With `renderJs: auto` a page is rendered in a browser only when the HTML is an app shell or shows no logo and no colors. Images, fonts, media and trackers are blocked in the browser.

### Who it is for

**CRM and lead enrichment**: a logo, a square icon, the name and the brand color for every domain, as a Clearbit logo alternative · **SaaS onboarding**: prefill a customer's workspace with their colors and logo · **Designers and agencies**: a brand sheet and design tokens of a client site in one run · **SEO and web teams**: favicon, manifest and social image checks across many domains · **Brand monitoring**: weekly runs that report a changed logo or color.

### Input

Store prefill: `startUrls: [{ "url": "https://urlwright.com/" }]`.

**A few domains with files:**

```json
{
  "startUrls": [{ "url": "https://urlwright.com/" }, { "url": "python.org" }],
  "downloadAssets": true,
  "maxAssetsPerDomain": 3
}
```

**A CRM export (CSV or Google Sheets):**

```json
{
  "csvUrl": "https://docs.google.com/spreadsheets/d/YOUR_SHEET_ID/edit",
  "csvColumn": "Website",
  "renderJs": "never",
  "maxDomains": 5000
}
```

**Domains from another run** (Sitemap URL Extractor, Tech Stack Detector):

```json
{
  "datasetId": "YOUR_DATASET_ID",
  "datasetField": "domain"
}
```

**Weekly monitoring:**

```json
{
  "startUrls": [{ "url": "example.com" }, { "url": "example.org" }],
  "monitor": true,
  "monitorStoreName": "my-brand-monitor",
  "downloadAssets": true
}
```

| Field | Default | Meaning |
|---|---|---|
| `startUrls` | | Domains or any URL of the site. Each domain is analysed once, from its home page. |
| `datasetId`, `datasetField` | `url` | Read domains from a dataset (falls back to `url`, `domain`, `finalUrl`). |
| `csvUrl`, `csvColumn` | | Public CSV, TSV or Google Sheets link. Columns named domain, website or url are found automatically. |
| `maxDomains` | 100 | Stop after this many unique domains. |
| `renderJs` | `auto` | `auto`, `always` or `never`. |
| `downloadAssets` | `false` | Store image files and the kit files. |
| `assetTypes` | all | `logo`, `favicons`, `touchIcons`, `manifestIcons`, `ogImage`. |
| `maxAssetsPerDomain` | 8 | Upper limit of billed image files per domain. |
| `normalize` | `true` | PNG versions of the logo and icons. |
| `storeFiles` | `both` | `both`, `zip` or `files`. |
| `scanBrandPages` | `true` | Open a linked brand or press page for the official logo. |
| `maxExtraPages` | 1 | Brand or press pages per domain (0 to 5). |
| `tokenFormats` | all | `css`, `tailwind`, `dtcg`. |
| `monitor`, `monitorStoreName` | `false`, `brand-kit-monitor` | Compare with the last run. |

### Output

#### Dataset row (real example for urlwright.com, shortened)

```json
{
  "schemaVersion": 1,
  "domain": "urlwright.com",
  "finalUrl": "https://urlwright.com/",
  "status": "ok",
  "charged": true,
  "chargedEvents": { "domain-analyzed": 1, "page-rendered": 0, "asset-downloaded": 8, "extra-page": 0 },
  "name": { "value": "Urlwright", "source": "json-ld", "confidence": 0.99 },
  "logo": {
    "primary": { "url": null, "inline": true, "format": "svg", "width": 64, "height": 64, "kind": "symbol", "theme": "dark", "source": "header-inline-svg", "confidence": 0.97, "official": false },
    "alternatives": []
  },
  "colors": {
    "primary": { "hex": "#B33F20", "oklch": "oklch(52.7% 0.157 35.6)", "sources": ["css link color"] },
    "background": { "hex": "#F4F1EA", "oklch": "oklch(95.9% 0.010 87.5)", "sources": ["css-var --bg"] },
    "text": { "hex": "#1B1A17", "oklch": "oklch(21.8% 0.006 91.6)", "sources": ["css-var --text"] }
  },
  "contrast": [{ "pair": "text/background", "fg": "#1B1A17", "bg": "#F4F1EA", "ratio": 15.43, "aa": true, "aaLarge": true, "aaa": true }],
  "fonts": [
    { "family": "IBM Plex Sans", "role": "body", "source": "self-hosted", "weights": [400, 500, 600] },
    { "family": "IBM Plex Mono", "role": "mono", "source": "self-hosted", "weights": [400, 500] }
  ],
  "tokens": { "tailwind": "@theme {\n  --color-brand-primary: #B33F20;\n  --color-brand-accent: #E4502B;\n  …\n}" },
  "checks": [{ "id": "touch-icon-180", "status": "pass", "detail": "touch icon 180×180" }],
  "files": [{ "key": "files-urlwright.com-logo.svg", "name": "logo.svg", "url": "https://api.apify.com/v2/key-value-stores/…", "zipPath": "urlwright.com/logo.svg" }]
}
```

The full row also has `favicons`, `touchIcons`, `maskIcon`, `manifest`, `ogImage`, `brandPages`, `assetUrls`, `changes`, `warnings`, `description`, `language` and `requests`. A logo `theme` of `light` is a light colored logo for dark backgrounds, `dark` a dark logo for light backgrounds.

#### Statuses

`ok` · `partial` (some requests were disallowed by robots.txt) · `no-brand-signal` (no name, or no logo, icon or brand color) · `parked` · `duplicate` · `redirected-duplicate` (redirects to a domain already in the run) · `robots-disallowed` · `blocked` (401, 403, 429, 503) · `dns-error` · `tls-error` · `timeout` · `http-error` · `ssrf-blocked` · `invalid-input`. Every row carries `charged` and, when free, a `skipReason`.

#### Key-value store

| Key | Content |
|---|---|
| `brand-kit.zip` | All files, one folder per domain with `brand.json`, `brand-sheet.html`, `tokens.css`, `tailwind.css`, `tokens.json`, plus `NOTICE.txt`. Very large runs write `brand-kit-part-1.zip`, `brand-kit-part-2.zip`, … (500 MB each). |
| `files-<domain>-<file>` | Single files (`storeFiles` `files` or `both`). |
| `ASSET_URLS.csv` | Domain, kind and URL of every logo, icon and OG image found. Works as CSV input for the Image Downloader. |
| `REPORT.json`, `REPORT.md` | Coverage per field, statuses, failed checks, domains without a logo, low contrast, changes. |
| `RUN_SUMMARY` | Totals, statuses, free reasons and charged events, also after a fatal error. |

### Pipelines

- **Sitemap URL Extractor → Brand Kit**: pass the dataset of a sitemap run, Brand Kit reduces it to unique domains. Check every country and subdomain of a brand for consistency.
- **Tech Stack Detector → Brand Kit**: both use the `domain` field, so the datasets join on it. A lead list with stack, logo and color.
- **Brand Kit → Image Downloader**: `ASSET_URLS.csv` is a ready CSV input; every row also has `assetUrls`.

### Pricing

Pay per event (USD). **No start fee.** You only pay for domains with a brand signal. Recommended minimum **Max total charge** per run: **$0.02**.

| Event | When | FREE | BRONZE | SILVER | GOLD |
|---|---|---:|---:|---:|---:|
| `domain-analyzed` (primary) | Domain with status `ok` or `partial` | 0.0040 | 0.0038 | 0.0036 | 0.0035 |
| `page-rendered` | Page rendered in a browser and loaded | 0.0060 | 0.0055 | 0.0050 | 0.0045 |
| `asset-downloaded` | Each stored image file (original or PNG version) | 0.0008 | 0.0007 | 0.0006 | 0.0006 |
| `extra-page` | Each brand or press page that was read | 0.0010 | 0.0009 | 0.0008 | 0.0007 |

**Per 1,000 (FREE → GOLD):** `domain-analyzed` **$4.00 → $3.50** · `page-rendered` **$6.00 → $4.50** · `asset-downloaded` **$0.80 → $0.60** · `extra-page` **$1.00 → $0.70**. Platinum and Diamond pay the Gold price.

**Example (FREE):** 1,000 domains without files and without rendering: `1,000 × 0.004 = $4.00`. With up to 3 files per domain: at most `$4.00 + 3,000 × 0.0008 = $6.40`.

The brand sheet, token files, `brand.json`, the ZIP, `ASSET_URLS.csv` and the reports are included.

### What is free

- DNS, TLS, timeout and HTTP errors, 401/403/429/503 responses.
- Domains disallowed by robots.txt.
- Parked domains and domains without a brand signal.
- Duplicate domains, also after a redirect to a domain already in the run.
- Failed renderings, brand pages that did not load.
- Files that did not change since the last run in monitor mode.

The run stops cleanly before a domain that would exceed your **Max total charge**; `RUN_SUMMARY` shows `stoppedByChargeLimit`.

### Use responsibly

**Use responsibly.** Logos, names and colors are trademarks of their owners. Brand Kit reads only the public website of each domain you provide, respects robots.txt, and does not log in or bypass any protection. Use the results to identify a company or to work on your own or your client's brand; do not use them in a way that suggests endorsement or affiliation. Font files are never downloaded. No personal data is collected.

- **Robots and rate limits**: `UrlwrightBot` rules apply to the page, stylesheets and every file, also on CDN hosts. At most one request per second per host. A missing robots.txt (404) allows fetching; server errors on robots.txt count as disallowed.
- **Request cap**: in the default mode a domain costs at most 11 requests (robots.txt, home page, manifest, up to 4 stylesheets, up to 4 file checks). Files, brand pages and rendering come on top only when you turn them on.
- **SSRF protection**: private, link-local and metadata addresses are blocked after DNS resolution and after redirects.
- **No personal data**: no emails, phone numbers, people, authors or social profiles. JSON-LD is read for organisations only.

More tools: [urlwright.com](https://urlwright.com) · Bot details: [urlwright.com/bot](https://urlwright.com/bot)

### Limitations

- **Passive only**: no login, cookies, captcha bypass or proxy rotation. Sites with strict bot protection return free `blocked` rows.
- **Home page first**: the kit comes from the home page, its stylesheets and linked files. Logos that only appear after interaction are not found.
- **Colors are inferred** from CSS, meta tags and the logo. A site that uses many accent colors may get a different `secondary` than its style guide.
- **Fonts**: names come from CSS and font services. Licenses are hints, not legal advice.
- **Size limits**: HTML 5 MB, CSS 2 MB per file, files 10 MB.

### FAQ

#### Is this a Clearbit logo API alternative?

For bulk work, yes: one run gives a verified logo, a square `icon-512.png`, the name and the primary color for every domain, as files and as JSON. It is a batch actor, not a hosted image URL.

#### Why is a domain `partial`?

Some requests (often a stylesheet or a CDN host) were disallowed by robots.txt. The row is billed once like `ok`, and `warnings` says what was skipped.

#### Why did a domain get `no-brand-signal`?

No name could be found, or neither a logo, an icon nor a brand color. The row is free.

#### Does rendering load images and fonts?

No. Images, fonts, media and trackers are blocked in the browser, and robots.txt is checked for every request the page makes.

#### What is in `brand-sheet.html`?

A single HTML file with the logo, icons, colors with contrast, fonts and tokens of a domain. All images are embedded, so it works offline.

#### How does monitor mode know what changed?

State per domain is kept in a named key-value store in your account. Logos are compared by hash and perceptual hash, the primary color by CIEDE2000 (a difference above 5 counts), plus name, favicon and fonts.

### No warranty

Results are generated automatically from public website signals. They can be incomplete or wrong. They are provided as-is, without warranty. Use them at your own judgment; Urlwright accepts no liability for decisions based on them.

# Actor input Schema

## `startUrls` (type: `array`):

Domains or any URL of the site (example.com, https://www.example.com/about). Each domain is analysed once, from its home page.

## `datasetId` (type: `string`):

Read domains from a dataset, for example the output of Sitemap URL Extractor or Tech Stack Detector.

## `datasetField` (type: `string`):

Field on the dataset rows that holds the domain or URL. Falls back to url, domain and finalUrl.

## `csvUrl` (type: `string`):

Public CSV, TSV or Google Sheets link with one domain per row. Columns named domain, website or url are found automatically.

## `csvColumn` (type: `string`):

Column with the domains. Leave empty to detect it.

## `maxDomains` (type: `integer`):

Stop after this many unique domains.

## `renderJs` (type: `string`):

auto renders a page in a browser only when the HTML is an app shell or has no logo or colors. Rendering is billed per page. never keeps every domain on plain HTTP.

## `downloadAssets` (type: `boolean`):

Store the logo, favicons, touch icon, manifest icon and OG image as files and in a ZIP, plus PNG versions, a brand sheet and token files. Each stored image file is billed.

## `assetTypes` (type: `array`):

Which files to store when Download files is on.

## `maxAssetsPerDomain` (type: `integer`):

Upper limit of billed image files per domain.

## `normalize` (type: `boolean`):

Add logo.png from SVG logos, a square icon-512.png and favicon PNGs in 32 and 180 px.

## `storeFiles` (type: `string`):

Single files in the key-value store, one ZIP, or both.

## `scanBrandPages` (type: `boolean`):

Open a linked brand, press or media kit page on the same site to find the official logo. Billed per page.

## `maxExtraPages` (type: `integer`):

Brand or press pages opened per domain.

## `tokenFormats` (type: `array`):

Design token formats in the dataset and the kit: CSS variables, Tailwind v4 @theme and W3C design tokens (DTCG JSON).

## `monitor` (type: `boolean`):

Compare with the last run and list changes to logo, primary color, name, favicon and fonts. Unchanged files are not stored again and are free.

## `monitorStoreName` (type: `string`):

Named key-value store that keeps the state between runs.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://urlwright.com/"
    }
  ],
  "datasetField": "url",
  "maxDomains": 3,
  "renderJs": "auto",
  "downloadAssets": true,
  "assetTypes": [
    "logo",
    "favicons",
    "touchIcons",
    "manifestIcons",
    "ogImage"
  ],
  "maxAssetsPerDomain": 3,
  "normalize": true,
  "storeFiles": "both",
  "scanBrandPages": true,
  "maxExtraPages": 1,
  "tokenFormats": [
    "css",
    "tailwind",
    "dtcg"
  ],
  "monitor": false,
  "monitorStoreName": "brand-kit-monitor"
}
```

# Actor output Schema

## `brands` (type: `string`):

Dataset items: one brand kit per domain.

## `zip` (type: `string`):

All files with one folder per domain (when Download files is on).

## `assetUrls` (type: `string`):

Every logo, icon and OG image URL found.

## `runSummary` (type: `string`):

Status counts, free reasons and charged events.

## `report` (type: `string`):

Coverage, failed checks and changes.

## `reportMd` (type: `string`):

Human-readable run report.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "startUrls": [
        {
            "url": "https://urlwright.com/"
        }
    ],
    "maxDomains": 3,
    "downloadAssets": true,
    "maxAssetsPerDomain": 3
};

// Run the Actor and wait for it to finish
const run = await client.actor("urlwright/brand-kit").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "startUrls": [{ "url": "https://urlwright.com/" }],
    "maxDomains": 3,
    "downloadAssets": True,
    "maxAssetsPerDomain": 3,
}

# Run the Actor and wait for it to finish
run = client.actor("urlwright/brand-kit").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "startUrls": [
    {
      "url": "https://urlwright.com/"
    }
  ],
  "maxDomains": 3,
  "downloadAssets": true,
  "maxAssetsPerDomain": 3
}' |
apify call urlwright/brand-kit --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,urlwright/brand-kit"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/azszD9psaATTfCJuR/builds/CBrY4vqUCUxq9nZ7U/openapi.json
