# Creative DTC API

Human-readable. Machine-readable.

# One collection.Every way in.

The same products, editorial tags, and sources you see in the gallery. Read the entire catalog or a single record, without a key.

All products. Stable IDs, explicit unknowns, source links, and a content revision. No pagination.

A single record. Also available as readable Markdown.

Eight editorial design lenses, their definitions, and product counts.

## Start with the contract

Agent guide · JSON Schema · OpenAPI 3.1 · llms.txt

## Find a specific idea

The browser supports shareable searches, categories, and multiple design lenses. All selected lenses must match.

Solid, concentrated personal care · Compact objects, by brand

The JSON endpoints are static files: query parameters do not filter them. Fetch the full catalog and filter locally, or use the browser controls.

## Know what is unknown

This is an editorial snapshot dated 13 October 2026. Current price and stock are unknown; null does not mean free. Updates are manual. Design lenses describe editorial groupings, not certifications or independently tested claims.

## Access and boundaries

Public feeds support read-only discovery. Private saved products require sign-in and user authorization. No checkout or partner mutation API is provided. Photography remains the property of the brands; no training license is granted here.

## Try a real request

Use the origin hosting this page. For local development:

curl --fail-with-body -i 'http://localhost:4173/api/products/memobottle.json'

Filter the complete catalog locally. This example checks HTTP failure, enforces a timeout, and verifies the declared count:

// Runs unchanged in Node 24+ or a browser on any origin (public data only).
export async function findProducts(base, {tags = ['solid', 'concentrate'], category = 'personal-care'} = {}) {
  const response = await fetch(new URL('/api/products.json', base), {signal: AbortSignal.timeout(10000)});
  if (!response.ok) throw new Error(`Catalog request failed: HTTP ${response.status}`);
  const catalog = await response.json();
  if (!Array.isArray(catalog.products) || catalog.count !== catalog.products.length) throw new Error('Incomplete catalog response');
  return catalog.products.filter(product => product.category === category && tags.every(tag => product.tags.includes(tag)));
}

Download the runnable JavaScript module. In Node, import it and call findProducts('http://localhost:4173'); in a browser use findProducts(location.origin). It returns all personal-care records matching both lenses. Empty results are a valid empty array.

## Response contract

{"error":{"code":"not_found","message":"Resource not found. Use the catalog for valid product slugs."},"catalog":"/api/products.json"}

API errors are JSON on the bundled server; missing human pages are HTML. Other static hosts may return HTML errors, so check the status and content type before decoding. No writes or credentials are accepted by the public catalog interface.

## Cache without losing updates

Store the response body and its ETag. Send that validator in If-None-Match on the next request. A 200 replaces both; a 304 preserves both. Weak and comma-separated validators are supported. Cache-Control is no-cache: storing is allowed, but revalidate before reuse.

The body’s revision changes with product content. It is portable across hosts and is not an HTTP ETag. Updates are manual, with no refresh guarantee. verified_on is a legacy snapshot date, not independent verification; consult each source’s checked_at, which may be null.

## Browser access and compatibility

Public data and guide endpoints allow anonymous cross-origin GET/HEAD requests and expose ETag. Do not attach credentials. Account pages and private collections are outside this interface. CORS, conditional requests, and JSON errors are verified for the bundled Node server; test your deployment host before relying on them.

Category IDs, display names, and counts are available at /api/categories.json. Preserve unknown fields when storing records. Slugs and numeric IDs identify catalog entries, not purchasable variants. Current schema version: 4.1.0.

## Brand research & rankings

Brand directory · Provisional ranking feed · Scoring methodology

Six provisional scores from seven researched brands. Each dimension cites example IDs; incomplete reviews have a null total. Scores are editorial judgments, not performance measurements. Read scope, origin status and methodology_version alongside any score.

## From a question to a shortlist

The agent quickstart includes a downloadable CLI, a discovery skill, and a standalone JavaScript client. All use the same search rules as this website.

Browse task-based guides, build a shareable comparison, or retrieve the complete content index and plain-text collection. Public reading needs no account or API key.
