/ Case study · Ecommerce data and tooling

Supplier catalogue to Shopify

An engine spare parts company asked for a new website. The site was fine. The shop was not: it listed 192 products out of more than a thousand, because every supplier sends its catalogue in a different format and none of them has an API. So instead of a redesign I built the missing piece: a tool that reads any supplier file, unifies it, classifies it and hands Shopify a file it can import.

Client
Engine spare parts company, Spain
Platform
Shopify
Built with
Python, Streamlit, Shopify Admin API
Role
Diagnosis, data tool and store setup

What this page is: the client is kept anonymous, and so are its suppliers and brands. The figures come from running the tool on the full supplier catalogues. The code was written pair-programming with Claude, like the rest of this site: I decided what to build and checked every result against the data.

The project in numbers

192products listed in a shop that should have 1,000+
37,948supplier products unified into one table
0conflicts left unexplained between sources
15engine systems, assigned by editable rules
37automated tests on every change

First, what the site is for

Before proposing anything I asked what the site was for: information, sales, or both. Both. That pointed away from a redesign and towards the half that was broken.

Saved formats per supplier

The tool recognises a supplier file by its column headers. A new format is described once, saved as a profile, and recognised on its own from then on.

Rules in a spreadsheet

Categories, images, VAT and margins live in CSV files you can open in Excel. Adding a supplier or a category is configuration, not code.

Dry run by default

The script that sets up the store only reports what it would do. It writes only when told to, never deletes, and never stores credentials.

Update price and cost only

Once products are live, the tool compares against the store and sends only price and cost changes. Status, texts and photos are left exactly as they are.

Changes tested on the full catalogue

Every change was run against the full catalogue and compared product by product. One fix that looked obvious would have removed 840 products, so it was not made.

192 products in the shop out of more than a thousand

The brief was a new website. Looking at the existing one, it was not bad, so the useful question was what it was for. The answer was both: a company presentation and a shop. The presentation side worked. The shop listed 192 products when the catalogue runs to more than a thousand, and even those 192 had category errors and menu links that led nowhere.

The cause was upstream of the website. Each supplier sends prices and references in its own way: Excel sheets, CSV exports, PDF catalogues, each with different columns and names for the same thing. None offers an API. Putting a product in the shop meant copying it by hand from whichever file had it, which is why most products never got there.

Products in the shop against the catalogue

  • In the shop192
  • In the client’s catalogue1,000+

The decision: keep the site and the platform, and fix the process that feeds the shop. A redesign would have been more visible and would have changed nothing about the 192.

What supplier files contain

Before designing anything I put every supplier source through the same pipeline and counted what came out. The full supplier catalogues are far larger than what the shop will sell (around a thousand products to start with), which made them a good stress test.

From supplier rows to Shopify listings

Five sources together. Rows that are not engine parts are dropped, repeated parts are joined, and parts no source prices stay out.

  • Rows read110,492
  • Distinct products37,948
  • Variants ready for Shopify34,379
  • Product listings34,339
Measured on the full filesResult
Distinct products after merging every source37,948
Ready for Shopify34,379 variants in 34,339 listings
Left out because no source gives a price3,569, listed in the report with the reason
Conflicts between sources0 (when two files disagree, the tool says which file and which row)
Products arriving with a photo, weight, barcode or lead time0
Products arriving without a description36,514

What that meant: the tool could not just move data around. It had to supply what nobody sends, such as images and an availability message, and say clearly what is still missing rather than invent it.

How the tool works

A Python pipeline with a small local web interface on top. Files go in as they arrive; a Shopify import file and a quality report come out. It works the same with 200 products as with 37,948. There is no server and no database: everything runs on the computer of whoever uses it, starting from the files each time.

  1. 01Read

    Excel, CSV or PDF. Finds the header row even under titles, skips headers repeated on every PDF page.

  2. 02Recognise

    Matches the column headers against saved supplier profiles. A new format is described once in the interface.

  3. 03Clean and merge

    Normalises references and prices, merges the same part across files and records every disagreement.

  4. 04Classify and complete

    Translates names, assigns one of 15 engine systems, vehicle data, a generic image per part type and variants by size.

  5. 05Write

    Shopify CSV, a master table with the source of every value, and an Excel report of what needs a human.

