Dan Murphy’s Scraper - Prices, Stock, Ratings & Specials avatar

Dan Murphy’s Scraper - Prices, Stock, Ratings & Specials

Pricing

from $1.00 / 1,000 product listings

Go to Apify Store
Dan Murphy’s Scraper - Prices, Stock, Ratings & Specials

Dan Murphy’s Scraper - Prices, Stock, Ratings & Specials

Scrape Dan Murphy’s products by category, keyword, or specials. Extract 45+ fields, including regular, sale and member prices, case and unit pricing, stock, ratings, reviews, country, variety, and ABV. Incremental mode tracks changes.

Pricing

from $1.00 / 1,000 product listings

Rating

0.0

(0)

Developer

Abot API

Abot API

Maintained by Community

Actor stats

0

Bookmarked

3

Total users

0

Monthly active users

9 days ago

Last modified

Categories

Features

Limited permissions

Share

Dan Murphy's Scraper: Products, Prices & Member Offers

Dan Murphy's Scraper turns danmurphys.com.au, Australia's largest liquor retailer, into structured product data. Get regular, sale and member-offer prices, case and per-unit pricing, ratings, review counts, stock levels and product attributes (country, style, ABV, standard drinks) across the departments the site's data service currently answers. Browse by department, search by keyword, filter by brand, country, region, style, price or specials, or paste category and product links directly, then export to JSON, CSV or Excel, or pull the results straight into your app through the API.

Why This Scraper?

  • Two ways to find products. Browse a department (with sub-department, brand, country, region and style filters), or paste category, style and product links directly. No need to build URLs by hand. Beer, spirits, whisky, accessories and gifts answer today; the site's data service rejects the wine, premix and some other routes right now, and the run log says so instead of returning an empty list.
  • Every price that applies, at once. Regular price, sale price, member-only offers, case pricing and per-unit pricing, so you always know which price a shopper actually pays.
  • 45+ fields per product. Ratings, review counts, stock levels, country of origin, variety/style, ABV, standard drinks, awards, closures and more.
  • Specials and member-offer filters in both modes. Narrow any department, sub-department or pasted category link to only current offers or member deals.
  • Built for scheduled price-watch runs. Incremental mode classifies every product NEW, UPDATED, UNCHANGED, REAPPEARED or EXPIRED between runs, so a recurring schedule reports only what changed.
  • Optional detail-page enrichment. Switch on rich descriptions, tasting notes and recommended products only when you need them, charged only for the products it actually fetches.
  • A refused or mistyped run fails loudly instead of returning an empty dataset. A category the site rejects, a sub-department or brand that matches nothing (the log lists the valid names) and a connection failure each say what happened, so "nothing matched" is never confused with "couldn't be read".

Use Cases

  • Price monitoring and comparison tools: track regular, sale and member-offer prices across departments to power price-history or comparison sites.
  • Liquor retailers and distributors: watch a competitor's specials, stock levels and new listings in your category.
  • Wine and spirits content creators: build rating- and review-based recommendation lists by variety, style, country or ABV.
  • Market researchers and analysts: track assortment changes, award-winning products and how often member offers appear over time.
  • Personal shopping tools and alerts: get notified when a tracked product or department goes on special.

Data You Get

Sample shape: values are illustrative placeholders, not from a live record.

FieldExample
stockcode"812345"
name"Sample Brewing Co Pale Ale 6x355mL Cans"
brand"Sample Brewing Co"
department"beer"
urlDan Murphy's link to the product
price / wasPrice45.99 / 52.99 (AUD)
isOnSpecial / isOnOffertrue / true
isMemberOffer / memberOfferLabeltrue / "Member Price"
casePrice / singlePrice179.99 / 52.99
inAnySixPrice42.99
rating / reviewCount4.3 / 87
country"Australia"
style"Pale Ale" (beer style, wine variety or spirit type)
size / packageSize"355mL" / "6 pack"
abv / standardDrinks"4.5%" / "9.8"
stockQty / purchasable240 / true
deliveryOnlyfalse
award"Gold Medal"
badges["Top seller", "Queensland, Australia"] (the badges the site shows on the product)
categoryUrlthe link this product was collected from (your pasted link, or the site link of the department / sub-department / keyword)
imageUrlproduct image link
richDescription / tastingNoteslong-form description / tasting notes (when fetchDetails is on)
recommendedStockcodesrelated product stock codes (when fetchDetails is on)
changeType / changedFields"UPDATED" / ["price"] (incremental mode)
firstSeenAt / lastSeenAtISO timestamps

