Getting started

Five minutes from key to first call.

Recipe Pub is HTTP, JSON, and bearer tokens. If you have written one fetch call you have written all of Recipe Pub. This page is the curated tour: auth, the five endpoints that matter, and the patterns most callers settle into within a week.

For the live API reference and playground, see the interactive docs. This page is the human-paced quickstart.

Get a key

Subscribe and the key arrives in your inbox within seconds. Keys are prefixed with rp_ and you pass them as a bearer token. There is no separate dashboard, no rotation ceremony, no client secret. If a key is compromised, email patrick@recipekit.com and we will revoke it and issue a new one within the business day.

Free-tier exploration is also available. Use /v1/topics and /v1/categories without a key — they are unauthenticated for the catalog. Paid endpoints return 401.

Hello, Recipe Pub

The smallest useful call is the topic catalog. No auth, returns every scoreable topic with its category:

curl https://recipepub.com/v1/topics

Returns something like:

{
  "data": [
    { "topic": "bbq",        "category": "bbq",        "category_keyword_count": 7  },
    { "topic": "hot-honey",  "category": "condiments", "category_keyword_count": 25 },
    { "topic": "tofu",       "category": "general",    "category_keyword_count": 4  }
  ],
  "meta": { "count": 40, "generated_at": "2026-05-28T18:00:00Z" }
}

The five endpoints that matter

Most callers use only these five. Everything else is supporting cast.

1. GET /v1/topics/{topic} — the headline call

Type any topic from the catalog and get the whole picture: trend score and contributions, persistence, network engagement / quality / conversion scores, search volume band and rising queries, press peak status, active recalls. Free tier returns verdict-only; paid returns the full shape.

# curl
curl https://recipepub.com/v1/topics/tofu \
  -H "Authorization: Bearer rp_yourkey"
// Node
const res = await fetch("https://recipepub.com/v1/topics/tofu", {
  headers: { Authorization: "Bearer " + process.env.RECIPE_PUB_KEY }
});
const { data } = await res.json();
console.log(data.verdict.recommendation);
// → "Publish this week. Search is leading press — the window is open. Add-to-cart rate runs 1.8x the median..."
# Python
import os, requests
r = requests.get(
    "https://recipepub.com/v1/topics/tofu",
    headers={"Authorization": f"Bearer {os.environ['RECIPE_PUB_KEY']}"},
)
print(r.json()["data"]["verdict"]["recommendation"])

2. GET /v1/topics/{topic}/trajectory — history + projection

Returns 26 weeks of history by default and 12 weeks of projection with confidence intervals. Tune with ?lookback=52w&forward=8. Method selection is automatic based on data depth. See the methodology page for which method fires when.

# curl
curl "https://recipepub.com/v1/topics/tofu/trajectory?lookback=52&forward=8" \
  -H "Authorization: Bearer rp_yourkey"
// Node — plot the projection
const { data } = await (await fetch(
  "https://recipepub.com/v1/topics/tofu/trajectory?lookback=52&forward=8",
  { headers: { Authorization: "Bearer " + key } }
)).json();
const points = [
  ...data.history.weekly.map(p => ({ ...p, kind: "actual" })),
  ...data.projection.weekly.map(p => ({ ...p, kind: "projected" })),
];

3. GET /v1/topics/{topic}/ideas — recipe brief evidence

Returns ranked recipe-brief seeds with structured evidence (top title shapes, top pairs, rising queries, format hints) ready for downstream LLM synthesis or editor workflow. Recipe Pub does not generate finished copy itself — that is a deliberate design choice. You bring the LLM, we bring the signal.

# curl
curl "https://recipepub.com/v1/topics/tofu/ideas?n=5" \
  -H "Authorization: Bearer rp_yourkey"
// Node — fan ideas into your own LLM prompt
const { data: seeds } = await (await fetch(
  "https://recipepub.com/v1/topics/tofu/ideas?n=5",
  { headers: { Authorization: "Bearer " + key } }
)).json();

for (const seed of seeds) {
  // seed.ingredient_list_suggestion, seed.title_seeds, seed.evidence are
  // the structured inputs to whatever copywriter or LLM you use next.
  console.log(seed.archetype, seed.confidence, seed.evidence.sample_size_band);
}

POST variant accepts exclude_recipe_urls for dedup against an existing recipe library:

curl -X POST https://recipepub.com/v1/topics/tofu/ideas \
  -H "Authorization: Bearer rp_yourkey" \
  -H "Content-Type: application/json" \
  -d '{ "n": 5, "exclude_recipe_urls": ["https://mybrand.com/recipes/crispy-tofu-1"] }'

4. GET /v1/movers/{kind} — breakouts by entity type

kind is one of ingredient, cuisine, pair, title_shape. Returns the top movers in that entity type, ranked by 6-week brand-count delta. Each row carries the engagement columns (print, share, ATC rates, rating, revenue per view) where the sample-size gate passes.

# curl
curl "https://recipepub.com/v1/movers/cuisine?limit=10" \
  -H "Authorization: Bearer rp_yourkey"
// Node — find the highest converting cuisine this week
const { data } = await (await fetch(
  "https://recipepub.com/v1/movers/cuisine?limit=20",
  { headers: { Authorization: "Bearer " + key } }
)).json();
const ranked = data
  .filter(r => r.engagement.atc_rate_pct !== null)
  .sort((a, b) => b.engagement.atc_rate_pct - a.engagement.atc_rate_pct);
console.log(ranked[0].label, ranked[0].engagement.atc_rate_pct);

5. GET /v1/news and /v1/recalls — feeds

Filterable press feed (LLM-tagged for primary topic, audience fit, sentiment) and a risk feed (FDA + USDA, LLM-categorised by hazard and affected ingredient). Both use the same pagination shape as the topics endpoint.

# Just hot-honey press from the last 30 days
curl "https://recipepub.com/v1/news?primary_tag=hot-honey&since=2026-04-28&limit=20" \
  -H "Authorization: Bearer rp_yourkey"

# Only ongoing recalls naming gelatin in the last 90 days
curl "https://recipepub.com/v1/recalls?ingredient=gelatin&days=90" \
  -H "Authorization: Bearer rp_yourkey"

Patterns most callers settle into

Weekly polling

Recipe Pub data refreshes every Sunday in the upstream cron. If your use case is "every Monday morning, surface the strongest signal of the week," you only need to call once. Default cache TTL on /v1/topics/{topic} is one hour; /v1/topics/{topic}/trajectory is six hours; /v1/topics/{topic}/ideas is 24 hours.

# A daily-at-9am cron pattern
0 9 * * * curl -s https://recipepub.com/v1/movers/ingredient?limit=10 \
  -H "Authorization: Bearer $RECIPE_PUB_KEY" \
  | jq -r '.data[] | select(.metrics.delta_brands > 5) | .label'

Filter by category

Most paginated endpoints accept ?category=condiments to narrow to a single category bucket. Use this to scope responses to the categories your shelf actually plays in.

curl "https://recipepub.com/v1/movers/ingredient?category=condiments&limit=5" \
  -H "Authorization: Bearer rp_yourkey"

Free verdict, paid evidence

If you are building a marketing card or a dashboard tile that surfaces a one-line recommendation, the free tier's verdict-only response on /v1/topics/{topic} is the right call. Upgrading to Pro unlocks the same response with trend, network, search, press, and recalls blocks — same shape, more depth.

Pro-tier custom weights

Pro and Enterprise can rebalance the composite score at request time. Supplier customers tend toward network-heavy; food media customers toward press-heavy. The default canonical score stays in score_default so alerts that need a stable signal use that field instead.

# Network-heavy view, suitable for ingredient supplier dashboards
curl "https://recipepub.com/v1/trends?weights=search:0.2,press:0.2,network:0.6" \
  -H "Authorization: Bearer rp_yourkey"

Rate limits + error envelope

Quotas are monthly per key. Three response headers carry the current state:

Errors use a single envelope shape across every endpoint. Code values are stable; the message can change.

{
  "error": {
    "code": "tier_required",
    "message": "Trajectory is a Pro feature. Upgrade at https://recipepub.com/pricing."
  }
}

Stable error codes: unauthenticated (401), invalid_key (401), revoked_key (403), tier_required (403), quota_exceeded (429), not_found (404), no_data (404), invalid_kind (400). Treat unknown codes as 5xx-equivalent: retry with backoff, then give up.

Where to read next

Stuck? Email patrick@recipekit.com. I read everything.