Grocery Shopping API Reference

Pepesto’s API connects recipes and meal plans directly to real supermarket products, so users can add the right ingredients to their cart and check out in just a few clicks. Our product-matching engine ensures every ingredient maps to the best store item.


Who It’s For?

Meal Planning & Nutrition Apps
AI Agents
Recipe Creators & Food Bloggers
Market Analysts
Meal Planning & Nutrition Apps

Offer one-tap grocery fulfillment to increase retention and everyday engagement.

Recipe Creators & Food Bloggers

Recipe creators & food bloggers can use our separate, lightweight no-code integration to make their recipes shoppable without touching this API. Details are available on our Pepesto Creators page.

Licensed Recipes

If you are looking for permanent access to high-quality licensed recipes, please refer to this section.

Market Analysts

Use the /catalog to track prices across supermarkets and countries and build engaging reports.

Why Partner with Pepesto?

Benefit
More Engagement

Deliver a seamless meal-to-cart journey.

Benefit
Revenue

You can increase revenue via referrals - you profit each time your user does shopping.

Benefit
Frictionless Integration

An easy-to-implement API that starts adding value to your platform right away.

Benefit
Proven at Scale

Built, tested, and optimized to drive real conversions for food and grocery platforms.

Setup & Authentication

Pepesto API uses a pay-as-you-go model with pre-paid credits assigned to an API key.

Purchase API credits via Stripe here.
note: The page below shows base prices. We offer volume and commitment discounts - please refer to Pricing for more details.
note: If you are a student, or a startup building a new product and would like to explore our API, reach out to us and describe your use case. Eligible new customers can receive a discount promo code.
note: By using the alias field in the payment form, you can create multiple API keys associated with the same Stripe email address.

After the first purchase, get an API key, by making a call to /link endpoint (unauthenticated).

Include your API key in the Authorization header as a Bearer token in all API requests:

Example request
curl -X POST https://s.pepesto.com/api/$ENDPOINT \
      -H "Authorization: Bearer $PEPESTO_API_KEY" \
      -H "Content-Type: application/json" \
      -d '$REQUEST_JSON'

Billing response headers

Successful API responses include billing information in HTTP response headers:

HeaderDescription
Pepesto-Eurocents-ChargedThe number of eurocents charged for this request, after applicable discounts. Free requests return 0.

API Overview

Pepesto grocery shopping API overview
Oneshot
POST /api/oneshot
How it works
A convenience endpoint that combines parsing, product matching, default product selection, and session creation in a single call. If you need finer control over product selection, or want to keep the checkout session in your own app instead of handing it off to Pepesto, use /parse, /products, and /session separately to create the checkout session. Then use /checkout to add the items to the basket.
Input

You provide recipe URLs, free-form shopping list text, and/or an image via content_urls, content_text, content_image, and supermarket domain. Pepesto uses these inputs to build a complete shopping cart.

Output

A redirect_url that opens Pepesto's hosted UI, allowing the user to review and modify the shopping cart before proceeding to checkout.
The UI is hosted on app.pepesto.com and is designed to work well inside an iframe in your app, but you can also redirect the user to it directly.
Once the user clicks "Checkout", they are redirected to the Pepesto mobile app, which completes the checkout. See /mcheckout for more details.

Pricing

0.32 € per request.

Full /oneshot protocol (Go)
Show protocol definition
type ShopRequest struct {
	// URL containing recipes or shopping lists.
	// The content must be accessible via a simple HTTP GET request (no authentication, no client-side JavaScript, etc.).
	// If the content is not accessible, use a scraping service and pass the content as plain text in the field below.
	ContentUrl string `json:"content_url"`

	// Free-form text describing a recipe or a shopping list.
	ContentText string `json:"content_text"`

	// Image of a recipe or a shopping list.
	ContentImageUrl string `json:"content_image,omitempty"`

	// The supermarket in which to search for products.
	// This is optional - if not specified we will automatically pick a supermarket, suitable for the user's location.
	SupermarketDomain SupermarketDomain `json:"supermarket_domain,omitempty"`
}

type ShopResponse struct {
	// A URL pointing to "https://s.pepesto.com".
	// Opening the result page allows the user to view the intended products and complete the purchase.
	// The result page can either be opened in an external browser, or embedded as an iframe in a client website, or app.
	//
	// Here is a simple HTML that can be used for embedding:
	// ---
	// <div id="pepesto-widget-container"></div>
	// <script src="https://s.pepesto.com/get_recipe.js" data-source-url="REDIRECT_URL_RETURNED_BY_THE_SHOPPING_API"></script>
	// ---
	RedirectUrl string `json:"redirect_url"`
}


// List of supported supermarket domains can be found at https://api.pepesto.com/supermarkets
type SupermarketDomain string
Example request
curl -X POST https://s.pepesto.com/api/oneshot \
    	-H "Authorization: Bearer $PEPESTO_API_KEY" \
    	-H "Content-Type: application/json" \
    	-d '{"content_url":"https://www.bbcgoodfood.com/recipes/pizza-margherita-4-easy-steps","content_text":""}'
Predirect
POST /api/predirect
How it works
/predirect is semantically equivalent to /oneshot: both turn a shopping list into a matched basket and hand the checkout flow to Pepesto. The difference is when the work happens and who pays for it.

With /oneshot, Pepesto parses the input and matches products during the API request, and the cost is deducted from the API client's credits. /predirect is free to the API client: it immediately returns a deferred deep link to the Pepesto mobile app, and the user is charged when they proceed with checkout.

Parsing and product matching are performed lazily, only after the user opens the returned link. If the app is not installed, the user is first directed to the appropriate app store, and the shopping list is preserved until the app opens.

This is a public endpoint; no API key or authentication is required.
Input

A JSON object containing a single shopping_list string field. The field may contain multiple lines of free-form shopping-list text, with at most 30 ingredients.

Output

A redirect_url containing a deferred deep link to Pepesto's mobile app.

Pricing

Free to the API client. The user is charged when they proceed with checkout in the Pepesto app.

Full /predirect protocol (Go)
Show protocol definition
type PredirectRequest struct {
	ShoppingList string `json:"shopping_list"`

	// User's locale (e.g., "de-DE").
	Locale Locale `json:"locale,omitempty"`
}

type PredirectResponse struct {
	// Deferred deep link to Pepesto's mobile app. Opening it starts parsing the
	// shopping list supplied in the request body.
	RedirectUrl string `json:"redirect_url"`
}


type Locale string
Example request
curl -X POST https://s.pepesto.com/api/predirect \
     \
    	-H "Content-Type: application/json" \
    	-d '{"shopping_list":"2 avocados\n1 loaf of bread\n500 g tomatoes"}'
Parse
POST /api/parse
Input

A recipe provided as free text, or a publicly crawlable URL.

Output

Structured recipe data (title, ingredients, nutrition, and more) plus a KgToken that you can pass to the /products endpoint to build a fully itemized shopping cart with concrete products, quantities, and images.

Pricing

0.32 € per request.

