Headless API Access
- What you can do with the API
- Getting an API key
- Making your first request
- GET /recipes
- GET /recipes/{id}
- GET /shop
- Keeping a copy in sync
- Rendering recipes with the widget script
- Rate limits
- Errors
- Rotating and revoking a key
What you can do with the API
The Recipe Kit API returns your recipes as JSON so you can use them outside your Shopify theme: a headless storefront, a mobile app, a meal planner, a nightly export, anything that can make an HTTP request from a server.
It is read-only. You create and edit recipes in the Recipe Kit app; the API hands them out.
Available on: Enterprise plan only.
Base URL: https://sapp.recipekit.com/api/v1
Getting an API key
- Open Recipe Kit and go to Settings.
- Find the API access card.
- Click Generate API key.
- Copy the key and store it somewhere safe.
The key looks like rk_live_ followed by 32 characters. You see it once. Recipe Kit stores only a hash of it, so nobody, including support, can read it back to you. Lose it and you generate a new one.
The key is tied to your shop. Every request returns your recipes, and there is no shop parameter to set.
Never put the key in browser JavaScript. Anyone who opens the page can read it and use it. Calls to /api/v1 from a browser fail on purpose, with a 405 explaining this, so a leak like that cannot ship by accident. Keep the key on your server, in an environment variable.
Making your first request
Send the key in the X-API-Key header:
curl https://sapp.recipekit.com/api/v1/shop \ -H "X-API-Key: rk_live_your_key_here"
If that returns your shop name and recipe count, you are connected.
GET /recipes
Lists your recipes, newest first.
curl "https://sapp.recipekit.com/api/v1/recipes?limit=50" \ -H "X-API-Key: rk_live_your_key_here"
Query parameters
| Parameter | Default | Description |
|---|---|---|
| limit | 100 | Recipes per page. Up to 500, or 100 when fields=full . Higher values clamp to the maximum. |
| offset | 0 | How many recipes to skip. Use with limit to page through. |
| status | all | Filter by status, as a whole number. |
| fields | summary | summary returns the card fields. full adds the recipe body. |
| updated_since | none | ISO 8601 timestamp. Returns only recipes changed after it. See "Keeping a copy in sync". |
Response
{
"success": true,
"shop": {
"domain": "your-store.myshopify.com",
"allows_shop_metafields": true
},
"recipes": [
{
"id": 12345,
"title": "Brown Butter Chocolate Chip Cookies",
"author": "Jane Doe",
"description": "Chewy in the middle, crisp at the edge.",
"date": "2026-08-14T00:00:00.000Z",
"status": 1,
"image": "https://cdn.shopify.com/.../cookies.jpg",
"category": "Dessert",
"cuisine": "American",
"servingSize": "24 cookies",
"prepTime": "20 minutes",
"cookTime": "12 minutes",
"enableRating": true,
"updatedAt": "2026-08-20T14:02:11.482Z",
"metafieldKey": "12345"
}
],
"pagination": {
"total": 214,
"limit": 50,
"offset": 0,
"hasMore": true
}
}
Getting the whole recipe in one call
Add fields=full and each recipe also carries imageAltText , ingredients , directions , equipment , note , video , calories , tags , nutritionData , nutritionServingSize and customFields . The names match the detail endpoint exactly, so one page of fields=full replaces a listing plus a detail call per recipe.
curl "https://sapp.recipekit.com/api/v1/recipes?fields=full&limit=100" \ -H "X-API-Key: rk_live_your_key_here"
These rows are much heavier, so limit tops out at 100 here.
Ingredients, directions and equipment come back as HTML, exactly as stored. That markup carries the product links behind add-to-cart, so strip it and shoppable ingredients stop working.
GET /recipes/{id}
Returns one recipe, plus where it lives in Shopify and the display settings your storefront uses.
curl https://sapp.recipekit.com/api/v1/recipes/12345 \ -H "X-API-Key: rk_live_your_key_here"
The response has three parts:
recipeis the full recipe, same field names asfields=fullabove.recipe.metafieldtells you the namespace and key holding this recipe in Shopify, and the blog article URL if the recipe is attached to one.widgetis everything the standalone widget script needs: design, colour, feature toggles and your translated labels.
GET /shop
Returns your plan, install date, recipe count and feature flags. Useful as a connection check and for spotting a plan change before your key stops working.
{
"success": true,
"is_recipe_kit_user": true,
"shop_domain": "your-store.myshopify.com",
"shop_name": "Your Store",
"plan": "enterprise",
"install_date": "2025-11-02T18:41:03.221Z",
"status": 1,
"recipe_count": 214,
"features": {
"shop_metafields": true,
"analytics": true
},
"integration_ready": true
}
Keeping a copy in sync
Every recipe carries an updatedAt timestamp, and updated_since filters on it. So a nightly job does not have to re-download the catalogue:
curl "https://sapp.recipekit.com/api/v1/recipes?fields=full&updated_since=2026-08-20T14:02:11.482Z" \ -H "X-API-Key: rk_live_your_key_here"
Store the highest updatedAt you received and pass it back on the next run. Subtract a few seconds first. An edit saved just before your cursor but committed just after it would otherwise land in the gap and never reach you. Receiving the same recipe twice costs you nothing. Missing one costs you a wrong page.
Deletes do not show up here. A deleted recipe simply stops appearing in the results, and an incremental run has no way to notice it went. If you mirror recipes into your own database, run an occasional full pass (fields=summary is cheap) and remove anything the API no longer lists.
Rendering recipes with the widget script
You have two options once you have the JSON.
Render it yourself, with your own markup and styles. You get the raw fields and full control.
Or let Recipe Kit render it. The detail response includes widget.scriptUrl , the standalone widget bundle, along with the design, colour, toggles and labels the shop is configured with. Load that script on your page and you get the same recipe card your Shopify storefront shows, including ratings, cook mode, servings adjuster and add-to-cart where the plan allows it.
Rate limits
60 requests per minute per key.
Go over and you get a 429 with a Retry-After header saying how many seconds to wait. Back off and retry rather than hammering; repeated failures from one address get throttled harder.
Errors
Every error returns JSON with a human-readable error and a stable code . Branch on code . The wording of error can change.
{
"error": "API access requires an active Enterprise subscription. Renew in Recipe Kit to re-enable this key.",
"code": "SUBSCRIPTION_INACTIVE"
}
| Status | Code | What happened |
|---|---|---|
| 400 | INVALID_PARAMETER | A query parameter is the wrong shape. The message names it. |
| 401 | API_KEY_MISSING | No X-API-Key header. |
| 403 | API_KEY_INVALID | The key is wrong, or it was rotated or revoked. |
| 403 | PLAN_MISSING_FEATURE | The shop is not on Enterprise. Upgrade and the same key works again. |
| 403 | SUBSCRIPTION_INACTIVE | Enterprise plan, no active subscription. Renew and the same key works again. |
| 404 | RECIPE_NOT_FOUND | No recipe with that ID on this shop. |
| 404 | SHOP_NOT_FOUND | The shop is not in Recipe Kit. |
| 405 | BROWSER_REQUESTS_NOT_SUPPORTED | The call came from a browser. Call the API from your server. |
| 429 | RATE_LIMITED | Over 60 requests in a minute. Wait retryAfter seconds. |
| 500 | INTERNAL_ERROR | Something broke on our side. Retry, then contact support if it persists. |
A downgrade or a lapsed subscription disables the key the moment it happens. Nothing is deleted, and the key starts working again as soon as the plan is back.
Rotating and revoking a key
Both live in the API access card in Settings.
Rotate issues a new key and kills the old one on the spot. Anything still sending the old key gets a 403 until you update it, so have the new key ready to deploy before you rotate.
Revoke switches API access off and leaves you with no key. Generate a new one whenever you want.
Rotate if the key leaked or you are cycling credentials on a schedule. Revoke if you are shutting the integration down.