Migros Sanalmarket API & Scraper
The Migros API turns Migros Sanalmarket, the online grocery of Turkey's largest supermarket chain, into clean JSON in seven actions: search, product/detail, product/barcode, deals, category, categories and suggest.
🤖 Using an AI assistant? Copy this link into ChatGPT / Claude / Cursor — it reads every endpoint and parameter instantly and tells you if this API fits your use case.
Every product comes back with two prices kept apart - price, what a shopper without a Money loyalty card pays, and member_price, the Money-card price - because the number Migros displays is the member price whenever a Money promotion runs. On 2026-09-15 the products flagged with a member price were exactly the products on Migros's own Money discount list: 73 of 73 for the keyword peynir, no difference either way. Rows also carry the unit price per kg or litre re-based on the non-member price, whether the product is sold by the piece or by weight, availability, the order cap, multi-buy offers such as 2.si %80, clearance flags, basket-only discounts kept out of the price, brand and the full category path. product/detail adds every image, the label sheet as structured specs (ingredients, allergen warning, net quantity, origin, storage), and the supplier details Migros is required to show. Verification on 2026-09-15: 20 of 20 searches and 30 of 30 product reads succeeded, 37 of 37 controlled cases passed, 7 of 7 products matched an independent read of the source on title, price, member price and availability, and search rows agreed with product records on 30 of 30 products. Prices are TRY. No Migros account needed - one ReefAPI key and the standard { ok, data, meta, error } envelope.
The price Migros displays is the Money-card price whenever a Money promotion runs
This is the one thing to know before you build a Turkish grocery price tracker on Migros. When a Money promotion is active, the price shown on the shelf is the loyalty-card price; a shopper without the card pays more. The API returns both, and never merges them. Measured on 2026-09-15.
| Migros product | price (no Money card) | member_price (Money card) | Unit price, no card / with card |
|---|---|---|---|
| Bahçıvan Çeçil Peyniri 200 G | 229.95 TRY | 149.90 TRY | 1,149.75 / 749.50 TRY per kg |
| Kahve Dünyası Tambol %85 Kakao Bitter Çikolata 77 G | 99.90 TRY | 69.90 TRY | per-kg price re-based the same way |
| Torku Bitter Çikolata 60 G | 69.95 TRY | 59.90 TRY | per-kg price re-based the same way |
| Torku Tam Yağlı Taze Kaşar Peyniri 600 G | 479.90 TRY | null - no Money promotion | 799.83 TRY per kg |
| Akpınar Taze Beyaz Peynir Kg | 429.00 TRY per kg (sold by weight) | null | null - the price is already per kg |
member_discount_percent is computed from the two prices and equalled Migros's own discount figure (35 percent on the first row). The unit price Migros prints is calculated on the member price; unit_price is re-based on the non-member price and the printed figure is returned as member_unit_price. A basket discount such as "Sepette %20 İndirim" is applied only in the basket, so it is returned in basket_discounts and never subtracted from price.
Real request and response JSON
Captured from the indexed primary action, search, on .
{
"method": "POST",
"url": "https://api.reefapi.com/migros/v1/search",
"headers": {
"x-api-key": "$REEF_KEY",
"content-type": "application/json"
},
"body": {
"query": "peynir"
}
}{
"ok": true,
"meta": {
"api": "migros",
"endpoint": "search",
"mode": "live",
"latency_ms": 525.8,
"record_count": 30,
"bytes": 80652,
"cache_hit": false,
"pagination": {
"page": 1,
"has_more": true,
"page_size": 30,
"total": 454
}
},
"data": {
"results": [
{
"product_id": "10406815",
"store_product_id": "20000010406815",
"title": "Bahçıvan Çeçil Peyniri 200 G",
"brand": {
"id": 729,
"name": "Bahçıvan",
"url": "https://www.migros.com.tr/bahcivan-b-2d9"
},
"category": {
"id": 10046,
"name": "Yöresel Peynir",
"url": "https://www.migros.com.tr/yoresel-peynir-c-273e"
},
"breadcrumb": [
{
"id": "[trimmed-depth]",
"name": "[trimmed-depth]",
"url": "[trimmed-depth]"
},
{
"id": "[trimmed-depth]",
"name": "[trimmed-depth]",
"url": "[trimmed-depth]"
},
{
"id": "[trimmed-depth]",
"name": "[trimmed-depth]",
"url": "[trimmed-depth]"
}
],
"url": "https://www.migros.com.tr/bahcivan-cecil-peyniri-200-g-p-9ecb9f",
"image": "https://images.migrosone.com/sanalmarket/product/10406815/10406815_1-701de7-1650x1650.jpg",
"price": 229.95,
"price_display": "229,95 TL",
"currency": "TRY",
"original_price": null,
"original_price_display": null,
"discount_percent": null,
"member_price": 149.9,
"member_price_display": "149,90 TL",
"member_discount_percent": 35,
"member_program": "Money",
"unit_price": 1149.75,
"unit_price_unit": "Kg",
"member_unit_price": 749.5,
"sold_by": "piece",
"price_per": "piece",
"availability": "in_stock",
"in_stock": true,
"max_order_quantity": 8,
"max_order_grams": null,
"order_step_grams": null,
"min_order_grams": null,
"badges": [
{
"type": "[trimmed-depth]",
"text": "[trimmed-depth]"
},
{
"type": "[trimmed-depth]",
"text": "[trimmed-depth]"
}
],
"multi_buy_offer": null,
"clearance": false,
"basket_discounts": [],
"best_seller": {
"rank": 1,
"category": "Yöresel Peynir",
"text": "Yöresel Peynir kategorisinin en çok satan 1. ürünüdür"
},
"rating": null,
"review_count": null,
"rating_scale": null,
"sponsored": false
},
{
"product_id": "10100027",
"store_product_id": "20000010100027",
"title": "Tahsildaroğlu Taze Tam Yağlı Dil Peyniri 200 G",
"brand": {
"id": 728,
"name": "Tahsildaroğlu",
"url": "https://www.migros.com.tr/tahsildaroglu-b-2d8"
},
"category": {
"id": 10046,
"name": "Yöresel Peynir",
"url": "https://www.migros.com.tr/yoresel-peynir-c-273e"
},
"breadcrumb": [
{
"id": "[trimmed-depth]",
"name": "[trimmed-depth]",
"url": "[trimmed-depth]"
},
{
"id": "[trimmed-depth]",
"name": "[trimmed-depth]",
"url": "[trimmed-depth]"
},
{
"id": "[trimmed-depth]",
"name": "[trimmed-depth]",
"url": "[trimmed-depth]"
}
],
"url": "https://www.migros.com.tr/tahsildaroglu-taze-tam-yagli-dil-peyniri-200-g-p-9a1d3b",
"image": "https://images.migrosone.com/sanalmarket/product/10100027/10100027-794c6a-1650x1650.jpg",
"price": 199.9,
"price_display": "199,90 TL",
"currency": "TRY",
"original_price": null,
"original_price_display": null,
"discount_percent": null,
"member_price": null,
"member_price_display": null,
"member_discount_percent": null,
"member_program": null,
"unit_price": 999.5,
"unit_price_unit": "Kg",
"member_unit_price": null,
"sold_by": "piece",
"price_per": "piece",
"availability": "in_stock",
"in_stock": true,
"max_order_quantity": 8,
"max_order_grams": null,
"order_step_grams": null,
"min_order_grams": null,
"badges": [],
"multi_buy_offer": null,
"clearance": false,
"basket_discounts": [
"Sepette %20 İndirim"
],
"best_seller": {
"rank": 2,
"category": "Yöresel Peynir",
"text": "Yöresel Peynir kategorisinin en çok satan 2. ürünüdür"
},
"rating": null,
"review_count": null,
"rating_scale": null,
"sponsored": false
},
{
"product_id": "10409164",
"store_product_id": "20000010409164",
"title": "Bahçıvan Hellim Peyniri 225 G",
"brand": {
"id": 729,
"name": "Bahçıvan",
"url": "https://www.migros.com.tr/bahcivan-b-2d9"
},
"category": {
"id": 10046,
"name": "Yöresel Peynir",
"url": "https://www.migros.com.tr/yoresel-peynir-c-273e"
},
"breadcrumb": [
{
"id": "[trimmed-depth]",
"name": "[trimmed-depth]",
"url": "[trimmed-depth]"
},
{
"id": "[trimmed-depth]",
"name": "[trimmed-depth]",
"url": "[trimmed-depth]"
},
{
"id": "[trimmed-depth]",
"name": "[trimmed-depth]",
"url": "[trimmed-depth]"
}
],
"url": "https://www.migros.com.tr/bahcivan-hellim-peyniri-225-g-p-9ed4cc",
"image": "https://images.migrosone.com/sanalmarket/product/10409164/10409164-c6d9cc-1650x1650.jpg",
"price": 259.9,
"price_display": "259,90 TL",
"currency": "TRY",
"original_price": null,
"original_price_display": null,
"discount_percent": null,
"member_price": 149.9,
"member_price_display": "149,90 TL",
"member_discount_percent": 42,
"member_program": "Money",
"unit_price": 1155.11,
"unit_price_unit": "Kg",
"member_unit_price": 666.22,
"sold_by": "piece",
"price_per": "piece",
"availability": "in_stock",
"in_stock": true,
"max_order_quantity": 8,
"max_order_grams": null,
"order_step_grams": null,
"min_order_grams": null,
"badges": [
{
"type": "[trimmed-depth]",
"text": "[trimmed-depth]"
},
{
"type": "[trimmed-depth]",
"text": "[trimmed-depth]"
}
],
"multi_buy_offer": null,
"clearance": false,
"basket_discounts": [],
"best_seller": null,
"rating": null,
"review_count": null,
"rating_scale": null,
"sponsored": false
}
],
"count": 30,
"total_results": 454,
"page": 1,
"page_count": 16,
"sponsored_dropped": 6,
"sort_applied": "relevance",
"filters_available": {
"categories": [
{
"id": 4,
"name": "Süt, Kahvaltılık",
"count": 392
},
{
"id": 70651,
"name": "Atıştırmalık",
"count": 36
},
{
"id": 74872,
"name": "Maç Özel",
"count": 16
}
],
"discounts": [
{
"id": "money",
"name": "Money İndirimli Market Ürünleri",
"count": 74
},
{
"id": "multi_buy",
"name": "Çoklu İndirim Kampanyaları ve Hediyeli Ürünler",
"count": 6
}
],
"brands": [
{
"id": 728,
"name": "Tahsildaroğlu",
"count": 29
},
{
"id": 729,
"name": "Bahçıvan",
"count": 25
},
{
"id": 149,
"name": "Ariste",
"count": 20
}
],
"lifestyle": [
{
"id": 113,
"name": "Glutensiz",
"count": 5
},
{
"id": 116,
"name": "Laktozsuz",
"count": 10
},
{
"id": 102,
"name": "Organik",
"count": 6
}
]
},
"title": "Arama",
"breadcrumb": [
{
"name": "Arama",
"url": null
}
],
"query": "peynir"
}
}What the Migros Sanalmarket API does
| Action | Description | Concrete use case | Key params |
|---|---|---|---|
| search | Search migros.com.tr by keyword. Each product comes with its shelf price and, separately, the Money-card member price when Migros runs one, the per-kg or per-litre unit price, availability, campaign badges (multi-buy, clearance, basket discount), brand and category. Sort by price, discount or best sellers, and filter by brand, category, discount type or lifestyle facet. Paid placements are removed unless you ask for them. | Pricing teams call search to search migros.com.tr by keyword. | query, page, sort, brand_ids, category_ids, ... |
| product/detail | The full Migros product record by `product_id` (or product URL): title, brand, breadcrumb, every image, the shelf price, the struck-through price and discount, the Money-card member price kept separate, unit price, availability and order limits, campaign badges, the label sheet (ingredients, allergen warning, net quantity, origin, storage and every other labelled line as `specs`), the supplier details Migros is legally required to show, and the store the prices belong to. | Marketplace operators call product/detail to get the full Migros product record by `product_id` (or product URL). | product_id, max_rotations |
| product/barcode | Look a product up by the EAN/GTIN barcode on its pack and get the same full record as product/detail — the quickest way to match your own catalogue to Migros prices. | Catalog enrichment teams call product/barcode to look a product up by the EAN/GTIN barcode on its pack and get the same full record as product…. | barcode, max_rotations |
| deals | Migros's own discount pages: all discounted products, Money-card member discounts, multi-buy campaigns, Migroskop and 'Gördüğünüze İnanın'. Same rows, sorting and filters as search. | Retail analysts call deals to get migros's own discount pages. | type, page, sort, brand_ids, category_ids, ... |
| category | Every product in one Migros category, paged, with the same rows, sorting and filters as search. Category ids come from `categories`, from search filters or from any row's breadcrumb. | Pricing teams call category to get every product in one Migros category, paged, with the same rows, sorting and filters as search. | category, page, sort, brand_ids, discount, ... |
| categories | Migros Sanalmarket's top-level shelf: every main category with its id and URL, plus the site's campaign pages (mapped to the `deals` type that reads them). | Marketplace operators call categories to get migros Sanalmarket's top-level shelf. | max_rotations |
| suggest | Autocomplete as a Migros shopper types: suggested search phrases, matching brands and categories, and the top matching products with prices. | Catalog enrichment teams call suggest to get autocomplete as a Migros shopper types. | query, max_rotations |
Call search from your stack
curl -X POST https://api.reefapi.com/migros/v1/search \
-H "x-api-key: $REEF_KEY" \
-H "content-type: application/json" \
-d '{"query":"peynir"}'import requests
r = requests.post(
"https://api.reefapi.com/migros/v1/search",
headers={"x-api-key": REEF_KEY},
json={
"query": "peynir"
},
)
print(r.json()["data"])const res = await fetch("https://api.reefapi.com/migros/v1/search", {
method: "POST",
headers: {
"x-api-key": process.env.REEF_KEY,
"content-type": "application/json",
},
body: JSON.stringify({
"query": "peynir"
}),
});
const { ok, data, meta, error } = await res.json();Ask your MCP-connected assistant: call reefapi.migros.search with {"query":"peynir"}.Who uses this API and why
- Turkish grocery price trackers record price and member_price separately, because the displayed shelf price is the Money-card price whenever a promotion runs.
- FMCG brands monitor their products' Money discounts, multi-buy offers and clearance flags across Migros categories with deals and category.
- Retail analysts compare per-kg and per-litre unit prices on a like-for-like non-member basis using unit_price.
- Catalogue teams match their own EANs to Migros products with product/barcode and pull ingredients, allergen warnings and origin from the label sheet.
Questions developers ask before integrating
Which price should I store - price or member_price?
Store both, and label them. price is what a shopper without a Money card pays; member_price is the Money-card price and is null when no Money promotion runs. On 2026-09-15, 12 of 30 sampled products had a member price, and on the Money discount page 30 of 30 rows did - always below price. member_program reads Money whenever member_price is set.
How do I know a member price is really member-only?
Because it lines up with Migros's own list. For the keyword peynir, the 73 products the API flagged with a member price were exactly the 73 products on Migros's Money discount filter - none missing, none extra. On every one of those products checked in full, the regular and non-member prices were equal and only the member price was lower.
Why is unit_price different from the per-kg price on the Migros page?
Because the page calculates it on the member price. Bahçıvan Çeçil Peyniri 200 G prints 749.50 TRY per kg, which is 149.90 divided by 0.2 kg - the Money price. unit_price returns 1,149.75, the per-kg price at the non-member 229.95, and member_unit_price returns the 749.50 the page prints. Products without a Money promotion have one unit price and member_unit_price is null.
Some products are priced per kg - how do I tell?
sold_by reads weight and price_per reads kg. Akpınar Taze Beyaz Peynir Kg is 429.00 TRY per kg, ordered from 1,000 g in 100 g steps up to 8 kg; the order limits come back in grams. Piece-sold products read sold_by piece, and max_order_quantity is Migros's order cap - 8 on most products - not a stock count.
Do filters and sorting really apply?
Yes, and Migros confirms the sort it used in sort_applied. Measured on 2026-09-15 for peynir: 451 results unfiltered, 55 with two brand ids, 73 with the Money discount filter, 35 with one category id, 10 with the lactose-free lifestyle facet. Price sorts came back in order on page 2. Migros places sponsored products on the first page regardless of your filters and sort, so they are removed by default and counted in sponsored_dropped; include_sponsored=true keeps them, flagged.
Can I look a product up by barcode?
Yes. product/barcode takes the EAN on the pack and returns the same full record as product/detail - the Coca-Cola 1.5 L EAN 5449000000439 resolved to Migros product 08010903 at 80.00 TRY. An unknown barcode returns NOT_FOUND. Migros does not print the barcode inside the product record itself, so product/detail returns barcode as null.
Which campaign pages can I read?
deals reads Migros's own discount pages: all discounted products (3,157 on 2026-09-15), Money-card discounts (2,100), multi-buy and gift campaigns (363, each row carrying its offer text such as 2.si %80), Migroskop (2,467) and Gördüğünüze İnanın, which was empty between campaigns that day and returns an honest empty list. Rows, sorting and filters are the same as search.
What does Migros NOT publish?
Product ratings and reviews - there are none, so rating, review_count and rating_scale are null. No stock count, only availability. No structured nutrition table: in 30 sampled products the label information came as labelled lines, returned in specs. Label sheets are missing on some non-food categories (baby, produce, paper, some cosmetics and cleaning products), so description and specs are null there. Prices are those of Migros's default online store for a visitor with no delivery address; prices for a specific delivery address are not offered.
What is the Migros Sanalmarket API?
Migros Sanalmarket API is a ReefAPI endpoint group for turkish online grocery: shelf prices, money-card member prices and full label sheets. It returns live JSON through POST requests under /migros/v1.
Is the Migros Sanalmarket API free to try?
Yes. ReefAPI starts with 1,000 free credits, no card required. Migros Sanalmarket calls use the same shared credit balance as every other ReefAPI engine.
Do I need a Migros Sanalmarket login or account?
No login to Migros Sanalmarket is needed for the API response. You call ReefAPI with your x-api-key header, and the playground can run live examples before you create a production key.
How fresh is the Migros Sanalmarket data?
The page example is captured from a live search call, and production requests fetch live data through ReefAPI rather than a static sample.
How many credits does the Migros Sanalmarket API use?
Migros Sanalmarket actions currently cost 1 credit per successful call. Failed or blocked calls are free, and all APIs draw from one credit pool.
Can I call Migros Sanalmarket from an AI assistant or MCP client?
Yes. Connect ReefAPI once through MCP and your assistant can call migros actions with the same key, credit pool and JSON envelope used by normal REST requests.