Brand API
Returns a company's identity from its domain: name and description, logos and icons, share images, brand colors and fonts, the type scale for body text, headings, buttons and links, social profiles and the contact details it publishes for machines. Every value says where on the site it came from.
Need a page's content rather than the company's identity? Use Scrape.
https://rankfield.dev/v1/brand?domain={domain}Send Authorization: Bearer YOUR_API_KEY. Get a free key; no account needed.
Parameters#
| Name | Type | Description |
|---|---|---|
domain | string | The company's domain, e.g. acme.com. A URL works too: https://acme.com/about means acme.com. Send domain or url, not both. |
url | string | Instead of domain: a full URL on the site. We read the homepage, not this page. |
roles | boolean | true computes the colour roles on a large page too. They are computed by default on pages up to about 6,000 tags and 1.6 MB of CSS (four times the earlier limit); on a larger page colors.roles is null with a reason unless you ask. On a very large page even roles=true can come back null with over the CPU budget. |
screenshot | string | viewport or fullPage: also return a WebP image of the homepage as a browser shows it. |
viewport | string | desktop (1366×768) or mobile (412×915): the screen for the screenshot. |
maxAge | integer | Seconds a stored answer may be old (default 24 hours), up to 2,592,000. 0 reads the site fresh. |
timeout | integer | Seconds the call may take, 1 to 60. |
Response#
| Field | Type | Description |
|---|---|---|
domain finalUrl status | string, string, integer | The site we read, the homepage that answered after redirects, and its HTTP status. A 404 or 500 comes back here with empty brand fields. |
blocked | object | null | The firewall that answered instead of the homepage ({vendor, challenged, kind, signals}). Then every brand field is empty: we never guess. |
name description | object | {value, source}. Sources, best first: schema.org Organization, meta tags, the web app manifest, the page title. |
logos logosReason | array, string | null | The site's own logos, best first: {url, source, confidence, type, width, height, variant, fallback}, starting with the one the page shows in its header (inline SVGs as data: URLs with their colors). Never a favicon or share image, with one exception: when the page has no logo image, the site's best icon stands in as the only entry, with fallback: true. variant is light (drawn for dark backgrounds), dark or null. logosReason says why the list is empty or holds only the fallback. |
wordmark | object | null | {text, source}: the site's name as the page sets it in type in its link home (KYLIAN on kylian.ai), when that link holds no image and names the site. null otherwise. |
icons images | object | best, the one icon to show the site by (an SVG, else the largest; null when none is declared), then favicons, Apple touch icons, manifest icons and the mask icon; the og:image and Twitter image. |
colors | object | themeColor, the manifest's colors, CSS custom properties holding colors, and mostUsed colors weighted by where they paint (score, seen), each with a hex. stock marks a framework's default colors (WordPress, Bootstrap, Tailwind, shadcn/ui), ranked after the site's own. |
colors.roles colors.rolesDark colors.rolesReason | object | null, object | null, string | null | The colors the page's own elements get: primary, accent, background, text and headerBackground, each {hex, from, element, method: "computed"}, and the same in dark mode when the site has one. On a large page roles is null unless you send roles=true; rolesReason says why. |
fonts | array | {family, source, uses}: Google Fonts, @font-face, Adobe Fonts, then fonts the CSS names. |
typography | object | null | The type scale at 1280×800, from the site's own stylesheets: body, h1 to h6, button and link, each with family, size, weight, lineHeight and letterSpacing. Each value is {value, declared, px, basis, source, selector, media, inherited, reason}, with number for weight and primary and generic for family. null on a blocked or error page. |
social | array | {network, url, source} from schema.org sameAs and links on the page. We never visit them. |
contact | object | Email, phone, address and contact points, only from the site's own schema.org data; null values otherwise. |
language fetches | object, array | The homepage's language, and each request we made for this answer. |
screenshot | object | With screenshot: a WebP image, {format, kind, viewport, bytes, data} (base64). |
cached ageSeconds | boolean, integer | Whether the answer came from our store, and its age. |
Errors#
Errors come as {"error":{"code":"…","message":"…","details":{…}}} with the HTTP status.
| Status | Code | When |
|---|---|---|
400 | invalid_input | A parameter is missing, unknown or malformed. |
401 | unauthorized | The key is missing, unknown or revoked. |
403 | robots_disallowed | robots.txt disallows Rankfield-User for the homepage; details has the rule and line. |
403 | blocked | The website's bot protection refused us. Reported at once and never worked around; details says which protection. Not counted as usage. |
403 | site_excluded | The site is on the exclusion list: Reddit, LinkedIn, Facebook, Instagram, X/Twitter and TikTok (subdomains included), Google search result pages, and sites whose owners asked us. |
415 | unsupported_content_type | The homepage is not HTML. |
422 | address_not_public | The host resolves to a private or internal address. |
429 | busy | Our fetch capacity is busy: the call was refused at once, waited 4 seconds without a turn, or its time limit ran out while it waited (then details.reason is capacity). A new job also gets it when the shared job queue is full (Retry-After 60). Not counted as usage. Retry after Retry-After seconds. |
500 | internal | Our bug. Retry. |
502 | fetch_failed | The website could not be reached (DNS, connection, TLS, more than 5 redirects or a bad one, a malformed answer); details.reason says which. |
503 | unavailable | The service is restarting or briefly unavailable. Retry after Retry-After. (A full fetch capacity answers 429 busy.) |
504 | timeout | The website did not answer within timeout. With details.reason: "sitePace", the call waited its turn for this website instead, which we fetch gently, shared by all customers. Retry after Retry-After, or allow a longer timeout. |
Price and limits#
- Free; no per-call charge.
- No per-key limits. When the server is busy you get
429busywithRetry-After. - Only answered calls count as usage (plus the website errors listed below); our own errors (
500,503),429busyand a504withdetails.reasonsitePacedon't.
Up to 10,000 domains in one job#
POST /v1/brand/jobs with an Idempotency-Key header and {"items":[{"domain":"acme.com"}, …]}. Each item takes the same parameters as one call, except timeout: items use the default, 20 seconds. Every item is checked first: if any is invalid, the whole request is a 400 that lists the first 20. A new job answers 202; the same Idempotency-Key with a byte-identical body returns the same job (200, Idempotency-Replayed: true), and with any other body is a 409.
Read GET /v1/brand/jobs/{id}/results?cursor=0&limit=100: finished items as they complete, each {index, ok, result} or {index, ok: false, error}. Pass cursor back until hasMore is false. POST /v1/brand/jobs/{id}/cancel stops a job. A job runs 16 items at a time; an item that meets busy waits and retries by itself. Results are kept 24 hours.
Send as many jobs as you like. Jobs from all keys share one queue: when 200,000 items are already waiting, a new job answers 429 busy with Retry-After (60 seconds).
Reference: https://rankfield.dev/docs/get-started/batch-jobs
Notes#
- We read the homepage, its web app manifest, up to 4 of its own stylesheets (on its own host or its CDN) and
/favicon.icowhen the page names no icon: 8 requests at most, robots.txt included. - Colour roles are computed on the page as fetched, without JavaScript: a page whose buttons get their colours from script gets the colours of its static HTML and CSS.
- We fetch as
Rankfield-User, obey robots.txt on every request and redirect, run no JavaScript unless the call asks for it, use no cookies or proxies, and never work around a firewall. We fetch each site gently, shared by all customers. More: rankfield.dev/bot.
See also#
- Full reference: https://rankfield.dev/docs/brand/get-a-brand
- MCP tools on
https://rankfield.dev/mcp:brand_profile,start_brand_job,get_brand_results - Try it with your key: the API playground
- This page as Markdown: /endpoints/brand.md