# 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

```bash
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

```ts
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 })
```

```python
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.

---

Source: https://brandcontextapi.com/recipes/json-or-markdown
Last updated: 2026-11-11
Published by The Brand Object. The Brand Object is run by Rainbrand, which makes two of the endpoints described here. Their pages follow the same rules as the others.
