Building a Recipe API with Cooklang
Most recipe APIs charge per request, lock your data into their schema, and limit what you can do with recipes you own. There's a simpler path: your .cook files already contain structured data. CookCLI can expose that data as JSON, run a local server, and give you everything you need to build a recipe app — without signing up for anything.
This post walks through using CookCLI as a recipe API backend, building a minimal frontend, generating a static JSON API, and extending with Schema.org output and the Rust parser library.
The JSON Output
Every .cook file parses to structured JSON with a single command. Given a recipe file like risotto.cook:
The output is a complete structured representation of the recipe:
The steps array preserves the inline structure of the original prose — ingredients and cookware appear in context, not just as a flat list. This means you can render step text with ingredient quantities highlighted, the same way Cooklang apps do.
Scaling works too. Pass a serving count with the :N syntax:
All ingredient quantities in the JSON output adjust to the scaled serving count. The API layer is just the command.
The Built-in Server
cook server starts a web interface for browsing your recipe collection:
This runs at http://localhost:9080 by default and serves the full web UI. But the server also exposes recipe data as JSON through its API endpoints. You can query individual recipes, browse collections, and pull ingredient data programmatically.
The server is a single Rust binary with no database and no configuration. Point it at a directory of .cook files and it works. The server reads files from disk on each request, so adding or editing a recipe is reflected immediately.
For a different port:
If you're building a frontend that needs live data from your local collection, this is the backend. No setup beyond installing CookCLI.
Building a Static JSON API
For production deployments, you probably want a static JSON API rather than a running server process. A simple shell script turns a folder of .cook files into a directory of JSON files:
Run the script after any recipe change and you have a /api/ directory of static JSON files. Serve it with Nginx, GitHub Pages, Netlify, or any static host. No server process, no database — just files.
For CI, add the script to your pipeline. Every push to your recipe repo rebuilds the API automatically.
A Minimal Recipe Frontend
With the JSON API in place, rendering a recipe in a browser takes about thirty lines of JavaScript:
Open recipe.html?recipe=risotto and it fetches /api/risotto.json, renders the ingredient list, and walks through the steps with ingredient names highlighted inline.
This works from any static host. No backend required once you've built the JSON files. Add a second fetch to /api/index.json for a recipe list page, and you have a complete browsable recipe site.
Shopping Lists as JSON
The same JSON flag works for shopping lists:
The output is a combined, deduplicated ingredient list across all three recipes:
Matching ingredients from different recipes are combined and quantities summed. This output pipes directly into any frontend that needs a shopping list, or into a grocery integration if you're building one.
With :N scaling per recipe:
Each recipe scales independently before the lists merge.
Schema.org Output for SEO
If you're publishing recipes on a public site, structured data matters. Search engines use Schema.org Recipe markup to display cooking times, ingredients, and ratings in search results.
CookCLI generates this directly:
Output:
Drop this into a <script type="application/ld+json"> tag in your recipe page and search engines can parse the recipe structure directly. Add it to the build script:
Then include it in your HTML template alongside the regular recipe data.
Other Output Formats
cook recipe supports several other formats you might find useful depending on your use case:
--format yaml— same structure as JSON but YAML, useful if you're feeding into Ansible playbooks or other YAML-native tooling--format markdown— renders the recipe as formatted Markdown, good for documentation sites or static site generators--format cooklang— round-trips the recipe back to Cooklang syntax, useful for normalization--format latex— generates LaTeX output for printable recipe books (pairs with the cookbook PDF scripts covered in the server docs)
For an API use case, JSON and schema are the most useful. YAML is handy if you're generating recipe data for a Hugo or Jekyll site where YAML front matter is expected.
Embedding the Parser Directly
For tighter integration, the cooklang-rs Rust library lets you embed the parser in your own application rather than shelling out to CookCLI:
This is the same parser CookCLI uses internally. You get full access to the parsed AST — ingredients, steps, metadata, cookware, timers — with no subprocess overhead.
The library also compiles to WebAssembly. The cooklang-rs playground runs it in-browser, which means you can parse .cook files on the client side with zero server round-trips. A recipe editor that parses and validates as you type is a straightforward WASM integration.
For other languages, there are parser libraries in Python, TypeScript, Go, Swift, and others. The full list is at /docs/for-developers/.
What You End Up With
Putting this together, the full stack looks like:
.cookfiles as the source of truth — version-controlled, plain text, human-readable- A build script that runs
cook recipe --format jsonon each file and writes to/api/ - A static frontend that fetches from
/api/and renders recipe pages - Schema.org JSON-LD embedded in each page for search engine structured data
- The CookCLI server for local development and in-kitchen browsing
No third-party API keys. No rate limits. No data locked in someone else's schema. The files are yours, the API is yours, and the whole stack runs on a $5 VPS or a Raspberry Pi.
The CookCLI documentation at /cli/ covers every flag. If you want to understand the Cooklang format itself before writing recipes programmatically, the spec at /docs/spec/ is the canonical reference.
-Alex