Also carried when present: sash (the badges as raw text, kept for stable change detection; use badges), description, promotionType, packMessage, casePackMessage, unit, closure, foodMatch, isNew, imageLarge, and detailFetched (true only on a product whose detail payload was actually fetched this run).

How to Use

  1. Pick a mode: search (department, sub-department, keyword, brand) or url (paste category or product links).
  2. Fill in the fields for that mode, and switch on specialsOnly / memberOffersOnly if you only want current or member offers.
  3. Set Max products per scope to control run size and cost (it applies to each scope separately, see the tip below), then click Start.
  4. Download the dataset as JSON, CSV or Excel, or read it through the API.

Browse a department with a specials filter:

{
"mode": "search",
"department": "beer",
"subDepartment": "craft beer",
"specialsOnly": true,
"maxProducts": 50
}

Keyword search plus a department (two scopes in one run):

{
"mode": "search",
"department": "beer",
"searchTerm": "heineken zero",
"maxProducts": 100
}

The keyword runs as its own site-wide search next to the department scope; it is not narrowed to the department, and the specials, member-offer and brand filters do not apply to it. The price and the country / region / style / variety filters do. When the site answers a keyword with a category, style or brand page instead of products (for example pale ale, vodka or guinness), that page is read instead; a keyword whose curated page returns no products is reported in the log, never an empty success.

Paste a category link, member offers only:

{
"mode": "url",
"urls": ["https://www.danmurphys.com.au/whisky/scotch-whisky"],
"memberOffersOnly": true,
"maxProducts": 0
}

Recurring price-watch run (scheduled, incremental):

{
"mode": "search",
"department": "spirits",
"incrementalMode": true,
"emitExpired": true,
"maxProducts": 0
}

Run it from your code

Python:

from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/danmurphys-scraper").call(run_input={"mode": "search", "department": "beer", "maxProducts": 50})
for product in client.dataset(run["defaultDatasetId"]).iterate_items():
print(product["name"], product["price"])

JavaScript:

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/danmurphys-scraper').call({ mode: 'search', department: 'beer', maxProducts: 50 });
const { items } = await client.dataset(run.defaultDatasetId).listItems();

Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the Integrations tab.

Caps and run size

Max products per scope is counted per scope, not across the whole run. Each scope (the keyword search, the department, and every pasted category link) scans up to that many products on its own, so a run with a keyword and a department can return up to twice the cap, and three pasted category links up to three times it. Pasted product links are not counted against the cap at all. To bound a run's total size, divide your budget by the number of scopes. Max pages per scope (24 products per page) also applies to each scope separately.

Resume and recurring updates

  • Resume (resumeFromRunId) continues one interrupted run: paste its run id and products it already collected are skipped, so you don't pay twice. It cannot be combined with incremental mode: the two solve different problems (one continues a single run, the other tracks a recurring scope), and the run refuses to start if both are set.
  • Incremental mode (incrementalMode) is for scheduled runs over the same search. Each product is classified NEW, UPDATED (with changedFields), UNCHANGED (suppressed and not billed unless emitUnchanged is on), REAPPEARED or EXPIRED (only after a run that scanned its whole tracked scope). stateKey names or shares the stored baseline; leave it empty to auto-key on mode, department and filters.

What counts as a change. Every output field is compared (price, was price, special and member-offer flags, case/single/any-six prices, stock quantity, purchasable and delivery flags, name, description, ratings, badges, images). scrapedAt, categoryUrl and the detail-enrichment flags are never compared. Use Ignore fields when detecting changes (incremental mode only) (ignoreFieldsForChanges) to leave more fields out; the fields stay in the output but never create an UPDATED row or appear in changedFields. Dotted paths such as variations.image drop that key from every array element.

Price and stock monitoring (all optional, off by default). Set changeScope to priceAndStock so a product is UPDATED only when a price, special flag or stock field moves. Description, image and label changes then never create a row. Turn on compactUpdates for slim UPDATED and EXPIRED rows (ids, url, name, current price and stock, and priceChanges with old and new values); compact UPDATED rows skip detail enrichment, so they carry no detail-enrichment charge. expireAfterMissedScans (default 1) marks a product EXPIRED only after it was missing from that many consecutive complete scans. EXPIRED means the product dropped out of the scanned listing, which a delisting and a temporary drop both cause; read stockQty and purchasable for availability. Changing the scope or ignore list under an automatic state key starts a fresh baseline; set a stateKey to keep one.

