cURL
curl --request POST \
--url 'https://api.branduo.io/v1/platform/brands' \
--header 'Content-Type: application/json' \
--header 'X-Api-Key: bduo_platform_live_…' \
--data '{
"brands": [
{
"domain": "https://www.NorthwindGoods.com/shop",
"businessName": "Northwind Goods",
"status": "active"
},
{
"domain": "harborlantern.com",
"status": "closed"
}
]
}'
const response = await fetch("https://api.branduo.io/v1/platform/brands", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Api-Key": "bduo_platform_live_…",
},
body: JSON.stringify({
brands: [
{
domain: "https://www.NorthwindGoods.com/shop",
businessName: "Northwind Goods",
status: "active",
},
{
domain: "harborlantern.com",
status: "closed",
},
],
}),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.message);
}
import requests
response = requests.post(
"https://api.branduo.io/v1/platform/brands",
headers={
"Content-Type": "application/json",
"X-Api-Key": "bduo_platform_live_…",
},
json={
"brands": [
{
"domain": "https://www.NorthwindGoods.com/shop",
"businessName": "Northwind Goods",
"status": "active",
},
{
"domain": "harborlantern.com",
"status": "closed",
},
]
},
)
response.raise_for_status()
{
"error": "invalid_request",
"message": "brands accepts at most 1000 items."
}
{
"error": "invalid_request",
"message": "JSON body must be 1 MB or smaller."
}
{
"error": "unauthorized",
"message": "Invalid or missing API key."
}
Platform
Sync platform brands
Upsert brands for a platform key. Omitted brands stay. Close a brand with status closed.
POST
/
platform
/
brands
cURL
curl --request POST \
--url 'https://api.branduo.io/v1/platform/brands' \
--header 'Content-Type: application/json' \
--header 'X-Api-Key: bduo_platform_live_…' \
--data '{
"brands": [
{
"domain": "https://www.NorthwindGoods.com/shop",
"businessName": "Northwind Goods",
"status": "active"
},
{
"domain": "harborlantern.com",
"status": "closed"
}
]
}'
const response = await fetch("https://api.branduo.io/v1/platform/brands", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Api-Key": "bduo_platform_live_…",
},
body: JSON.stringify({
brands: [
{
domain: "https://www.NorthwindGoods.com/shop",
businessName: "Northwind Goods",
status: "active",
},
{
domain: "harborlantern.com",
status: "closed",
},
],
}),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.message);
}
import requests
response = requests.post(
"https://api.branduo.io/v1/platform/brands",
headers={
"Content-Type": "application/json",
"X-Api-Key": "bduo_platform_live_…",
},
json={
"brands": [
{
"domain": "https://www.NorthwindGoods.com/shop",
"businessName": "Northwind Goods",
"status": "active",
},
{
"domain": "harborlantern.com",
"status": "closed",
},
]
},
)
response.raise_for_status()
{
"error": "invalid_request",
"message": "brands accepts at most 1000 items."
}
{
"error": "invalid_request",
"message": "JSON body must be 1 MB or smaller."
}
{
"error": "unauthorized",
"message": "Invalid or missing API key."
}
Upsert brands for the platform on your platform key.
Each call creates or updates only the brands you send. Brands you leave out stay as they are, including brands you closed earlier. This is not a snapshot replace.
The stored domain is at most 100 characters. Sending the same domain again, in any of the forms above, updates that brand. It does not insert a second row.
Split a larger catalog into more requests. Each request upserts only the brands in that body, so a later batch does not reopen or remove brands from an earlier batch unless you send those domains again.
cURL
curl --request POST \
--url 'https://api.branduo.io/v1/platform/brands' \
--header 'Content-Type: application/json' \
--header 'X-Api-Key: bduo_platform_live_…' \
--data '{
"brands": [
{
"domain": "https://www.NorthwindGoods.com/shop",
"businessName": "Northwind Goods",
"status": "active"
},
{
"domain": "harborlantern.com",
"status": "closed"
}
]
}'
const response = await fetch("https://api.branduo.io/v1/platform/brands", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Api-Key": "bduo_platform_live_…",
},
body: JSON.stringify({
brands: [
{
domain: "https://www.NorthwindGoods.com/shop",
businessName: "Northwind Goods",
status: "active",
},
{
domain: "harborlantern.com",
status: "closed",
},
],
}),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.message);
}
import requests
response = requests.post(
"https://api.branduo.io/v1/platform/brands",
headers={
"Content-Type": "application/json",
"X-Api-Key": "bduo_platform_live_…",
},
json={
"brands": [
{
"domain": "https://www.NorthwindGoods.com/shop",
"businessName": "Northwind Goods",
"status": "active",
},
{
"domain": "harborlantern.com",
"status": "closed",
},
]
},
)
response.raise_for_status()
Authenticate with a platform key in
X-Api-Key. Do not send X-Platform-Id.
A brand API key or an OAuth access token is rejected. See
Authentication.Authentication
A platform key identifies the platform whose brands you are syncing. It is not a brand API key. Send the full key inX-Api-Key. Platform keys begin with bduo_platform_live_ or bduo_platform_test_. We show the plaintext once, when the key is issued. There is no brand Settings page that creates these keys.
The key selects the platform. Do not send X-Platform-Id. Do not send a platform id in the body. If the body includes a platform id that does not match the key, the request returns 400.
A brand API key cannot call POST /v1/platform/brands. A platform key cannot call brand routes such as Get partners.
Store the platform key securely. Never expose it in client-side code, logs, or
public repositories.
Headers
string
required
application/jsonRequest body
object[]
required
Brands to create or update. At most 1000 items. See Limits.
Show properties
Show properties
string
required
The brand’s custom domain. We normalize it before storing. After
normalization it must be 1–100 characters. See
Domain normalization.
string
Brand name. Optional. At most 100 characters. When you send it, we store
that name on the brand.
string
default:"active"
active or closed. Omitted means active. closed sets EndDate.
active clears EndDate.{
"brands": [
{
"domain": "https://www.NorthwindGoods.com/shop",
"businessName": "Northwind Goods",
"status": "active"
},
{
"domain": "harborlantern.com",
"status": "closed"
}
]
}
Domain normalization
Send the brand’s public custom domain. We store one row per platform and normalized domain. Syncing does not create a Branduo company. Before we storedomain, we:
- Lowercase the host.
- Remove the scheme (
https://orhttp://). - Remove a leading
www.. - Remove the port.
- Remove the path, including a query string or fragment.
shop.northwindgoods.com is not rewritten to the apex domain.
| You send | We store |
|---|---|
https://www.NorthwindGoods.com/shop | northwindgoods.com |
HTTP://HarborLantern.com:8443/about?ref=1 | harborlantern.com |
shop.northwindgoods.com | shop.northwindgoods.com |
Upsert behavior
We match each item on the normalized domain for the platform on the key.- A domain we have not stored is added.
statusdefaults toactive, soEndDateis null. - The same domain on a later call updates that brand in place.
statusclosedsoft-closes the brand. We setEndDateand keep the row. We do not delete it.statusactive, or a body that omitsstatus, clearsEndDateand makes the brand active again.- Domains you omit are unchanged. A brand you do not send stays active if it was active, and stays closed if it was closed.
This route does not replace your catalog. To stop a brand from staying
active, send that domain with
status closed. Omitting it leaves the
previous EndDate in place.Limits
Both checks run on the request you send. Either one returns400.
| Check | Result |
|---|---|
brands contains more than 1000 objects | 400 Bad Request |
| JSON body is larger than 1 MB | 400 Bad Request |
Response
200 means the brands in this request were upserted. The response is not a catalog of every brand on the platform. Confirm success from the status code, then send the next batch if you have one.
Errors
Errors use the OpenAPI error object (error and message). message explains which check failed.
| Status | Cause |
|---|---|
400 Bad Request | The body failed a limit or field check. See below. |
401 Unauthorized | The platform key is missing, invalid, or expired, or the caller sent a brand API key or an OAuth access token. |
400 covers:
- More than 1000 brands, or a JSON body larger than 1 MB.
- Malformed JSON, or
brandsmissing or not an array. - A missing
domain, astatusother thanactiveorclosed, orbusinessNameor the normalized domain longer than 100 characters. - A platform id in the body that does not match the platform key.
{
"error": "invalid_request",
"message": "brands accepts at most 1000 items."
}
{
"error": "invalid_request",
"message": "JSON body must be 1 MB or smaller."
}
{
"error": "unauthorized",
"message": "Invalid or missing API key."
}

