Instacart Scraper at a glance
Instacart Scraper is a ready-to-run Apify actor from abotapi for collecting ecommerce data. Its documented output includes product, change, brand, price and price (number), which you can export as JSON, CSV or Excel or read through the Apify API. It costs $2.40 per 1,000 results on the Apify Free plan and from $1.50 on paid plans, with smaller per-run or optional feature charges listed on Apify.
- Runs on
- Apify cloud: console, API, schedules and integrations
- Data type
- Ecommerce
- Pricing
- $2.40 per 1,000 results (Free plan), from $1.50 on paid plans
- Output
- Documented below · JSON, CSV, Excel
- Inputs
- 25 options
- Run success
- 97.2% of completed public runs in the last 30 days
Scrape Instacart grocery catalogs with per-store prices. Run keywords across several stores at once to compare what each banner charges, discover every store serving a US location, or paste Instacart links. Returns name, brand, size, price, regular price, unit price, stock and full store detail.
What Instacart Scraper can do
- Prices from several stores in one run. One keyword is run at every store you name, so the output is a price comparison rather than one store's list.
- Every price carries its store. Banner, service type and store id ride on every row; the branch's street address also rides along when stores are named in the Stores field, because a grocery price without its store is not a fact about anything.
- Real unit prices, where published. Price per ounce, pound or count, plus a unit price ordering, so different pack sizes are actually comparable when the store quotes them.
- Sale detection that means something. onSale is set from the regular price the store is striking through, not guessed from a badge.
- Store discovery built in. Ask which stores serve a postal code before you decide what to scrape, with departments, coordinates and postal addresses.
- Change monitoring that names what moved. Schedule it and get only new and changed rows, with the changed fields named, so a price change is one row rather than a fresh catalog.
Choose what to collect
These inputs let you configure Instacart Scraper - Grocery Prices, Products & Stores. Open the actor on Apify to enter your values and review the current options.
| Input | What it controls |
|---|---|
modestring · required | Choose 'search' to run keywords against one or more stores and get each store's own prices, 'store' to list every store serving your location, or 'url' to scrape pasted Instacart links (store pages, search links, and product links are all accepted). |
queriesarray | One or more keywords to search, for example 'organic milk' or 'greek yogurt'. Each keyword is searched separately at each store. A narrower keyword returns fewer matches, so start broad and narrow down. |
retailersarray | Store names as they appear in an Instacart address, for example 'costco', 'safeway', 'kroger', 'publix'. Copy one from a store page address: instacart.com/store/STORE-NAME/storefront. Leave empty to search EVERY store serving your location, which can be a few hundred stores, so set Max products before you do that. |
urlsarray | Mix freely: search links such as https://www.instacart.com/store/costco/s?k=milk , product links such as https://www.instacart.com/store/costco/products/19208138 , store pages such as https://www.instacart.com/store/safeway/storefront , or bare product ids such as 19208138. Department links (/collections/...) are refused by the source for this actor and are skipped with a message. A cross-store link such as https://www.instacart.com/store/s?k=milk searches every store at your location. A page=2 link returns the same results as page 1 (the source has no page cursor); duplicates are never emitted twice. |
includeDepartmentsboolean | Add each store's own department list (produce, dairy, frozen and so on) with the department link, to every store record. Costs one extra request per store. |
postalCodestring | The US ZIP code to shop from, for example 10001. This alone is enough to run a search. Defaults to 10001 (Manhattan). |
streetAddressstring | Optional US street address, for example 2300 Griffin Road. It is geocoded to pick the stores around that exact point. Precedence: Latitude/Longitude if both are set, then this address, then the postal code. If the address cannot be located the postal code is used instead. |
Show 18 more inputs
| Input | What it controls |
|---|---|
latitudenumber | Only needed for a point more precise than the postal code's own centre, or to shop from a different area than the postal code you gave. Leave empty to use the postal code's coordinates automatically. Must be set together with Longitude. |
longitudenumber | Only needed for a point more precise than the postal code's own centre, or to shop from a different area than the postal code you gave. Leave empty to use the postal code's coordinates automatically. Must be set together with Latitude. |
sortBystring | Ordering. This is the source's own ordering, sent with the search request, so the results come back already ranked. Across several stores the combined list is re-ordered the same way. |
inStockOnlyboolean | Keep only products the store currently reports as available. |
onSaleOnlyboolean | Keep only products the store is currently discounting, that is, products showing a regular price above the current price. |
minPriceinteger | Optional. Keep only products priced at or above this amount. Products the store quoted no price for are excluded when this is set. |
maxPriceinteger | Optional. Keep only products priced at or below this amount. Products the store quoted no price for are excluded when this is set. |
fetchDetailsboolean | Add the source's own product category, the written description and the canonical product name to each product, by loading its product page. Costs one extra request per product and is charged per product. Nutrition panels are NOT promised: the source publishes them for only some products. |
maxItemsinteger | Maximum number of records to return across the whole run. This is the run's only cap. Use 0 for unlimited, but note that leaving Stores empty searches every store serving your location. |
resumeFromRunIdstring | Paste a previous run ID or dataset ID to continue a large crawl without returning or charging for records already collected there. Use this after an interrupted run. For recurring monitoring of the same search, use Incremental mode below instead. |
incrementalModeboolean | Turn this on for recurring price monitoring. The first run returns everything as NEW. Later runs return only NEW, UPDATED and REAPPEARED records, so a daily run shows you the price moves and nothing else. UPDATED is decided by comparing price, sale price, unit price, availability, stock level, name, brand, size, image and the other product fields; scrapedAt, sourceUrl, searchQuery and boughtCountLabel are never compared, image links are compared without their rotating CDN query string, and categories are compared as a set (use Ignore fields to exclude more). Turn on 'Emit unchanged' or 'Emit expired' only when you also want those rows returned (and billed). State is kept separately for each keyword, store, location and detail setup; use State key to name or deliberately share a campaign. |
stateKeystring | Optional. Name this monitoring campaign to keep its state stable, or to deliberately share state across differently configured runs. Leave empty to let the actor derive a key from the keywords, stores, location and settings, so two different searches never mix state. |
emitUnchangedboolean | Off by default. Turn on to also return products whose price and details have not moved since the last run, marked UNCHANGED. This returns, and bills, extra rows you already have. |
emitExpiredboolean | Off by default. Turn on to also return products that were present in a previous run but are no longer found, marked EXPIRED. Only produced after a run that fully scanned the tracked search, so never when Max products capped it, when Resume was used, or when a keyword had more results than the source serves in one request, or when a filter (In stock only, On sale only, Min/Max price) is on, or when a pasted link starts past page 1. This returns, and bills, extra synthetic rows. |
ignoreFieldsForChangesarray | Optional. Top-level field names to leave out of UPDATED change detection, on top of the built-in handling. Always left out: scrapedAt, sourceUrl, searchQuery and boughtCountLabel; image links are compared without their rotating CDN query string, and categories are compared as a set, so a reshuffle is not a change. Price, sale price, availability and stock level changes drive UPDATED. Use this if another field turns out to be noisy in your setup. |
mcpConnectorsarray | Optionally send results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize one under Apify, Settings, API & Integrations, then select it here. Notion gets a rich page per item; other connectors get a best effort write or digest. Leave empty to skip; this never changes the dataset output. Supported: Notion (https://mcp.notion.com/mcp), Linear (https://mcp.linear.app/sse), Airtable (https://mcp.airtable.com/mcp), Apify (https://mcp.apify.com). |
notionParentPageUrlstring | URL or id of the Notion page under which item pages are created. Required to enable the Notion export; ignored by other connectors. |
maxNotifyListingsinteger | Cap on items written to each connector per run. Does not affect the dataset. |
Fields in the output
The documented dataset includes the fields below. Availability can depend on the source page and selected mode.
nameProductchangeTypeChangebrandBrandpriceStringPricepricePrice (number)fullPriceRegular priceunitPriceStringUnit priceonSaleOn salesizeSizeavailableIn stockstockLevelLabelStock levelcategoryCategoryShow 28 more fields
retailerNameStoreserviceTypeServicestoreAddressStore addressdietaryAttributesDietaryproductIdProduct idimageImage (image)productUrlProduct link (link)searchQueryKeywordchangedFieldsChanged fieldsfirstSeenAtFirst seenlastSeenAtLast seenretailerTypeTypeaddressAddresslatitudeLatitudelongitudeLongitudecategoriesCategoriesdepartmentCountDepartmentsshopIdStore idretailerSlugStore namelogoLogo (image)storefrontUrlStore link (link)descriptionDescriptioncanonicalNameCanonical nameunitCountUnit countdetailsDetailsseoDescriptionPage summarydetailImageDetail image (image)detailAvailabilityDetail availabilityExample output
An example from this actor’s documentation. Values are illustrative; this is not a fresh live result.
{
"recordId": "100000|items_500000-20000001",
"itemId": "items_500000-20000001",
"productId": "20000001",
"name": "Store Brand Whole Milk, 1 Gallon, 2-count",
"brand": "store brand",
"size": "each",
"priceString": "$8.94",
"price": 8.94,
"onSale": false,
"available": true,
"stockLevel": "highlyInStock",
"stockLevelLabel": "Many in stock",
"retailerName": "Costco",
"retailerSlug": "costco",
"retailerId": "900000",
"shopId": "100000",
"storeLocationId": "500000",
"serviceType": "delivery",
"storeAddress": "500 Example Ave, San Francisco, CA 94105",
"image": "https://d2lnr5mha7bycj.cloudfront.net/product-image/file/large_00000000.jpg",
"productUrl": "https://www.instacart.com/store/costco/products/20000001",
"sourceUrl": "https://www.instacart.com/store/costco/s?k=organic+whole+milk",
"category": "Plain Milk",
"canonicalName": "Store Brand Homogenized Milk, 2 gal",
"unitCount": "2",
"searchQuery": "organic whole milk",
"scrapedAt": "2026-09-29T06:01:44+00:00"
}Ways to use this data
- Grocery price comparison shoppers: run one keyword at every nearby store to find the cheapest banner before placing a delivery or pickup order.
- Recipe and meal-planning apps: check ingredient prices and stock across retailers for a shopping list.
- Retail and pricing analysts: track how prices and unit prices move store by store over time.
- Coupon and deal-alert tools: watch a keyword list and get notified only when something goes on or off sale.
- Researchers and market analysts: build a grocery price index across US postal codes.
Before you run
Start with a small input and check the resulting records against your expected fields. Source content and actor options can change; the current Apify listing is the reference for availability and pricing.
Questions about this actor
What data does Instacart Scraper return?
The documented output for Instacart Scraper includes product, change, brand, price, price (number), regular price, unit price and on sale. Availability of each field depends on the source page and the selected mode.
How much does Instacart Scraper cost?
It costs $2.40 per 1,000 results on the Apify Free plan and from $1.50 on paid plans, with smaller per-run or optional feature charges listed on Apify. The Apify listing shows the current rate for every plan.
Why did I get fewer results than I expected?
The source serves one page of results per keyword per store and publishes no way to page past it. A broad keyword like "yogurt" is capped at that one page just as a narrow one is, so it can return proportionally fewer of the store's actual matches. Narrow the keyword (for example "greek yogurt" rather than "yogurt") to reach more of what a store carries, and check the run log: it names any keyword or store combination where more results existed than could be read.
Why did my run fail instead of returning an empty dataset?
If the source refuses every request, or every keyword and store target errors out before returning anything, the run fails with a clear message, since no real search was ever completed. A search that genuinely matches nothing, or a store that genuinely has nothing in stock, is reported as a quiet, successful, empty result instead, so "no products matched" is never confused with "nothing could be read".
Can I use it with AI agents or MCP?
Yes. Call it from any Apify integration or MCP client, and use the connector field to push results into Notion, Linear or Airtable.