Full /parse protocol (Go)
Show protocol definition
type ParseRecipeRequest struct {
	// The URL of the recipe to parse. Exactly one of `RecipeUrl` or `RecipeText` must be provided.
	RecipeUrl string `json:"recipe_url"`

	// The recipe text to parse. Exactly one of `RecipeUrl` or `RecipeText` must be provided.
	RecipeText string `json:"recipe_text,omitempty"`

	// Deprecated: temporary rollout field. If set to a supermarket supported by the next-gen matcher,
	// /parse returns a KgToken containing a matcher interpretation instead of the legacy recipe payload.
	DeprecatedDomain SupermarketDomain `json:"deprecated_domain,omitempty"`
}

type ParseRecipeResponse struct {
	KgToken KgToken `json:"kg_token"`

	// Deprecated, we only popuate the KG token within this field
	DeprecatedRecipe RetrievedRecipe `json:"recipe,omitempty"`
}


type KgToken string


type RetrievedRecipe struct {
	// The human-readable title, ingredients, and cooking steps for the recipe.
	// Typically in English but translated based on the `Personalization.Locale` setting.
	Title       string   `json:"title,omitempty"`
	Ingredients []string `json:"ingredients,omitempty"`
	Steps       []string `json:"steps,omitempty"`

	// URL of a 1024x1024px image of the recipe in WebP format.
	ImageUrl string `json:"image_url,omitempty"`

	// The nutritional information for the recipe.
	Nutrition Nutrition `json:"nutrition,omitempty"`

	// Allergens
	Allergens []string `json:"allergens,omitempty"`

	// Portions. This field is not guaranteed to be set.
	Portions Portions `json:"portions,omitempty"`

	// An opaque, encrypted token representing the semantic understanding of the recipe's ingredients.
	// This token is required when calling the "products" endpoint.
	KgToken KgToken `json:"kg_token,omitempty"`
}


// List of supported supermarket domains can be found at https://api.pepesto.com/supermarkets
type SupermarketDomain string


type Nutrition struct {
	// Number of calories in the recipe.
	Calories int `json:"calories,omitempty"`
	// Number of grams of carbohydrates in the recipe.
	CarbohydratesGrams int `json:"carbohydrates_grams,omitempty"`
	// Number of grams of protein in the recipe.
	ProteinGrams int `json:"protein_grams,omitempty"`
	// Number of grams of fat in the recipe.
	FatGrams int `json:"fat_grams,omitempty"`
}

type Portions struct {
	From int `json:"from"`
	To   int `json:"to"`
}
Example request
curl -X POST https://s.pepesto.com/api/parse \
    	-H "Authorization: Bearer $PEPESTO_API_KEY" \
    	-H "Content-Type: application/json" \
    	-d '{"recipe_url":"https://www.bbcgoodfood.com/recipes/kibbeh"}'
Suggest
POST /api/suggest
How it works
❗ This API call returns Pepesto licensed images.
You can display the recipes in your app or website without attribution.
Input

Explicit user query and optional preferences and constraints for meal planning, such as ingredients to include or avoid, cuisine, dietary tags, time constraints, and number of servings. The endpoint uses these criteria to search Pepesto’s recipe graph.

Output

A list of suggested recipes from Pepesto’s recipe database (1M+ recipes), in the same format returned by /parse. Each recipe includes rich metadata (title, ingredients, nutrition, instructions, allergens, portions, etc.) and a KgToken that can be passed to the /products endpoint to build a fully itemized shopping cart.

Pricing

1.50 € per request.

Full /suggest protocol (Go)
Show protocol definition
type SuggestRecipesRequest struct {
	QueryRecipe string `json:"query"`

	// Optional personalization settings to tailor recipe retrieval to the user's preferences.
	// These preferences are stored for future use.
	Personalization *Personalization `json:"personalization,omitempty"`
}

type SuggestRecipesResponse struct {
	// An array of retrieved recipes.
	Recipes []RetrievedRecipe `json:"recipes,omitempty"`

	// A short, human-readable response to the user's query, localized to the user's language.
	// For subsequent query turns, this response should be appended to the `Query` field of the next `SuggestRecipesRequest`.
	ShortResponse string `json:"short_response,omitempty"`
}

type RecipeQueryDialogTurn struct {
	// The content of the query turn. This can be free text (e.g., "I'm looking for a quick and easy dinner recipe") or more structured (e.g., "cuisine:Italian ingredients:tomato").
	Content string `json:"content,omitempty"`

	// The role of the participant issuing this query turn. Must be either "user" or "assistant" (Pepesto). This mirrors the structure of chat completion APIs in large language models like OpenAI's.
	Role string `json:"role,omitempty"`

	// Specifies whether the query represents a strict requirement or a preference.
	//
	// If `true`, all returned recipes *must* satisfy the query (e.g., "cuisine:Greek" - all recipes should be Greek).
	// If `false`, the query is treated as a preference or suggestion (e.g., "I have chickpeas at home" - some recipes may contain chickpeas, but it's not a requirement).
	Strict bool `json:"strict,omitempty"`
}

type Personalization struct {
	// The locale in which recipes should be translated (e.g., "de-DE").  Defaults to "en-UK" if empty.
	Locale Locale `json:"locale,omitempty"`

	// The number of people the recipes should serve.  Recipes will be scaled accordingly. Defaults to 3.
	Portions int `json:"portions,omitempty"`

	// The user's preferred supermarket (e.g., "coop.ch").  Used to prioritize recipes with ingredients available at that supermarket or on promotion.
	SupermarketDomain SupermarketDomain `json:"supermarket_domain,omitempty"`
}


type Locale string


type RetrievedRecipe struct {
	// The human-readable title, ingredients, and cooking steps for the recipe.
	// Typically in English but translated based on the `Personalization.Locale` setting.
	Title       string   `json:"title,omitempty"`
	Ingredients []string `json:"ingredients,omitempty"`
	Steps       []string `json:"steps,omitempty"`

	// URL of a 1024x1024px image of the recipe in WebP format.
	ImageUrl string `json:"image_url,omitempty"`

	// The nutritional information for the recipe.
	Nutrition Nutrition `json:"nutrition,omitempty"`

	// Allergens
	Allergens []string `json:"allergens,omitempty"`

	// Portions. This field is not guaranteed to be set.
	Portions Portions `json:"portions,omitempty"`

	// An opaque, encrypted token representing the semantic understanding of the recipe's ingredients.
	// This token is required when calling the "products" endpoint.
	KgToken KgToken `json:"kg_token,omitempty"`
}


type Nutrition struct {
	// Number of calories in the recipe.
	Calories int `json:"calories,omitempty"`
	// Number of grams of carbohydrates in the recipe.
	CarbohydratesGrams int `json:"carbohydrates_grams,omitempty"`
	// Number of grams of protein in the recipe.
	ProteinGrams int `json:"protein_grams,omitempty"`
	// Number of grams of fat in the recipe.
	FatGrams int `json:"fat_grams,omitempty"`
}

type Portions struct {
	From int `json:"from"`
	To   int `json:"to"`
}


// List of supported supermarket domains can be found at https://api.pepesto.com/supermarkets
type SupermarketDomain string


type KgToken string
Example request
curl -X POST https://s.pepesto.com/api/suggest \
    	-H "Authorization: Bearer $PEPESTO_API_KEY" \
    	-H "Content-Type: application/json" \
    	-d '{"query":"Find me a vegan pasta recipe"}'
Products
POST /api/products
How it works
/products and /search are semantically equivalent: both search for and match product requests to real supermarket products.

