Chewy Product Catalog: Prices, Autoship and Pet Food SKUs avatar

Chewy Product Catalog: Prices, Autoship and Pet Food SKUs

Pricing

Pay per usage

Go to Apify Store
Chewy Product Catalog: Prices, Autoship and Pet Food SKUs

Chewy Product Catalog: Prices, Autoship and Pet Food SKUs

Daily prices, Autoship prices, ratings and stock of Chewy dog food and treats, plus every product id from the public sitemaps, also outside these categories. No browser, no proxy.

Pricing

Pay per usage

Rating

0.0

(0)

Developer

Sergey Faraday

Sergey Faraday

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

0

Monthly active users

15 minutes ago

Last modified

Categories

Share

Tracks Chewy dog food and treats: price, Autoship price, list price, rating, review count, stock and the seller's key benefits, every day if you like. It reads the same catalog data the category pages use, with plain HTTP requests, no browser and no proxy.

A second mode lists the product ids in the public sitemaps, including products outside the dog food categories.

How to use

  1. Keep the default mode prices and set maxProducts to try it on a small sample.
  2. Give each run a snapshotId (for example the date) and compare snapshots to follow prices. Use one snapshotId per mode: both modes write the same row key for a product.
  3. To find new products, compare parentId between full prices passes. Use mode sitemap when you need ids outside these categories.

Input

FieldDefaultMeaning
modepricesprices: prices and ratings per product family. sitemap: all product ids and names, no prices
scopedog_food_and_treatsdog_food, dog_food_and_treats; sitemap mode also has all_dog, pet_food_all_species, all
maxProductsallstop after this many rows. This is a cut, not a pause, see below
startFromfrom the startprices mode: listing position to start from, from nextCursor in the MANIFEST of a run that was cut
startGroupfirstprices mode: the category startFrom belongs to (dog_food, dog_treats). Categories before it are not read, categories after it start at their beginning
delayMs1000pause between catalog pages
includeCanada, maxSitemapFilessitemap mode only
snapshotId<actor>.<runId>your id for this snapshot; re-running with the same id does not create duplicates
{ "mode": "prices", "scope": "dog_food_and_treats", "maxProducts": 72, "snapshotId": "2026-11-02" }

Output

One product row per product family:

{
"recordType": "product",
"platform": "chewy",
"sourceRecordId": "3969182",
"canonicalUrl": "https://www.chewy.com/pedigree-complete-nutrition-grilled/dp/3969182",
"time": { "observedAt": "2026-11-02T08:00:00.000Z", "collectedAt": "2026-11-02T08:00:00.120Z" },
"metrics": {
"price": { "value": 25.97, "unit": "USD", "sourceName": "advertisedPrice", "priceCondition": "advertised" },
"priceAutoship": { "value": null, "nullReason": "not_provided_by_source" },
"priceList": { "value": 26.98, "unit": "USD" },
"rating": { "value": 4.6356, "unit": "stars_1_5", "scope": "family" },
"reviewCount": { "value": 11248, "unit": "count", "scope": "family" },
"pricePerKg": {
"value": 1.43,
"unit": "USD/kg",
"history": "derived",
"basis": { "priceMetric": "price", "priceSource": "advertisedPrice", "priceCondition": "advertised", "sku": "3969182", "currency": "USD", "netWeightG": 18143.69, "packCount": 1, "weightSource": "title_and_ppu" }
}
},
"data": {
"name": "Pedigree Complete Nutrition Grilled Steak & Vegetable Flavor Dog Kibble Adult Dry Dog Food, 40-lb bag",
"brand": "Pedigree",
"parentId": "141433",
"productType": "food",
"productTypeSource": "title_head_noun",
"inStock": true,
"foodFormat": "dry",
"netWeightG": 18143.69,
"weightResolvedBy": "title_and_ppu",
"sellerStatements": [{ "text": "Enriched with omega-6 fatty acid...", "status": "seller_statement" }]
},
"sampling": { "group": "dog_food", "position": 7, "sort": "byNewest" }
}

The MANIFEST record in the key-value store reports the status of each category (ok, partial, blocked, failed), the total Chewy reports, pages read, any truncation, and rows by product type and by how the weight was resolved.

