Skip to content
Deal Extractor

Turn retail broadsheets intostructured deal data.

Send a broadsheet, circular or catalogue page to the API. Our vision model crops out every deal, however crowded the page, and each crop is then extracted on its own. You get back every deal as JSON: products, pack sizes, prices, savings and deal type, checked against retail rules.

See a real example
  • $0.04 per extracted deal
  • No setup fees or minimums
  • REST API, JSON out
A crowded broadsheet page is scanned and every deal is cropped. One crop is lifted out and read on its own, and its fields type into a validated JSON record.1Crop1Crop2Extract2Extract3JSON3JSONWEEKLY SPECIALSValid Oct 7–13Meadow Farm Milk 1 LSAVE $1.98$52 FOR$1.29$3.49$8.99$4.50$6$2$3.99$15COMBO$9ANY 3$5.49$7.9912 deals croppedMeadow Farm Milk 1 LSAVE $1.98$52 FORCrop 1 of 12x 70 · y 270 · w 680 · h 480deal.jsonValidated{"brand": "Meadow Farm","name": "Whole Milk","dealType": "Bulk","packSizeNotation": "2x1x1L","price": 5.00,"savingValue": 1.98,"coordinates": {"x":70,"y":270,…},"contentHash": "eb035f19…",}

Sample retailer page. "2 for $5" is the store's offer to its shoppers, read off the page. Our price is $0.04 per deal extracted.

The problem

Promotions are printed, so someone has to type them up.

Retailers publish new broadsheets every week, across regions, banners and channels. Brands, analysts, retail media teams and price-comparison sites need the deals in them as data, and most of it is still keyed in by hand.

Typing it in doesn't scale

Every deal on every page is read and keyed in by a person. Add a retailer, a region or an edition and the cost and the delay grow with it.

Deals are easy to misread

"2 for $7", a 6-pack priced each, a three-product bundle, a members-only price. One small misreading gives you the wrong price, unit or deal type.

The same deal appears many times

One promotion runs in several regional editions, each with different artwork. Unless they're matched, it gets captured and counted more than once.

The data goes stale quickly

A broadsheet is usually valid for a week or two. If capture takes days, most of that window has passed before anyone can use the data.

What makes it work

Every deal cropped accurately, consistently and fast.

Before a deal can be read, it has to be cut out of the page. On a broadsheet that's the hard part: deals are packed edge to edge, price flashes overlap the next product, and every retailer lays its pages out differently. We couldn't find anyone who did this reliably, so we trained our own model to do it.

SPAR Super Savings broadsheet page with detected deal regions
Combo
Straight
Straight
Bulk
Straight
Straight
Straight
Bulk
Bulk
Straight
A real SPAR broadsheet page. Every box is a deal the model cropped on its own; each crop was then extracted separately.
Step 1 · Crop

A vision model trained to find deals

Built through a long labelling and training effort on real retailer broadsheets. It draws a tight box around each deal, taking in the product, its price and its deal wording without pulling in the deal next door.

  • AccurateOne whole deal per crop, even where tiles touch and price flashes overlap.
  • ConsistentThe same layout is cropped the same way every week, across retailers, banners and editions.
  • FastA whole page is cropped in one go, so a week's broadsheets are done while they're still running.
  • CheckableEvery crop comes with pixel coordinates and an image, so anyone can see what was read.
Step 2 · Extract

Each crop is read on its own

The extraction engine only ever sees one deal at a time, so a price can't be borrowed from a neighbouring deal and products from different tiles can't be merged. Every field is checked against retail rules for deal type, pack size and pricing before it's returned.

A cropped SPAR combo deal: rice, maize meal and sunflower oil, buy all for R240
  • dealType: Combo
  • 3 products
  • price: 240
  • savingValue: 123
How it works

From page image to deal records.