The difference is coverage and latency. /products matches only against Pepesto's daily-updated cache of about 1,000-2,000 SKUs per supermarket, focused on the most commonly used cooking ingredients. Because it searches this cache, it is near-instant and cheaper than /search.

If a requested item is not returned by /products, it does not necessarily mean the supermarket does not sell it. It means the item was not found in Pepesto's cache. Missing items can still be passed to /search for a broader asynchronous search, or to /mcheckout for checkout-time resolution.
Input

One or more recipe KgToken values from the /parse or /suggest endpoints, an optional manual shopping list as free text, and the target supermarket, such as a domain or ID. The service uses these inputs to build a combined cart for all requested recipes.

Output

A fully itemized shopping cart with concrete products, recent prices, quantities, images, and other metadata. Items are merged across recipes to reduce waste and cost, and for each generic ingredient (e.g. “tomatoes”) multiple matching products are returned so the user or client app can choose the best option. Items that are not returned were not found in Pepesto's cache; they may still be available at the supermarket and can be retried through /search or /mcheckout.

Pricing

0.04 € per request.

Full /products protocol (Go)
Show protocol definition
type OfferProductsRequest struct {
	// Tokens for recipes retrieved by the /suggest or /parse endpoints.
	RecipeKgTokens []KgToken `json:"recipe_kg_tokens"`

	// A free-text shopping list (e.g., "4 eggs, pasta, 2 chicken breasts"). Maximum length: 5000 characters.
	ManualShoppingList string `json:"manual_shopping_list,omitempty"`

	// The supermarket in which to search for products.
	// Required. List of supported supermarket domains can be found at https://api.pepesto.com/supermarkets.
	SupermarketDomain SupermarketDomain `json:"supermarket_domain,omitempty"`

	// Product URLs preferred by the user. Used by the next-gen matcher to boost matching products.
	PreferredProductUrls []string `json:"preferred_product_urls,omitempty"`

	// The locale in which to display item names in the response (e.g., "de-DE").
	ItemNamesLocale Locale `json:"item_names_locale,omitempty"`
}

type OfferProductsReponse struct {
	// A list of items and their corresponding products.
	Items []ItemToProducts `json:"items,omitempty"`

	// A list of items that were present in the request, but we could not find any suitable matching products for them.
	// Either because the item was not found in the index, or because it was not available in the selected supermarket.
	NotIndexedItems []string `json:"not_indexed_items,omitempty"`

	// Currency code of the resolved supermarket.
	Currency string `json:"currency,omitempty"`
}


// For a given item (e.g., "eggs"), a list of possible products that the user can buy.
// The list is sorted, with the most appropriate product listed first.
// Sorting criteria include user preferences and past purchases, minimizing food waste and costs, choosing higher quality/bio products, and product reviews.
// An empty list indicates that no suitable products were found in the index for this item. Appropriate products may still be offered in Pepesto's UI during checkout.
type ItemToProducts struct {
	// Human-readable item name (e.g., "eggs").
	ItemName string `json:"item_name,omitempty"`

	// Concrete supermarket products offered to the user for this item.
	Products []ProductAndSuggestedQuantity `json:"products,omitempty"`
}


type KgToken string


type Locale string


// List of supported supermarket domains can be found at https://api.pepesto.com/supermarkets
type SupermarketDomain string


// Specifies the quantity of a product to buy.
// If `NumUnitsToBuy` is zero, Pepesto suggests treating the product as a checklist item.
// In this case, Pepesto doesn't suggest buying it by default but displays it in the UI as a reminder (useful for pantry staples like salt).
type ProductAndSuggestedQuantity struct {
	// The product.
	Product IndexedProduct `json:"product,omitempty"`

	// A session token that can be used as an input to /session endpoint.
	SessionToken SessionToken `json:"session_token,omitempty"`

	// The number of units to buy.  A value of 0 indicates a checklist item.
	NumUnitsToBuy int `json:"num_units_to_buy,omitempty"`
}


type IndexedProduct struct {
	// The product name, capturing the most important aspects. Displayed in the default language for the selected `SupermarketDomain`.
	ProductName string `json:"product_name,omitempty"`

	// The product category (e.g., "canned tomatoes", "pasta", etc.).
	Category string `json:"category,omitempty"`

	// The quantity per unit of this product.
	Quantity *QuantityPerUnit `json:"quantity,omitempty"`

	// The price per unit of this product.
	Price *PricePerUnit `json:"price,omitempty"`

	// This is typically URL of the product page on the supermarket's website.
	ProductId string `json:"product_id,omitempty"`

	// URL of an image of the product, usually points to retailer's website.
	ImageUrl string `json:"image_url,omitempty"`

	// URL of an image of the product hosted by Pepesto (not the retailer's website).
	PepestoHostedImageUrl string `json:"pepesto_hosted_image_url,omitempty"`

	// A unique identifier for products sold in multiple supermarket chains.  Allows comparing prices for the same product across different supermarkets.  Populated only for packaged products commonly sold in multiple chains.
	UniversalId int64 `json:"universal_id,omitempty"`

	Classification *ProductClassification `json:"classification,omitempty"`

	// A token that can be passed to the /session endpoint to create a session with this product, if
	// the client app, or the user chooses to add this product to the shopping cart.
	SessionToken SessionToken `json:"session_token,omitempty"`
}


type SessionToken string


type ProductClassification struct {
	IsBio          bool `json:"is_bio,omitempty"`
	IsFrozen       bool `json:"is_frozen,omitempty"`
	IsSubstitution bool `json:"is_substitution,omitempty"`
}

type QuantityPerUnit struct {
	// Estimated weight per unit (in grams).
	Grams float64 `json:"grams,omitempty"`

	// Volume per unit (in milliliters). Used for liquid products (e.g., 100ml of milk).
	Milliliters float64 `json:"milliliters,omitempty"`

	// Number of pieces per unit (e.g., 2 for a package of 2 chicken breasts).
	Pieces float64 `json:"pieces,omitempty"`

	// Number of bunches per unit (e.g., 2 bunches of parsley).
	Bunches float64 `json:"bunches,omitempty"`
}

type PricePerUnit struct {
	// Price of the product in the smallest currency unit used by the `SupermarketDomain` (e.g., 199 represents 1.99 CHF for coop.ch).
	Price int `json:"price,omitempty"`

	// Price promotion details.
	Promotion PricePromotion `json:"promotion,omitempty"`
}

type PricePromotion struct {
	// `true` if the product is currently on promotion.
	Promo bool `json:"promo,omitempty"`

	// Price discount percentage (not always available).  May not be populated, or the promotion may be of a different type (e.g., "buy 2 get 3").
	PromoPercentage float64 `json:"promo_percentage,omitempty"`
}
Example request
curl -X POST https://s.pepesto.com/api/products \
    	-H "Authorization: Bearer $PEPESTO_API_KEY" \
    	-H "Content-Type: application/json" \
    	-d '{"recipe_kg_tokens":["rec123"],"supermarket_domain":"coop.ch"}'
Search
POST /api/search
How it works
/search and /products are semantically equivalent: both search for and match product requests to real supermarket products.

Unlike /products, which searches Pepesto's daily-updated cache of about 1,000-2,000 commonly used cooking-ingredient SKUs per supermarket, /search can search for any products in the supermarket, such as wine, cosmetics, ready meals, and other products outside the cache. This broader coverage is why /search is asynchronous, slower, and more expensive.

