# A101 API scraper — Turkish discount grocery chain (a101.com.tr): search A101 Kapıda groceries and A101 Ekstra products, list the Aldın Aldın and Haftanın Yıldızları campaigns, read a product — shelf price, struck price, campaign badges and dates, in-basket and multi-buy prices kept apart, unit price, barcodes, net weight, storage, origin, stock, description. Prices in TRY. No account, no browser.

> Search A101's online shops by keyword: `kapida` (groceries and household at the delivery store's prices) or `ekstra` (electronics, home, fashion) — 60 products a page, Ekstra's sort orders. Each row: product id, title, brand, URL, image, shelf price, struck price and discount, campaign badges (Aldın Aldın, Haftanın Yıldızları …) with dates, and for Kapıda the conditional in-basket price, multi-buy price and unit price kept apart.
> ReefAPI engine `a101` · 4 endpoints · clean JSON, no scraping or browsers to manage.

## How to call
- **Endpoint:** `POST https://api.reefapi.com/a101/v1/<action>` with a JSON body.
- **Auth:** header `x-api-key: <YOUR_REEFAPI_KEY>` — create one free (1,000 credits, no card): https://reefapi.com/signup
- **Response (every call):** `{ ok: boolean, data: ..., meta: { record_count, credits, ... }, error: { code, message } }` — branch on `ok`. Failed calls are free except verified SHEIN NOT_FOUND on product/detail and price (4 credits).
- **One key + one shared credit pool** across every ReefAPI API. Per-call credits are listed on each endpoint below.
- **Use it from an AI agent (MCP):** connect `https://api.reefapi.com/mcp` (remote streamable-http, `Authorization: Bearer <key>`) and your assistant can call these actions directly.

## Endpoints

### POST /a101/v1/search — 1 credit
Search A101's online shops by keyword: `kapida` (groceries and household at the delivery store's prices) or `ekstra` (electronics, home, fashion) — 60 products a page, Ekstra's sort orders. Each row: product id, title, brand, URL, image, shelf price, struck price and discount, campaign badges (Aldın Aldın, Haftanın Yıldızları …) with dates, and for Kapıda the conditional in-basket price, multi-buy price and unit price kept apart.

**Parameters:**
- `query` (string, required) — What to search for, in Turkish ('peynir', 'yumurta', 'deterjan', 'televizyon', 'kulaklık'). Brand and product words work; typos are tolerated by A101's search.
- `channel` (enum, optional, default "kapida") — Which A101 online shop to search. Groceries (cheese, milk, detergent) are only in `kapida`; TVs, phones, furniture and clothes are in `ekstra`. [one of: kapida, ekstra]
- `page` (integer, optional, default 1) — Result page, 1-based. A101 serves 60 products per page; `total_pages` says how many exist.
- `sort` (enum, optional, default "relevance") — Result order — Ekstra's own sort options. Kapıda's search has no working sort, so on `kapida` only `relevance` is accepted. [one of: relevance, price_asc, price_desc, newest, discount, best_selling]
- `include_fallback_results` (boolean, optional, default false) — When A101 has nothing matching the query it fills the page with unrelated products. They are dropped by default (`fallback_results_dropped` counts them); set true to also get them in `fallback_results`.
- `max_rotations` (integer, optional, default 4) — Advanced: how many times to retry a difficult request (1-8, default 4).