Two stages do the work. A vision model trained on retail pages crops out each deal, then an extraction engine reads every crop separately and checks it against retail rules.

  1. 01

    Submit the page

    Send a broadsheet, circular or catalogue page image to the REST API. Multi-page catalogues can be submitted as one advert.

  2. 02

    Crop every deal

    The vision model cuts each deal out of the page and returns its pixel coordinates, even when tiles touch and price flashes overlap.

  3. 03

    Extract and check

    Each crop is read on its own, so nothing is borrowed from the deal next door. Deal type, pack size and pricing are checked against retail rules before anything is returned.

  4. 04

    Collect the JSON

    Poll for status or fetch results in bulk. Each deal has a match key that stays the same across regional editions, and a link to its cropped image.

Real examples

Hard deals from real broadsheets.

Crops from retailer adverts, and what the API returned for each. These are South African pages, so prices are in rand.

Joko Loose Tea in four pack sizes, each with its own price

Four sizes, four prices

One crop comes back as four records that share a dealImageKey, each with its own pack and price.

  • Joko Loose Tea4x1x250gBulkR183.99
  • Joko Loose Tea8x1x125gBulkR199.99
  • Joko Loose Tea10x1x50gBulkR126.99
  • Joko Loose Tea15x1x20gBulkR96.99
Nutri-Fruit Juice assorted 6 x 200ml, any 3 for R90

Any 3 six-packs

Three packs of six, not three cartons: quantity 3, pack size 6. Mixed flavours are flagged as assorted.

  • Nutri-Fruit Juice3x6x200mlBulkR90

assorted: true

Grocery combo: Sasko Cake Flour, White Star Maize Meal and Spekko Rice for R99.99

Three products, one price

Each product gets its own row with its own brand and size, all sharing the combo price.

  • Sasko Cake Wheat Flour1x1x2.5kgComboR99.99
  • White Star Super Maize Meal1x1x2.5kgComboR99.99
  • Spekko Long Grain Parboiled White Rice1x1x2kgComboR99.99
The data

One fixed schema for every deal.

Every deal comes back in the same shape, ready for analytics, price monitoring and compliance work. These two records are real output from a SPAR broadsheet page.

Example 1 · StraightSpring Meadow UHT · 6 × 1 litre
{
  "coordinates": {
    "x": 432,
    "y": 974,
    "width": 405,
    "height": 387
  },
  "products": [
    {
      "brand": "Spring Meadow",
      "subBrand": "UHT",
      "name": "Full Cream Milk",
      "packSizeNotation": "1x6x1L",
      "quantity": 1,
      "packSize": 6,
      "unitOfSize": 1,
      "unitOfMeasure": "L",
      "assorted": true
    },
    {
      "brand": "Spring Meadow",
      "subBrand": "UHT",
      "name": "Low Fat Milk",
      "packSizeNotation": "1x6x1L",
      "quantity": 1,
      "packSize": 6,
      "unitOfSize": 1,
      "unitOfMeasure": "L",
      "assorted": true
    }
  ],
  "dealType": "Straight",
  "description": "Spring Meadow UHT Full Cream or Low Fat Milk 6 x 1 litre",
  "savingText": "Save R10",
  "savingValue": 10,
  "price": 89.99,
  "loyalty": false,
  "asSeenOnTv": false,
  "discountPercentage": 0,
  "contentHash": "eb035f19022a963e",
  "dealImageKey": "9cd752bf-92aa-4d53-9801-30415db3a17e"
}
Example 2 · ComboRice + maize meal + oil
{
  "coordinates": {
    "x": 78,
    "y": 519,
    "width": 885,
    "height": 449
  },
  "products": [
    {
      "brand": "Allsome",
      "subBrand": "",
      "name": "Long Grain Parboiled Rice",
      "packSizeNotation": "1x1x10kg",
      "quantity": 1,
      "packSize": 1,
      "unitOfSize": 10,
      "unitOfMeasure": "kg",
      "assorted": false
    },
    {
      "brand": "SPAR",
      "subBrand": "",
      "name": "Super Maize Meal",
      "packSizeNotation": "1x1x10kg",
      "quantity": 1,
      "packSize": 1,
      "unitOfSize": 10,
      "unitOfMeasure": "kg",
      "assorted": false
    },
    {
      "brand": "SPAR",
      "subBrand": "",
      "name": "Sunflower Oil",
      "packSizeNotation": "1x1x2L",
      "quantity": 1,
      "packSize": 1,
      "unitOfSize": 2,
      "unitOfMeasure": "L",
      "assorted": false
    }
  ],
  "dealType": "Combo",
  "description": "Combo deal for Allsome Long Grain Parboiled Rice, SPAR Super Maize Meal, and SPAR Sunflower Oil",
  "savingText": "Save R123",
  "savingValue": 123,
  "price": 240,
  "loyalty": false,
  "asSeenOnTv": false,
  "discountPercentage": 0,
  "contentHash": "61ebe15ea801660d",
  "dealImageKey": "68e7a9b8-e6f4-4b7e-a46f-932ab978a284"
}
3 productseach on its own row, with its own brand and pack size
1 priceR240 for the combo, with the R123 saving captured
Comboclassified from the "Buy all for" wording

