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.

GEThttps://rankfield.dev/v1/brand?domain={domain}

Send Authorization: Bearer YOUR_API_KEY. Get a free key; no account needed.

Parameters#

NameTypeDescription
domainquery, required unless urlstringThe company's domain, e.g. acme.com. A URL works too: https://acme.com/about means acme.com. Send domain or url, not both.
urlquery, optionalstringInstead of domain: a full URL on the site. We read the homepage, not this page.
rolesquery, optionalbooleandefault falsetrue 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.
screenshotquery, optionalstringviewport or fullPage: also return a WebP image of the homepage as a browser shows it.
viewportquery, optionalstringdefault desktopdesktop (1366×768) or mobile (412×915): the screen for the screenshot.
maxAgequery, optionalintegerdefault 86400Seconds a stored answer may be old (default 24 hours), up to 2,592,000. 0 reads the site fresh.
timeoutquery, optionalintegerdefault 20Seconds the call may take, 1 to 60.

Response#

FieldTypeDescription
domain finalUrl statusstring, string, integerThe site we read, the homepage that answered after redirects, and its HTTP status. A 404 or 500 comes back here with empty brand fields.
blockedobject | nullThe firewall that answered instead of the homepage ({vendor, challenged, kind, signals}). Then every brand field is empty: we never guess.
name descriptionobject{value, source}. Sources, best first: schema.org Organization, meta tags, the web app manifest, the page title.
logos logosReasonarray, string | nullThe 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.
wordmarkobject | 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 imagesobjectbest, 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.
colorsobjectthemeColor, 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.rolesReasonobject | null, object | null, string | nullThe 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.
fontsarray{family, source, uses}: Google Fonts, @font-face, Adobe Fonts, then fonts the CSS names.
typographyobject | nullThe 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.
socialarray{network, url, source} from schema.org sameAs and links on the page. We never visit them.
contactobjectEmail, phone, address and contact points, only from the site's own schema.org data; null values otherwise.
language fetchesobject, arrayThe homepage's language, and each request we made for this answer.
screenshotobjectWith screenshot: a WebP image, {format, kind, viewport, bytes, data} (base64).
cached ageSecondsboolean, integerWhether the answer came from our store, and its age.

Errors#

Errors come as {"error":{"code":"…","message":"…","details":{…}}} with the HTTP status.

StatusCodeWhen
400invalid_inputA parameter is missing, unknown or malformed.
401unauthorizedThe key is missing, unknown or revoked.
403robots_disallowedrobots.txt disallows Rankfield-User for the homepage; details has the rule and line.
403blockedThe website's bot protection refused us. Reported at once and never worked around; details says which protection. Not counted as usage.
403site_excludedThe 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.
415unsupported_content_typeThe homepage is not HTML.
422address_not_publicThe host resolves to a private or internal address.
429busyOur 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.
500internalOur bug. Retry.
502fetch_failedThe website could not be reached (DNS, connection, TLS, more than 5 redirects or a bad one, a malformed answer); details.reason says which.
503unavailableThe service is restarting or briefly unavailable. Retry after Retry-After. (A full fetch capacity answers 429 busy.)
504timeoutThe 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 429 busy with Retry-After.
  • Only answered calls count as usage (plus the website errors listed below); our own errors (500, 503), 429 busy and a 504 with details.reason sitePace don'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.ico when 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#