API reference
Stylumia Catalog API · v0.2.0 · OpenAPI 3.1.0
All paths are relative to https://api.stylumia.com. Every request carries
Authorization: Bearer <your API key> — see Authentication.
Read-only access to Stylumia's retailer product catalog crawl.
Every operation in this document is a draft (x-status: draft). Shapes,
field names, and error codes may still change before general availability;
treat this as a preview, not a stability guarantee.
One filter set, three operations
retailer, category, department, rcd, start_date, and end_date
are the same six parameters on every operation below — narrow once and
reuse the query anywhere:
GET /v1/sample— up to 100 rows, for judging the shape of the data.GET /v1/catalog— the complete filtered set, cursor-paginated.GET /v1/coverage— how much exists for those filters, before you pull any of it.
There is no discovery chain to walk. A guessed retailer, category, or
department fails with valid_options naming the real values, which is
the only vocabulary lookup an integrator needs. rcd is shorthand for a
retailer:category:department combination — send one parameter instead
of three, or several comma-separated to select more than one combination.
Every response echoes the filters the server actually resolved in
meta.resolved, and carries a compact meta.coverage block, so one
request answers both "what did I get" and "how much is there".
Error contract
Every non-2xx response is an RFC 9457 problem document
(application/problem+json) shaped by the Problem schema, carrying
code, retryable, recovery, and — on vocabulary errors —
valid_options. retryable is the field to branch on: false means fix
the request before resending, true means back off and retry. recovery
is human-readable guidance that matches the public error catalogue
byte-for-byte. Each operation below documents the exact set of codes it
can emit via x-error-codes.
Credentials
Sign in to the developer portal, create a named API key, and copy it — the
secret is shown exactly once. Send it as
Authorization: Bearer <your API key>.
Keys do not expire; revoking is the only thing that ends one, and it takes effect on the very next request. Create as many as you need — one per service, script or agent — so a leak or a revocation never takes down more than the one integration.
/v1/sample
Sample rows
Up to 100 rows for the given filters. Uncursored — a sample is for
judging the shape of the data before committing to a full
GET /v1/catalog walk; anything larger belongs there instead.
Requires the catalog:sample scope, distinct from the catalog:read
scope GET /v1/catalog and GET /v1/coverage need.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
retailer |
string | optional | Restrict to this retailer's slug. An unrecognized value fails with unknown_retailer, whose valid_options names the closest real slugs. e.g. home_depot |
category |
string | optional | Restrict to this category's slug, scoped by retailer if given. An unrecognized value fails with unknown_category, whose valid_options names the closest real slugs. e.g. power_tools |
department |
string | optional | Restrict to this department's slug, scoped by retailer if given. An unrecognized value fails with unknown_department, whose valid_options names the closest real slugs. e.g. tools |
rcd |
string | optional | One or more retailer:category:department triples, comma-separated. Shorthand for sending retailer, category, and department individually — send this instead when you already know the exact combination(s) you want. A malformed value fails with invalid_request. e.g. home_depot:power_tools:tools |
start_date |
string (date) | optional | Only rows with crawl_date on or after this date, inclusive. ISO 8601 (YYYY-MM-DD). Malformed, or after end_date, fails with invalid_date_range. e.g. 2026-06-14 |
end_date |
string (date) | optional | Only rows with crawl_date on or before this date, inclusive. ISO 8601 (YYYY-MM-DD). Malformed, or before start_date, fails with invalid_date_range. e.g. 2026-08-14 |
Responses
| Status | Body | Description |
|---|---|---|
200 |
ProductListResponse |
Up to 100 sample product rows matching the given filters. |
400 |
Problem |
The request is malformed, or references an unrecognized retailer, category, department, date range, cursor, or `rcd` value. |
401 |
Problem |
No credentials were presented, or the presented token is unknown, expired, or revoked. |
403 |
Problem |
The token is valid but the team is not entitled to this operation. |
429 |
Problem |
The published per-team rate limit was exceeded. |
5XX |
Problem |
Something unforeseen failed on the server side, the service is down or degraded, or the server was too slow to answer. The request was not necessarily the problem — see `retryable`. |
Errors this operation can return
Enumerated in the spec as x-error-codes. Branch on code, never on detail.
Example
curl "https://api.stylumia.com/v1/sample?retailer=home_depot" \
-H "Authorization: Bearer $STYLUMIA_TOKEN"
/v1/catalog
Get catalog
The complete filtered set, walked with a cursor. Results are sorted
by sku so a page's contents never shift under a caller mid-walk.
The cursor encodes position only — the same filters must be resent
unchanged on every page. Continue with the cursor from
pagination.next_cursor until pagination.has_more is false.
Requires the catalog:read scope.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
retailer |
string | optional | Restrict to this retailer's slug. An unrecognized value fails with unknown_retailer, whose valid_options names the closest real slugs. e.g. home_depot |
category |
string | optional | Restrict to this category's slug, scoped by retailer if given. An unrecognized value fails with unknown_category, whose valid_options names the closest real slugs. e.g. power_tools |
department |
string | optional | Restrict to this department's slug, scoped by retailer if given. An unrecognized value fails with unknown_department, whose valid_options names the closest real slugs. e.g. tools |
rcd |
string | optional | One or more retailer:category:department triples, comma-separated. Shorthand for sending retailer, category, and department individually — send this instead when you already know the exact combination(s) you want. A malformed value fails with invalid_request. e.g. home_depot:power_tools:tools |
start_date |
string (date) | optional | Only rows with crawl_date on or after this date, inclusive. ISO 8601 (YYYY-MM-DD). Malformed, or after end_date, fails with invalid_date_range. e.g. 2026-06-14 |
end_date |
string (date) | optional | Only rows with crawl_date on or before this date, inclusive. ISO 8601 (YYYY-MM-DD). Malformed, or before start_date, fails with invalid_date_range. e.g. 2026-08-14 |
cursor |
string | optional | Opaque pagination cursor from a previous response's pagination.next_cursor. Omit for the first page. A cursor this server did not issue fails with invalid_cursor. |
limit |
integer | optional | Maximum rows to return in this page. |
Responses
| Status | Body | Description |
|---|---|---|
200 |
ProductListResponse |
A page of product rows matching the given filters. |
400 |
Problem |
The request is malformed, or references an unrecognized retailer, category, department, date range, cursor, or `rcd` value. |
401 |
Problem |
No credentials were presented, or the presented token is unknown, expired, or revoked. |
403 |
Problem |
The token is valid but the team is not entitled to this operation. |
429 |
Problem |
The published per-team rate limit was exceeded. |
5XX |
Problem |
Something unforeseen failed on the server side, the service is down or degraded, or the server was too slow to answer. The request was not necessarily the problem — see `retryable`. |
Errors this operation can return
Enumerated in the spec as x-error-codes. Branch on code, never on detail.
Example
curl "https://api.stylumia.com/v1/catalog?retailer=home_depot" \
-H "Authorization: Bearer $STYLUMIA_TOKEN"
/v1/coverage
Get coverage
Coverage metrics for each retailer/category/department combination
matching the given filters: how many rows the full corpus holds,
how many match this filter, and the crawl-date range they span. Use
this to decide which start_date / end_date window on
GET /v1/catalog will actually return rows before spending a call
finding out.
Requires the catalog:read scope.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
retailer |
string | optional | Restrict to this retailer's slug. An unrecognized value fails with unknown_retailer, whose valid_options names the closest real slugs. e.g. home_depot |
category |
string | optional | Restrict to this category's slug, scoped by retailer if given. An unrecognized value fails with unknown_category, whose valid_options names the closest real slugs. e.g. power_tools |
department |
string | optional | Restrict to this department's slug, scoped by retailer if given. An unrecognized value fails with unknown_department, whose valid_options names the closest real slugs. e.g. tools |
rcd |
string | optional | One or more retailer:category:department triples, comma-separated. Shorthand for sending retailer, category, and department individually — send this instead when you already know the exact combination(s) you want. A malformed value fails with invalid_request. e.g. home_depot:power_tools:tools |
start_date |
string (date) | optional | Only rows with crawl_date on or after this date, inclusive. ISO 8601 (YYYY-MM-DD). Malformed, or after end_date, fails with invalid_date_range. e.g. 2026-06-14 |
end_date |
string (date) | optional | Only rows with crawl_date on or before this date, inclusive. ISO 8601 (YYYY-MM-DD). Malformed, or before start_date, fails with invalid_date_range. e.g. 2026-08-14 |
Responses
| Status | Body | Description |
|---|---|---|
200 |
CoverageListResponse |
Coverage metrics for every combination matching the given filters. |
400 |
Problem |
The request is malformed, or references an unrecognized retailer, category, department, date range, cursor, or `rcd` value. |
401 |
Problem |
No credentials were presented, or the presented token is unknown, expired, or revoked. |
403 |
Problem |
The token is valid but the team is not entitled to this operation. |
429 |
Problem |
The published per-team rate limit was exceeded. |
5XX |
Problem |
Something unforeseen failed on the server side, the service is down or degraded, or the server was too slow to answer. The request was not necessarily the problem — see `retryable`. |
Errors this operation can return
Enumerated in the spec as x-error-codes. Branch on code, never on detail.
Example
curl "https://api.stylumia.com/v1/coverage?retailer=home_depot" \
-H "Authorization: Bearer $STYLUMIA_TOKEN"
Schemas
Every object the responses above refer to. Nullable means the field can be absent or null.
Product
A single crawled product row.
| Field | Type | Nullable | Description |
|---|---|---|---|
product_id |
string | no | Stable id, a content hash of the row. Listings are re-crawled, not diffed against a permanent catalog: a product relisted under a new title mints a new product_id, it does not update the old one. |
sku |
string | no | Retailer-specific product identifier. Format varies by retailer. Unique within a retailer only — the same sku string can appear under a different retailer for an unrelated product. |
url |
string (uri) | no | Canonical URL of the product page as crawled. |
domain |
string | no | Domain the product page was crawled from. |
retailer |
string | no | Slug of the retailer this row was crawled from. |
department |
string | no | Slug of the department this row belongs to. |
category |
string | no | Slug of the category this row belongs to. |
brand |
string | no | Brand name as listed on the retailer's page. |
model_number |
string | no | Manufacturer model number as listed on the retailer's page. |
title |
string | no | Product title as listed on the retailer's page. |
mrp |
number | no | Manufacturer's listed (undiscounted) price, in currency. |
selling_price |
number | no | Actual price charged at crawl time, in currency. |
discount_pct |
number | no | Discount off mrp, as a percentage, at crawl time. |
currency |
string | no | ISO 4217 currency code for mrp and selling_price. |
color |
string | yes | Listed color, when the retailer publishes one for this product. null when not applicable or not listed. |
image_url |
string (uri) | no | URL of the primary product image as crawled. |
crawl_date |
string (date) | no | Date (YYYY-MM-DD) this row was crawled. |
Coverage
Coverage metrics for one retailer/category/department combination.
| Field | Type | Nullable | Description |
|---|---|---|---|
retailer |
string | no | Slug of the retailer. |
department |
string | no | Slug of the department. |
category |
string | no | Slug of the category. |
rcd |
string | no | This combination as a retailer:category:department triple, ready to pass back as rcd. |
corpus_rows |
integer | no | Total rows the full dataset holds for this combination, independent of any filter applied. |
matching_rows |
integer | no | Rows within this combination that also satisfy the request's date filters, if any. |
earliest_date |
string (date) | no | Earliest crawl_date (YYYY-MM-DD) among matching rows. |
latest_date |
string (date) | no | Most recent crawl_date (YYYY-MM-DD) among matching rows. |
CoverageSummary
The compact coverage block carried on every response, answering "how much exists" alongside "what did I get" — summed across every combination matching the request's filters.
| Field | Type | Nullable | Description |
|---|---|---|---|
combinations |
integer | no | Number of retailer/category/department combinations matching the request's filters. |
matching_rows |
integer | no | Total rows across those combinations that also satisfy the request's date filters, if any. |
corpus_rows |
integer | no | Total rows the full dataset holds across those combinations, independent of any date filter. |
earliest_date |
string (date) | yes | Earliest crawl_date (YYYY-MM-DD) among matching rows. null when no combination matches. |
latest_date |
string (date) | yes | Most recent crawl_date (YYYY-MM-DD) among matching rows. null when no combination matches. |
Pagination
Cursor pagination state for this response. `limit` and `total_matching` are present on `GET /v1/catalog`; `GET /v1/sample` and `GET /v1/coverage` are uncursored and omit them.
| Field | Type | Nullable | Description |
|---|---|---|---|
limit |
integer | yes | Maximum rows requested for this page. |
total_matching |
integer | yes | Total rows matching the request's filters, across every page. |
next_cursor |
string | yes | Opaque cursor for the next page. Pass back as the cursor parameter to continue. null when there is no further page. |
has_more |
boolean | no | Whether a further page exists beyond this one. |
Meta
Response metadata.
| Field | Type | Nullable | Description |
|---|---|---|---|
request_id |
string | no | Unique id for this request. Quote it when reporting a problem, especially a 5xx. |
resolved |
object | no | Every filter the server actually applied, including omitted ones as null, so a response is never ambiguous about what was queried. |
coverage |
CoverageSummary | no |
ProductListResponse
Response envelope for `GET /v1/sample` and `GET /v1/catalog`.
| Field | Type | Nullable | Description |
|---|---|---|---|
data |
array<Product> | no | The matching product rows for this page. |
pagination |
Pagination | no | |
meta |
Meta | no |
CoverageListResponse
Response envelope for `GET /v1/coverage`.
| Field | Type | Nullable | Description |
|---|---|---|---|
data |
array<Coverage> | no | Coverage metrics, one entry per matching retailer/category/department combination. |
pagination |
Pagination | no | |
meta |
Meta | no |
Problem
RFC 9457 problem details, extended for programmatic handling. Every non-2xx response has this shape, served as `application/problem+json`.
| Field | Type | Nullable | Description |
|---|---|---|---|
type |
string (uri) | no | Stable URI identifying this error type; resolves to its entry in the public error catalogue ({base}/errors#{code}). |
title |
string | no | Short, human-readable summary of the error type. Constant per code. |
status |
integer | no | HTTP status code, repeated from the response line for convenience. |
detail |
string | no | Human-readable explanation specific to this occurrence of the problem. |
code |
string | no | Stable machine-readable error code. Append-only — safe to branch on in code. |
retryable |
boolean | no | false means fix the request before resending; true means back off and retry — the request itself was not the problem. |
recovery |
string | no | What to do about this error, byte-identical to the public error catalogue. |
request_id |
string | no | Unique id for the request that produced this error. Quote it when reporting a problem. |
field |
string | yes | Name of the request parameter at fault. Present only on errors tied to one parameter. |
valid_options |
array<string> | yes | On vocabulary errors, the closest real values to what was sent (or the first few valid values), so a failed call can self-correct without a documentation round trip. |
Want to run these? The Explorer builds a request and executes it against the live API. This page has a .md twin.