Location

coordinates
Position of the deal on the original page (x, y, width, height), for highlighting, cropping or auditing.
dealImageKey
Links the record to the cropped image of the deal. When one crop carries several prices, each price is its own record and they share this key.
contentHash
Match key. The same deal gets the same hash in every regional edition, even when the artwork differs.

Products

brand · subBrand
Brand and range. Retailer private labels count as brands.
name
One row per variant: "Whole or Low Fat" becomes two linked product rows.
assorted
Set when the deal covers several flavours, variants or sizes.
packSizeNotation
Quantity x pack x size, e.g. 1x6x1L.
quantity · packSize
Items the shopper must buy, and units inside each physical pack.
unitOfSize · unitOfMeasure
Size of one unit in the unit printed (g, kg, ml, L, oz, lb, each). Never converted.

Pricing and mechanics

dealType
Straight, Bulk, Combo or Multibuy.
price
What the shopper pays for the deal as advertised.
savingText · savingValue
The saving as printed ("Save $2") and as a number.
discountPercentage
Only set when a percentage is printed.
loyalty
Member, rewards-card or app-only pricing.
asSeenOnTv
Only set when the page says so.
description
A one-line summary of the deal, for search and listings.
Deal types

Four deal types, one set of rules.

The same wording always gets the same classification, whichever retailer, edition or market it comes from.

Straight

One item or one pack

A single sellable item at a price. "Each" and "A or B" offers are Straight, and so is a multipack when the pack is physical.

Organic Whole Milk 6 x 1 L, $8.99 per pack

1x6x1L
Bulk

Several for one price

The shopper has to buy more than one item: "2 for", "any 3", "buy 6". Ambiguous quantities default to Bulk.

Baked Beans 14 oz, 2 for $3

2x1x14oz
Combo

Different products together

A bundle of different products sold as one deal. Each product gets its own row, sharing one price.

Rice 5 kg + Pasta Sauce + Olive Oil, all for $15

3 products · 1 price
Multibuy

Cheapest one free

Mix-and-match promotions where the lowest-priced item in the basket is free.

Buy any 3 haircare, cheapest free

Multibuy
2x6x330ml
  • Deal quantity: items the shopper must buy (always 1 for Straight)
  • Units per pack: units inside one physical pack
  • Unit size: size of each unit, in the unit printed on the broadsheet

Pack size depends on the deal type

Almost identical wording can mean one multipack or several separate items. Get it wrong and every unit price and comparison built on it is wrong too.

"Yogurt 6-pack 150 g, $4"

Straight1x6x150g
One physical pack of six

"6 yogurts 150 g for $4"

Bulk6x1x150g
Six separate items

"Any 2 cola 6 x 330 ml packs for $9"

Bulk2x6x330ml
Two packs of six
Compared

Why not OCR or a general-purpose model?

A general-purpose model can describe a broadsheet. It can't reliably crop a crowded page into separate deals, or tell "2 for $7" from a 6-pack priced each. We looked for a service that could, found none, and built Deal Extractor to do exactly that.

CapabilityOCRGeneral-purpose AIDeal Extractor
Crops each deal cleanly on a crowded pageNoUnreliablePurpose-trained model
Coordinates and a crop for each dealNoNoYes
Reads each deal from its own cropNoReads the whole page at onceYes
Deal type and pack-size rulesNoInconsistentChecked on every deal
Output formatRaw textVaries between runsFixed JSON schema
Matches a deal across regional editionsNoNoYes
Advert validity datesRaw textOften picks the wrong dateIgnores side-promotion dates
Integration