/search starts an asynchronous product search. The request returns immediately with a search_session_id, which can later be passed to /retrieve.

Results are typically ready within 10 minutes, although processing may occasionally take longer during periods of high traffic. Clients can poll /retrieve to check the search status and retrieve the results when they are ready.

Alternatively, clients can provide a webhook URL. The webhook is called immediately after the results are ready and receives an HTTP POST with the search session ID appended as the sid query parameter.

Search sessions and their results are retained for 14 days. After that period the data is permanently deleted, so clients should retrieve and store any results they need on their end.
Input

Up to 10 free-text product descriptions, a supported supermarket domain, and an optional response locale and webhook URL.

Output

A search_session_id identifying the asynchronous search. Pass it to /retrieve to check progress and obtain the results. The session and its results remain available for 14 days.

Pricing

0.32 € per request.

Full /search protocol (Go)
Show protocol definition
const MaxCustomProductsPerSearch = 10

type CustomProductsSearchRequest struct {
	// Free text descriptions of the products that we should search for.
	// E.g. 'organic strawberries 500g, H-Milch 3.5% 1L, free-range eggs 10-pack'.
	// At most MaxCustomProductsPerSearch products may be specified.
	Products []string `json:"products"`

	// Supermarket where we should search these items
	SupermarketDomain SupermarketDomain `json:"supermarket_domain"`

	// A webhook that should be called once the search is completed.
	// If specified, we make HTTP POST request to this URL with empty body.
	// Note: we append "sid=$sessionId" argument when calling your webhook.
	// The sessionId is the same as the session ID in the response.
	WebhookURL string `json:"webhook_url,omitempty"`
}

type CustomProductsSearchResponse struct {
	// Session ID that can be passed to /retrieve endpoint to retrieve the results of this search.
	SearchSessionId string `json:"search_session_id"`
}


// List of supported supermarket domains can be found at https://api.pepesto.com/supermarkets
type SupermarketDomain string
Example request
curl -X POST https://s.pepesto.com/api/search \
    	-H "Authorization: Bearer $PEPESTO_API_KEY" \
    	-H "Content-Type: application/json" \
    	-d '{"products":["organic strawberries 500g","free-range eggs 10-pack"],"supermarket_domain":"coop.ch","webhook_url":"https://example.com/hooks/pepesto-search"}'
Retrieve
POST /api/retrieve
How it works
/retrieve checks the state of an asynchronous product search started with /search. Poll this endpoint until the returned status is done.
Input

The search_session_id returned by /search.

Output

The current status: queued, in_progress, or done. Once done, the response includes one product-result entry for each requested item and the supermarket currency.

Pricing

Free.

Full /retrieve protocol (Go)
Show protocol definition
type RetrieveCustomProductSearchRequest struct {
	// The session ID returned from /search endpoint.
	SearchSessionId string `json:"search_session_id"`
}

type RetrieveCustomProductSearchResponse struct {
	// One of "queued" (accepted, but not started yet), "in_progress", "done".
	Status string `json:"status"`

	// Populated only when status is "done".
	// Contains exactly as many elements as were passed in the original request.
	Items []ItemToProducts `json:"items,omitempty"`

	// Currency code of the resolved supermarket.
	Currency string `json:"currency,omitempty"`
}


// For a given item (e.g., "eggs"), a list of possible products that the user can buy.
// The list is sorted, with the most appropriate product listed first.
// Sorting criteria include user preferences and past purchases, minimizing food waste and costs, choosing higher quality/bio products, and product reviews.
// An empty list indicates that no suitable products were found in the index for this item. Appropriate products may still be offered in Pepesto's UI during checkout.
type ItemToProducts struct {
	// Human-readable item name (e.g., "eggs").
	ItemName string `json:"item_name,omitempty"`

	// Concrete supermarket products offered to the user for this item.
	Products []ProductAndSuggestedQuantity `json:"products,omitempty"`
}


// Specifies the quantity of a product to buy.
// If `NumUnitsToBuy` is zero, Pepesto suggests treating the product as a checklist item.
// In this case, Pepesto doesn't suggest buying it by default but displays it in the UI as a reminder (useful for pantry staples like salt).
type ProductAndSuggestedQuantity struct {
	// The product.
	Product IndexedProduct `json:"product,omitempty"`

	// A session token that can be used as an input to /session endpoint.
	SessionToken SessionToken `json:"session_token,omitempty"`

	// The number of units to buy.  A value of 0 indicates a checklist item.
	NumUnitsToBuy int `json:"num_units_to_buy,omitempty"`
}


type IndexedProduct struct {
	// The product name, capturing the most important aspects. Displayed in the default language for the selected `SupermarketDomain`.
	ProductName string `json:"product_name,omitempty"`

	// The product category (e.g., "canned tomatoes", "pasta", etc.).
	Category string `json:"category,omitempty"`

	// The quantity per unit of this product.
	Quantity *QuantityPerUnit `json:"quantity,omitempty"`

	// The price per unit of this product.
	Price *PricePerUnit `json:"price,omitempty"`

	// This is typically URL of the product page on the supermarket's website.
	ProductId string `json:"product_id,omitempty"`

	// URL of an image of the product, usually points to retailer's website.
	ImageUrl string `json:"image_url,omitempty"`

	// URL of an image of the product hosted by Pepesto (not the retailer's website).
	PepestoHostedImageUrl string `json:"pepesto_hosted_image_url,omitempty"`

	// A unique identifier for products sold in multiple supermarket chains.  Allows comparing prices for the same product across different supermarkets.  Populated only for packaged products commonly sold in multiple chains.
	UniversalId int64 `json:"universal_id,omitempty"`

	Classification *ProductClassification `json:"classification,omitempty"`

	// A token that can be passed to the /session endpoint to create a session with this product, if
	// the client app, or the user chooses to add this product to the shopping cart.
	SessionToken SessionToken `json:"session_token,omitempty"`
}


type SessionToken string


type ProductClassification struct {
	IsBio          bool `json:"is_bio,omitempty"`
	IsFrozen       bool `json:"is_frozen,omitempty"`
	IsSubstitution bool `json:"is_substitution,omitempty"`
}

type QuantityPerUnit struct {
	// Estimated weight per unit (in grams).
	Grams float64 `json:"grams,omitempty"`

	// Volume per unit (in milliliters). Used for liquid products (e.g., 100ml of milk).
	Milliliters float64 `json:"milliliters,omitempty"`

	// Number of pieces per unit (e.g., 2 for a package of 2 chicken breasts).
	Pieces float64 `json:"pieces,omitempty"`

	// Number of bunches per unit (e.g., 2 bunches of parsley).
	Bunches float64 `json:"bunches,omitempty"`
}

type PricePerUnit struct {
	// Price of the product in the smallest currency unit used by the `SupermarketDomain` (e.g., 199 represents 1.99 CHF for coop.ch).
	Price int `json:"price,omitempty"`

	// Price promotion details.
	Promotion PricePromotion `json:"promotion,omitempty"`
}