How to read the data

One row is one variant of a family.

  • Prices mode returns one variant per product family, the one shown in the newest-first listing. For about a quarter of families this is another size than the default listing shows, for example a 1-lb bag instead of a 22-lb bag.
  • The history key is sourceRecordId, not parentId: sourceRecordId is the variant, parentId the family. When another variant takes over the row of a family, that is a change of pack, not a change of price. Group by parentId, compare price per kg rather than price across families. hasMoreVariants says when other sizes exist.
  • rating and reviewCount belong to the whole family (scope: "family", data.ratingScope): a variant flagged isNew can show thousands of reviews.
  • isNew is a source flag on this variant, not a launch date: a new size of an old product carries it too, and which date stands behind it is not established. To find a new product, compare parentId between full passes: two passes 3.5 hours apart differed by 6 of 14,482 families and lost none. Do not compare sitemap snapshots: the sitemap changes between fetches, and 2,107 of about 19,000 ids differed within 34 minutes.
  • Newest-first is a sort, not a sales or popularity rank, and position is a place in that sort. Chewy's default order changes between requests, so a pass on it skips products.

A limit is a cut, not a pause.

  • maxProducts ends the run. nextFrom in the MANIFEST is the position after the last row taken, visibleSize the positions taken by this run, and nextCursor where to go on; nextCursor is null when the category was read to its end.
  • To continue, start a run with the same snapshotId and startFrom set to nextCursor (and startGroup when the cut fell in dog_treats). The rows of both runs together are the rows of one longer run, with the same positions, as long as the listing did not move in between. A repeat with a larger limit and no startFrom gets you there too, it just reads the first rows again.
  • A category that the limit cut before its first row is failed, with nextCursor 0. A run started with startFrom reports only what it read: the earlier positions are in the run before it.
  • Sitemap mode has no cursor: applied.cutAtFile names the file of the cut, matched what the files offered, matchedDelivered what reached the dataset. A repeat with a larger maxProducts under the same snapshotId completes the snapshot. maxSitemapFiles also ends partial with reason maxSitemapFiles, so a repeat with a larger limit under the same snapshotId completes the snapshot. An empty or short page far below the total leaves nextCursor at that position, and the error says the listing ended early.

Restarts.

  • A restart reads the page or file in flight once more, without using the limit, so the MANIFEST says what one clean run says. Out of time it cannot, then nextFrom moves by the rows read back and a note says what the counts leave out.

Time.

  • observedAt is taken after the response of the page or file arrived, collectedAt when this actor built the row, so "what did we know at time T" uses collectedAt. runStartedAt is the platform start, the same on every row of a run.
  • Prices mode: the catalog JSON has no date of its own, so sourceUpdatedAt is null and sourceUpdatedAtBasis says why. Sitemap mode: sourceUpdatedAt is the entry's lastmod, else the Last-Modified of the sitemap file, and the basis says which. availableAt stays null with a reason in both modes.

Records and the total.

  • A category is partial with reason listing_shifted when more than 0.5% of the total Chewy reports was served twice within the category or never read.
  • Some products are listed in both categories, such as veterinary treats and bundles of food and treats. They get one row, under dog_food, so records of dog_treats stays below the total by that many: 25 of 9,089 positions on a full pass. applied.listedInEarlierCategory counts them and applied.listedInEarlierCategoryIds names them, a note says so, and the status stays ok.
  • The total is not exact. dog_treats served 9,090 positions under a total of 9,088, so reading stops at a short page and not at the total. The total also moves: on 2026-11-02 it read 9,087, 9,092 and 9,088 within about 8 hours. applied.totalMin and totalMax report it within a pass. A product removed above the reading position during a pass can skip one row without a trace.

