Headless API Access





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

  1. Open Recipe Kit and go to Settings.
  2. Find the API access card.
  3. Click Generate API key.
  4. 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:


  • recipe  is the full recipe, same field names as fields=full  above.
  • recipe.metafield  tells you the namespace and key holding this recipe in Shopify, and the blog article URL if the recipe is attached to one.
  • widget  is 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.

Did this answer your question? Thanks for the feedback There was a problem submitting your feedback. Please try again later.

Still need help? Contact Us Contact Us