Integrate the TrueSpec catalog API
One bearer key reads every catalog your organization has been granted, merged into a single normalized feed. Clean JSON, cursor pagination, conditional requests, and incremental sync so your nightly job pulls only what changed. This page is the practical walkthrough; the full reference is the interactive OpenAPI docs (ReDoc if you prefer).
1. Get a key
An owner in your organization creates API keys under
Keys once a publisher has granted you access (or
after your access request is approved). The raw
token — dxk_… — is shown once at
creation. Store it in your secret store; it can be rotated or revoked
at any time without touching the grant.
2. First call
curl -s https://truespec.net/v1/products \
-H "Authorization: Bearer dxk_YOUR_KEY"
Every list response is the same envelope — items plus an opaque cursor:
{
"items": [
{
"id": "prod_01ksessas63k7c5e375aqbgj8r",
"sku": "TT-HRV-1X6-SQR-BS",
"brand": "TimberTech",
"name": "1x6 Square-Shouldered Harvest Decking - Brownstone",
"attributes": { "color": "Brownstone", "material": "composite" },
"primary_image_url": "https://images.truespec.net/i/.../medium.webp",
"variant_count": 3,
"variants_url": "/v1/products/prod_01ksessas.../variants",
"updated_at": "2026-06-04T18:22:31Z"
}
],
"next_cursor": "eyJ1IjoiMjAyNi0wNi0wNFQxOD..."
}
Pages are 50 items by default, up to 200 with ?limit=200.
Pass next_cursor back as ?cursor=… for
the next page; null means you have everything. Cursors are
opaque — don't parse them. GET /v1/products/{id} returns the
full detail (inline variants, typed documents[] — spec
sheets, warranties, SDS), and GET /v1/categories returns the
category tree with each category's attribute_schema, so your
importer knows which attribute keys to expect per category.
3. Sync deltas, not full catalogs
Every list endpoint takes updated_since (ISO 8601;
strictly-greater-than) and orders results oldest-change-first. So the
checkpoint for your next run is simply the updated_at of the
last item you processed:
# nightly_sync.py — pull only what changed since the last run
import requests
BASE = "https://truespec.net/v1"
HEADERS = {"Authorization": "Bearer dxk_YOUR_KEY"}
def sync(checkpoint: str | None) -> str | None:
params = {"limit": 200}
if checkpoint:
params["updated_since"] = checkpoint
while True:
r = requests.get(f"{BASE}/products", headers=HEADERS, params=params)
r.raise_for_status()
page = r.json()
for product in page["items"]:
upsert_into_your_system(product) # keyed on product["id"]
checkpoint = product["updated_at"]
if page["next_cursor"] is None:
return checkpoint # save for the next run
params["cursor"] = page["next_cursor"]
Responses also carry ETag and Last-Modified;
send If-None-Match back and an unchanged page costs you a
304 with no body. From PowerShell-land, the first call is
one line:
Invoke-RestMethod -Uri "https://truespec.net/v1/products" `
-Headers @{ Authorization = "Bearer dxk_YOUR_KEY" }
4. Resolve your own item numbers
After your organization uploads its item list at Enrichment, your storefront or ERP can look up vendor product data under your own SKUs:
curl -s https://truespec.net/v1/products/by-consumer-sku/YOUR-ITEM-123 \
-H "Authorization: Bearer dxk_YOUR_KEY" -i
The body is the standard product detail; how the match was made rides in
headers: X-TrueSpec-Match-Basis (gtin /
vendor_sku / manual),
X-TrueSpec-Match-Confidence, and
X-TrueSpec-Matched-Variant. Ad-hoc lookups work too:
?vendor_sku=LCGV5412E and ?gtin=… on the
list endpoint, alongside ?brand= and
?category= filters (brand matching is case- and
punctuation-insensitive).
5. Operational facts
| Thing | Behavior |
|---|---|
| Rate limit | Per key (default 600 req/min). Watch
X-RateLimit-Limit / -Remaining / -Reset; a 429 carries
Retry-After. |
| Errors | Always {"error": {"code", "message",
"request_id"}} with a closed, documented code enum
(e.g. invalid_cursor, rate_limited). Quote
request_id (also the X-Request-Id header) in
support requests. |
| IDs | prod_… / var_…
public ids are permanent — safe foreign keys in your system. |
| Contract | Changes within /v1/ are additive
only — new fields appear, existing ones never break. Breaking changes
would ship as /v2/. |
Integrating an ERP or eCommerce platform and want a sounding board? Talk to us — we've wired this into the systems this industry actually runs.