Nobody has to add a brand first.
The question people bring to a brand API is whether the domains they need are in it. That question does not apply here, and the reason is the whole product.
- a database of brands
- somebody adds each brand to it
- you get what they added, until somebody updates it
- so the question is
- “is the domain I need in there?”
- and the answer is
- as old as the last edit
- a reader, pointed at a page
- you ask for a domain
- we open the site and read it, and the answer is the page as it is at the moment you asked
- so there is no question
- every site that serves a page
- and every answer is dated
- extracted_at
Try one nobody would have bothered to add. Sample a domain.
Anywhere your product shows another company.
The screens where a bare name becomes that company's own logo, colour and type.
Ask for a work email and the first screen already carries their logo and their colour. Activation moves because the product looks like it was set up for them.
Every account, lead and company record with a real mark instead of two grey initials. One call at write time, cached from then on.
Proposals, invoices and slides that pick up the recipient's brand at render time — colour roles, type and spacing, not just a logo drop.
A generator needs more than a hex triple. Roles, contrast pairs, the faces that actually load and what to substitute when they do not.
Read from these pages, cached for the next caller.
The most recent brands anyone asked for. None of them were submitted to us — each one is a page somebody pointed us at, held so the next caller reads it for nothing.
If we've already read your site, you can take it over.
Every brand we publish was read from the company's own pages, not submitted by anyone — which means the record is only ever as current as the last time we looked. Prove you control the domain and that stops.
Name the domain and an address on it. No DNS, no code — we send a link, you click it.
The record freezes where you leave it. Nothing we run measures that page again.
Correct the logo, the colours, the copy — one field at a time, from your own dashboard.
Claim your brand — free, and always will be.
85 properties, from the hex roles to the tone of voice.
Every field is what the brand publishes on its own pages, measured from the page as it renders rather than read off the stylesheet, and versioned so a redesign never silently changes shape underneath you.
- name
- tagline
- description
- industry
- audience
- positioning_statement
- values
- hex
- role
- on
- names
- coverage
- family
- role
- availability
- font_confidence
- url
- svg
- on_dark_url
- on_light_url
- source
- description
- dominant_colors
- luminance
- has_transparency
- contrast_profile
- background_color
- theme
- corner_style
- headline_transform
- headline_tracking
- headline_weight
- shadow_style
- uses_gradients
- border_style
- photography_treatment
- density
- aesthetic_tags
- headline_letter_spacing
- headline_font_weight
- border_radius
- box_shadow
- icon_style
- illustration_style
- photography_subjects
- photography_composition
- photography_color_grading
- patterns
- personality
- intensity
- speed
- easing_style
- signature_effects
- detected_libraries
- has_scroll_animations
- has_hover_effects
- reduced_motion_support
- base_unit
- scale
- content_margin
- contrast_target
- min_font_size_px
- colorblind_safe
- tone
- voice
- preferred_terms
- avoided_terms
- cta_style
- headline_case
- tone_by_context
- company_summary
- known_for
- design_personality
- design_conventions
- slide_guidance
- brand_values
- inferred_audience
- writing_style
- confidence
- confidence_per_field
- confidence_signals
- gaps
- platform
- screenshot_blob_url
- schema_version
Three of those groups are the ones you were expecting. The rest are what a generator needs and a decoration does not.
Reading the CSS is not the same as seeing the page.
Most extractors parse stylesheets and rank colours by how often they appear in the source. That breaks in ways your users notice, because a modern page paints almost nothing the way it declares it.
Dead tokens
Design systems ship dozens of custom properties that never get painted. Source readers return them as brand colours. Measurement never sees them, so it never reports them.
Gradients
A hero gradient has no single hex. Parsers pick a stop or average the ramp and return a colour the brand does not use. We sample the painted region and report what dominates it.
Weighting
Frequency in source is not prominence on screen. A border colour declared in forty rules loses to an accent painted across a fifth of the viewport. Roles come from rendered area and position.
Fonts
The stack is a wish list, not the result. A declared family means nothing if the file fails to load or the glyphs fall through. We report the face that rendered, with the fallback that actually resolved.
Six passes over one page, and nothing returned unverified.
The candidates come from measurement. A model chooses among them — it is shown the page as it rendered, not the source, and asked one question at a time. Then measurement checks the choice, and if the model does not answer, a deterministic result is already waiting. Ranking is measurement, naming is judgement, and the two are kept apart so a wrong name can never move a right hex.
- 01renderwe open the site in a real browser and let it finish painting
- 02placewe recognise the platform the site is built on and read what it publishes about itself
- 03readfour readings of one page — colour, type, logo, voice — each answering one question alone
- 04reconcilewhere the readings agree, that is the answer. Where they do not, the score says so
- 05verifyevery candidate is checked before it is returned. Most are not returned
- 06scoreconfidence per field, and the fields nobody could resolve, named
What verification looks like
One page's logo candidates, and what happened to each. The rejections are the work — a mark returned is a mark that survived them.
We tell you when we are not sure.
A brand read from a clean, well-marked page is not the same result as one pieced together from a page that fought the render. Both come back; only one says it is sure.
Every extraction scores itself
- confidence — the overall figure, 0 to 1
- confidence_per_field — the same, per extracted field
- gaps — fields no specialist could resolve, named
- confidence_signals — what drove the number, in words
Set a floor in your own code and fall back to your defaults below it, rather than shipping a guess to your customer. A low score is reported, never hidden and never rounded up.
And when we cannot read a page at all
Some pages will not be read — they block automated browsers, sit behind a login, or never resolve. When that happens we say so.
We never hand you someone's guess instead. The useful answer to a page we could not read is that we could not read it — an answer you can branch on, rather than a plausible brand you would have shipped to your customer.
One versioned shape, with the evidence attached.
Every extraction reports how sure it is, per field, and names the fields it could not resolve. Set a floor in your own code and fall back to your defaults below it, rather than shipping a guess to your customer.
$ curl https://api.extractbrand.dev/v1/brands/helio.com \
-H "Authorization: Bearer YOUR_API_KEY"
{
"object": "brand",
"domain": "helio.com",
"cache_status": "hit",
"extracted_at": "2026-07-30T09:14:22.104Z",
"expires_at": "2026-08-29T09:14:22.104Z",
"brand": {
"name": "Helio",
"tagline": "Power that follows the sun",
"positioning_statement": "Helio sells rooftop solar to homeowners who …",
"colors": [
{ "hex": "#f2542d", "role": "primary", "on": "#14161a",
"names": ["--brand-ember"], "coverage": 0.11 },
{ "hex": "#0e1e2b", "role": "secondary", "on": "#ffffff",
"names": [], "coverage": 0.07 },
{ "hex": "#ffffff", "role": "background", "on": "#14161a",
"names": ["--surface"], "coverage": 0.62 },
{ "hex": "#14161a", "role": "text", "on": "#ffffff",
"names": [], "coverage": 0.09 },
{ "hex": "#c8ccd0", "role": null, "on": "#14161a",
"names": [], "coverage": 0.03 }
],
"fonts": [
{ "family": "Tiempos", "role": "display", "availability": "custom-webfont" },
{ "family": "Inter", "role": "body", "availability": "google" }
],
"logo": {
"url": "https://helio.com/logo.svg",
"on_dark_url": "https://helio.com/logo-light.svg",
"source": "inline-svg",
"dominant_colors": ["#f2542d"], …
},
"visual_style": { "theme": "light", "corner_style": "rounded", … },
"motion": { "intensity": "subtle", "speed": "moderate", … },
"copywriting": { "cta_style": "imperative — Get, Start, Book", … },
"tone": ["direct", "warm"],
"confidence": 0.94,
"gaps": [],
"schema_version": "v2"
},
"quota": { "used": 128, "limit": 5000, "period_end": "2026-08-01T00:00:00.000Z" }
}An HTTP call, or one line in your agent's config.
The REST surface is the contract. The hosted MCP server is the same three operations behind the same key, the same cache and the same meter — so an agent that adds it can read a brand without anyone writing the fetch.
Call it yourself
One GET per domain, cached from then on. The full request and response shape is in the reference.
curl https://api.extractbrand.dev/v1/brands/helio.com \
-H "Authorization: Bearer YOUR_API_KEY"Hand it to your agent
Streamable HTTP at https://api.extractbrand.dev/v1/mcp, authenticated with the same key. Paste this into Claude Code, or point Cursor and VS Code at the same endpoint.
claude mcp add extractbrand --transport http https://api.extractbrand.dev/v1/mcp --header "Authorization: Bearer YOUR_API_KEY"- get_brand — Read a domain's brand — logos, colour roles, type, and the confidence behind each.
- refresh_brand — Re-extract a domain whose site has changed since we last read it.
- get_usage — Check the key's reads against its allowance before spending another.
Metered on calls, not on seats.
One unit per answered call, and nothing per seat. A domain nobody has sampled takes a browser, two model calls and a few seconds; one we already hold comes back in milliseconds — the wait differs, the price does not. Growth is $20 a month with 500 answered calls included, then $0.01 a call — billed in your local currency at checkout. Every answered call counts, whether we extract the brand for you or serve one we already hold.
10 calls a month
- Every answered call counts as one
- No card to start
- Full response shape, no trimmed fields
- On-demand refresh needs a paid plan
$20 a month
- 500 answered calls included
- $0.01 per call after that, new domain or not
- Set your own monthly ceiling
- Refresh a brand on demand
Volume and platform terms
- Committed-volume pricing
- Hosted MCP server for your agents
- Direct line to the people who built it