type PricePromotion struct {
	// `true` if the product is currently on promotion.
	Promo bool `json:"promo,omitempty"`

	// Price discount percentage (not always available).  May not be populated, or the promotion may be of a different type (e.g., "buy 2 get 3").
	PromoPercentage float64 `json:"promo_percentage,omitempty"`
}
Example request
curl -X POST https://s.pepesto.com/api/retrieve \
    	-H "Authorization: Bearer $PEPESTO_API_KEY" \
    	-H "Content-Type: application/json" \
    	-d '{"search_session_id":"search-session-id"}'
Mcheckout
POST /api/mcheckout
How it works
Your user stays your user. /mcheckout is a white-label integration: Pepesto app handles the difficult checkout loop, then sends the user back—without competing for their attention or exposing other Pepesto features.

This endpoint returns a deferred deep link to the Pepesto app. When the user clicks the link, the app opens automatically if it is installed. Otherwise, the user is directed to the App Store or Google Play to install it.

Pepesto app remembers that the user arrived through an integrated client and restricts the app to checkout only. Meal planning, recipe search, promotions, and other Pepesto app functionality are not available. This setting is sticky: if the user opens Pepesto appagain later, the app remains restricted to checkout.

Once the app opens, Pepesto app runs the checkout loop and populates the user's shopping basket with the provided products. Afterward, the user is redirected back to the source app when redirect_url is set. Otherwise, the user is sent to the supermarket's cart or login page.

The goal of /mcheckout is to remove the complexity of implementing and maintaining the checkout loop. Pepesto app handles that process on behalf of the client and does not intend to compete with the client for its users.
Input

A list of concrete products selected from the /products response (each wrapped in a TokenAndSuggestedQuantity), along with the target supermarket_domain and user_locale. The list represents the final basket the user intends to purchase. The session_token for each item is obtained from the /products call.

Optionally, redirect_url specifies where the user should be sent after checkout. Pepesto appends the supermarket cart URL to it as the escaped cartUrl query parameter.

Output

An URL - deferred deep link to Pepesto's mobile app.

Pricing

0.60 € per request.

Full /mcheckout protocol (Go)
Show protocol definition
// Re-uses the request and response types from session.go
type MCheckoutRequest = CreateSessionRequest
type MCheckoutResponse = CreateSessionResponse


// CreateSessionRequest creates a checkout session for the given supermarket and
// products that must be added to the user's shopping cart.
type CreateSessionRequest struct {
	SupermarketDomain SupermarketDomain `json:"supermarket_domain"`
	UserLocale        Locale            `json:"user_locale,omitempty"`
	RedirectUrl       string            `json:"redirect_url,omitempty"`

	// List of SKUs returned by /products
	SKUs []TokenAndSuggestedQuantity `json:"skus,omitempty"`

	// Deprecated - do not use - use Items instead.
	DeprecatedItems []ProductAndSuggestedQuantity `json:"items,omitempty"`

	// List of additional free-text items that have not yet been matched to concrete
	// products. Our agent will search for these items during the checkout session.
	UnresolvedItems []string `json:"unresolved_items,omitempty"`
}

type TokenAndSuggestedQuantity struct {
	SessionToken  SessionToken `json:"session_token,omitempty"`
	NumUnitsToBuy int          `json:"num_units_to_buy,omitempty"`
}

type CreateSessionResponse struct {
	// SessionId is always returned.
	//
	// If ChargeUser was set to true in the request, the session has been
	// created but is not yet ready for checkout. The referrer should wait for
	// the ChargedUserWebhook callback before proceeding with the checkout flow.
	SessionId string `json:"session_id"`

	// Deferred deep link to Pepesto's mobile app.
	// Client app can redirect their user to this URL. Once the user installs and opens the Pepesto app,
	// checkout will start automatically with the created session.
	MobileHostedUrl string `json:"mobile_hosted_url,omitempty"`
}


type Locale string


// Specifies the quantity of a product to buy.
// If `NumUnitsToBuy` is zero, Pepesto suggests treating the product as a checklist item.
// In this case, Pepesto doesn't suggest buying it by default but displays it in the UI as a reminder (useful for pantry staples like salt).
type ProductAndSuggestedQuantity struct {
	// The product.
	Product IndexedProduct `json:"product,omitempty"`

	// A session token that can be used as an input to /session endpoint.
	SessionToken SessionToken `json:"session_token,omitempty"`

	// The number of units to buy.  A value of 0 indicates a checklist item.
	NumUnitsToBuy int `json:"num_units_to_buy,omitempty"`
}


type SessionToken string


// List of supported supermarket domains can be found at https://api.pepesto.com/supermarkets
type SupermarketDomain string


type IndexedProduct struct {
	// The product name, capturing the most important aspects. Displayed in the default language for the selected `SupermarketDomain`.
	ProductName string `json:"product_name,omitempty"`

	// The product category (e.g., "canned tomatoes", "pasta", etc.).
	Category string `json:"category,omitempty"`

	// The quantity per unit of this product.
	Quantity *QuantityPerUnit `json:"quantity,omitempty"`

	// The price per unit of this product.
	Price *PricePerUnit `json:"price,omitempty"`

	// This is typically URL of the product page on the supermarket's website.
	ProductId string `json:"product_id,omitempty"`

	// URL of an image of the product, usually points to retailer's website.
	ImageUrl string `json:"image_url,omitempty"`

	// URL of an image of the product hosted by Pepesto (not the retailer's website).
	PepestoHostedImageUrl string `json:"pepesto_hosted_image_url,omitempty"`

	// A unique identifier for products sold in multiple supermarket chains.  Allows comparing prices for the same product across different supermarkets.  Populated only for packaged products commonly sold in multiple chains.
	UniversalId int64 `json:"universal_id,omitempty"`

	Classification *ProductClassification `json:"classification,omitempty"`

	// A token that can be passed to the /session endpoint to create a session with this product, if
	// the client app, or the user chooses to add this product to the shopping cart.
	SessionToken SessionToken `json:"session_token,omitempty"`
}


type ProductClassification struct {
	IsBio          bool `json:"is_bio,omitempty"`
	IsFrozen       bool `json:"is_frozen,omitempty"`
	IsSubstitution bool `json:"is_substitution,omitempty"`
}

type QuantityPerUnit struct {
	// Estimated weight per unit (in grams).
	Grams float64 `json:"grams,omitempty"`

	// Volume per unit (in milliliters). Used for liquid products (e.g., 100ml of milk).
	Milliliters float64 `json:"milliliters,omitempty"`

	// Number of pieces per unit (e.g., 2 for a package of 2 chicken breasts).
	Pieces float64 `json:"pieces,omitempty"`

	// Number of bunches per unit (e.g., 2 bunches of parsley).
	Bunches float64 `json:"bunches,omitempty"`
}

type PricePerUnit struct {
	// Price of the product in the smallest currency unit used by the `SupermarketDomain` (e.g., 199 represents 1.99 CHF for coop.ch).
	Price int `json:"price,omitempty"`

	// Price promotion details.
	Promotion PricePromotion `json:"promotion,omitempty"`
}

