Biklo for developersTESTsandbox-api.biklo.comchange

Bulk upsert

POST/v1/products/batch

scope products.write

Up to 1000 products, worked through on a queue. Returns immediately with a batch id — NOTHING is written yet. Poll the batch for per-item results. Each product takes every field of POST /v1/products, images included, so a whole catalogue — pictures and all — is a handful of requests.

Request body#

FieldTypeRequiredDescription
productsarrayrequiredUp to 1000 products per batch. An empty or missing array is rejected with field_required (400); more than 1000 with batch_too_large (400), which tells you how many you sent. The call returns a batch_id immediately and the work runs behind it — poll GET /v1/products/batch/{batchId} for per-item results.
products[].external_idstringrequiredAs on POST /v1/products: the upsert key, and required for the same reason. It matters most here — a retried batch without it would duplicate every product in it.
products[].titlestringrequiredRequired for an item that creates a new product.
products[].descriptionstringrequiredRequired for an item that creates a new product.
products[].category_idintegerrequiredRequired for an item that creates a new product.
products[].priceobjectrequiredRequired for an item that creates a new product.
products[].price.amountintegerrequiredMinor units.
products[].stockintegerrequiredUnits available. Required for an item that creates a new product; 0 is accepted.
products[].attributesarrayoptionalCategory attributes, exactly as on POST /v1/products. Items are validated one at a time: a failure marks that item and the rest of the batch still runs, so one bad category attribute does not lose the other 999.
products[].attributes[].field_idintegerrequiredThe attribute id.
products[].attributes[].option_idintegeroptionalThe chosen option, where the attribute has a list.

Example

{
    "products": [
        {
            "external_id": "ERP-1001",
            "title": "Bicicleta MTB Hardtail 29 inch",
            "description": "A hardtail mountain bike in very good condition.",
            "category_id": 4291,
            "price": {
                "amount": 249900
            },
            "stock": 4,
            "attributes": [
                {
                    "field_id": 32,
                    "option_id": 226
                },
                {
                    "field_id": 47,
                    "option_id": 422
                }
            ]
        }
    ]
}

Response#

FieldTypeDescription
batch_idstringThe batch handle. Poll GET /v1/products/batch/{batchId} with it.
statusstringqueued — accepted, and nothing written yet. The call returns immediately; the work happens behind it.
totalintegerHow many products were accepted into the batch.
messagestringA reminder that nothing is written yet, and where the per-item results appear.

Example

{
    "batch_id": "batch_v4lfngtgb6sxqkzwagrqv8c2",
    "status": "queued",
    "total": 1,
    "message": "Accepted. Nothing is written yet — poll GET /v1/products/batch/{id} for per-item results."
}

Failures use the shared error envelope — a machine-readable code and a request_id to quote to support. Error handling.

curl -X POST "https://sandbox-api.biklo.com/v1/products/batch" \
  -H "Authorization: Bearer bk_test_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "products": [
        {
            "external_id": "ERP-1001",
            "title": "Bicicleta MTB Hardtail 29 inch",
            "description": "A hardtail mountain bike in very good condition.",
            "category_id": 4291,
            "price": {
                "amount": 249900
            },
            "stock": 4,
            "attributes": [
                {
                    "field_id": 32,
                    "option_id": 226
                },
                {
                    "field_id": 47,
                    "option_id": 422
                }
            ]
        }
    ]
}'
<?php

$ch = curl_init('https://sandbox-api.biklo.com/v1/products/batch');

curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST  => 'POST',
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer bk_test_YOUR_KEY',
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS     => json_encode([
      'products' => [
        0 => [
          'external_id' => 'ERP-1001',
          'title' => 'Bicicleta MTB Hardtail 29 inch',
          'description' => 'A hardtail mountain bike in very good condition.',
          'category_id' => 4291,
          'price' => [
            'amount' => 249900,
          ],
          'stock' => 4,
          'attributes' => [
            0 => [
              'field_id' => 32,
              'option_id' => 226,
            ],
            1 => [
              'field_id' => 47,
              'option_id' => 422,
            ],
          ],
        ],
      ],
    ]),
]);

$response = json_decode(curl_exec($ch), true);
curl_close($ch);
using System.Net.Http;
using System.Net.Http.Headers;

var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("Bearer", "bk_test_YOUR_KEY");

var request = new HttpRequestMessage(
    new HttpMethod("POST"),
    "https://sandbox-api.biklo.com/v1/products/batch");

request.Content = new StringContent(
    @"{ ""products"": [ { ""external_id"": ""ERP-1001"", ""title"": ""Bicicleta MTB Hardtail 29 inch"", ""description"": ""A hardtail mountain bike in very good condition."", ""category_id"": 4291, ""price"": { ""amount"": 249900 }, ""stock"": 4, ""attributes"": [ { ""field_id"": 32, ""option_id"": 226 }, { ""field_id"": 47, ""option_id"": 422 } ] } ] }",
    System.Text.Encoding.UTF8,
    "application/json");

var response = await client.SendAsync(request);
var json = await response.Content.ReadAsStringAsync();
import requests

response = requests.post(
    "https://sandbox-api.biklo.com/v1/products/batch",
    headers={"Authorization": "Bearer bk_test_YOUR_KEY"},
    json={
        "products": [
            {
                "external_id": "ERP-1001",
                "title": "Bicicleta MTB Hardtail 29 inch",
                "description": "A hardtail mountain bike in very good condition.",
                "category_id": 4291,
                "price": {
                    "amount": 249900
                },
                "stock": 4,
                "attributes": [
                    {
                        "field_id": 32,
                        "option_id": 226
                    },
                    {
                        "field_id": 47,
                        "option_id": 422
                    }
                ]
            }
        ]
    },
)

data = response.json()
const response = await fetch("https://sandbox-api.biklo.com/v1/products/batch", {
  method: "POST",
  headers: {
    Authorization: "Bearer bk_test_YOUR_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
      "products": [
          {
              "external_id": "ERP-1001",
              "title": "Bicicleta MTB Hardtail 29 inch",
              "description": "A hardtail mountain bike in very good condition.",
              "category_id": 4291,
              "price": {
                  "amount": 249900
              },
              "stock": 4,
              "attributes": [
                  {
                      "field_id": 32,
                      "option_id": 226
                  },
                  {
                      "field_id": 47,
                      "option_id": 422
                  }
              ]
          }
      ]
  })
});

const data = await response.json();
import java.net.http.*;
import java.net.URI;

var client = HttpClient.newHttpClient();

var request = HttpRequest.newBuilder()
    .uri(URI.create("https://sandbox-api.biklo.com/v1/products/batch"))
    .header("Authorization", "Bearer bk_test_YOUR_KEY")
    .header("Content-Type", "application/json")
    .method("POST", HttpRequest.BodyPublishers.ofString(
        "{ \"products\": [ { \"external_id\": \"ERP-1001\", \"title\": \"Bicicleta MTB Hardtail 29 inch\", \"description\": \"A hardtail mountain bike in very good condition.\", \"category_id\": 4291, \"price\": { \"amount\": 249900 }, \"stock\": 4, \"attributes\": [ { \"field_id\": 32, \"option_id\": 226 }, { \"field_id\": 47, \"option_id\": 422 } ] } ] }"))
    .build();

var response = client.send(request, HttpResponse.BodyHandlers.ofString());