Guides
View as MarkdownSyncing the catalog
The products list returns one page of deals inside your inventory scope. Load the catalog into your own database once, then pull only what changed every few hours. Your site renders deals from your database, never live from Groupon. The full response shape, with every field, is in the API reference.
How to use the fields
- Price:
option.price.retailis the per-unit price the shopper pays.option.price.originalis the strike-through price. - What to list: show a product only when
statusisactiveandavailabilityRequiredis false. Show an option only when itsactiveis not false (nullmeans unknown: treat it as sellable).sold_outandexpiredproducts cannot be bought. - Products with
availabilityRequired: trueneed a time-slot booking and cannot be added to a cart. They are rare. Skip them. - Quantity limits: enforce
minUnitsandmaxUnitsfrom the option in your UI (nullmaximum means no maximum). - Promotion:
option.promotionis a promotional price the shopper gets by enteringpromotion.promoCodeon Groupon's checkout page beforepromotion.endsAt. It is informational: show it as "Use code SAVE15 at checkout." Cart totals always use retail.promotioncan benullat any time. - Images: each entry in a product's
mediahas arolethat names a size (small,medium,large,extra_large,wide_crop). Every entry is the same picture at a different size, and not every size is present on every deal. Pick by role, for examplelargefor a product page andmediumfor a grid, falling back to the entry with the largestwidth.widthandheightcan benull. - A page can contain fewer products than
limit, or even none, whilehasMoreis true. Keep paging untilhasMoreis false.
Keeping your catalog fresh
Deals keep changing: prices move, and deals are activated and deactivated. Load the catalog
once, then pull only what changed every few hours using updatedSince. Store the time of your
last refresh so the next pull knows where to start.
Step 1: initial full load, once
params = { country: "US", limit: 10 } # no updatedSince
cursor = none
repeat:
page = GET https://api-core.livingsocial.com/partner_storefront/products?params (+ cursor if set)
upsert every product in page.data by its id
cursor = page.nextCursor
until page.hasMore == false
lastRefreshAt = page.timestamp - 10 minutes # the final page's timestamp; persist ONLY after
# the whole walk succeedsStep 2: delta refresh, every few hours
params = { country: "US", limit: 10, updatedSince: lastRefreshAt } # do NOT send active
cursor = none
repeat:
page = GET https://api-core.livingsocial.com/partner_storefront/products?params (+ cursor if set)
upsert every product in page.data by its id # replace options, prices, status
cursor = page.nextCursor
until page.hasMore == false
lastRefreshAt = page.timestamp - 10 minutes # the final page's timestamp; persist ONLY after
# the whole walk succeedsStore lastRefreshAt durably, as a database row. If a walk fails part-way, do not advance it:
restart the walk from the previous lastRefreshAt.
The 10-minute overlap is intended. Upserts are idempotent, so re-reading a deal is harmless, and missing one is not.
Take the time from the final page's timestamp (Groupon's clock), not your own clock: an
updatedSince in the future is rejected.
Do not send active=true on a delta. A deal that was deactivated since your last refresh comes
back with a status other than active; you need that row to stop listing it.
After each upsert, recompute whether the product is listable (see "How to use the fields" above).
A delta can legitimately return the whole catalog. When a promotion starts, many prices change
at once without touching each deal's own update time, so Groupon ignores updatedSince for that
walk. The decision is made once, on the first page, and carried in the cursor, so a walk is
never half incremental and half full. Your code must handle a delta of any size.
You can repeat the full load at any time, for example to rebuild your database.
Rules for every walk
- Page sequentially. Do not request pages in parallel.
- Send the same parameters on every page of a walk. Only
cursorchanges. Changing a filter mid-walk invalidates the cursor. - A cursor is valid for 24 hours.
- If a page returns
BAD_REQUESTwith a cursor problem, discard the cursor and restart that walk from page 1. - On a retryable failure, retry the same page with backoff. See Handling errors.
- The catalog in your database is a cache. The cart endpoints re-check price and availability live, so they are the final authority at add-to-cart time.
Example request
curl -s "https://api-core.livingsocial.com/partner_storefront/products?country=US&limit=10&active=true" \
-H "Authorization: Bearer $GROUPON_API_KEY" \
-H "User-Agent: MyStore/1.0 (+https://mystore.example)" \
-H "x-request-id: $(uuidgen)"