Product type.

  • productType comes from the words the title ends on, so line names like "Daily Dish" or "Farm to Bowl" do not count, with the catalog category filling gaps. Values: food, treat, supplement, medication, kit, accessory, non_food, other. kit is a toy, gear or supplement sold together with food or treats, out of the food and treat filters; the parts are in productTypeEvidence.
  • productTypeSource says how the type was found and productTypeDoubtful why it may be wrong. Exclude rows with productTypeDoubtful when you need a clean set: 332 of 14,476 rows of a full pass carry it. A title that names gear or a kit beside the food (with Puzzle Toy, Treats & Pouch, Cake Mold Kit) keeps its type and carries productTypeDoubtful: 40 rows of a full pass. A title with a food part and a treat part carries it too: 5 rows.
  • species is other with evidence for food of other animals (Snake Food, Fish Food). The dog food category holds such rows. species: null means neither the title nor the URL names a species. When only the URL names it, speciesEvidence starts with slug:, as on compounded drugs whose title names no animal.
  • Format and life stage come from the product name, at about 95% precision on a manual sample. Check the *Evidence fields when it matters. Format, life stage, dog size and claims come from the product name; the URL is read for them only when a row has no name. A claim quotes the name word for word.

Weight and price per kg.

  • netWeightG is set only when one reading of the title survives. 2.12-oz bag, 3 count can mean three bags or three pieces in a bag, so it stays null with weightAmbiguous: true, weightAmbiguityReason and unitWeightG, until the source's price per unit settles it. weightResolvedBy says what the weight rests on: title, title_and_ppu, ppu, single_serve_container.
  • The source's price per unit is a witness, not the truth: Chewy computes it from size times count, even where that gives a 53.8 kg bag for $18.99. impliedQuantityG shows what it implies and never becomes netWeightG.
  • pricePerKg is null with a reason: ambiguous (a weight is there, but not one), no_weight (no weight in the title), sold_by_volume. Its basis names the price and the weight behind the value. It uses the advertised price, not the Autoship price. ambiguous also covers a title that names gear or a kit, because the price can pay for more than the food; metrics.pricePerKg.note says so. 24 rows of a full pass.
  • bundlePartCount is the number of part numbers behind the listing: 2 or 3 on a bundle of different products (isBundle: true), 1 on the same product in multiples. A title that names several products or weights gets no net weight.
  • inStock is the flag of this variant for the US without a delivery ZIP (data.stockScope); which warehouses it covers is unknown.
  • Key benefits are the seller's own words, stored as seller statements, not verified claims.

When the source changes.

  • Rows were read but what defines a row is missing or unreadable: id, name, link, price. On every row at any count, or on 10% or more of the rows from 10 rows on, per page, per sitemap file or for the source: error SCHEMA_CHANGED, status partial when rows hold data, failed when none does.
  • In prices mode the pass stops at that page, nextCursor stands after the delivered rows and the error names the position to read again. Such a row has status: fetch_error with statusReason, and a repeat under the same snapshotId replaces it.
  • A value that is there and not readable (From $12.99, a range, another currency mark, a bare number) is null with unparseable and raw. A price of 0 is null with zero_not_confirmed_as_real. A rating outside 1 to 5 is null with unparseable.
  • Fields every row sends (parent id, brand, Autoship and strike price, rating and count, price per unit, key benefits, stock and the other flags) gone or of another shape on that share of rows also give SCHEMA_CHANGED and partial, but the pass goes on. A response without recordSetTotal counts too.
  • applied.rowsInspected, rowsNotReadable, unreadableFields and shapeChangedFields show what was seen.

FAQ

Where are the ingredients and guaranteed analysis? Not in this data. The companion Unified Scraper Adapter actor has them in module chewy_details, all variants in chewy_products and reviews in chewy_reviews.

What happens if Chewy blocks access? The run stops and reports blocked. No bypass is attempted. In sitemap mode the first blocked file stops the reading, applied.filesNotAttempted names the rest. A page answered with HTTP 200 instead of a sitemap counts as a block, and 429 reads as blocked in both modes.

Why is a zero rating shown as null? A count of 0 means nobody rated the product, which is not a rating of 0: rating is null with reason no_ratings and reviewCount stays 0.

Is this allowed? Check Chewy's terms for your use case. Collecting content for third parties may need Chewy's written consent.

Cost

A full prices pass reads about 400 catalog pages in about 20 minutes for about $0.11 of platform usage at 256 MB of memory, which is enough for this mode. A full sitemap pass reads about 270,000 entries in under a minute for about $0.08 of platform usage.