Link a whole brand

Get a link for every product of a brand, then keep them up to date with a nightly run. About 40 requests for a 2,000-product brand.

1. Find the brand

Terminal
curl "https://api.stilo.io/v1/brands?q=nars" \
  -H "Authorization: Bearer $STILO_API_KEY"

Each brand's id is its slug, for example nars.

2. List its products, 100 at a time

Terminal
curl "https://api.stilo.io/v1/products?brand=nars&limit=100" \
  -H "Authorization: Bearer $STILO_API_KEY"

Keep requesting with cursor=<next_cursor> until next_cursor is null. Each product already says whether you have a link to it (link) and what you earn (commission; null means nothing to earn).

3. Create the links in batches

Terminal
curl https://api.stilo.io/v1/links/batch \
  -H "Authorization: Bearer $STILO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"items": [{"product_id": "prd_..."}, {"product_id": "prd_..."}]}'

Up to 100 items per call. Each result says created, existing or error, in the same order you sent them.

4. Run it every night

Because you get one link per product, running the same script again returns the same links, never duplicates. To pick up only what changed, pass updated_since with the time of your last run:

Terminal
curl "https://api.stilo.io/v1/products?brand=nars&updated_since=2026-11-04T03:00:00Z&limit=100" \
  -H "Authorization: Bearer $STILO_API_KEY"
A product that no longer appears in the list was removed or sold out. Its link keeps working; hide it on your site if you like.

Full example (Python)

sync_brand.py
import os
import requests

API = "https://api.stilo.io/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['STILO_API_KEY']}"}


def products(brand, updated_since=None):
    params = {"brand": brand, "limit": 100}
    if updated_since:
        params["updated_since"] = updated_since
    while True:
        page = requests.get(f"{API}/products", headers=HEADERS, params=params, timeout=30).json()
        yield from page["data"]
        if not page["next_cursor"]:
            return
        params["cursor"] = page["next_cursor"]


def sync(brand, updated_since=None):
    todo = [p for p in products(brand, updated_since) if p["commission"] and not p["link"]]
    links = {}
    for start in range(0, len(todo), 100):
        chunk = todo[start:start + 100]
        body = {"items": [{"product_id": p["id"]} for p in chunk]}
        result = requests.post(f"{API}/links/batch", headers=HEADERS, json=body, timeout=60).json()
        for product, item in zip(chunk, result["data"]):
            if item["link"]:
                links[product["id"]] = item["link"]["short_url"]
            else:
                print("skipped", product["name"], item["error"]["type"])
    return links


if __name__ == "__main__":
    for product_id, url in sync("nars").items():
        print(product_id, url)

Tips

  • Respect Retry-After on a 429; the limits are generous, so honest scripts rarely hit them.
  • A store link (the retailer's whole store) is {"retailer_id": "ret_..."}.
  • To pin a link to a variant (a color, a size), send variant_id with the product_id, or paste a store URL that names the variant.