# Publix API scraper — delivery.publix.com product search, categories, BOGO deals, product detail and store locator with each Publix store's price. No account, no browser.

> Search delivery.publix.com at one Publix store: regular price (price list), was-price, member/card price and promotions apart, unit price, stock level, brand and size, in the site's own result order.
> ReefAPI engine `publix` · 7 endpoints · clean JSON, no scraping or browsers to manage.

## How to call
- **Endpoint:** `POST https://api.reefapi.com/publix/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). Send the key as `Authorization: Bearer <key>`, or put it in the URL (`?key=<key>`) when the client has no header field, as ChatGPT does.

## Endpoints

### POST https://api.reefapi.com/publix/v1/search — 3 credits
Search delivery.publix.com at one Publix store: regular price (price list), was-price, member/card price and promotions apart, unit price, stock level, brand and size, in the site's own result order.

**Parameters:**
- `query` (string, required) — What to look for, as typed in the delivery.publix.com search box ('milk').
- `page` (integer, optional, default 1) — 1-based page. The site's order can shift between calls, so reading page by page can repeat or skip a product; for a complete scan follow `next_cursor` instead. A page past the end is INVALID_PARAM; read `last_page`/`has_more`.
- `per_page` (integer, optional, default 20) — Products per page (1-40, default 20).
- `include_unmatched` (boolean, optional, default false) — delivery.publix.com pads a no-match search with unrelated products and does not flag them. Every row carries `matches_query`; false (default) drops a page on which no row shares a word with the query (counted in `unmatched_dropped`), true keeps it.
- `cursor` (string, optional) — `next_cursor` from the previous answer, sent WITH the same query/slug/type, store parameters and per_page (and no `page`). Returns the next page without products already returned: rows the site moved are dropped and counted in `duplicates_dropped`, the page is filled from the rest of the current list (`skipped_recovered` = products offset paging would have skipped).
- `postal_code` (string, optional) — US ZIP code whose Publix store prices the results (Publix prices and range differ by store). Default 33801 — then `store_defaulted` is true.
- `store_id` (string, optional) — Optional Publix store id from `stores` (wins over postal_code for the store choice).

**Returns:** products[]{product_id, item_id, name, brand, size, url, image, currency, price (regular price), price_display, original_price, discount_percent, savings, loyalty_price (member/card price, apart from price), loyalty_label, unit_price_text, price_is_estimate, estimated_weight_text, promotions[]{label,type,coupon} (BOGO, multi-buy, digital coupons), deal_terms, on_sale, buy_one_get_one, available, stock_level, stock_label, likely_out_of_stock, sold_by, dietary[], store_brand, badge, service_type, matches_query (search), retailer{shop_id,service_type,retailer_location_id}}, count, total, page, per_page, last_page, has_more, unmatched_dropped, next_cursor, duplicates_dropped (cursor), skipped_recovered (cursor), query, store{store_id,name,street,city_state_zip,latitude,longitude}, service_type, postal_code, store_defaulted, currency

**Example request body:**
```json
{
  "query": "milk",
  "postal_code": "33801"
}
```

### POST https://api.reefapi.com/publix/v1/category — 3 credits
Every product of a delivery.publix.com collection (a category, a brand or a diet collection from `categories`) priced at one store.

**Parameters:**
- `slug` (string, required) — Collection slug from `categories` (n-milk-22980 …) or a delivery.publix.com /store/publix/collections/<slug> URL.
- `page` (integer, optional, default 1) — 1-based page. The site's order can shift between calls, so reading page by page can repeat or skip a product; for a complete scan follow `next_cursor` instead. A page past the end is INVALID_PARAM; read `last_page`/`has_more`.
- `per_page` (integer, optional, default 20) — Products per page (1-40, default 20).
- `cursor` (string, optional) — `next_cursor` from the previous answer, sent WITH the same query/slug/type, store parameters and per_page (and no `page`). Returns the next page without products already returned: rows the site moved are dropped and counted in `duplicates_dropped`, the page is filled from the rest of the current list (`skipped_recovered` = products offset paging would have skipped).
- `postal_code` (string, optional) — US ZIP code whose Publix store prices the results (Publix prices and range differ by store). Default 33801 — then `store_defaulted` is true.
- `store_id` (string, optional) — Optional Publix store id from `stores` (wins over postal_code for the store choice).

**Returns:** collection{id,name,slug,url}, products[]{product_id, item_id, name, brand, size, url, image, currency, price (regular price), price_display, original_price, discount_percent, savings, loyalty_price (member/card price, apart from price), loyalty_label, unit_price_text, price_is_estimate, estimated_weight_text, promotions[]{label,type,coupon} (BOGO, multi-buy, digital coupons), deal_terms, on_sale, buy_one_get_one, available, stock_level, stock_label, likely_out_of_stock, sold_by, dietary[], store_brand, badge, service_type, matches_query (search), retailer{shop_id,service_type,retailer_location_id}}, count, total, page, per_page, last_page, has_more, next_cursor, duplicates_dropped (cursor), skipped_recovered (cursor), store{store_id,name,street,city_state_zip,latitude,longitude}, service_type, postal_code, store_defaulted, currency

**Example request body:**
```json
{
  "slug": "n-milk-22980",
  "postal_code": "33801"
}
```

### POST https://api.reefapi.com/publix/v1/product/detail — 3 credits
One Publix product: name, brand, size, category, ingredients, directions, warnings, images, rating — and its price, unit price, promotions and stock at one store.

**Parameters:**
- `product_id` (string, required) — Product id (every row's `product_id`) or a delivery.publix.com /store/publix/products/ URL.
- `postal_code` (string, optional) — US ZIP code whose Publix store prices the results (Publix prices and range differ by store). Default 33801 — then `store_defaulted` is true.
- `store_id` (string, optional) — Optional Publix store id from `stores` (wins over postal_code for the store choice).

**Returns:** product{product_id,name,brand,size,category{id,name},url,description,details,ingredients,directions,nutrition_text,warnings,other_sections[],images[],breadcrumb[],rating,rating_count,currency}, price, retailer_item (the store's row) or null + retailer_item_note when this store does not list it, store{store_id,name,street,city_state_zip,latitude,longitude}, service_type, postal_code, store_defaulted, currency

**Example request body:**
```json
{
  "product_id": "324877",
  "postal_code": "33801"
}
```

### POST https://api.reefapi.com/publix/v1/categories — 3 credits
The delivery.publix.com category tree (departments and their collections) or one collection's sub-collections; slugs feed `category`.

**Parameters:**
- `slug` (string, optional) — Optional collection slug: returns its sub-collections. Without it: the whole category tree.
- `postal_code` (string, optional) — US ZIP code whose Publix store prices the results (Publix prices and range differ by store). Default 33801 — then `store_defaulted` is true.
- `store_id` (string, optional) — Optional Publix store id from `stores` (wins over postal_code for the store choice).

**Returns:** collections[]{slug,name,url,children[]}

**Example request body:**
```json
{
  "slug": "n-milk-22980"
}
```

### POST https://api.reefapi.com/publix/v1/stores — 3 credits
Publix stores around a US ZIP code, nearest first, with address, coordinates, distance and which services delivery.publix.com offers there.

**Parameters:**
- `postal_code` (string, required) — US ZIP code to find Publix stores around (nearest first).
- `page` (integer, optional, default 1) — 1-based page. The site's order can shift between calls, so reading page by page can repeat or skip a product; for a complete scan follow `next_cursor` instead. A page past the end is INVALID_PARAM; read `last_page`/`has_more`.
- `limit` (integer, optional, default 20) — Stores per page (1-100, default 20).

**Returns:** stores[]{store_id,name,street,city_state_zip,postal_code,latitude,longitude,distance_miles,service_types[],services_text,unavailable_text}, count, total, page, has_more

**Example request body:**
```json
{
  "postal_code": "33801"
}
```

### POST https://api.reefapi.com/publix/v1/offers — 3 credits
Publix's current deal lists at one store: BOGO (buy one, get one free).

**Parameters:**
- `type` (enum, optional, default "bogo") — Which deal list: bogo (BOGO (buy one, get one free)). [one of: bogo]
- `page` (integer, optional, default 1) — 1-based page. The site's order can shift between calls, so reading page by page can repeat or skip a product; for a complete scan follow `next_cursor` instead. A page past the end is INVALID_PARAM; read `last_page`/`has_more`.
- `per_page` (integer, optional, default 20) — Products per page (1-40, default 20).
- `cursor` (string, optional) — `next_cursor` from the previous answer, sent WITH the same query/slug/type, store parameters and per_page (and no `page`). Returns the next page without products already returned: rows the site moved are dropped and counted in `duplicates_dropped`, the page is filled from the rest of the current list (`skipped_recovered` = products offset paging would have skipped).
- `postal_code` (string, optional) — US ZIP code whose Publix store prices the results (Publix prices and range differ by store). Default 33801 — then `store_defaulted` is true.
- `store_id` (string, optional) — Optional Publix store id from `stores` (wins over postal_code for the store choice).

**Returns:** type, collection{id,name,slug,url}, products[]{product_id, item_id, name, brand, size, url, image, currency, price (regular price), price_display, original_price, discount_percent, savings, loyalty_price (member/card price, apart from price), loyalty_label, unit_price_text, price_is_estimate, estimated_weight_text, promotions[]{label,type,coupon} (BOGO, multi-buy, digital coupons), deal_terms, on_sale, buy_one_get_one, available, stock_level, stock_label, likely_out_of_stock, sold_by, dietary[], store_brand, badge, service_type, matches_query (search), retailer{shop_id,service_type,retailer_location_id}}, count, total, page, has_more, next_cursor, duplicates_dropped (cursor), skipped_recovered (cursor), store{store_id,name,street,city_state_zip,latitude,longitude}, service_type, postal_code, store_defaulted, currency

**Example request body:**
```json
{
  "postal_code": "33801"
}
```

### POST https://api.reefapi.com/publix/v1/promotions — 3 credits
Which Publix deal lists are live now at one store, each with its name and product count.

**Parameters:**
- `postal_code` (string, optional) — US ZIP code whose Publix store prices the results (Publix prices and range differ by store). Default 33801 — then `store_defaulted` is true.
- `store_id` (string, optional) — Optional Publix store id from `stores` (wins over postal_code for the store choice).

**Returns:** promotions[]{type,name,slug,url,product_count,available}, store{store_id,name,street,city_state_zip,latitude,longitude}, service_type, postal_code, store_defaulted, currency

**Example request body:**
```json
{
  "postal_code": "33801"
}
```

## At scale
- **Volume:** 5M+ requests a day, measured at 60 requests a second across the fleet with no
  central bottleneck. Per-key limits are raised for high-volume accounts; volume pricing on request.
- **Missing a source:** tell us a site we do not cover and it becomes an engine. A customer asked
  for bestprice.gr on 21 Sep 2026 and it was in the catalog on 22 Sep.
- **Support:** 2 minute median time from a question in the live chat to the first answer. Setup
  help included, no support tier to buy.
- **One key, one credit pool** across every API. No per-site plans, no separate subscriptions.

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