The Brand Object

JSON or Markdown?

When to ask a brand context API for Markdown and when to keep JSON: Brandfetch offers both, Rainbrand's guidance is Markdown inside JSON, the rest is JSON.

If the whole response goes straight into a prompt, use Markdown. If your code picks fields, merges sources, or stores them to compare later, use JSON. Most endpoints offer only JSON. Two endpoints give you Markdown in some form.

Endpoint Formats
Brandfetch Brand Context API JSON by default. Accept: text/markdown returns the same profile as a Markdown document, without the empty fields
Rainbrand Brief JSON, with the task's guidance as a Markdown string in guidance
Rainbrand Brand Skill JSON, with each guidance file's Markdown in files[].content
Brandfetch Brand API, Context.dev, Firecrawl, Logo.dev JSON only

Asking Brandfetch for Markdown

curl https://api.brandfetch.io/v2/context/linear.app \
  -H "Authorization: Bearer $BRANDFETCH_API_KEY" \
  -H "Accept: text/markdown"

The Brandfetch documentation says that the Markdown starts with the brand's name as a heading. The domain and the date of the context come next. The sections that follow are the same as in the JSON, in the same order.

When Markdown is the wrong choice

  • You need to drop fields. For example, a cold email does not need the competitor list. To remove a section from Markdown, you must parse the Markdown again.
  • You combine sources. In JSON, a merge of the Brandfetch voice with the Context.dev colors takes a few lines. In prose, the same merge is difficult.
  • You store the context and compare it later. Dates and versions are easier to read from keys than from a heading.

Turning JSON into Markdown yourself

export function toMarkdown(name: string, sections: Record<string, string | string[] | undefined>): string {
  const out = [`# ${name}`];
  for (const [title, value] of Object.entries(sections)) {
    if (!value || (Array.isArray(value) && value.length === 0)) continue;
    out.push("", `## ${title}`, "");
    out.push(Array.isArray(value) ? value.map((v) => `- ${v}`).join("\n") : value);
  }
  return out.join("\n");
}

// toMarkdown("Linear", { Voice: c.brand.voice?.summary, Avoid: c.brand.voice?.avoid })
def to_markdown(name: str, sections: dict) -> str:
    out = [f"# {name}"]
    for title, value in sections.items():
        if not value:
            continue
        out += ["", f"## {title}", ""]
        out.append("\n".join(f"- {v}" for v in value) if isinstance(value, list) else value)
    return "\n".join(out)

Leave out empty sections. A model reads a heading with nothing under it as "this brand has no rules". An empty field does not mean that.

Last updated 11 November 2026