Title: Merchlint – Catalog Audit for WooCommerce
Author: baltano
Published: <strong>September 1, 2026</strong>
Last modified: September 25, 2026

---

Search plugins

![](https://ps.w.org/merchlint/assets/banner-772x250.png?rev=3676536)

![](https://ps.w.org/merchlint/assets/icon.svg?rev=3676536)

# Merchlint – Catalog Audit for WooCommerce

 By [baltano](https://profiles.wordpress.org/baltano/)

[Download](https://downloads.wordpress.org/plugin/merchlint.0.3.3.zip)

 * [Details](https://srd.wordpress.org/plugins/merchlint/#description)
 * [Reviews](https://srd.wordpress.org/plugins/merchlint/#reviews)
 *  [Installation](https://srd.wordpress.org/plugins/merchlint/#installation)
 * [Development](https://srd.wordpress.org/plugins/merchlint/#developers)

 [Support](https://wordpress.org/support/plugin/merchlint/)

## Description

Merchlint reads your WooCommerce catalogue and tells you where it is losing you 
money. Every
 finding comes with the value it was based on, so you can check it 
yourself instead of taking our word for it.

It changes nothing. No product, no price, no stock level, no order. The audit reads
and reports;
 every fix is yours to make, in your own admin.

#### What it looks for

Seventeen checks, in six groups:

 * **Money** — products that sold in the past year and can no longer be bought; 
   goods selling
    without a photo or hidden from the catalogue; sale prices whose
   scheduled end date has passed and are still running; products with no price at
   all.
 * **Catalogue and navigation** — products outside every category; duplicate SKUs;
   variable
    products with every variation sold out.
 * **Content** — descriptions too short to answer a customer’s question; images 
   with no alt text.
 * **Performance and media** — oversized image files; bloated autoloaded options;
   media entries
    whose file is missing from disk.
 * **Environment** — PHP, WordPress or WooCommerce on a version that no longer gets
   security fixes;
    a scheduled-task queue that stopped running — WP-Cron and Action
   Scheduler are reported separately — which is what stalls every piece of work 
   meant to happen later.
 * **Product identifiers** — products with no GTIN, UPC, EAN or ISBN, and two products
   carrying
    the same one. An empty or duplicated identifier is the usual reason
   an item quietly drops out of a shopping feed, and nothing in WooCommerce says
   a word about it.

#### What makes it different

 * **Every finding shows its evidence.** Not “12 636 problems” but the field we 
   read, the value we
    found and the threshold we compared it against.
 * **It tells you where to start.** The three items with the largest amount at stake,
   first — before
    the full list.
 * **A zero means “checked, clean”.** A rule that could not run says so instead 
   of quietly
    reporting nothing. “No data” and “no problems” are different answers,
   and you get the right one.
 * **It never adds up currencies.** A store selling in euros and dollars gets a 
   separate figure for
    each. Adding them without an exchange rate would be inventing
   a number.
 * **Two scans of an unchanged store give the same result**, item for item. When
   something does
    change, a separate screen tells you what — and whether the difference
   came from your store or from a rule of ours that got smarter.
 * **“Hide” is permanent.** Seasonal stock you already know about stops coming back
   after every
    scan, and stays hidden across plugin updates.

#### Every check has a page that explains it

Seventeen checks, seventeen pages — linked from the audit screen, next to the rule
they belong to.
 Each page says what the problem costs, which fields the rule reads
and which thresholds it compares them against, how to fix it, and when the finding
is **not** a problem in your store.

That last part is why the pages exist. A rule you cannot argue with is a rule you
end up ignoring
 wholesale, so every page names the cases where the right answer
is “hide this and move on”: seasonal goods, spare parts, products sold only by phone.

 * [All seventeen checks, explained](https://merchlint.com/checks/)
 * [Why there is no “Fix it” button](https://merchlint.com/changes-nothing/)
 * [What a finished report looks like](https://merchlint.com/sample-report/)

#### Your data stays yours

The free version works entirely on your server. No account, no sign-up, no phoning
home: the
 plugin makes no outbound connections at all, and an automated check enforces
that on every build.

The links to merchlint.com are ordinary links. They open in your browser when you
click them, and
 the plugin sends nothing along the way — not your store’s address,
not its version, not an identifier of any kind. The only thing in the address is
the language your admin is set to, so that a Polish store gets the Polish page. 
Without a click, nothing leaves your store at all.

## Screenshots

[⌊The result of the last audit on the first screen you see after logging in: the
revenue sitting
in products that can no longer be bought, and how many findings 
are waiting behind it.⌉⌊The result of the last audit on the first screen you see
after logging in: the revenue sitting
in products that can no longer be bought, 
and how many findings are waiting behind it.⌉[

The result of the last audit on the first screen you see after logging in: the revenue
sitting in products that can no longer be bought, and how many findings are waiting
behind it.

[⌊Where to start: the three products with the largest amount at stake, one row per
product with
everything that is wrong with it, before the full list.⌉⌊Where to start:
the three products with the largest amount at stake, one row per product with
everything
that is wrong with it, before the full list.⌉[

Where to start: the three products with the largest amount at stake, one row per
product with everything that is wrong with it, before the full list.

[⌊One rule up close: what it checks, what to do about it, and every item with the
evidence behind
it — the fields read, the values found, and the money at stake.⌉⌊
One rule up close: what it checks, what to do about it, and every item with the 
evidence behind
it — the fields read, the values found, and the money at stake.⌉[

One rule up close: what it checks, what to do about it, and every item with the 
evidence behind it — the fields read, the values found, and the money at stake.

[⌊Every rule with its count, split into what came from your own data and what came
from your theme
or plugins — including the rules that found nothing, which say "
clean" rather than staying silent.⌉⌊Every rule with its count, split into what came
from your own data and what came from your theme
or plugins — including the rules
that found nothing, which say "clean" rather than staying silent.⌉[

Every rule with its count, split into what came from your own data and what came
from your theme or plugins — including the rules that found nothing, which say “
clean” rather than staying silent.

[⌊What changed since the previous audit — here, findings that disappeared because
the store itself
was fixed. When a number moves because a rule of ours got smarter,
the screen says so separately.⌉⌊What changed since the previous audit — here, findings
that disappeared because the store itself
was fixed. When a number moves because
a rule of ours got smarter, the screen says so separately.⌉[

What changed since the previous audit — here, findings that disappeared because 
the store itself was fixed. When a number moves because a rule of ours got smarter,
the screen says so separately.

## Installation

 1. Install and activate the plugin. WooCommerce must be active first — WordPress enforces
    this for you.
 2. Open **Store audit** in the admin menu.
 3. Press **Scan now**. A store with 50 000 products takes about a minute and a half
    on the hardware we measure on; a slower host takes longer. You can stop the scan
    at any point.

The audit reads your catalogue and writes only to its own tables. Removing the plugin
removes them.

**Short PHP time limit?** Nothing to do — the audit reads your host’s own limit 
and sizes each chunk to fit inside it, then books the next one itself. A shorter
chunk means more of them, not less work: the audit still finishes, and it still 
resumes from where it stopped.

If your host reports a limit it does not actually enforce, you can set the chunk
yourself in wp-config.php:

    ```
    define( 'MERCHLINT_BUDGET_SECONDS', 8 );
    ```

## FAQ

### Does it change anything in my store?

No. It reads your catalogue and reports what it finds. Every change is made by you,
in your own
 admin. This is enforced in the code, not just promised: an automated
check refuses to build the plugin if it calls any function that would modify products,
categories, media or orders.

### Will it slow my store down?

The scan runs in the background, in short slices, and it only touches the admin 
side. Your
 storefront is not involved. If the scan is interrupted — a server restart,
a deployment — it picks up where it left off and gives the same result as an uninterrupted
run.

### Can I audit a store with tens of thousands of products?

Yes. A scan of 50 000 products completes in about 90 seconds and the results are
paginated, so
 a rule with twelve thousand findings still opens instantly.

### Why does it check WP-Cron and Action Scheduler?

Because a stalled queue fails quietly. WP-Cron and Action Scheduler are what WooCommerce
and its
 extensions book later work on — order emails, the end of a scheduled sale,
stock released from abandoned carts, subscription renewals — and neither your storefront
nor your order list will tell you they stopped. The store keeps selling. It just
stops doing everything that was meant to happen later.

The two queues are reported as separate findings, because they stop for different
reasons and are
 fixed in different places. What the rule measures is the age of
the oldest overdue action, not how many are waiting: a store with no visitors always
has a queue full of them and is perfectly healthy, because the first visitor clears
it. Anything overdue by more than a day is not quiet, it is broken — and that threshold
is yours to move.

### What does “store health” cover here?

Everything the seventeen checks touch, not only product fields: the catalogue and
its categories, the
 media library and the files behind it, the size of your autoloaded
options, and the versions of PHP, WordPress and WooCommerce you are running. A store
can have faultless product data and still be losing money to a stalled queue or 
to an unsupported PHP version, so the report covers both.

### Does it work with multilingual stores?

Yes. Translations of one product are counted as one product: their SKU is shared
deliberately, and
 their stock is one physical stock, so we never report the same
goods twice or double the amount at stake. Polylang is tested with the plugin itself;
WPML is recognised through its translation table, which we test against the same
data it produces.

### Does it work with HPOS (High-Performance Order Storage)?

Yes, with HPOS on and off. Both modes give identical results, amounts included.

### Some of the findings do not apply to my store.

Then hide them. Every rule’s page explains when the finding is NOT a problem — seasonal
goods,
 spare parts, products sold only by phone — and hiding is one click. The 
decision survives future scans, and the hidden items stay visible as a count, so
nothing disappears silently.

## Reviews

There are no reviews for this plugin.

## Contributors & Developers

“Merchlint – Catalog Audit for WooCommerce” is open source software. The following
people have contributed to this plugin.

Contributors

 *   [ baltano ](https://profiles.wordpress.org/baltano/)

“Merchlint – Catalog Audit for WooCommerce” has been translated into 1 locale. Thank
you to [the translators](https://translate.wordpress.org/projects/wp-plugins/merchlint/contributors)
for their contributions.

[Translate “Merchlint – Catalog Audit for WooCommerce” into your language.](https://translate.wordpress.org/projects/wp-plugins/merchlint)

### Interested in development?

[Browse the code](https://plugins.trac.wordpress.org/browser/merchlint/), check 
out the [SVN repository](https://plugins.svn.wordpress.org/merchlint/), or subscribe
to the [development log](https://plugins.trac.wordpress.org/log/merchlint/) by [RSS](https://plugins.trac.wordpress.org/log/merchlint/?limit=100&mode=stop_on_copy&format=rss).

## Changelog

#### 0.3.3

 * **The plugin no longer does anything at all when WooCommerce is not active.**
   Until now it
    created its own tables on every page load, registered the audit
   screen and added its WP-CLI command even on a site with no WooCommerce – and 
   then showed an empty screen without a word of explanation. It now checks first,
   says so in one sentence on the dashboard, and touches nothing. Saved results 
   are left exactly as they were: activate WooCommerce and everything comes back.
 * **New display name: Merchlint – Catalog Audit for WooCommerce.** Only the displayed
   name
    changed: same plugin, same directory address, same settings, same saved
   audits. Nothing in your shop needs doing, and the screen in your admin menu is
   where it was.
 * **The identifier checks now say when your numbers are kept in another plugin’s
   field.** Both
    checks read WooCommerce’s own GTIN, UPC, EAN or ISBN field. When
   products hold their number only in a field of EAN for WooCommerce, Germanized
   for WooCommerce, Trusted Shops Easy Integration, Google for WooCommerce, Customer
   Reviews for WooCommerce or Product Feed Manager for WooCommerce, the audit now
   says how many, names the field and says plainly which check cannot see those 
   numbers — on the results screen, on the check’s own page, in WP-CLI and in the
   CSV export. Germanized passes its numbers through WooCommerce’s own field when
   that field is empty, so with Germanized active only the duplicate check is affected.
   Nothing is counted differently: no finding appears or disappears, and earlier
   scans stay comparable.
 * **A check’s own page now shows what that check could not see.** Notes about gaps
   were only on
    the main results screen, above the list of checks — not next to
   the findings they explain.
 * **“What this audit could NOT check” no longer says “these rules found nothing”
   about rules that
    ran.
    A rule that missed only part of what it looks at is now
   listed separately, with a sentence
    of its own.
 * **Invisible characters in product codes now show in the evidence.** Two codes
   that differ only
    by a non-breaking space or a zero-width character look the 
   same on screen, so a pair of duplicates could show one value twice, with no way
   to tell which product carries the extra character. The evidence now writes each
   such character as ⟨U+00A0⟩ and similar, on the screen and in the CSV export. 
   Clean codes look exactly as before.
 * **Three WP-CLI commands now stop on bad input instead of guessing.** With a mistyped
   check code,
    “findings” listed and “export” exported every finding instead of
   none; an invalid separator in “export” ended in a critical error; “cancel” reported
   success for a scan that does not exist. Each of them now says what was wrong.
 * **Two WP-CLI outputs now keep gaps apart from findings.** The message after “
   export” counted the
    rows about gaps — what the audit could not check — as findings,
   so a check that never ran was reported as one saved finding. It now counts findings
   only and names the gap rows separately. “status –format=json” now lists the gaps
   the same way “rule –format=json” does; until now a check that did not run simply
   disappeared from its counts.
 * **The advice for missing sales data now points to a button that exists.** When
   WooCommerce’s
    order summary table was empty, the audit told you to run “Regenerate
   the order lookup tables” in Status  Tools, and WooCommerce has no such tool. 
   It now sends you to Analytics  Settings  “Import historical data”, and the WP-
   CLI warning gives the same advice instead of naming a script that is not part
   of the plugin.
 * **One request for a review, under the results.** It appears once per user after
   a finished
    audit and goes away for good when you open it or choose “Don’t ask
   again”. No reward is offered and no rating is suggested.

#### 0.3.2

 * **On WooCommerce older than 9.2 the two identifier checks now say they did not
   run, instead of
    reporting numbers.
    That WooCommerce has no field for a product
   identifier at all. If the
    field had been written straight into the database –
   after moving back from a newer version, or by an import – the plugin used to 
   treat the store as one that keeps identifiers while reading every one of them
   as empty: the duplicate check then reported nothing even when duplicates were
   there. Both checks now stay quiet and say why, naming the WooCommerce version
   that first had the field.
 * **The evidence column no longer shows a Polish word on an English screen.** Where
   a check reads
    a field and finds it empty, the value now reads “(empty)”. It 
   had been coming straight from the audit engine, which has no access to translations,
   so the Polish “(puste)” appeared for anyone reading the screen in English – in
   the one column whose whole job is to let you verify us.
 * **The duplicate-identifier check now says when it had nothing to compare.** On
   a catalogue where
    not one product carries an identifier, it used to report “
   nothing found – this check is clean” – a clean bill of health for a field nobody
   fills, and two different sentences about the same field one under the other, 
   because the missing-identifier check said so correctly. Both checks now give 
   the same answer, with the same reason.
 * **A finding on a variation now links straight to the tab where you fix it.** 
   The link in
    the list opened the parent product at the top of the page; on a 
   product with twenty variations that is a long way from the one you were sent 
   to. The export file had carried the right link all along – only the screen did
   not.

#### 0.3.1

 * **Both duplicate checks now see codes stored on variations, not only on products.**
   Until now the
    comparison ran over products alone, so a code repeated between
   one product and another product’s variation was invisible — even though an export
   file treats every variation as a separate item. On a store with variable products
   the numbers can go up after this update.
 * **The identifier check also reports a product that shares its number with its
   own variation. The
    SKU check does not
    — and that difference is deliberate. 
   WooCommerce shows the parent’s SKU on a
    variation that has none, so for SKU 
   that pair is how the platform behaves, not a fault. For the identifier there 
   is no such inheritance: WooCommerce requires it to be unique across products 
   and variations, yet never notices when an import writes the pair straight to 
   the database, so this is the only place it surfaces. It matters when an export
   file carries the parent alongside its variations — then two items go out with
   one number.
 * One pair is still left out and the knowledge pages now say so: two variations
   of one product
    sharing a code while the product itself carries none. A finding
   belongs to a product, and there is nothing to attach it to there.
 * **The three checks whose behaviour changed carry a new version number, and that
   is deliberate.**
    The “Changes since the previous audit” screen reads those numbers
   to tell you when a movement comes from us rather than from your store, and anything
   you hid under the old version is marked for a second look — your decision was
   an answer to a question that is now worded differently. Nothing is un-hidden 
   behind your back; it is only flagged.
 * **Variations sitting in the trash no longer count — anywhere.** A variable product
   whose living
    variations all carried an identifier could still be reported as
   missing one, because a trashed variation had none; the same ghost could make 
   a code look duplicated. Nothing in the shop shows those variations, and WooCommerce’s
   own uniqueness check ignores the trash as well — now so do we. This one was in
   the previous release too, not only in the change above.
 * **Two additions to the identifier check’s knowledge page.** What to do when the
   check is clean
    and the export file still shows no identifier — an export plugin
   may read the field through a cached list of fields, which a product save does
   not refresh. And why a number copied onto variations while a file is being built
   hides the gap rather than closing it: a shopping service expects each variation
   to carry its own, and this check reports what WooCommerce holds.

#### 0.3.0

 * **Two new checks, both free and with no product cap: a product with no GTIN, 
   UPC, EAN or ISBN,
    and two products sharing the same one.
    Shopping services 
   use that identifier to match your
    offer to the same product sold elsewhere, 
   so an empty or duplicated one is the usual reason an item quietly drops out of
   a feed. WooCommerce has had the field since 9.2, on the Inventory tab, and nothing
   in the admin ever points at it. The check reads WooCommerce’s own field; identifiers
   kept in a plugin’s field or in a product attribute are not read.
 * **The missing-identifier check stays quiet in shops that do not use identifiers
   at all.** If not
    one product has the field filled, you get a single sentence
   saying so instead of a list of your entire catalogue — handmade goods, services
   and your own production carry no manufacturer’s barcode and need none.
 * On a variable product the identifier belongs on each variation, so such a product
   gives **one**
    finding with the evidence “6 variations without a GTIN” — not 
   six separate rows.
 * Translations of one product are not reported as a duplicate, exactly as with 
   SKU.
 * **After an update, “Changes since the previous audit” now names the checks that
   are new.**
    Two checks arriving at once means findings arriving at once, and 
   the screen showed the movement without saying where it came from — so it read
   as if something had broken in your store overnight. A check that is new and found
   nothing stays out of the way.
 * **A check that could not run no longer reads as a clean one — on every screen,
   not just some.**
    The audit screen has always had a section saying what could
   not be checked and why. Two places did not: the dashboard tile counted such a
   check among the rules it had gone through, and “Changes since the previous audit”
   showed its findings dropping to zero exactly as it shows a real fix. On a store
   that keeps no product identifiers at all, that meant being congratulated for 
   something nobody had measured. The tile now says how many checks could not run,
   and the comparison says plainly that a drop there is not a fix.
 * **An empty catalogue is no longer reported as a clean one.** A store with no 
   products got a row
    of zeros and a tile saying nothing needed fixing — which 
   is what a fresh WooCommerce install looks like before the first product is added.
   The audit now says plainly that there was nothing to check.
 * Ready since 0.2.2 and shipping here: **the audit engine no longer assumes every
   store can answer
    every question.
    A rule now says what it needs, and a store
   that cannot provide it gets a plain
    note saying the rule was not applicable —
   instead of a silent zero that reads like a clean result. On WooCommerce every
   rule still applies, so every check you already had gives exactly the same result
   as it did under 0.2.1.
 * The list of rules moved into the audit engine itself.

#### 0.2.1

 * **New display name: Merchlint – Store Audit for WooCommerce.** Same plugin, same
   address, same
    settings — the name now says what it does, for people who find
   plugins by searching for the job rather than for a brand.
 * **Every rule now links to its own page**, which explains what the finding costs,
   exactly how the
    rule decides, how to fix it, and when it is not a problem worth
   fixing. The link opens in your browser and carries nothing but the language of
   your admin; the plugin itself still makes no outbound connections at all.
 * The plugin’s name on your plugins list is now a link to its home page. Until 
   now it led nowhere.
 * No change to what the audit checks or reports. A scan of an unchanged store gives
   exactly the
    same result as it did under 0.2.0.

#### 0.2.0

 * **First public release.** Versions 0.1.0 and 0.1.1 below were pre-release: they
   were reviewed but
    never published, so this is the first version you can install
   from the directory. Everything the audit does is listed under 0.1.0.
 * Two extension points so add-ons can build on the audit: an action when a scan
   finishes and
    a filter over the scan settings, plus the scan history behind both.
 * Rules the current scan did not run are now marked “not in this audit” instead
   of “clean”.
 * A scan now keeps its settings even if an add-on hands it a value that cannot 
   be stored — before,
    one such value silently dropped every threshold the scan
   was running with, and the second half of the scan used the defaults.
 * Comparing two audits no longer writes a PHP notice to the log when a setting 
   holds a list rather
    than a number.
 * No change to what the audit checks on its own.

#### 0.1.1

 * Translations now come from translate.wordpress.org instead of files bundled in
   the plugin.
 * No change to what the audit checks or reports.

#### 0.1.0

 * First release. Fifteen checks, resumable scanning, CSV export, per-rule knowledge
   pages,
    permanent “hide”, English and Polish.

## Meta

 *  Version **0.3.3**
 *  Last updated **2 days ago**
 *  Active installations **Fewer than 10**
 *  WordPress version ** 6.5 or higher **
 *  Tested up to **7.1.2**
 *  PHP version ** 7.4 or higher **
 *  Languages
 * [English (US)](https://wordpress.org/plugins/merchlint/) and [Polish](https://pl.wordpress.org/plugins/merchlint/).
 *  [Translate into your language](https://translate.wordpress.org/projects/wp-plugins/merchlint)
 * Tag
 * [autoloaded options](https://srd.wordpress.org/plugins/tags/autoloaded-options/)
 *  [Advanced View](https://srd.wordpress.org/plugins/merchlint/advanced/)

## Ratings

No reviews have been submitted yet.

[Your review](https://wordpress.org/support/plugin/merchlint/reviews/#new-post)

[See all reviews](https://wordpress.org/support/plugin/merchlint/reviews/)

## Contributors

 *   [ baltano ](https://profiles.wordpress.org/baltano/)

## Support

Got something to say? Need help?

 [View support forum](https://wordpress.org/support/plugin/merchlint/)