Skip to main content
BrewMarkBREWMARK
Developer Documentation

Embed brew recipes on your site

Add interactive recipe cards to any website with two lines of HTML, or use our REST API to build custom integrations.

Quick Start

Two lines. Any website.

Add to your HTML
<!-- 1. Include the BrewMark widget script -->
<script src="https://brewmark.io/embed/widget.js"></script>

<!-- 2. Place this where you want the widget -->
<div data-brewmark-widget="your-roaster-slug"></div>

The widget script auto-initializes on page load. Each data-brewmark-widget element becomes an interactive coffee card rendered in a Shadow DOM for full CSS isolation. No API key required.

Configuration

Widget options

Customize the widget with HTML data attributes. No API key required—all public endpoints are open and CORS-enabled.

Data Attributes
data-brewmark-widgetstringrequired

Your roaster slug (e.g. "onyx-coffee-lab"). Find this in your Roaster Portal URL.

data-theme"light" | "dark"

Color theme. Light uses warm paper tones, dark uses deep brown. Default: "light".

data-layout"card" | "badge" | "inline"

Display layout. Cards show full detail, badges are compact pills, inline is a simple list. Default: "card".

data-coffeestring

Filter to a single coffee by slug (e.g. "southern-weather"). Omit to show all coffees.

data-accent-color"#rrggbb"

Custom accent color as hex. Overrides the roaster's saved accent color. Default: "#1D3557".

data-border-radius0-24

Border radius in pixels for cards and the widget container. Default: 4.

data-show-powered-by"true" | "false"

Show or hide the "powered by BrewMark" badge. Premium roasters can hide it via their portal settings.

Widget Features

Shadow DOM isolation
Styles never conflict with your site CSS
Light & dark themes
Match your site with a single attribute
3 layout modes
Card, badge, and inline to fit any context
Zero dependencies
Pure vanilla JS, no framework required
Responsive
Adapts from 280px to 420px max-width
No API key needed
Public endpoints, just add the script tag

Programmatic Re-initialization

If you dynamically add widget elements after page load (e.g., in a single-page app), call the init function manually:

// After adding new data-brewmark-widget elements to the DOM
window.__brewmarkWidgetInit();

Iframe Embed (Alternative)

If you prefer an iframe over the JS widget, use the iframe embed URL directly. Supports the same theme, layout, and coffee parameters as query strings.

<iframe
  src="https://brewmark.io/embed/your-roaster-slug?theme=light&layout=card"
  width="420" height="400"
  frameborder="0" />

Platform Guides

Shopify
  1. Edit product description
  2. Click "Show HTML"
  3. Paste embed code
WordPress
  1. Add Custom HTML block
  2. Paste embed code
  3. Preview and publish
Squarespace
  1. Add Code block
  2. Paste embed code
  3. Uncheck "Display Source"
Live Preview

Try it out

Toggle theme and layout options to see how the widget looks on your site. The embed code updates automatically.

Theme
Layout
Your embed code
<!-- 1. Include the widget script -->
<script src="https://brewmark.io/embed/widget.js"></script>

<!-- 2. Place widget where you want it -->
<div
  data-brewmark-widget="onyx-coffee-lab"></div>
REST API

Public endpoints

Looking for the full API reference with auth, brew logs, user profiles, and all endpoints? View the complete API documentation →

Authentication

All public endpoints are open and require no API key. Requests return JSON and support CORS for cross-origin use. Base URL: https://brewmark.io

GET/api/embed/{roasterSlug}/{coffeeSlug}CORS5 min

Fetch all brew recipes for a specific coffee. Returns roaster info, coffee details, and an array of recipes across brew methods. Best for embed widgets.

Response
roasterobject{ name, slug }
coffeeobject{ name, slug, roastLevel, flavorNotes[] }
recipesarray{ method, doseGrams, waterGrams, ratio, waterTempF, grindSize, brewTimeSeconds, notes, isDefault }
Example
curl https://brewmark.io/api/embed/onyx-coffee-lab/southern-weather