**Returns:** results[]{position, product_id, channel, title, brand, url, image, price (number, TRY — the shelf price; Kapıda: the delivery store's price), price_before_discount (struck price, null when none), discount_percent, currency=TRY, in_stock, campaigns[{code, label, site_name, starts_on, ends_on}], category, category_id, category_path[]; Kapıda only: sold_at_store, store_code, campaign_conditions[] (the site's campaign text), basket_offer{price, regular_price, code, label, condition, ends_on} (a price that applies only inside a qualifying basket — NOT the shelf price), multi_buy{min_quantity, unit_price, regular_unit_price, code, label, ends_on}, unit_price{price, per, label} (the site's own unit price), sales_unit, base_unit, max_order_quantity; Ekstra only: barcode (GTIN, check digit validated), sales_channels[], max_order_quantity, country_of_origin}, count, total_results (null when unknown), total_pages, page, page_size (60), has_more, channel, store_code (kapida), keyword_matches_on_page, fallback_results_dropped, fallback_total, fallback_results[] (only with include_fallback_results), page_notice

**Example request body:**
```json
{
  "query": "peynir",
  "channel": "kapida"
}
```

### POST /a101/v1/product/detail — 1 credit
One A101 product by id or URL, from Kapıda or Ekstra: shelf price, struck price and discount, campaign badges with dates, in-basket and multi-buy prices, unit price, barcodes, net and gross weight, storage condition, country of origin, shelf life, stock count (Ekstra), instalment offers (Ekstra), images, breadcrumb, description and specifications.

**Parameters:**
- `product_id` (string, optional) — The A101 product id — `product_id` of a search or campaigns row ('12002661' Kapıda, '26077738' Ekstra).
- `url` (string, optional) — Alternative to `product_id`: an a101.com.tr product URL (…_p-<id> or /kapida/product/<id>).
- `channel` (enum, optional, default "auto") — Which A101 shop the product id belongs to. `auto` looks in both; a URL decides it by itself. [one of: auto, kapida, ekstra]
- `max_rotations` (integer, optional, default 4) — Advanced: how many times to retry a difficult request (1-8, default 4).

**Returns:** product{position, product_id, channel, title, brand, url, image, price (number, TRY — the shelf price; Kapıda: the delivery store's price), price_before_discount (struck price, null when none), discount_percent, currency=TRY, in_stock, campaigns[{code, label, site_name, starts_on, ends_on}], category, category_id, category_path[]; Kapıda only: sold_at_store, store_code, campaign_conditions[] (the site's campaign text), basket_offer{price, regular_price, code, label, condition, ends_on} (a price that applies only inside a qualifying basket — NOT the shelf price), multi_buy{min_quantity, unit_price, regular_unit_price, code, label, ends_on}, unit_price{price, per, label} (the site's own unit price), sales_unit, base_unit, max_order_quantity; Ekstra only: barcode (GTIN, check digit validated), sales_channels[], max_order_quantity, country_of_origin, barcodes[] (Kapıda: every barcode A101 lists), net_weight_g, gross_weight_g, storage_condition, shelf_life_days, private_label, return_days, description, breadcrumb[{id, name, url}], images[], campaign_period{name, starts_on, ends_on}; Ekstra: base_id, stock_count, stock_level, collections[], badges[], installments[{card, max_interest_free_installments}], max_interest_free_installments, specifications[{name, value}], variants[{product_id, size, color, price, in_stock, url}], product_page (false when only the search index could be read), other_channel_offer{channel, product_id, price, price_before_discount, discount_percent, in_stock, url, store_code} (the same product in the other A101 shop, at that shop's own price — prices differ between Kapıda and Ekstra; null when it is sold in one shop)}

**Example request body:**
```json
{
  "product_id": "12002661"
}
```

### POST /a101/v1/campaigns — 1 credit
The products of an A101 campaign page — Aldın Aldın, Haftanın Yıldızları, Aldın Aldın X, Aylık Fırsat, 10 TL ve üzeri sepette and Çok Al Az Öde on Kapıda; Aldın Aldın and Aldın Aldın Ekstra on Ekstra — 60 per page, with the same row fields as search.

**Parameters:**
- `campaign` (enum, optional, default "aldin_aldin") — Which A101 campaign to list. Each campaign exists in the channel(s) named in brackets. [one of: aldin_aldin, haftanin_yildizlari, aldin_aldin_x, aylik_firsat, sepette_10_tl, cok_al_az_ode, aldin_aldin_ekstra]
- `channel` (enum, optional, default "kapida") — Which A101 online shop to search. Groceries (cheese, milk, detergent) are only in `kapida`; TVs, phones, furniture and clothes are in `ekstra`. [one of: kapida, ekstra]
- `page` (integer, optional, default 1) — Result page, 1-based. A101 serves 60 products per page; `total_pages` says how many exist.
- `max_rotations` (integer, optional, default 4) — Advanced: how many times to retry a difficult request (1-8, default 4).

**Returns:** results[]{position, product_id, channel, title, brand, url, image, price (number, TRY — the shelf price; Kapıda: the delivery store's price), price_before_discount (struck price, null when none), discount_percent, currency=TRY, in_stock, campaigns[{code, label, site_name, starts_on, ends_on}], category, category_id, category_path[]; Kapıda only: sold_at_store, store_code, campaign_conditions[] (the site's campaign text), basket_offer{price, regular_price, code, label, condition, ends_on} (a price that applies only inside a qualifying basket — NOT the shelf price), multi_buy{min_quantity, unit_price, regular_unit_price, code, label, ends_on}, unit_price{price, per, label} (the site's own unit price), sales_unit, base_unit, max_order_quantity; Ekstra only: barcode (GTIN, check digit validated), sales_channels[], max_order_quantity, country_of_origin}, count, total_results, total_pages, page, page_size (60), has_more, channel, campaign, campaign_code, campaign_page, store_code (kapida)

### POST /a101/v1/suggest — 1 credit
A101's own search autocomplete for a partial keyword in Kapıda or Ekstra: the top matching products with price and the category / brand suggestions the site shows.

**Parameters:**
- `query` (string, required) — What the user has typed so far (2+ characters).
- `channel` (enum, optional, default "kapida") — Which A101 online shop to search. Groceries (cheese, milk, detergent) are only in `kapida`; TVs, phones, furniture and clothes are in `ekstra`. [one of: kapida, ekstra]
- `max_rotations` (integer, optional, default 4) — Advanced: how many times to retry a difficult request (1-8, default 4).

**Returns:** products[]{position, product_id, channel, title, brand, url, image, price (number, TRY — the shelf price; Kapıda: the delivery store's price), price_before_discount (struck price, null when none), discount_percent, currency=TRY, in_stock, campaigns[{code, label, site_name, starts_on, ends_on}], category, category_id, category_path[]; Kapıda only: sold_at_store, store_code, campaign_conditions[] (the site's campaign text), basket_offer{price, regular_price, code, label, condition, ends_on} (a price that applies only inside a qualifying basket — NOT the shelf price), multi_buy{min_quantity, unit_price, regular_unit_price, code, label, ends_on}, unit_price{price, per, label} (the site's own unit price), sales_unit, base_unit, max_order_quantity; Ekstra only: barcode (GTIN, check digit validated), sales_channels[], max_order_quantity, country_of_origin}, categories[{name, count}], brands[{name, count}] (ekstra), total_results, channel, query

**Example request body:**
```json
{
  "query": "peynir"
}
```

## More
- Try it live, no code: https://reefapi.com/playground?engine=a101
- Human docs page: https://reefapi.com/docs/a101
- Overview page: https://reefapi.com/a101-api
- Every ReefAPI API in one file (for your AI): https://reefapi.com/llms-full.txt
