How a script or an agent manages a paid member's products on Garden101.co.uk. The same text as Markdown: https://www.garden101.co.uk/ai-agents.md
The member API lets a script or an AI agent manage a paid member's products and ranges on garden101.co.uk: create them, update them, add images from your own URLs, unpublish them, or mirror your whole catalogue in one call.
Paid members' products go live immediately; anything our checks flag waits for a human review and comes back as "moderation": "queued".
The API never deletes anything. DELETE unpublishes, and the member can restore from their profile.
Log in, open Profile → Accounts → API Keys, create a key and copy it: it is shown once. Keys are per directory and per member; one key covers all of that member's businesses here. Revoke a key at any time from the same page.
Base URL: https://www.garden101.co.uk/api/v1/ Header: X-API-KEY: garden101_live_7f3a9c21… Keys look like garden101_live_ followed by 64 hex characters.
curl -s -X GET 'https://www.garden101.co.uk/api/v1/me' -H 'X-API-KEY: garden101_live_7f3a9c21…'
Every write names a business with business_id. If you own one business on this directory you may leave it out. GET /api/v1/me lists your businesses with their ids, your tier, cap and usage.
| Method | Path | Does |
|---|---|---|
GET | /api/v1/me | you, your businesses, tier, cap, usage, rate-limit state |
GET | /api/v1/categories | this directory's categories: the only valid category values |
GET | /api/v1/products | list (business_id, page, status=live|unpublished); 50 per page |
POST | /api/v1/products | create one |
GET | /api/v1/products/{id} | read one |
PATCH | /api/v1/products/{id} | update the fields sent; null clears a nullable field |
DELETE | /api/v1/products/{id} | unpublish (204) |
POST | /api/v1/products/{id}/images | {"urls":[…]} replaces the images (max 8) |
DELETE | /api/v1/products/{id}/images | remove all images (204) |
GET | /api/v1/ranges | list ranges |
POST | /api/v1/ranges | create a range (heading; sku, description, link, hero_url optional) |
GET | /api/v1/ranges/{id} | read a range |
PATCH | /api/v1/ranges/{id} | update a range |
DELETE | /api/v1/ranges/{id} | unpublish a range (204) |
POST | /api/v1/products/sync | the whole catalogue of one business in one call |
| Field | Rule |
|---|---|
heading | required on create; 3–150 characters |
description | optional; HTML or text up to 20,000 characters; unsafe markup is removed |
price | optional free text up to 150 characters, e.g. "£49.99 per m²" |
link | optional http(s) URL up to 500 characters (shown to visitors on paid listings) |
category | optional; a slug from GET /api/v1/categories |
sku | your own code; optional on POST, required in sync; letters, digits . _ - / up to 64; unique per business, matched regardless of case |
range_id or range_sku | optional; a range of the same business |
image_urls | optional; up to 8 http(s) URLs we fetch (jpeg/png/webp/gif, 5 MB each, public hosts only) |
curl -s -X POST 'https://www.garden101.co.uk/api/v1/products' -H 'X-API-KEY: garden101_live_7f3a9c21…' -H 'Content-Type: application/json' -d '{"business_id":42,"sku":"OAK-190","heading":"Natural Oak 190mm Plank","price":"£49.99 per m²","category":"engineered-wood-flooring","image_urls":["https://shop.example.com/i/oak.jpg"]}'
{"product":{"id":1234,"sku":"OAK-190","heading":"Natural Oak 190mm Plank","status":"live","url":"https://www.garden101.co.uk/product/natural-oak-190mm-plank-1234","...":"..."},"moderation":"published","rejected_images":[]}
POST /api/v1/products/sync with business_id and products (up to 500 items, each with a sku). Items are matched on sku (regardless of case): known → updated, new → created, and any product of yours with a sku that is missing from the payload is unpublished. Products without a sku (made in the profile form) are never touched; the response counts them as unmanaged.
Updates are partial, like PATCH: a field an item leaves out keeps its current value, so you can sync just sku + price without touching descriptions or images. Send a field with null to clear it.
Moderation inside a run: the first 50 new or changed items are checked by the AI checker; beyond that the deterministic checks alone decide and the queue row says so. An item a moderator has suspended stays queued however many times you send it, until a moderator approves it.
Safety valve: if a run would unpublish more than half of your live catalogue (and at least two products) it is refused with 409 removals_exceed_threshold. Send "confirm_removals": true to go ahead. A feed whose every item is rejected removes nothing.
Images: at most 100 image fetches per run. Items still waiting are listed in images_pending; run sync again (unchanged items cost nothing) or call POST /api/v1/products/{id}/images.
One sync per business every 10 minutes (429 sync_too_soon with Retry-After).
curl -s -X POST 'https://www.garden101.co.uk/api/v1/products/sync' -H 'X-API-KEY: garden101_live_7f3a9c21…' -H 'Content-Type: application/json' -d '{"business_id":42,"confirm_removals":false,"products":[{"sku":"OAK-190","heading":"Natural Oak 190mm Plank","price":"£49.99 per m²"},{"sku":"WAL-150","heading":"Walnut 150mm Plank"}]}'
{"created":1,"updated":1,"unpublished":0,"unchanged":0,"unmanaged":2,"rejected":[],"queued":[],"images_pending":[],"products":[{"sku":"OAK-190","id":1234,"action":"updated","moderation":"unchanged"},{"sku":"WAL-150","id":1301,"action":"created","moderation":"published"}]}
Clean products from paid members publish at once ("moderation": "published"). Anything our automated checks flag waits for review ("queued") and appears once approved.
Caps: 500 products per business. Creates beyond the cap are rejected per item with over_cap; updates always work.
Rate limit: 600 requests per hour per key. Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset; over the limit you get 429 with Retry-After. Request bodies are limited to 2 MB.
Errors are {"error":{"code":"…","message":"…","field":"…","details":{…}}}; field and details appear when they apply. 422 validation_failed lists every failing field in details so you can fix a payload in one go.
| HTTP | Codes |
|---|---|
400 | bad_json |
401 | missing_key, invalid_key, key_revoked |
403 | api_disabled, paid_tier_required, business_not_live |
404 | unknown_domain, business_not_found, product_not_found, range_not_found, not_found |
409 | duplicate_sku, removals_exceed_threshold |
413 | payload_too_large |
422 | validation_failed, invalid_category, invalid_range, sku_required, duplicate_sku_in_payload, over_cap, image_url_invalid, image_url_private, image_unsupported, image_too_large, image_fetch_failed |
429 | rate_limited, sync_too_soon |
500 | server_error |
A quick run-through you can paste into a terminal (replace the key and ids).
curl -s -X GET 'https://www.garden101.co.uk/api/v1/me' -H 'X-API-KEY: garden101_live_7f3a9c21…'
curl -s -X GET 'https://www.garden101.co.uk/api/v1/categories' -H 'X-API-KEY: garden101_live_7f3a9c21…'
curl -s -X POST 'https://www.garden101.co.uk/api/v1/products' -H 'X-API-KEY: garden101_live_7f3a9c21…' -H 'Content-Type: application/json' -d '{"business_id":42,"sku":"TEST-1","heading":"Test product","category":"not-a-category"}' # 422 invalid_category
curl -s -X POST 'https://www.garden101.co.uk/api/v1/products' -H 'X-API-KEY: garden101_live_7f3a9c21…' -H 'Content-Type: application/json' -d '{"business_id":42,"sku":"TEST-1","heading":"Test product","image_urls":["https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png"]}'
curl -s -X POST 'https://www.garden101.co.uk/api/v1/products/sync' -H 'X-API-KEY: garden101_live_7f3a9c21…' -H 'Content-Type: application/json' -d '{"business_id":42,"products":[{"sku":"TEST-1","heading":"Test product"}]}'
curl -s -X DELETE 'https://www.garden101.co.uk/api/v1/products/1234?business_id=42' -H 'X-API-KEY: garden101_live_7f3a9c21…' # 204