REST API guide
The public REST API is the read/write surface for external systems — custom ERPs, Zapier, in-house scripts — to consume and feed pricing data without touching the panel. This guide covers authentication and concepts; for the full endpoint-by-endpoint reference (request/response schemas, try-it-out), see Interactive reference docs below.
Base URL
All API routes are prefixed with /api
on your instance's domain, e.g.
https://your-instance.example.com/api.
Authentication
The API uses Laravel Sanctum personal access tokens (bearer tokens), one per integration/use case.
- Log into the panel, go to Settings → API Tokens.
- Enter a name for the token (e.g. "Zapier", "ERP sync") and click Create token.
- Copy the token immediately — it's shown once, in plaintext, and never again.
- Send it on every request:
curl https://your-instance.example.com/api/products \
-H "Authorization: Bearer <token>" \
-H "Accept: application/json"
A token has full access to everything your account can see — there's no scoping below the account level. Revoke a token from the same Settings page if it's compromised or no longer needed; revocation is immediate.
Concepts
- Everything is scoped to your account. A token only ever sees/modifies data belonging to the account it was created under — there's no cross-tenant access.
- Pagination. List endpoints return Laravel's standard paginated shape (
data,links,meta). Followmeta.current_page/links.next, or pass?page=. sell_pricevssmart_price.sell_priceis the live price.smart_priceis what the currently-matching pricing rule computes — always kept fresh, but only becomessell_priceonce an applied price change writes it. Read-only via the API; never accepted on create/update.
Common endpoints
| Method & path | What it does |
|---|---|
GET /api/products |
List your products. Supports ?sort= (name, sell_price, smart_price, created_at, updated_at), ?dir=, ?q= (search name/SKU/GTIN), ?updated_since=, ?smart_price_since= (only products whose computed price changed). |
POST /api/products |
Create a product. |
GET /api/products/{id} |
Fetch one product, including which pricing rules currently apply to it. |
PATCH /api/products/{id} |
Update a product. |
DELETE /api/products/{id} |
Delete a product. |
POST /api/products/import |
Bulk create/update by gtin. Each row is validated and upserted independently — one bad row doesn't fail the batch. See Bulk import below. |
GET /api/products/export |
CSV snapshot of your catalog. |
GET /api/products/{id}/competitor-urls |
List a product's tracked competitor URLs and latest prices. |
POST /api/products/{id}/competitor-urls |
Track a new competitor URL for a product. |
DELETE /api/products/{id}/competitor-urls/{urlId} |
Stop tracking a competitor URL. |
POST /api/products/{id}/competitor-urls/{urlId}/scrape |
Trigger an on-demand scrape (async — price updates once the worker finishes, usually within seconds). |
GET /api/products/{id}/price-history |
Competitor price time series for a product. ?since= filters by scrape date. |
GET /api/price-changes |
Pricing rule suggestion/applied history. ?status= (pending/applied/rejected), ?since=. |
Bulk import
POST /api/products/import
accepts a products array; each
row is matched/upserted by gtin:
curl -X POST https://your-instance.example.com/api/products/import \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"products": [
{"gtin": "0123456789012", "name": "Example Product", "sell_price": 29.99, "sku": "EX-001"}
]
}'
Response reports per-row outcome so partial failures are visible, not silently swallowed:
{
"created": 1,
"updated": 0,
"failed": 0,
"results": [
{"index": 0, "gtin": "0123456789012", "status": "created"}
]
}
Errors
Standard Laravel conventions:
401 for a missing/invalid token,
403 for accessing another account's resource,
404 for a resource that doesn't exist (or isn't yours — not distinguished, to avoid leaking existence),
422 with a Laravel-shaped errors object for validation failures.
Interactive reference docs
The full API reference — every endpoint, parameters, request/response schemas, and a browser "try it" console — is generated directly from the code (routes, form requests, models), so it can't drift out of sync with what's actually deployed.
- Browse it: /docs/api on your instance.
- Raw OpenAPI 3 spec:
/docs/api.json— feed this into Postman, Insomnia, an SDK generator, or any OpenAPI-compatible tool.
To regenerate the spec as a file (e.g. for committing a snapshot or CI):
php artisan scramble:export
Writes api.json to the
api/ directory (gitignored —
treat it as a build artifact, regenerate rather than hand-edit).
viewApiDocs
gate in AppServiceProvider if
you'd rather not expose your API shape to anonymous visitors.