type PricePromotion struct {
	// `true` if the product is currently on promotion.
	Promo bool `json:"promo,omitempty"`

	// Price discount percentage (not always available).  May not be populated, or the promotion may be of a different type (e.g., "buy 2 get 3").
	PromoPercentage float64 `json:"promo_percentage,omitempty"`
}
Example request
curl -X POST https://s.pepesto.com/api/mcheckout \
    	-H "Authorization: Bearer $PEPESTO_API_KEY" \
    	-H "Content-Type: application/json" \
    	-d '{"supermarket_domain":"coop.ch","redirect_url":"https://example.com/checkout-complete","skus":[{"session_token":"eyJwcm9kdWN0X2lkIjogImh0dHBzOi8vY29vcC5jaC9wYXN0YS8xMjMifQ==","num_units_to_buy":1},{"session_token":"eyJwcm9kdWN0X2lkIjogImh0dHBzOi8vY29vcC5jaC90b21hdG9lcy80NTYifQ==","num_units_to_buy":2}]}'
Session
POST /api/session
How it works
This endpoint creates a checkout session for the user's shopping basket. The returned session ID can be passed to the /checkout endpoint to fulfill the purchase.
Input

A list of concrete products selected from the /products response (each wrapped in a TokenAndSuggestedQuantity), along with the target supermarket_domain and user_locale. The list represents the final basket the user intends to purchase. The session_token for each item is obtained from the /products call.

Output

A shopping session object that encapsulates the user’s final basket for the chosen supermarket, including a session_id that can be passed to the /checkout endpoint.

Pricing

1.20 € per request.

Full /session protocol (Go)
Show protocol definition
// CreateSessionRequest creates a checkout session for the given supermarket and
// products that must be added to the user's shopping cart.
type CreateSessionRequest struct {
	SupermarketDomain SupermarketDomain `json:"supermarket_domain"`
	UserLocale        Locale            `json:"user_locale,omitempty"`
	RedirectUrl       string            `json:"redirect_url,omitempty"`

	// List of SKUs returned by /products
	SKUs []TokenAndSuggestedQuantity `json:"skus,omitempty"`

	// Deprecated - do not use - use Items instead.
	DeprecatedItems []ProductAndSuggestedQuantity `json:"items,omitempty"`

	// List of additional free-text items that have not yet been matched to concrete
	// products. Our agent will search for these items during the checkout session.
	UnresolvedItems []string `json:"unresolved_items,omitempty"`
}

type TokenAndSuggestedQuantity struct {
	SessionToken  SessionToken `json:"session_token,omitempty"`
	NumUnitsToBuy int          `json:"num_units_to_buy,omitempty"`
}

type CreateSessionResponse struct {
	// SessionId is always returned.
	//
	// If ChargeUser was set to true in the request, the session has been
	// created but is not yet ready for checkout. The referrer should wait for
	// the ChargedUserWebhook callback before proceeding with the checkout flow.
	SessionId string `json:"session_id"`

	// Deferred deep link to Pepesto's mobile app.
	// Client app can redirect their user to this URL. Once the user installs and opens the Pepesto app,
	// checkout will start automatically with the created session.
	MobileHostedUrl string `json:"mobile_hosted_url,omitempty"`
}


type Locale string


// Specifies the quantity of a product to buy.
// If `NumUnitsToBuy` is zero, Pepesto suggests treating the product as a checklist item.
// In this case, Pepesto doesn't suggest buying it by default but displays it in the UI as a reminder (useful for pantry staples like salt).
type ProductAndSuggestedQuantity struct {
	// The product.
	Product IndexedProduct `json:"product,omitempty"`

	// A session token that can be used as an input to /session endpoint.
	SessionToken SessionToken `json:"session_token,omitempty"`

	// The number of units to buy.  A value of 0 indicates a checklist item.
	NumUnitsToBuy int `json:"num_units_to_buy,omitempty"`
}


type SessionToken string


// List of supported supermarket domains can be found at https://api.pepesto.com/supermarkets
type SupermarketDomain string


type IndexedProduct struct {
	// The product name, capturing the most important aspects. Displayed in the default language for the selected `SupermarketDomain`.
	ProductName string `json:"product_name,omitempty"`

	// The product category (e.g., "canned tomatoes", "pasta", etc.).
	Category string `json:"category,omitempty"`

	// The quantity per unit of this product.
	Quantity *QuantityPerUnit `json:"quantity,omitempty"`

	// The price per unit of this product.
	Price *PricePerUnit `json:"price,omitempty"`

	// This is typically URL of the product page on the supermarket's website.
	ProductId string `json:"product_id,omitempty"`

	// URL of an image of the product, usually points to retailer's website.
	ImageUrl string `json:"image_url,omitempty"`

	// URL of an image of the product hosted by Pepesto (not the retailer's website).
	PepestoHostedImageUrl string `json:"pepesto_hosted_image_url,omitempty"`

	// A unique identifier for products sold in multiple supermarket chains.  Allows comparing prices for the same product across different supermarkets.  Populated only for packaged products commonly sold in multiple chains.
	UniversalId int64 `json:"universal_id,omitempty"`

	Classification *ProductClassification `json:"classification,omitempty"`

	// A token that can be passed to the /session endpoint to create a session with this product, if
	// the client app, or the user chooses to add this product to the shopping cart.
	SessionToken SessionToken `json:"session_token,omitempty"`
}


type ProductClassification struct {
	IsBio          bool `json:"is_bio,omitempty"`
	IsFrozen       bool `json:"is_frozen,omitempty"`
	IsSubstitution bool `json:"is_substitution,omitempty"`
}

type QuantityPerUnit struct {
	// Estimated weight per unit (in grams).
	Grams float64 `json:"grams,omitempty"`

	// Volume per unit (in milliliters). Used for liquid products (e.g., 100ml of milk).
	Milliliters float64 `json:"milliliters,omitempty"`

	// Number of pieces per unit (e.g., 2 for a package of 2 chicken breasts).
	Pieces float64 `json:"pieces,omitempty"`

	// Number of bunches per unit (e.g., 2 bunches of parsley).
	Bunches float64 `json:"bunches,omitempty"`
}

type PricePerUnit struct {
	// Price of the product in the smallest currency unit used by the `SupermarketDomain` (e.g., 199 represents 1.99 CHF for coop.ch).
	Price int `json:"price,omitempty"`

	// Price promotion details.
	Promotion PricePromotion `json:"promotion,omitempty"`
}

type PricePromotion struct {
	// `true` if the product is currently on promotion.
	Promo bool `json:"promo,omitempty"`

	// Price discount percentage (not always available).  May not be populated, or the promotion may be of a different type (e.g., "buy 2 get 3").
	PromoPercentage float64 `json:"promo_percentage,omitempty"`
}
Example request
curl -X POST https://s.pepesto.com/api/session \
    	-H "Authorization: Bearer $PEPESTO_API_KEY" \
    	-H "Content-Type: application/json" \
    	-d '{"supermarket_domain":"coop.ch","skus":[{"session_token":"eyJwcm9kdWN0X2lkIjogImh0dHBzOi8vY29vcC5jaC9wYXN0YS8xMjMifQ==","num_units_to_buy":1},{"session_token":"eyJwcm9kdWN0X2lkIjogImh0dHBzOi8vY29vcC5jaC90b21hdG9lcy80NTYifQ==","num_units_to_buy":2}]}'
Checkout
POST /api/checkout
How it works
The client app typically runs in an environment that can automate a web page by injecting and running server-provided JavaScript (for example: a mobile WebView, a browser extension, or a Playwright script). The client starts with a checkout-driving session ID (e.g. from /session) and repeatedly calls /checkout with that value as continue_session_id. Each call advances the same session by one “turn”, optionally including the previous turn’s JS result (prev_turn_response) and/or a page screenshot. Each /checkout response returns a NextCheckoutIterationResponse with exactly one instruction.