Integrate over a plain REST API.

Submit page images, poll for status and collect JSON over HTTPS. We host and run the service, so there's nothing to deploy, no models to run and no prompts to tune.

Nothing to host

No models, GPUs or ML team on your side. Your developers work with JSON over HTTPS.

Queued and retried

Pages go through a background queue with automatic retries. Every result is validated, and failures alert us, not you.

Admin portal

Review extracted deals, track usage and cost, and export reports to Excel.

POST/Advertisement/SubmitAdvertisementImageSubmit a broadsheet page to Deal Extractor
POST/Advertisement/SubmitAdvertisementMetaImagesSubmit one or more pages for advert metadata
GET/Advertisement/GetImageStatusFetch status and extracted deals for an image
POST/Advertisement/GetImageStatusBulkFetch results for many images in one call
POST/Advertisement/ResubmitAdvertisementImageRe-run extraction on an image
Pricing

Pay for the deals we extract.

Pages with nothing usable, and deals that can't be read, aren't billed. There are no setup fees, platform fees or minimums.

One price

$0.04

per deal extracted

  • Structured JSON for every deal
  • Matching across regional editions
  • Advert dates, terms and key number
  • Admin portal and Excel export
  • No setup, training or platform fees
  • No minimum commitment

Estimate your monthly cost

Set your monthly volume, and what manual capture costs you per deal today.

10,000
50010k100k1M

Deal Extractor

$400

per month at $0.04 a deal

Manual capture

$7,500

About $7,100 a month less than capturing by hand, 95% lower.

The manual figure assumes a few minutes of a data-entry operator's time per deal. Change it to your own number.

FAQ

Questions, answered.

The detector was trained to recognise deals, not one retailer's design, so it works on grocery, pharmacy, DIY and general-merchandise broadsheets from any market. Prices come back as numbers in the currency printed on the page.

If you need specific retailers or languages covered, send us a few sample pages and we'll show you the output before you commit to anything.

Reading a whole page at once is where most mistakes come from: a price gets attached to the wrong product, two neighbouring deals are merged, or a small deal is missed. Cropping first means the extraction step only ever sees one deal.

Cropping a crowded broadsheet accurately is the hard part. We couldn't find a service that did it reliably, so we trained our own vision model on real retailer pages. It's the core of the product, and it keeps improving as we retrain on new layouts.

$0.04 for each deal successfully extracted. A page with eight deals costs $0.32. Pages with nothing usable, and deals that can't be read, are free. There are no setup fees, platform fees or minimums.

Each deal is extracted from its own crop, then checked against retail rules for deal type, pack size and pricing. Discount percentage, loyalty pricing and "as seen on TV" are only set when they're printed on the page.

Every record links to the image it was read from, so anyone on your team can check a deal against the artwork. We track quality in production and retrain as retailers change their layouts, and those improvements reach the API without any change on your side. The best test is your own pages: send us some and we'll show you the results.

Submit page images to the REST API, poll for status or fetch results in bulk, and receive JSON in a fixed schema. There's nothing to host, and most teams are integrated within days. The admin portal lets non-developers review deals, track usage and export reports to Excel.

Retailers run the same promotion in many regional editions, each with different artwork and layout, so comparing pixels doesn't work. We match on what the deal says instead. Each deal carries a content hash that is the same for the same offer wherever it appears, so you can remove duplicates across regions without extra work.

Happen Software, an engineering company working from Ireland and South Africa since 2016. Deal Extractor already processes real retailer broadsheets, through the same API and portal you would integrate with.

Tell us which broadsheets you work with and roughly how many deals a month. One of our engineers will run a sample of your pages, show you the results, and help with the integration if you go ahead.

Try it on your own broadsheets.

Send us a few pages. We'll run them and send back the extracted deals, with no commitment.

  • Reply within one business day
  • Results on your own pages
  • $0.04 per extracted deal