Groupon Local Deals Scraper and New-Deal Alert (US)
Pricing
from $1.40 / 1,000 deal scrapeds
Groupon Local Deals Scraper and New-Deal Alert (US)
Groupon local deals for any US city and category, one flat row each: title, merchant, price, original value, discount %, rating and review count, location, distance, deal URL - and whether each deal is new or re-priced since the last run.
Pricing
from $1.40 / 1,000 deal scrapeds
Rating
0.0
(0)
Developer
Superslow Sloth
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
1
Monthly active users
5 days ago
Last modified
Categories
Share
Read the deals on Groupon's US city and category pages, one flat row each, and see which ones are new since your last run or have changed price. Give it cities (and optionally categories and sort orders), schedule it, and turn on Only new and re-priced deals to get an alert feed that costs nothing for deals you have already seen.
No account, no login, no Groupon API key. It reads the public local pages the same way a browser does.
Input
| Field | Notes |
|---|---|
cities | City slugs as in groupon.com/local/<slug>: los-angeles (default), chicago, new-york, ... A full groupon.com/local/<slug> URL also works. |
categories | Category slugs as in groupon.com/local/<city>/<slug>. Empty (default) reads only the plain city page. The eight top-level ones: things-to-do, food-and-drink, beauty-and-spas, automotive, retail, personal-services, home-improvement, health-and-fitness. Sub-categories such as restaurants also resolve. |
sorts | Any of relevance (default), rating, price:asc, price:desc, distance. |
maxItems | Stop after this many distinct deals (default 100). |
changesOnly | Return and charge only deals that are new or re-priced since the last run for the same cities. |
proxyConfiguration | Required, Apify datacenter proxy by default. A run with no proxy stops before it fetches or charges anything. |
How many deals a page gives, and why there are categories and sorts
Groupon's server renders only the first screen of deals for a page: 9 on a
city or category page (25 on the New York State page, 66 on one automotive
fetch). The rest are loaded by a call its own page makes in the browser, and
the ?page=N parameter is ignored (pages 0, 2, 3, 50 and 500 of Los Angeles
each returned an ordinary first screen of 9 and never a later one, measured
2026-10-07). What does change the first screen is the category page and the
sort order: the Los Angeles city page plus its eight top-level categories,
each in all five sorts (45 requests), returned 49 distinct deals. So the way to
read more deals is a longer list of categories and
sorts, not a bigger maxItems.
Two more things measured on the same day:
- The same URL fetched twice can return a different first screen. Groupon
A/B-tests two front ends (
nextandtanstack) and a different ranking comes with them. This actor reads the same fields from both. - Groupon has no newest-first sort, so a "new" deal can show up anywhere in a feed, and a deal that was on Groupon all along can look new if an earlier run happened not to see it. The memory fills up over the runs: a longer list of categories and sorts makes that happen less.
Unknown cities
Groupon does not return a 404 for a city or category it does not have: it
answers 200 with the generic /local page (or the parent city). This actor
compares the page's canonical link with the one it asked for, discards the
tiles when they differ, and reports the page in the run log instead of labelling
another place's deals with your city.
Output
| Field | Notes |
|---|---|
change | new, price_drop, price_rise or unchanged, compared with the previous run for the same cities. |
deal_id, deal_uuid, url | deal_id is the /deals/<slug> slug and is what change tracking keys on. url is https://www.groupon.com/deals/<slug> without the tracking query. |
title, merchant, merchant_location_count | The merchant is the line above the title (a chain's name for a chain); merchant_location_count is the "(443 Locations)" beside it. |
price, currency | Groupon's price, in USD. |
original_value | The crossed-out value. Null when the tile shows none. |
discount_percent | The percentage on the red badge as Groupon publishes it. With a sale price it is measured to the sale price (value 94, price 69, sale price 62.10 is "-34%"), so it is not always 1 - price / original_value. |
sale_price | The price after Groupon's sitewide sale, when the tile shows one. |
promo_price, promo_code, promo_ends_at | The price with the promo code applied, the code, and when it ends (ISO-8601 UTC). |
valid_through | When the deal stops being sold, from the page's schema.org data. Often absent. |
rating, rating_count | Groupon's star rating and its number of ratings. Null for a deal with none. |
location, distance_text, distance_miles | The tile's location line (a neighbourhood or a street address) and its distance as printed, plus the number when it is in miles. The distance is measured from Groupon's reference point for the page - the city centre on every page read - not from you. |
badge, is_sponsored | The label on the tile's image ("Popular Gift", "10% Cashback"), and Groupon's own sponsored flag. |
image_url, redemption_location_id | The tile image, and which location of a multi-location deal the tile links to. |
city, category | The page the row was read from. A deal listed on several pages carries the first one this run read it on. |
Honest nulls
A field Groupon did not give is null, never 0 or "". original_value is
null for a deal shown without a value, discount_percent null for one without a
badge - a 0 would read as a deal with no discount, which is a different claim.
rating is null for a deal with no ratings, not 0 stars.
No "bought" count
The tile does not show how many people bought a deal, and neither the tile's data nor the page's schema.org list carries it, so there is no such column.
Where the fields come from
Each tile carries Groupon's tracking JSON in a data-bhd attribute: title,
prices (in cents), discount, location line, rating, image. Merchant, distance
and badge are read off the tile's text. The deal expiry comes from the page's
schema.org ItemList, which can also list a deal that has no tile in the page
as received - such a deal is still returned, with the tile-only fields null.
Change tracking
The memory (deal id to price) lives in a named key-value store on your
Apify account, groupon-deals-watch, so no one else can see it. It is keyed on
the set of cities, not on categories or sorts: those are only different
views onto the same city's deals, so adding a category reports that category's
unseen deals as new and leaves the ones already seen unchanged. The first
run for a set of cities labels every deal new. From the second on, a deal is
new if no earlier run saw it, price_drop / price_rise if price moved,
and unchanged otherwise. With changesOnly, unchanged deals are neither
returned nor charged.
Pricing
Pay per event: $0.002 per run (actor-start, charged after your input is
validated, so a run that fails on bad input costs nothing), and $0.0014 per
deal row written (deal-scraped). With changesOnly, unchanged deals are not
charged.
Proxy and blocking
Groupon is behind Cloudflare. A plain request is answered with HTTP 403 and a "Just a moment..." challenge page; measured 2026-10-07 with a default client from an ordinary connection, and from Apify's own IP address on both days it was probed. Apify datacenter proxy exits were answered with the real page, so the proxy is mandatory here. A 403, a 429, a 5xx, and a 200 that turns out to be the challenge are all retried from a fresh exit address; a page that is not Groupon's at all is treated the same way. The actor presents a browser's TLS fingerprint (curl_cffi, current Chrome).
Each page is 0.8 to 1.3 MB of HTML, about 9 deals, so proxy traffic is roughly 100 KB per deal.