❗ By following the instructions provided by Pepesto's AI agent, the client app would be able to authenticate the user to the website of the grocery supermarket, and add all necessary products to the shopping cart. However, we do not automate the payment flow - user is still required to verify the shopping cart and checkout manually.
Input

A continue_session_id referencing an existing checkout-driving session, plus optional prev_turn_response (JavaScript execution result) and screenshot payload from the previous turn. Each call advances the same session one step further.

Output

A NextCheckoutIterationResponse containing the current session_id, exactly one instruction to execute (e.g. load_page, await_element, run_js, prompt_user_action, await_js_out_change, or done), and an optional attach_screenshot_on_next_turn flag telling the client to send a screenshot on the next call.
See the full response protocol here.
The most challenging part of the client side implementation is to handle the run_js instruction correctly. Here is a working node.js example that demostrates the correct handling.

Pricing

Free for sessions that have already been created via Pepesto.
Advancing a checkout session with additional /checkout calls does not incur extra per-request charges.

Full /checkout protocol (Go)
Show protocol definition
import pb "pepesto.com/go/proto"

// NextCheckoutIterationRequest represents the request body for advancing
// a checkout-driving session by one "turn" over a REST API.
//
// External clients:
//   - NEVER create sessions via this API.
//   - MUST always provide a valid ContinueSessionID obtained from Pepesto
//     (e.g. via the Pepesto app or other Pepesto APIs).
//
// On each call, the client:
//  1. Sends the current ContinueSessionID (the active session).
//  2. Optionally provides results from the previous instruction:
//     - PrevTurnResponse (JS execution result)
//     - Screenshot (page screenshot)
//  3. Receives a NextCheckoutIterationResponse with an instruction to execute.
type NextCheckoutIterationRequest struct {
	// ContinueSessionID is the ID of an existing driver session that the client
	// wants to advance.
	//
	// This value:
	//   - MUST be obtained by external clients via an out-of-band mechanism
	//     (Pepesto app, grocery search API, etc.).
	//   - MUST be echoed on every subsequent request for the same session.
	ContinueSessionID string `json:"continue_session_id"`

	// PrevTurnResponse contains the result of JavaScript execution performed
	// in response to the previous instruction (if any).
	//
	// If the server did not request JS execution on the previous turn, this
	// should be omitted.
	PrevTurnResponse *JsExecutionResult `json:"prev_turn_response,omitempty"`

	// Screenshot represents an optional screenshot taken after executing the
	// previous instruction. It's a base-64 encoded value.
	//
	// Clients should attach this when the previous response had
	// AttachScreenshotOnNextTurn = true.
	Screenshot string `json:"screenshot,omitempty"`
}

// JsExecutionResult carries the outcome of JavaScript execution done by
// the client on the previous turn.
type JsExecutionResult struct {
	// Result is the string returned by the JavaScript function when
	// execution succeeds.
	Result string `json:"result,omitempty"`

	// Error is a description of why execution failed (JS error, timeout, etc.).
	// If non-empty, the server should treat the execution as failed and prefer
	// this field over Result.
	Error string `json:"error,omitempty"`
}

// NextCheckoutIterationResponse represents the server's response for each turn.
//
// It contains:
//   - SessionID: the session that was advanced.
//   - Exactly one of the instruction fields set (LoadPage, etc.).
//   - Optional metadata for UI/debugging (AttachScreenshotOnNextTurn).
type NextCheckoutIterationResponse struct {
	ProtoResp *pb.NextCheckoutIterationResponse `json:"proto"`
}
Catalog
POST /api/catalog
Input

A supermarket_domain identifying the retailer whose catalog you want to fetch (e.g. "coop.ch").

Output

A list of all indexed products for the given supermarket in Pepesto’s database, including names, prices, images, identifiers, and other metadata required to build storefronts, comparison tools, or custom experiences. The indexed product list typically contains 1,000–2,000 SKUs focused on the most common ingredients used for cooking.

Pricing

9.90 € per request.

Full /catalog protocol (Go)
Show protocol definition
type RetrieveCatalogRequest struct {
	// The supermarket for which to retrieve the catalog.
	SupermarketDomain SupermarketDomain `json:"supermarket_domain"`
}

type RetrieveCatalogResponse struct {
	// A list of indexed products.
	Products []IndexedProduct `json:"products,omitempty"`
}


type IndexedProduct struct {
	// The product name, capturing the most important aspects. Displayed in the default language for the selected `SupermarketDomain`.
	ProductName string `json:"product_name,omitempty"`

	// The product category (e.g., "canned tomatoes", "pasta", etc.).
	Category string `json:"category,omitempty"`

	// The quantity per unit of this product.
	Quantity *QuantityPerUnit `json:"quantity,omitempty"`

	// The price per unit of this product.
	Price *PricePerUnit `json:"price,omitempty"`

	// This is typically URL of the product page on the supermarket's website.
	ProductId string `json:"product_id,omitempty"`

	// URL of an image of the product, usually points to retailer's website.
	ImageUrl string `json:"image_url,omitempty"`

	// URL of an image of the product hosted by Pepesto (not the retailer's website).
	PepestoHostedImageUrl string `json:"pepesto_hosted_image_url,omitempty"`

	// A unique identifier for products sold in multiple supermarket chains.  Allows comparing prices for the same product across different supermarkets.  Populated only for packaged products commonly sold in multiple chains.
	UniversalId int64 `json:"universal_id,omitempty"`

	Classification *ProductClassification `json:"classification,omitempty"`

	// A token that can be passed to the /session endpoint to create a session with this product, if
	// the client app, or the user chooses to add this product to the shopping cart.
	SessionToken SessionToken `json:"session_token,omitempty"`
}


// List of supported supermarket domains can be found at https://api.pepesto.com/supermarkets
type SupermarketDomain string


type ProductClassification struct {
	IsBio          bool `json:"is_bio,omitempty"`
	IsFrozen       bool `json:"is_frozen,omitempty"`
	IsSubstitution bool `json:"is_substitution,omitempty"`
}

type QuantityPerUnit struct {
	// Estimated weight per unit (in grams).
	Grams float64 `json:"grams,omitempty"`

	// Volume per unit (in milliliters). Used for liquid products (e.g., 100ml of milk).
	Milliliters float64 `json:"milliliters,omitempty"`

	// Number of pieces per unit (e.g., 2 for a package of 2 chicken breasts).
	Pieces float64 `json:"pieces,omitempty"`

	// Number of bunches per unit (e.g., 2 bunches of parsley).
	Bunches float64 `json:"bunches,omitempty"`
}

type PricePerUnit struct {
	// Price of the product in the smallest currency unit used by the `SupermarketDomain` (e.g., 199 represents 1.99 CHF for coop.ch).
	Price int `json:"price,omitempty"`

	// Price promotion details.
	Promotion PricePromotion `json:"promotion,omitempty"`
}

type PricePromotion struct {
	// `true` if the product is currently on promotion.
	Promo bool `json:"promo,omitempty"`

	// Price discount percentage (not always available).  May not be populated, or the promotion may be of a different type (e.g., "buy 2 get 3").
	PromoPercentage float64 `json:"promo_percentage,omitempty"`
}