PieceWhat it does
Supplier profilesOne small file per format: which column is the reference, which is the price, whether prices include VAT, how the supplier writes decimals.
Name rewritingCatalogue names in English come as "NOUN,DETAIL". They are turned round and translated with a glossary of about 270 expressions, so "GASKET,EXHAUST MANIFOLD" becomes "Exhaust manifold gasket" in Spanish.
Engine systems15 systems that follow the categories the shop already had. Assigned by ordered rules in a CSV; 177 of 34,339 listings still fall into "Other".
Generic imagesNo supplier sends photos, so each part type gets a generic image, marked as indicative in its alt text: 22,211 listings covered.
InterfaceFour steps in the browser, opened with a double click: upload, check the columns, choose what to generate, download.
Store setup scriptCreates the custom fields, one automatic collection per engine system and the menu links, through the Shopify Admin GraphQL API.

Decisions made for the people who use it

The person using it day to day will not be me. That changed more decisions than any technical constraint did.

DecisionWhy
Availability on requestWithout supplier APIs there is no reliable stock figure. Every product says "check availability" with the shop's phone number instead of showing an "in stock" nobody can guarantee. The field is already there for when real data exists.
Images by part type, not by nameMatching images on words in the name would add 3,100 more photos. I checked a sample: almost all were wrong, a pump cover showing a whole pump. The option exists, switched off.
Test on a sample firstA first import of around 33,000 products got stuck. Since then the tool can build a sample of any size spread across part families, and repeat exactly the same one to compare before and after.
A separate update modeRe-importing everything with "overwrite" would set live products back to draft and blank anything filled in by hand. The update mode reads the store export, matches by SKU and sends only the columns that change.
Safe store setupDry run unless told otherwise, checks what exists before creating anything, never deletes, asks for credentials at run time and uses a token that expires in 24 hours.
Warn before choosingTwo files disagreeing, the same reference under two brands, a profile about to overwrite another: each one is shown to the user rather than decided quietly.

Reimport everything with “overwrite”

A blank cell in the CSV clears the value in the shop.

  • Published products go back to draft
  • Texts and photos added by hand are emptied
  • Every row is uploaded, changed or not
Ruled out

Update mode

Reads the store export, matches by SKU and compares.

  • Only price and cost change, in a 7-column CSV
  • New products go in a separate file, as drafts
  • Status, texts and photos are left alone
What the tool does

Incidents and how they were solved

Most of the features above exist because something went wrong first. Where the cause was not investigated, the table says so.

SymptomCauseLesson
Import of ~33,000 products stuckNot investigated; started again in smaller parts.Start with a sample. Became the sample mode.
~3,000 imported products invisibleThey were not published to the online store channel.Publishing now goes in the import, as one more column.
Hand pumps vanished"HAND" was translated as "manual", and the filter drops maintenance manuals.A translation can trip a filter further down the line. Now "de mano".
514 products in "Other"No rules for bearings, seals, hoses, hydraulic parts or workshop tools.New rules and a new hydraulics system: down to 177. Rule order matters.
12 products with someone else's photoNot analysed; found by checking the shop by eye.Removed and checked one by one: 124 of 188 products with an image, none of the 12.
Setup script failed on WindowsThe local Python trusted an expired certificate.Ship the certificate bundle (certifi) with the tool instead of trusting the machine.
No way to create the app the old wayShopify stopped allowing new custom apps in the admin on 1 January 2026.App built in the Dev Dashboard with client credentials. Each app belongs to its store.
"0.206" kg read as 206 kgA dot followed by three digits was taken as thousands, as in European price lists.Found in code review before any weights arrived. Each supplier profile now states its decimal format.
The filter matched inside words"RING" also matches "SPRING".Not changed. Matching whole words would drop 840 products, 420 of them springs. Which parts to sell is a business decision, not a code fix.

What it shows

ClaimEvidence
Diagnosis before proposingThe brief was a website; the fix was a data process. The 192 against 1,000+ is what decided it.
Every exclusion listed37,948 products from sources that disagree in format, language and completeness, with every exclusion and conflict listed rather than hidden.
Safe for the shopDry-run setup, no deletions, no stored credentials, and an update mode that cannot touch status, texts or photos.
Every change checked37 automated tests, and every change compared product by product against the full catalogue before it was kept.
Scales without code changesTested with 37,948 products while the shop starts with about a thousand. A new supplier is a format saved once, and categories, images and margins are CSV files. The same process runs a 200-product sample or the whole catalogue.
Handed overA four-step interface, a two-page guide for the people who use it, full documentation, and a private repository with one commit per change.

The data behind it: what the five sources bring, counted field by field against what Shopify and Google need, is a separate study: What is a supplier catalogue missing for Shopify?

/ Next

Got a catalogue like this?

If your shop depends on supplier files that do not fit together, email me and we will look at it.