# Food City Scraper: Store Prices, ValuCard Deals & Stock (`moggedhq/food-city-scraper`) Actor

Get Food City grocery products for a selected store or ZIP: price, regular price, ValuCard offers, sale dates, unit price, pack size and pickup stock where public. Search, categories or product URLs. About $0.004 per product: 1,000 products cost $4. No login.

- **URL**: https://apify.com/moggedhq/food-city-scraper.md
- **Developed by:** [mogged](https://apify.com/moggedhq) (community)
- **Categories:** E-commerce, Automation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.20 / 1,000 products

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Food City Scraper: Store Prices, ValuCard Deals & Stock

For retail price analysts, brands and comparison services monitoring K-VA-T Food City groceries at a selected store. Collect the online advertised price, retailer UPC/lookup key, pack size, public digital coupon conditions, basket promotions and online orderability in one consistent table. About **$0.004 per delivered product**: **20 products cost $0.08**, or **1,000 products cost $4**, plus a minimum **$0.00005 Actor start charge**. No retailer login or retailer API key is required.

Use Foodcity.com searches, categories, weekly savings or known retailer product URL keys. Choose ZIP codes to discover stores or exact store IDs for repeat monitoring. Every product identifies its store and price scope.

### What you get

Shared US grocery fields follow the verdict. Missing values remain null.

| Field | What it means |
| --- | --- |
| `verdict` | Product, advertised price, size and selected store in readable text. |
| `retailer`, `country`, `currency` | Food City, US and USD. |
| `storeId`, `storeName`, `location` | Selected store number, public store title and the ZIP requested. Location is null for exact-store or default-locator selection. |
| `productId`, `sku` | Retailer product URL lookup key and separate internal cart item number, both strings with leading zeros preserved. |
| `gtin` | Null: these routes establish no validated source barcode. A lookup-key checksum alone is insufficient provenance. |
| `title`, `brand`, `size` | Source brand, product name and package or selling-unit label. |
| `price`, `regularPrice`, `onSale` | Current visible online advertised price, explicit base price when supplied, and the source sale flag. |
| `promoText`, `promoType` | Public promotion wording and its type. Basket and coupon conditions remain separate. |
| `promoStart`, `promoEnd` | Null for the current product-card route. Coupon expiration dates are inside `offers`. |
| `memberPrice`, `memberProgram` | Explicit member product price and program, when the price text identifies membership. |
| `unitPrice`, `unitOfMeasure` | Source per-unit rate and unit. A product sold “per lb” retains that advertised rate here. |
| `inStock`, `stockLevel` | Online Add to Cart/orderability signal; stock quantity remains null. |
| `rating`, `reviewCount` | Not supplied by this public product route; null. |
| `category`, `url`, `imageUrl` | Requested category label or product breadcrumb, selected-store product URL and product image URL. |
| `scrapedAt` | UTC observation timestamp. |

Food City fields:

| Field | What it means |
| --- | --- |
| `retailerUpc` | Raw Food City retailer UPC/lookup key, matching `productId`. Not a promise of a registered GTIN. |
| `foodCityBanner` | K-VA-T Food City, identifying this retailer. |
| `priceScope` | Selected-store online advertised price. |
| `priceText`, `priceBasis` | Exact displayed price wording and whether the amount is for a package or a weight unit. |
| `weightedItem`, `averageWeight` | Source weight-selling flag and source average-weight value. |
| `estimatedPackagePrice` | Calculated rate multiplied by the supplied average weight, rounded to cents. An estimate, not the checkout total. |
| `valucardRequired`, `priceRequiresMembership` | True only when the product-price text explicitly identifies the membership requirement. Null means no explicit statement was supplied. |
| `purchaseQuantity` | Quantity from explicit multi-buy price wording. |
| `digitalCouponAvailable`, `digitalCouponText` | Public coupon badge and retrieved offer wording. |
| `couponRequiresMembership`, `offers` | Separate digital coupon membership/clipping conditions, expiration dates, qualifying retailer UPC/lookup keys and terms. |
| `promotionConditions`, `weeklyAd`, `isDeal` | Basket-offer wording, weekly-savings source flag and combined deal flag. |
| `pickupAvailable` | Online ordering availability signal for the selected context. |
| `storeAddress`, `requestedLocations` | Public store address and ZIP selections resolving to that store. |
| `details` | Optional ingredients, nutrition or product information. |
| `sourceUrl`, `sourceRequest`, `storeSelectionEvidence` | Public request and confirmed store selection supporting the observation. Anonymous session cookies are never exported. |

The overview view emphasizes prices and stores; the deals view shows offers. Free `DEALS` JSON contains deal rows or links to bounded JSON parts for large exports. `SUMMARY` reports products, stores, deals, source outcomes, duplicate exclusions, free failures and spending-limit status.

Filter `recordType: "PRODUCT"` for products; `recordType: "NOTE"` rows are free explanations.

### Understand the prices

`price` is Food City's visible online advertised price in the selected-store session. It is not an audited shelf price or guaranteed checkout total. Store selection is confirmed anonymously, and listing product links must identify that store.

For an item labelled “per lb”, `price` and `unitPrice` retain the displayed rate per pound, with `priceBasis: "per-weight-unit"`. A source average weight of 0.625 lb at $2.49 per pound yields a calculated `estimatedPackagePrice` of $1.56. Actual selected weight can differ.

Digital coupon prices remain in `offers`, with clipping, membership, date and purchase conditions. They never replace the ordinary advertised price. One public chuck-roast offer specified $5.99 per pound, Friday and Saturday only, up to 10 lb, expiring November 7, 2026.

Basket discounts also stay separate. “Save $4.99” can require $20 across selected brands; it is not subtracted from a single product.

### Start in three steps

1. Supply search terms, supported Food City URLs or known retailer product URL keys.
2. Choose ZIP codes or exact store IDs and set a global product limit.
3. Run, inspect `SUMMARY`, then export the dataset as JSON, CSV or Excel.

The prefill searches milk near ZIP 24210, capped at 20 products. Store 823 is 151 Cook Street, Abingdon, Virginia.

### Inputs and examples

| Input | Example and behavior |
| --- | --- |
| `searchTerms` | `["milk", "coffee"]`. Each term is searched at each selected store. |
| `startUrls` | Foodcity.com search/category, weekly savings or product URLs. Plain strings are accepted. |
| `productIds` | `["0019347600001"]`. Final 8-14 digit retailer product URL key as a string; preserve leading zeros. No barcode validation required for lookup. |
| `locations` | `["24210", "37919"]`. The first public grocery pickup store returned by the locator for each ZIP is selected and echoed. |
| `storeIds` | `["823"]`. Exact store IDs take precedence over ZIP locations. |
| `maxItemsPerSearch` | New products per source and store. Default 20, maximum 1,000. |
| `maxItems` | Global product/store row limit. Default 20, maximum 10,000. |
| `dealsOnly` | Keep sale flags, weekly-savings products, coupon badges and basket offers. |
| `includeDetails` | Add product-page metadata with no extra product charge. Adds source requests. |
| `proxyConfiguration` | Default datacenter pool first, then anonymous US residential retries after refusals. Custom proxy settings are supported. |
| `postalCodes` | Alias for `locations`. Prefer the common family name when combining retailers. |
| `categoryUrls` | Optional alias for category URLs; `startUrls` remains the common family input. |

Empty locations use the site's locator default and report the selected store. URL store numbers never override run selections. Use `storeIds` for exact-store monitoring.

Search two terms at one ZIP:

```json
{
  "searchTerms": ["milk", "coffee"],
  "locations": ["24210"],
  "maxItemsPerSearch": 20,
  "maxItems": 40
}
```

Collect a grocery department:

```json
{
  "startUrls": ["https://www.foodcity.com/search/all/?searchType=All&department[]=Dairy%20and%20Eggs"],
  "storeIds": ["823"],
  "maxItemsPerSearch": 100,
  "maxItems": 100
}
```

Monitor known products:

```json
{
  "productIds": ["0019347600001", "0000000004799"],
  "storeIds": ["823"],
  "includeDetails": true,
  "maxItems": 2
}
```

For weekly savings, use `dealsOnly: true` without other sources, or a link such as `https://www.foodcity.com/circulars/weekly/823/?searchDisplay=grid`. With searches or categories, `dealsOnly` filters their results. Pagination stops at source exhaustion or your row/spending limits.

### Real output example

Selected fields for Food City store 823 milk with the corrected identifier mapping (November 5, 2026):

```json
{
  "retailer": "Food City",
  "storeId": "823",
  "storeName": "Food City Grocery Store in Abingdon, Virginia",
  "productId": "0019347600001",
  "sku": "98807",
  "retailerUpc": "0019347600001",
  "gtin": null,
  "title": "That's Smart! Milk",
  "size": "1 gal",
  "price": 3.99,
  "unitPrice": null,
  "unitOfMeasure": null,
  "weightedItem": false,
  "priceBasis": "package",
  "priceScope": "selected-store online advertised price",
  "inStock": true
}
```

### Pricing and spending limits

One product means one distinct product at one store. The same retailer lookup key at two stores is two observations. Overlapping inputs at one store are charged once. Empty results, duplicates, summaries, notes and failed requests have no product charge or default dataset-row fee.

| Delivered products | Base | Bronze | Silver | Gold |
| --- | --- | --- | --- | --- |
| 20 | $0.08 | $0.076 | $0.072 | $0.064 |
| 100 | $0.40 | $0.38 | $0.36 | $0.32 |
| 1,000 | $4.00 | $3.80 | $3.60 | $3.20 |

The base product price is $0.004. Bronze is $0.0038, Silver $0.0036 and Gold $0.0032. Add the minimum $0.00005 Actor start charge. Optional product details and public offer-condition requests do not add another event fee.

Set Apify's maximum total charge for a ceiling. Capacity is checked before fetching and saving products; `SUMMARY` records spending-limit stops.

### Monitor prices every day

Save a task with exact `storeIds`, a stable basket of retailer product IDs or category sources, and a deliberate cap. Add an Apify schedule. Preserve `scrapedAt` and join observations on `retailer`, `storeId` and `productId`, keeping selling units and coupon conditions together.

Send datasets to your database, Google Sheets, Make, Zapier or n8n. Check `SUMMARY.freeFailures` before interpreting fewer rows as a catalog change.

### API use

Start a run with your Apify token:

```bash
curl -X POST 'https://api.apify.com/v2/acts/moggedhq~food-city-scraper/runs' \
  -H 'Authorization: Bearer YOUR_APIFY_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"searchTerms":["milk"],"storeIds":["823"],"maxItems":20}'
```

```python
from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("moggedhq/food-city-scraper").call(
    run_input={"searchTerms": ["milk"], "storeIds": ["823"], "maxItems": 20}
)
rows = list(client.dataset(run["defaultDatasetId"]).iterate_items())
products = [row for row in rows if row.get("recordType") == "PRODUCT"]
```

```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('moggedhq/food-city-scraper').call({
  searchTerms: ['milk'], storeIds: ['823'], maxItems: 20
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

### Use with AI agents (MCP)

Connect to `https://mcp.apify.com`. Supply terms, retailer URL keys or categories and ZIPs/store IDs. Example: “Get milk and coffee prices at store 823, including retailer lookup keys, pack sizes, coupon conditions and price basis. Limit the run to 40 products.” Ask it to read `SUMMARY`, preserve nulls and keep conditional coupon prices separate.

### Limits

Coverage is the public K-VA-T Foodcity.com catalog. Prices are online advertisements, not audited shelf or checkout prices. Add to Cart is an orderability signal; stock quantities are usually absent.

Cards often omit regular prices, packaged-goods unit rates, main-sale dates, explicit ValuCard requirements and ratings. These remain null. A missing membership flag does not establish that no card is needed. Coupon expiry and conditions are retained when supplied; start dates are not inferred from weekday wording.

Weekly-savings rows are linked catalog products. Image-only flyer panels, personalized offers, account coupon state, loyalty balances and checkout eligibility are outside coverage. The Actor does not log in, clip coupons, place orders or solve CAPTCHAs.

Source outages or changes can prevent complete output. Prices and orderability are fetched live. Exact-store address metadata may be reused for seven days, identified by `storeMetadataFromCache` and `storeMetadataObservedAt`. Sequential paced requests retry with fresh anonymous sessions; free notes identify unavailable data. Product and charge ledgers survive reboots, and ambiguous billing evidence stops charges. Result caps do not guarantee that a store carries that many matches.

### FAQ

**Which Food City is covered?** K-VA-T Food City at foodcity.com. The output identifies the banner and US currency.

**Can I choose an exact store?** Yes. Use `storeIds`; a ZIP is useful for discovery and the resolved store is reported.

**What does productIds accept?** Final 8-14 digit retailer UPC/lookup keys from product URLs, supplied as strings. `productId` and `retailerUpc` preserve leading zeros; `sku` is the separate cart ID. `gtin` stays null without explicit barcode provenance and valid format/check digit. No digits are guessed.

**Why is regularPrice null?** The source provided no separate base price.

**Are digital coupons already applied?** No. Coupon text, expiry, clipping requirement and conditions are separate in `offers`.

**Does includeDetails cost extra?** No additional product event. It adds source requests for public metadata.

**What happens with bad inputs?** Parseable invalid input produces a free note and summary. Schema rejection adds no product charge.

**How do I combine grocers?** Join the shared retail fields on retailer, store ID and product ID, preserving selling units.

# Actor input Schema

## `searchTerms` (type: `array`):

Find products such as milk, chicken or coffee. Each term is searched at every selected store. Leave empty when using only URLs or product IDs.

## `startUrls` (type: `array`):

Foodcity.com search/category, weekly savings or product URLs. Only K-VA-T Food City. Run storeIds or locations select the store; a URL store number does not override it.

## `productIds` (type: `array`):

Food City retailer UPC/lookup keys from the final segment of product URLs, such as 0019347600001. Accepted keys are 8-14 digits. Supply strings and preserve leading zeros. These lookup IDs are not validated GTIN barcodes.

## `locations` (type: `array`):

US ZIP codes, such as 24210 or 37919. Resolves and echoes the first public grocery pickup store from Food City near each ZIP. storeIds take precedence. Empty uses the public locator default.

## `storeIds` (type: `array`):

Exact Food City store numbers, such as 823 (Abingdon). Each store and every product/store link are checked. Store numbers in pasted URLs do not override your selection.

## `maxItemsPerSearch` (type: `integer`):

Maximum new product rows from each term, category or deals source at one store. Duplicates are free.

## `maxItems` (type: `integer`):

Global limit across all products and stores. About $0.004 per product: 20 products cost about $0.08 plus the small start charge.

## `dealsOnly` (type: `boolean`):

Keep source sale flags, weekly-ad products, public coupon badges and basket promotions. Coupon and minimum-spend conditions are retained separately and are not subtracted from price.

## `includeDetails` (type: `boolean`):

Add slower product-page metadata such as ingredients and nutrition when public. No extra product fee. Stock is an online orderability signal, not an audited shelf count.

## `proxyConfiguration` (type: `object`):

Uses the default datacenter pool first; fresh anonymous US residential sessions are a fallback after source refusals. Custom proxy settings are supported.

## `postalCodes` (type: `array`):

Alias for locations. Use locations when stacking this Actor with the US grocery family.

## `categoryUrls` (type: `array`):

Optional alias for Food City search/category URLs. startUrls remains the family input name.

## Actor input object example

```json
{
  "searchTerms": [
    "milk"
  ],
  "locations": [
    "24210"
  ],
  "maxItemsPerSearch": 20,
  "maxItems": 20,
  "dealsOnly": false,
  "includeDetails": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `products` (type: `string`):

No description

## `dealsView` (type: `string`):

No description

## `dealsOnly` (type: `string`):

No description

## `summary` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "searchTerms": [
        "milk"
    ],
    "locations": [
        "24210"
    ],
    "maxItemsPerSearch": 20,
    "maxItems": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("moggedhq/food-city-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "searchTerms": ["milk"],
    "locations": ["24210"],
    "maxItemsPerSearch": 20,
    "maxItems": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("moggedhq/food-city-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "searchTerms": [
    "milk"
  ],
  "locations": [
    "24210"
  ],
  "maxItemsPerSearch": 20,
  "maxItems": 20
}' |
apify call moggedhq/food-city-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,moggedhq/food-city-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/Xj4CUqCusld6W28eZ/builds/Yjw25aVi5X6aJyeiN/openapi.json