type SessionToken string
Example request
curl -X POST https://s.pepesto.com/api/catalog \
    	-H "Authorization: Bearer $PEPESTO_API_KEY" \
    	-H "Content-Type: application/json" \
    	-d '{"supermarket_domain":"coop.ch"}'
Promotions
POST /api/promotions
Input

A supermarket_domain identifying the retailer whose promo catalog you want to fetch (e.g. "coop.ch").

Output

A list of all indexed promotional products for the given supermarket in Pepesto’s database, including names, prices, images, identifiers, and other metadata required to build storefronts, comparison tools, or custom experiences. The indexed product list only contains SKUs focused on the most common ingredients used for cooking.

Pricing

3.20 € per request.

Full /promotions protocol (Go)
Show protocol definition
type RetrieveCatalogRequest struct {
	// The supermarket for which to retrieve the catalog.
	SupermarketDomain SupermarketDomain `json:"supermarket_domain"`
}

type RetrieveCatalogResponse struct {
	// A list of indexed products.
	Products []IndexedProduct `json:"products,omitempty"`
}


type IndexedProduct struct {
	// The product name, capturing the most important aspects. Displayed in the default language for the selected `SupermarketDomain`.
	ProductName string `json:"product_name,omitempty"`

	// The product category (e.g., "canned tomatoes", "pasta", etc.).
	Category string `json:"category,omitempty"`

	// The quantity per unit of this product.
	Quantity *QuantityPerUnit `json:"quantity,omitempty"`

	// The price per unit of this product.
	Price *PricePerUnit `json:"price,omitempty"`

	// This is typically URL of the product page on the supermarket's website.
	ProductId string `json:"product_id,omitempty"`

	// URL of an image of the product, usually points to retailer's website.
	ImageUrl string `json:"image_url,omitempty"`

	// URL of an image of the product hosted by Pepesto (not the retailer's website).
	PepestoHostedImageUrl string `json:"pepesto_hosted_image_url,omitempty"`

	// A unique identifier for products sold in multiple supermarket chains.  Allows comparing prices for the same product across different supermarkets.  Populated only for packaged products commonly sold in multiple chains.
	UniversalId int64 `json:"universal_id,omitempty"`

	Classification *ProductClassification `json:"classification,omitempty"`

	// A token that can be passed to the /session endpoint to create a session with this product, if
	// the client app, or the user chooses to add this product to the shopping cart.
	SessionToken SessionToken `json:"session_token,omitempty"`
}


// List of supported supermarket domains can be found at https://api.pepesto.com/supermarkets
type SupermarketDomain string


type ProductClassification struct {
	IsBio          bool `json:"is_bio,omitempty"`
	IsFrozen       bool `json:"is_frozen,omitempty"`
	IsSubstitution bool `json:"is_substitution,omitempty"`
}

type QuantityPerUnit struct {
	// Estimated weight per unit (in grams).
	Grams float64 `json:"grams,omitempty"`

	// Volume per unit (in milliliters). Used for liquid products (e.g., 100ml of milk).
	Milliliters float64 `json:"milliliters,omitempty"`

	// Number of pieces per unit (e.g., 2 for a package of 2 chicken breasts).
	Pieces float64 `json:"pieces,omitempty"`

	// Number of bunches per unit (e.g., 2 bunches of parsley).
	Bunches float64 `json:"bunches,omitempty"`
}

type PricePerUnit struct {
	// Price of the product in the smallest currency unit used by the `SupermarketDomain` (e.g., 199 represents 1.99 CHF for coop.ch).
	Price int `json:"price,omitempty"`

	// Price promotion details.
	Promotion PricePromotion `json:"promotion,omitempty"`
}

type PricePromotion struct {
	// `true` if the product is currently on promotion.
	Promo bool `json:"promo,omitempty"`

	// Price discount percentage (not always available).  May not be populated, or the promotion may be of a different type (e.g., "buy 2 get 3").
	PromoPercentage float64 `json:"promo_percentage,omitempty"`
}


type SessionToken string
Example request
curl -X POST https://s.pepesto.com/api/promotions \
    	-H "Authorization: Bearer $PEPESTO_API_KEY" \
    	-H "Content-Type: application/json" \
    	-d '{"supermarket_domain":"coop.ch"}'
Link
POST /api/link
How it works

This endpoint is used to create an API key by linking it to a previously completed payment.

Expected flow:

  1. Purchase API credits via Stripe here.
  2. Get an API key by calling this endpoint and passing the email address you used for the Stripe payment.
  3. The API key is returned in the response. Note: the API key is returned only once, so make sure to store it safely.
Input

The email address that you used in the payment form. If you used the alias field in the payment form, pass the alias in addition to the email address.

Output

Your API key. Note: the API key is returned only once, so make sure to store it safely.

Pricing

free

Full /link protocol (Go)
Show protocol definition
type LinkEmailRequest struct {
	Email string `json:"email"`

	// Set this if you used an alias when purchasing credits.
	// This field can be used to support and distinguish between different
	// api keys for the same email.
	Alias string `json:"alias,omitempty"`
}

type LinkEmailResponse struct {
	ApiKey string `json:"api_key"`
}
Example request
curl -X POST https://s.pepesto.com/api/link \
     \
    	-H "Content-Type: application/json" \
    	-d '{"email":"nobody@nobody.com"}'
Credits
POST /api/credits
How it works
Check the remaining credits for your API key
Input

n/a (empty)

Output

Pricing

free

Full /credits protocol (Go)
Show protocol definition
type CheckCreditsRequest struct {
}

type CheckCreditsResponse struct {
	EuroCents int64 `json:"euro_cents"`
}
Example request
curl -X POST https://s.pepesto.com/api/credits \
    	-H "Authorization: Bearer $PEPESTO_API_KEY" \
    	-H "Content-Type: application/json" \
    	-d '{}'

Live Demo

Want to see Pepesto’s grocery shopping API in action? Explore our interactive demo, try out the /oneshot endpoint, and inspect real responses in the browser: Demo.

Supported Supermarkets

Below is an overview of supported supermarkets by country:

CountrySupermarkets
BEah.be colruyt.be delhaize.be
BGbulmag.org ebag.bg
CHaldi-now.ch coop.ch migros.ch
DEshop.rewe.de
DKnemlig.com
GBasda.com groceries.morrisons.com sainsburys.co.uk tesco.com waitrose.com
IEdunnesstoresgrocery.com shop.supervalu.ie tesco.ie
ITspesaonline.conad.it spesaonline.esselunga.it
NLah.nl jumbo.com plus.nl
NOmeny.no
PLfrisco.pl zakupy.auchan.pl

Please reach out and contact us if you would like to onboard more supermarkets relevant to your use-case.

Let's Build Together

Your food app or AI agent can go beyond planning — it can shop, too. Pepesto gives you the tools to make that happen across real European supermarkets, with zero infrastructure work

Whether you're building a personal AI assistant, a nutrition platform, or a recipe-based shopping app, Pepesto’s API can bring your user journey full circle — from recipe to real-world checkout.

Start integrating today. We’ll handle the groceries.

Interested in early access or co-developing features? Contact us to become a design partner and enable grocery shopping in your app or agent.

→ Contact Us