Sync a product catalogue into BIRP
Create and update the products of a shop in BIRP, without duplicates, one by one or in batches.
Version v1.1, updated
On this page
You will keep the products of a shop or another system in step with BIRP. Each product is linked to its id in your system, so every sync updates it instead of creating a copy.
Before you start
A key with
products:writeandproducts:read, pluscategories:writeif you create categories.A source name for your system, such as
woocommerce. Keep it the same forever: it is part of every external id.For each product, its kind:
goodsfor resale goods bought and sold as they are,finished_productfor what the company makes. The kind never changes.
Steps
Send each product with an upsert
PUT /v1/products/external/{source}/{external_id} creates the product when the external id is new, and updates it otherwise. The answer is 201 for a new product and 200 for an update.
curl -X PUT "https://api.birp.io/v1/products/external/woocommerce/1042" \
-H "Authorization: Bearer $BIRP_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"currency":"MDL","kind":"goods","name":"Wheat flour 25 kg","price":"12.50","sku":"SP-FLOUR-25","unit":"kg"}'const response = await fetch("https://api.birp.io/v1/products/external/woocommerce/1042", {
method: "PUT",
headers: {
Authorization: `Bearer ${process.env.BIRP_API_KEY}`,
"Idempotency-Key": crypto.randomUUID(),
"Content-Type": "application/json",
},
body: JSON.stringify({
"currency": "MDL",
"kind": "goods",
"name": "Wheat flour 25 kg",
"price": "12.50",
"sku": "SP-FLOUR-25",
"unit": "kg"
}),
});
if (!response.ok) throw new Error(`BIRP API error ${response.status}`);
const result = await response.json();<?php
$ch = curl_init("https://api.birp.io/v1/products/external/woocommerce/1042");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "PUT",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("BIRP_API_KEY"),
"Idempotency-Key: " . bin2hex(random_bytes(16)),
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => <<<'JSON'
{
"currency": "MDL",
"kind": "goods",
"name": "Wheat flour 25 kg",
"price": "12.50",
"sku": "SP-FLOUR-25",
"unit": "kg"
}
JSON,
CURLOPT_RETURNTRANSFER => true,
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);import os
import uuid
import requests
response = requests.request(
"PUT",
"https://api.birp.io/v1/products/external/woocommerce/1042",
json={
"currency": "MDL",
"kind": "goods",
"name": "Wheat flour 25 kg",
"price": "12.50",
"sku": "SP-FLOUR-25",
"unit": "kg"
},
headers={
"Authorization": f"Bearer {os.environ['BIRP_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
timeout=10,
)
response.raise_for_status()
result = response.json()Match the products BIRP already has
When the body has a sku that a product of the same kind already has, the upsert links your external id to that product. It updates that product and creates no duplicate, even on the first sync.
A write refuses a SKU that another product of the company already has, of either kind. Leave sku out on a create and BIRP gives the product a reference from the company numbering.
Send many products at once
POST /v1/products/batch takes up to 100 products, each matched by external id, SKU or id. See Batch operations.
curl -X POST "https://api.birp.io/v1/products/batch" \
-H "Authorization: Bearer $BIRP_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"items":[{"data":{"currency":"MDL","kind":"goods","name":"Wheat flour 25 kg","price":"12.50","sku":"SP-FLOUR-25","unit":"kg"},"match":{"external_id":"1042","source":"woocommerce"}},{"data":{"kind":"finished_product","name":"White bread 500 g","price":"3.20"},"match":{"sku":"FP-BREAD-500"}},{"data":{"kind":"goods","name":"Rye flour 25 kg"},"match":{"id":"00000000-0000-4000-8000-000000000099"}}]}'const response = await fetch("https://api.birp.io/v1/products/batch", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.BIRP_API_KEY}`,
"Idempotency-Key": crypto.randomUUID(),
"Content-Type": "application/json",
},
body: JSON.stringify({
"items": [
{
"data": {
"currency": "MDL",
"kind": "goods",
"name": "Wheat flour 25 kg",
"price": "12.50",
"sku": "SP-FLOUR-25",
"unit": "kg"
},
"match": {
"external_id": "1042",
"source": "woocommerce"
}
},
{
"data": {
"kind": "finished_product",
"name": "White bread 500 g",
"price": "3.20"
},
"match": {
"sku": "FP-BREAD-500"
}
},
{
"data": {
"kind": "goods",
"name": "Rye flour 25 kg"
},
"match": {
"id": "00000000-0000-4000-8000-000000000099"
}
}
]
}),
});
if (!response.ok) throw new Error(`BIRP API error ${response.status}`);
const result = await response.json();<?php
$ch = curl_init("https://api.birp.io/v1/products/batch");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("BIRP_API_KEY"),
"Idempotency-Key: " . bin2hex(random_bytes(16)),
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => <<<'JSON'
{
"items": [
{
"data": {
"currency": "MDL",
"kind": "goods",
"name": "Wheat flour 25 kg",
"price": "12.50",
"sku": "SP-FLOUR-25",
"unit": "kg"
},
"match": {
"external_id": "1042",
"source": "woocommerce"
}
},
{
"data": {
"kind": "finished_product",
"name": "White bread 500 g",
"price": "3.20"
},
"match": {
"sku": "FP-BREAD-500"
}
},
{
"data": {
"kind": "goods",
"name": "Rye flour 25 kg"
},
"match": {
"id": "00000000-0000-4000-8000-000000000099"
}
}
]
}
JSON,
CURLOPT_RETURNTRANSFER => true,
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);import os
import uuid
import requests
response = requests.request(
"POST",
"https://api.birp.io/v1/products/batch",
json={
"items": [
{
"data": {
"currency": "MDL",
"kind": "goods",
"name": "Wheat flour 25 kg",
"price": "12.50",
"sku": "SP-FLOUR-25",
"unit": "kg"
},
"match": {
"external_id": "1042",
"source": "woocommerce"
}
},
{
"data": {
"kind": "finished_product",
"name": "White bread 500 g",
"price": "3.20"
},
"match": {
"sku": "FP-BREAD-500"
}
},
{
"data": {
"kind": "goods",
"name": "Rye flour 25 kg"
},
"match": {
"id": "00000000-0000-4000-8000-000000000099"
}
}
]
},
headers={
"Authorization": f"Bearer {os.environ['BIRP_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
timeout=10,
)
response.raise_for_status()
result = response.json()Archive what you no longer sell
Send "status": "archived" in an upsert or a PATCH, and "active" to restore it. Nothing is deleted through the API.
What BIRP does not take
The API writes the name, SKU, barcode, unit, category, price, currency and status of a product. Purchase prices, suppliers, images, storage conditions and recipes are managed in BIRP only, and a body that sends them answers 422 validation_failed.
A price has at most two decimals. Use prices up to 99 999.99: a larger price can be refused with 422 validation_failed.
Check the result
Find a product by its external id with GET /v1/products?source=woocommerce&external_id=1042. After a sync, updated_since with the start time of the sync lists every product it changed.
What can go wrong
Related
- references-and-external-ids
- batches
- products
- categories