Tip: use maxProducts: 0 (or a cap larger than the scope) for incremental monitoring. The cap counts products scanned, including unchanged ones, and every run starts again from the first page. A capped incremental run therefore re-checks the same first products every time: price changes, new listings and removals further down the list are never seen, and the EXPIRED pass is skipped because the scan never finishes. Once those first products stop changing, a capped run can return 0 rows and log "No products matched" even though it scanned its full allowance; that means "nothing changed in the part that was scanned", not "nothing changed". The default cap is 20, so raise it before scheduling.

Tip: emitExpired batches respect maxProducts. When maxProducts is above 0, the EXPIRED rows pushed (and billed) in a run are limited to maxProducts minus every product row already pushed in that run, pasted product links included even though those links do not count against the per-scope cap. EXPIRED rows therefore never add to a run that has already reached maxProducts. Because maxProducts is applied per scope, a run with several scopes can already have pushed more than maxProducts; in that case no EXPIRED rows are emitted in that run and the missing products stay remembered for a later run. Any further missing products are left remembered, not expired, and are not billed in this run. With maxProducts: 0 the EXPIRED batch is not limited, so every product missing from a complete scan is pushed as EXPIRED, however many that turns out to be. A capped run rarely finishes its scan, and the EXPIRED pass only runs after a complete scan. If a tracked scope shrinks a lot between scheduled runs, start a fresh stateKey (or narrow the scope) and check the run log's change-count summary before widening a schedule.

Supported URL shapes

ShapeExampleBehaviour
Sub-departmenthttps://www.danmurphys.com.au/beer/craft-beerWalks the category.
Sub-department, multi-wordhttps://www.danmurphys.com.au/whisky/scotch-whiskyWalks the category.
Department onlyhttps://www.danmurphys.com.au/spiritsWalks the whole department.
Style pagehttps://www.danmurphys.com.au/beer/beerstyle-pale-aleWalks the department with that style filter applied.
Product pagehttps://www.danmurphys.com.au/product/DM_560632/balter-xpa-cans-375mlOne product row with the same price, offer, stock and attribute fields as a listing row.
Search linkhttps://www.danmurphys.com.au/search?searchTerm=pale%20aleRuns a keyword scope; when the site answers the term with a category or style page, that page is walked.
?page=N suffix.../beer/craft-beer?page=2The suffix is not used (warning logged); the walk starts at page 1.

Unrecognised links are skipped with a warning. A link the site's data service rejects (Category does not exist, today including the wine departments) or a product code that does not exist is named in the log, and a run in which every scope was rejected fails with that message instead of finishing empty. In an incremental run, a rejected or unreadable scope also switches the EXPIRED pass off, so a dead link can never expire the products of a live one.

Send results into your apps (MCP connectors)

Optionally pipe the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step after the scrape: the Apify dataset is never changed.