{
  "roaster": { "name": "Onyx Coffee Lab", "slug": "onyx-coffee-lab" },
  "coffee": {
    "name": "Southern Weather",
    "slug": "southern-weather",
    "roastLevel": "Light",
    "flavorNotes": ["Blueberry", "Chocolate", "Citrus"]
  },
  "recipes": [
    {
      "method": "V60",
      "doseGrams": 20,
      "waterGrams": 320,
      "ratio": 16,
      "waterTempF": 205,
      "grindSize": "Medium-Fine",
      "brewTimeSeconds": 210,
      "isDefault": true
    }
  ]
}
GET/api/embed/{shareId}

Fetch a single user recipe by its share ID. Automatically records an impression event for embed analytics.

Response
namestringRecipe name
methodstringPrimary brew method
doseGramsnumberCoffee dose in grams
waterGramsnumberWater amount in grams
rationumberWater-to-coffee ratio
brewTimeSecondsnumber|nullTotal brew time
waterTempFnumber|nullWater temperature (Fahrenheit)
grindSettingstringGrind size description
coffeeNamestring|nullAssociated coffee name
roasterstring|nullRoaster name
brewMethodsarray|nullAlternative brew methods with parameters
GET/api/roasters

List all roasters. Supports search and pagination.

Parameters
searchstringFilter by roaster name
pagenumberPage number (default: 1)
limitnumberResults per page (default: 20)
Example
curl "https://brewmark.io/api/roasters?search=onyx&limit=5"

{
  "roasters": [
    { "id": 1, "name": "Onyx Coffee Lab", "slug": "onyx-coffee-lab", ... }
  ],
  "total": 1,
  "page": 1
}
GET/api/roasters/{slug}

Get a single roaster by URL slug. Returns roaster profile details.

Parameters
slugstringURL-safe roaster identifier
GET/api/roasters/{slug}/coffees

List all coffees from a roaster. Supports filtering by roast level and cursor-based pagination.

Parameters
roastLevelstringFilter: Light, Medium, Dark
cursorstringPagination cursor
limitnumberResults per page (default: 20)
GET/api/roasters/{slug}/recipes

List community-submitted recipes for a roaster's coffees.

Parameters
cursorstringPagination cursor
limitnumberResults per page (default: 20)
GET/api/recipes/{id}

Get a single recipe by numeric ID. Returns full recipe detail including grinder, brewer, and filter information.

GET/api/recipes

Search and list recipes. Returns paginated results.

Parameters
searchstringSearch by recipe name or coffee
methodstringFilter by brew method
GET/api/coffees

List and search coffees across all roasters.

Parameters
searchstringSearch by coffee or roaster name
GET/api/brew-methods

List all available brew methods (V60, AeroPress, Chemex, French Press, etc.).

GET/api/health

Health check endpoint. Returns service status, uptime, and database latency.

Example
curl https://brewmark.io/api/health

{
  "status": "ok",
  "timestamp": "2026-03-12T20:00:00.000Z",
  "uptime": 86400,
  "checks": { "database": { "status": "ok", "latencyMs": 12 } },
  "version": "1.0.0"
}
Embed Analytics

Built-in tracking

The embed widget automatically tracks three event types. View your analytics in the Roaster Portal.

IMPRESSIONautomatic

Fired when the recipe card loads on a page. Includes the HTTP referer for source tracking.

CLICKautomatic

Fired when a user clicks the recipe link to view the full recipe on BrewMark.

METHOD_SWITCHautomatic

Fired when a user switches between brew method tabs. Records which method was selected.

Custom tracking

If you need to fire events from your own code, use the tracking endpoints directly:

// Track a click
fetch("https://brewmark.io/api/embed/track-click", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ shareId: "roaster-slug/coffee-slug" })
})
Technical Details

Rate limits, CORS, and caching

Rate Limits

Public API endpoints are rate-limited per IP. Embed endpoints have generous limits for production widget use. If you hit a rate limit, you'll receive a 429 Too Many Requests response with a Retry-After header.

CORS

Embed API endpoints set Access-Control-Allow-Origin: * so the widget works from any domain. The embed script handles CORS preflight automatically.

Caching

Roaster/coffee embed responses are cached for 5 minutes at the edge with a 10-minute stale-while-revalidate window. When you update recipes in the Roaster Portal, changes appear in embeds within 5 minutes.

Ready to get started?

Generate your embed code in seconds, or claim your roaster profile to manage recipes and track analytics.