Integrations
Product data: defined once, correct in every channel
The ERP knows the item number, the barcode and the weight. The PIM or the store knows the title, the images and the description. A product page needs both, grouped into one product with the right variants. We build that integration to your data model, host it and run it.
Which system owns which field?
Ownership is set per field, not per system. A product record in the store is assembled from two or three sources, and every field has one of them as its owner.
| Field | Owner | Direction | When it has to arrive |
|---|---|---|---|
| SKU, variant structure, item status | ERP | to the PIM and the store | before the item can be sold |
| UPC or GTIN, weight, dimensions, HS code | ERP | to the store | with the item, and on change |
| Title, description, attributes, images | PIM, or the store when there is no PIM | to the store, or stays in the store | when the content is approved |
| Categories and navigation | store | stays in the store | never synced |
| Translations | PIM | to the store, per locale | when the locale is complete |
The failure this prevents has a fixed pattern. ERP item 20731-BLK-048 carries the description TSHIRT CREW BLK 048, written for a pick list. An integration that maps the ERP description to the store title replaces "Crew-neck T-shirt" with that string on every run. The merchandising team retypes it until someone switches the product sync off.
The rule that holds: the ERP publishes identifiers, units, status and basic commercial data. Content stays with the PIM or the store, and the integration has no write path from the ERP to those fields. When two sources deliver a value for the same field, the owner's value is written and the other one is logged as a difference.
Product data is one of the five flows on the integrations overview.
Three layers between the ERP and the product page
Product data passes through three models, and each has its own job.
- Operational data, in the ERP. One item per thing that is bought, stocked and picked, with internal codes such as
BLKand048. - Commerce data, in the PIM or the integration. Which items form one product, what the options are called, which values a buyer sees.
- Customer experience, in the store. The product page, the swatches, the size picker and the navigation.
A limit of one layer should not reach the next. A T-shirt in 4 colors and 6 sizes is 4 times 6: 24 items in the ERP. Exported one to one, the store gets 24 products with one variant each. An ERP that keeps one item per color with a size run gives 4 products. In both cases a shopper looking at black cannot switch to navy, and a search for the shirt returns 4 or 24 results for one style. Grouped in the commerce layer, the same 24 items are 1 product with 2 options and 24 variants.
The grouping needs a key. With a matrix parent in the ERP, the parent is the key. Without one, it is a style number in a custom field or a fixed part of the item number: 20731-BLK-048 splits into style 20731, color BLK and size 048.
The codes then go through a translation table: BLK becomes Black, NVY becomes Navy, 048 becomes 48. That table is state, and someone has to own it. A color code that is missing from the table holds the product back and puts it on the run report. The alternative is a swatch on the product page labeled OLV.
What actually moves
No black box: these are the fields in a product flow, with what happens to each on the way.
| Field | Example | What is done with it |
|---|---|---|
| SKU | 20731-BLK-048 |
matching key, kept as text; Shopify treats it as case-sensitive |
| Parent or style | 20731 |
groups items into one product |
| Barcode | 850012345671 |
kept as text, check digit validated |
| Option values | BLK, 048 |
translated to Black, 48 |
| Status | active, inactive | mapped to ACTIVE, DRAFT or ARCHIVED |
| Weight | 0.45 lb |
value and unit, converted when the target uses another unit |
| Title and description | from the PIM, per locale | HTML reduced to the tags the theme renders |
| Images | URL, position, alt text | main image first, compared by content and not by filename |
| Attributes | material: 100% cotton |
written to metafields; the definition has to exist in the store |
On Shopify the whole product is written with one productSet call, documented in Shopify's productSet reference:
{
"identifier": { "handle": "crew-neck-t-shirt-20731" },
"input": {
"title": "Crew-neck T-shirt",
"status": "ACTIVE",
"productOptions": [
{ "name": "Color", "values": [{ "name": "Black" }, { "name": "Navy" }] },
{ "name": "Size", "values": [{ "name": "48" }, { "name": "50" }] }
],
"variants": [
{
"sku": "20731-BLK-048",
"barcodes": [{ "value": "850012345671" }],
"optionValues": [
{ "optionName": "Color", "name": "Black" },
{ "optionName": "Size", "name": "48" }
]
}
]
}
}
The fragment shows one variant of the 24. The call behaves differently per field type. For plain fields it updates what is sent and leaves the rest alone. For list fields, which include variants, metafields and collections, it creates and updates what is sent and deletes what is not. A call that carries 23 of the 24 variants removes the twenty-fourth from the store.
The source in the diagram is a NetSuite matrix child item. itemid, upccode, parent and salesdescription are standard fields. custitem_color and custitem_size are custom fields, and their names differ per account, so the mapping starts with a list of the fields your ERP has.
Variants: matrix items, configurable products and options
Every system models "one product, several versions" in its own way, and each has a ceiling.
| System | Parent | Child | Ceiling |
|---|---|---|---|
| Shopify | product | variant | 3 options and 2,048 variants per product |
| NetSuite | matrix parent item | subitem | 2,000 combinations of matrix options |
| Adobe Commerce | configurable product | simple product with its own SKU | set by the attributes used for variation |
| Akeneo | product model | variant product | set by the family variant |
The ceilings decide the mapping before any field is touched. Work pants in 12 colors, 9 waist sizes and 20 lengths are 12 times 9 times 20: 2,160 combinations. That is 112 over Shopify's limit and 160 over NetSuite's. The style is split into two products, by fit or by color group, and the split is a rule in the integration so both systems agree on it.
A fourth option has the same effect. Color, waist, length and fit are four options against Shopify's three. Either two of them are merged into one option ("32 x 34"), or fit becomes the product level.
SKUs have to be unique across the whole catalog, not per product. A configurable product in Adobe Commerce is built from simple products that each carry their own SKU. A size code used as a SKU, such as 048 under two styles, cannot exist twice: the second style's child is rejected or lands on the first style's record. The variants page covers grouping keys, split rules and option order.
Identifiers: SKU, UPC and GTIN
A SKU is your own code. A GTIN is assigned under a GS1 company prefix, and it comes in four lengths: GTIN-8, GTIN-12 (the UPC), GTIN-13 (the EAN) and GTIN-14 (cases and inner packs). The last digit of each is a check digit. From the right, the digits before it are multiplied by 3 and 1 in turn, and the check digit brings the sum up to the next multiple of ten. The method is on GS1's check digit page.
UPC without check digit 8 5 0 0 1 2 3 4 5 6 7
multiply by 3 1 3 1 3 1 3 1 3 1 3
result 24+5+0+0+3+2+9+4+15+6+21 = 89
check digit 90 - 89 = 1
GTIN-12 (UPC) 850012345671
GTIN-13 0850012345671
as 14 digits 00850012345671
case of 12, indicator 1 1085001234567 + check digit 8 = 10850012345678
Three things follow from this.
Padding does not change the number. 850012345671, 0850012345671 and 00850012345671 are the same GTIN. As strings they are three different values, and a match on string equality misses two of them. The integration compares identifiers on a normalized form: 14 digits, right justified, filled with zeros on the left.
A case has its own GTIN. The case of 12 above ends in 8, not in 1. A feed that lists the case GTIN on the unit item sells a single shirt under the barcode of a box. Shopify accepts up to 20 barcodes per variant, so a unit and its case can both be stored where the store needs them.
Spreadsheets damage identifiers. Excel removes leading zeros and shows a long number in scientific notation. A supplier's 00123456 becomes 123456, and 850012345671 becomes 8.50012E+11. A barcode that is too short or fails its check digit on import is the usual sign that a file went through a spreadsheet. Such rows are listed with the reason and are not written. The note on SKU, UPC and GTIN goes through each number type.
What goes wrong quietly
| Failure | What you see | How it is handled |
|---|---|---|
| ERP description mapped to the title | titles change back to TSHIRT CREW BLK 048 overnight |
no write path from the ERP to content fields |
| A list sent incomplete | a variant, a metafield or a collection link disappears | every list is sent complete, and a run that would remove variants is held |
| Wrong character set | Café table arrives as Café table |
encoding set per source, UTF-8 and Windows-1252 told apart before parsing |
| Supplier reuses an image filename | the old photo stays, or a new photo lands on the wrong product | images compared by content hash |
| Attribute missing in the target | the value is dropped and no error is raised | unmapped attributes listed per run |
| Missing translation | an empty title in one store language | rule per locale: hold back or use the fallback language |
| Paging past 10,000 | products beyond the ten-thousandth never arrive | cursor paging on large collections |
The last row is specific and easy to miss. Akeneo's REST API returns at most 100 items per page, and its offset paging stops at the ten-thousandth item with HTTP 422. An integration built and tested against a pilot catalog of 8,000 products works. At 10,001 products it stops reading, and the newest products are the ones that go missing. Collections of that size are read with the search_after cursor instead.
Records that fail validation go on a list with the SKU and the reason. They are not skipped silently. The note on field mapping describes the classes of mismatch, and what not to sync lists the fields that are better left with their owner.
Throughput and limits
| System | Limit | Number |
|---|---|---|
| Shopify | variants and options per product | 2,048 and 3 |
| Shopify, stores with 500,000 variants or more | new variants per day | 10,000, no limit on Plus |
| Shopify GraphQL Admin API | points restored per second | 100 standard, 200 Advanced, 1,000 Plus |
| Shopify bulk mutation | JSONL upload | 100 MB, done within 24 hours |
| Shopify product images | file size and resolution | under 20 MB, at most 25 megapixels |
| Akeneo REST API | requests | 100 a second per PIM instance, 4 at once per connection |
| Akeneo REST API | items per page | 100 |
| NetSuite | matrix option combinations | 2,000 per parent |
Sources: Shopify API limits, Shopify's help center and Akeneo's REST API documentation, checked on October 3, 2026.
A worked example: a catalog of 4,000 styles with 36,000 variants, from Akeneo to Shopify.
Reading it is 4,000 product models and 36,000 variant products at 100 per page: 40 plus 360 is 400 requests. At 4 requests at once that is a matter of minutes.
Writing it is 4,000 productSet calls, one per product with its variants. A mutation costs at least 10 points, and a standard plan restores 100 points a second, so the ceiling is 10 calls a second: 4,000 divided by 10 is 400 seconds, just under 7 minutes at the minimum cost. Shopify returns the actual cost of each call in the response, and the queue paces on that number. For a first load the same input goes up as a JSONL file through a bulk operation. Shopify does not count a bulk operation against the rate limit, so the regular budget stays free for stock and orders.
After the first load only changes are sent. If 180 products changed since the last run, that is 180 calls: 18 seconds.
Images are the slow part. 4,000 products with 5 images each are 20,000 files, and Shopify fetches each one from the URL it is given. A file of 20 MB or more, or above 25 megapixels, is refused. The integration checks size and dimensions first and lists the files that need a smaller export.
A new season on a large store hits a different limit. A store on a standard plan that already holds 500,000 variants can add 10,000 a day. A season of 36,000 new variants takes 36,000 divided by 10,000: 4 days, so the load starts 4 days before the launch date. The notes on Shopify API rate limits and integration throughput work through the cost model.
You stay in control
Every run is compared with the current state of the store before anything is written.
- Dry run. The integration reads the ERP and the PIM, builds every product and writes nothing. You see which products it would create, change or archive.
- Approval. New products can wait as drafts until someone approves them, while changes to existing products go through.
- Exclusions. A product, or one field of a product, is left out. A title written for a campaign stays as it is.
- Overrides. A value pinned for one product with an end date, such as a temporary title during a promotion.
- Diff per run. Which products changed, which field, from which value to which, and from which source.
A run that would remove variants or archive a large share of the catalog is held:
run 2026-10-02 02:00 source: PIM and ERP products: 4,000
changed: 182 new: 14 unchanged: 3,804
held: 6 products would lose 1 or more variants
20731 variants 24 -> 23 missing in source: 20731-BLK-048
action: waiting for release
In this run the ERP item was set to inactive by mistake. Released as it stands, the run removes the variant from the product page. Corrected in the ERP, the next run shows 24 of 24 and nothing is held. The dry run and approval page describes the controls.
In depth
- Variants: matrix items, configurable products, grouping keys and what to do above 2,048 combinations.
- Dry run and approval: how a run is checked before it writes.
- SKU, UPC and GTIN: the number families, check digits and matching rules.
- Supplier CSV and Excel files: encodings, leading zeros and column drift in product files from suppliers.
Product data connects to the other flows. The variant created here is the record that stock and availability writes quantities to and that pricing and customer terms writes prices to. An order line in orders, customers and shipping points back to the same SKU. A SKU that changes in the ERP without the integration knowing breaks all three.
Systems and platforms
PIM systems. Akeneo and Salsify. A PIM delivers content per locale and per channel, so the integration selects one channel and one locale per store and maps attributes to store fields and metafields.
ERP systems. NetSuite, Business Central, SAP Business One and Acumatica. In NetSuite a matrix parent holds at most 2,000 option combinations, and the UPC sits in the upccode field of the item.
Store platforms. Shopify, Magento, WooCommerce and BigCommerce. Shopify takes 3 options and 2,048 variants per product. Adobe Commerce builds a configurable product from simple products, each with its own SKU.
Combinations. Shopify and NetSuite and Magento and NetSuite have their own pages. Apparel catalogs, where the color and size grid is the main mapping problem, are covered under apparel and footwear.
A system that is not listed can be integrated when its product data can be read in a controlled way: an API, a database view, or a scheduled export file. Tell us what you run.
Frequently asked questions
Do we need a PIM to sync product data to the store?
No. Without a PIM the ERP owns identifiers, status, weight and variant structure, and the store owns titles, descriptions and images. The integration writes the first group and has no write path to the second. A PIM becomes useful when the same content has to reach several stores, languages or marketplaces, because then the content needs one owner outside any single store.
Which system should own product descriptions and images?
The system where the people who write them work: the PIM when there is one, the store when there is not. The ERP description is written for purchasing and picking and is rarely fit for a product page. The integration can copy it once as a first draft for new products and never touch the field again.
What happens to a change someone makes directly in the store admin?
That depends on the owner of the field. A change to a field the store owns stays. A change to a field the ERP or PIM owns is replaced on the next run, and the run diff shows the value that was replaced. A product with a manual arrangement can be excluded, per product or per field.
Our ERP has no parent items. Can colors and sizes still become variants?
Yes. The grouping key can be a style number in a custom field or a fixed part of the item number: 20731-BLK-048 gives style 20731, color BLK and size 048. Items without a recognizable key go on a list to be assigned by hand once, and the assignment is kept. The variants page covers the grouping rules.
Does the integration delete products that are removed from the ERP?
Only when you set that rule. An inactive or deleted ERP item can set the product to draft, archive it, or leave it alone, and the choice is made per connection. Deleting is the one action the next run cannot undo, so the default is to archive and report it in the run diff.
How fast does a product change reach the store?
Status changes and new items that are ready to sell go out within minutes, as events. Content and attributes go in a scheduled run of changed products, hourly or a few times a day, and the full catalog is compared once a night. Product data changes less often than stock, so it gets a smaller share of the API budget.
Can one product record feed several stores and languages?
Yes. Each store or channel gets its own selection of products, its own locale and its own mapping of attributes. A translation that is missing in the PIM is handled by a rule per locale: hold the product back for that store, or publish it in the fallback language. Held products are listed in the run report.
What if some items have no UPC or GTIN?
The product is still synced, with the barcode left empty and the item on a list. An invented or reused barcode is worse than none: a number with a wrong check digit fails validation in every system that checks it, and a reused number points to a different product. The note on SKU, UPC and GTIN explains the number families.
Tell us what you need
Tell us which systems you use and what needs to work better. We reply personally within one working day. We help you define the work, then agree the price and timing before you commit.
- You know the price and the timeline before you commit to anything
- We work out the connection that takes the work off your hands
- We handle the build, hosting, monitoring and maintenance
- Your team has less information to retype and fewer discrepancies to chase