What gets written to the connector: a condensed, human-readable summary of each product, not the full JSON. Each item becomes one entry with a title and its key fields flattened to plain text. The complete record always stays in the Apify dataset.

  1. Authorize a connector once under Apify → Settings → Integrations (Notion, or a compatible MCP target).
  2. Select it in the MCP connectors input field. (If the picker is empty, you haven't authorized a connector yet.)
  3. For Notion, also set notionParentPageUrl to the page where products should be created.

The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.

Input Parameters

ParameterTypeDefaultDescription
modestringsearchsearch (department / keyword) or url (paste category or product links).
departmentstringbeerDepartment slug. beer, spirits, whisky, accessories and gifts answer today; the wine, premix, other-drinks, zero-alcohol and food routes are currently rejected by the site's data service (reported in the log).
subDepartmentstring(none)Sub-department name, case-insensitive (Craft Beer, craft-beer, Scotch Whisky, Vodka). A name that matches nothing stops that scope with an error listing the valid names. Leave empty for the whole department.
searchTermstring(none)Free-text search. If given, a keyword scope runs in addition to the department scope (site-wide; department, brand and offer filters do not apply to it).
brandstring(none)Brand name or slug, case-insensitive (Stone & Wood, stone-and-wood). A brand that matches nothing stops that scope with an error listing the valid brands; it is never silently ignored. Search mode only.
urlsarrayfive example linksCategory routes and/or product links. Multi-URL supported; pagination walks forward automatically. In url mode with this field untouched, five examples are scraped: a sub-department, a multi-word sub-department, a whole department, a department /all link and a product link.
specialsOnlybooleanfalseOnly products with a current offer. Applies to the department scope and every URL-mode category (not to the keyword scope or pasted product links).
memberOffersOnlybooleanfalseOnly products carrying a member (My Dan Murphy's) offer. Applies to the department scope and every URL-mode category (not to the keyword scope or pasted product links).
minPricenumber(none)Lowest price in AUD, using the site's own price filter. Applies to the keyword scope, the department scope and every URL-mode category.
maxPricenumber(none)Highest price in AUD; either bound may be left empty. Same scope as minPrice.
sortBystringRelevanceSite-native sort: Relevance, Name (A-Z), PriceAsc (low to high) or PriceDesc (high to low). With a small maxProducts this picks the cheapest or dearest items.
countrystring(none)Country of origin facet, case-insensitive; comma-separated for several. Applies to the department scope, the keyword scope and every category URL; no match stops that scope with an error listing the valid values.
regionstring(none)Region facet (for example Geelong), same rules as country.
stylestring(none)Style facet of the department (beer style such as IPA, whisky style ...), same rules as country.
varietystring(none)The site's variety facet (the department's types such as Craft Beer), same rules as country.
fetchDetailsbooleanfalseFetch each pushed product's detail payload (rich description, tasting notes, recommendations). Adds a per-product surcharge.
maxPagesinteger0Cap on pages walked per scope. 0 walks until the department is exhausted.
maxProductsinteger20Cap on products scanned, applied to each scope separately (pasted product links are not counted). 0 = unlimited. Use 0 for incremental monitoring.
proxyConfigurationobjectApify Proxy (datacenter)Proxy and connection settings. The default Apify datacenter proxy works; Apify Residential with country AU is optional.
resumeFromRunIdstring(none)Continue one interrupted run. Cannot be combined with incremental mode.
incrementalModebooleanfalseFor scheduled monitoring: classify every product NEW / UPDATED / UNCHANGED / REAPPEARED / EXPIRED against the previous run of the same search.
stateKeystring(none)Manual key for the incremental baseline. Leave empty to auto-key on mode, department and filters (including price, sort and the facet filters when set).
emitUnchangedbooleanfalseIncremental mode only. Also return (and bill) products classified UNCHANGED.
emitExpiredbooleanfalseIncremental mode only. Also return (and bill) products that vanished since the last complete run.
changeScopestringallIncremental mode only. priceAndStock marks a product UPDATED only when price or stock fields move.
compactUpdatesbooleanfalseIncremental mode only. Slim UPDATED and EXPIRED rows with priceChanges.
expireAfterMissedScansinteger1Incremental mode only. Consecutive complete scans a product must be missing before it is EXPIRED.
ignoreFieldsForChangesarray(none)Incremental mode only. Fields (dotted paths allowed) left out of change detection.
mcpConnectorsarray(none)Optional: send a summary of each product to apps you authorized under Integrations.
notionParentPageUrlstring(none)Notion connector only: page under which products are created.
maxNotifyListingsinteger0Cap on items exported to connectors per run. 0 exports everything pushed.

Output views

The dataset has three views in the Console: Overview (prices, offers, ratings, stock and key attributes), Changes (incremental) (change type, changed fields, price and stock) and Product detail (fetchDetails) (descriptions, tasting notes, awards, recommendations).

Output Example

Sample shape: values are illustrative placeholders, not from a live record.

{
"stockcode": "812345",
"name": "Sample Brewing Co Pale Ale 6x355mL Cans",
"url": "https://www.danmurphys.com.au/product/812345/sample-brewing-co-pale-ale-6x355ml-cans",
"description": "Sample Brewing Co Pale Ale 6x355mL Cans",
"brand": "Sample Brewing Co",
"department": "beer",
"categoryUrl": "https://www.danmurphys.com.au/beer",
"badges": ["Top seller"],
"price": 45.99,
"wasPrice": 52.99,
"isOnSpecial": true,
"isOnOffer": true,
"isMemberOffer": true,
"memberOfferLabel": "Member Price",
"promotionType": "member offer",
"casePrice": 179.99,
"singlePrice": 52.99,
"inAnySixPrice": 42.99,
"unit": "Each",
"packageSize": "6 pack",
"abv": "4.5%",
"standardDrinks": "9.8",
"country": "Australia",
"style": "Pale Ale",
"size": "355mL",
"rating": 4.3,
"reviewCount": 87,
"stockQty": 240,
"purchasable": true,
"imageUrl": "https://cdn.danmurphys.com.au/images/812345-medium.jpg",
"scrapedAt": "2026-09-30T04:00:00Z",
"changeType": "UPDATED",
"changedFields": ["price", "stockQty"],
"firstSeenAt": "2026-08-01T04:00:00Z",
"lastSeenAt": "2026-09-30T04:00:00Z"
}

Plan Requirement

The default Apify datacenter proxy works for this site, so no residential proxy is required and any plan that includes Apify Proxy can run it. The site's pages are an empty shell and the product data comes from its own backend, which the default connection reaches. Apify Residential with country AU is optional. Apify free plans do not include proxy access at all: on such a plan the run continues on a backup connection where possible and may still return nothing. Switch the proxy group under Connection, or supply your own proxy URLs.

FAQ

How much does it cost?

Two billed events, both shown with their current rates on the Pricing tab. Product listing is charged once per product row returned (including EXPIRED and UNCHANGED rows only when you turn on emitExpired / emitUnchanged). Detail-page enrichment is charged once per pushed product whose detail payload was actually fetched, and only when fetchDetails is on. Suppressed unchanged rows and products that fail to load are not charged. Use Max products per scope to cap cost; it applies to each scope separately, so a run with a keyword plus a department, or several pasted category links, can return a multiple of it.

This actor collects only publicly available product data. You are responsible for how you use it: follow Dan Murphy's terms and the laws that apply to you, including any age-verification and liquor-advertising rules in your jurisdiction, and get legal advice if you plan commercial redistribution. Dan Murphy's requires visitors to be of legal purchasing age; make sure your use of this data respects that.

Can I get only new or changed prices on a schedule?

Yes. Schedule the actor from the Schedules tab and turn on Incremental mode. Each run then returns only new, updated, reappeared or expired products, and unchanged ones are not billed unless you turn on Emit unchanged products. Set Max products per scope to 0 for this: a capped run re-checks only the same first products every time and never detects expired ones.

Why does the "wine" department not work?

wine on its own is not a department, and the site's data service currently rejects the wine routes too (red-wine, white-wine, champagne-sparkling, fortified-wine and the premix, other-drinks, zero-alcohol and food routes answer "Category does not exist"; checked live on 2026-10-28). The run names the rejected route in the log, and a run where nothing else was requested fails with that message instead of finishing empty. Beer, spirits, whisky, accessories and gifts answer. The wine values stay selectable so they work again as soon as the site re-enables them.

Why did my run fail instead of returning an empty dataset?

If nothing can be read, the run stops with a clear message so "no products matched" is never confused with "nothing could be read". You will see one of these messages: the proxy connection was refused for your account (a plan with proxy access is required, or check your own proxy URLs); no results because residential proxies are unavailable and the backup connection could not load the pages; "Could not load any results page (the requests failed)", a connection problem to retry later; or "Nothing was collected: ...", which names the category, sub-department, brand, filter value or product code the site does not recognise (with the valid names where the site offers a list).

Can emitting expired products push more rows than my Max products setting?

No: when Max products per scope is above 0, EXPIRED rows never add to a run that has already reached that number. With Emit expired products on, the EXPIRED rows pushed (and billed) in a run are capped at Max products per scope minus every product row already pushed in that run, pasted product links included. Because the setting is applied per scope, a run with several scopes may already have pushed more rows than that number, and then no EXPIRED rows are emitted in that run; products beyond the cap stay remembered and are neither emitted nor billed in this run. With Max products per scope set to 0 (unlimited) the EXPIRED batch is not capped: every product missing from a complete scan is pushed as EXPIRED, so a scope that shrank a lot since the last complete run can bill many EXPIRED rows. Start a fresh State key for scopes that can shrink sharply, or check the run log's change-count summary before widening a schedule.

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 or another compatible connector.

🔗 Want more wine and drinks data?

Pair this actor with these related scrapers from the same team:

🥦 Woolworths AU Scraper
Scrape Woolworths Australia (woolworths.com.au) grocery products and reviews. Search by...
🍷 Untappd Beer, Brewery and Check-in Scraper
Scrape Untappd beers, breweries, venues and check-ins by keyword, brewery, Top Rated...
🍷 Wine-Searcher Scraper
Look up wines on wine-searcher.com by name, URL, or LWIN code. Returns 30+ fields...
🥦 Coles AU Scraper
Scrape Coles.com.au grocery products and customer reviews. Search by keyword, browse...
🍷 Vivino Wine Scraper
Scrape Vivino.com for wine ratings, prices, taste profiles, food pairings, grapes, and...
🥦 ALDI AU Scraper
Scrape ALDI Australia (aldi.com.au) products: name, brand, price, was-price, savings...

👉 Browse all abotapi scrapers

💬 Support

  • 🐞 Found a bug or a missing field? Open a ticket on the Issues tab. We usually reply within hours.
  • 🛠️ Need another site, extra fields or a private build? Email contact@abotapi.com.
  • ⭐ Enjoying it? A quick review on the actor page helps other users find it.
⌂ 换网址