Version 1 · api.ssandsi.com/pricing/v1

API reference

Everything you need to integrate: authentication, endpoints, limits and errors. This document can be shared as-is with whoever is writing the code.

What this API does

It returns the SS&Si catalog, with part numbers, categories, images and spec sheets, priced either at standard dealer pricing or at what one specific dealer pays.

Every endpoint comes in a pair. The standard version needs your key and one connected dealer, and once you have that you can build the whole integration against it without waiting on anyone else. The account-scoped version is the same data at that dealer's real numbers, the ones they see when they log in to shop.ssandsi.com, produced by the same pricing logic and accounting for their negotiated agreement. Moving from one to the other is a change of URL and nothing else.

It does not expose our list prices, discount structures, or anything about how a price was reached. You get one price per product.

Before you start

An API key

Looks like sspa_live_.... Treat it as a password. You collect it once from a single-use link, and we reissue it if it ever leaks.

Account references

An acct_... ref per dealer, arriving as each one agrees. Specific to you: the same dealer given to another partner gets a different reference. You need one before anything returns data.

Written permission

Nothing is released until one of your dealers has given SS&Si written permission. That first dealer opens standard pricing across the catalog; each dealer's own pricing switches on as they agree.

How you get your key

We never email you a key, and we could not read yours out to you if you asked. Once your request is approved we email a single-use link to your technical contact. Your key is generated the moment you open that link, shown once, and stored here only as a hash. The key alone does not return data: once your first dealer is connected and has agreed, it reaches the whole catalog at standard pricing, and each dealer's own pricing switches on as they agree.

The practical consequence: paste it into your secret store before you close the tab. A lost key is replaced rather than recovered, which means a new link and a short window where you are not authenticated. The link itself expires in 72 hours and works exactly once, so if your mail scanner or a colleague opens it first, ask us for another.

Authentication

Send your key as a bearer token on every request. There is no login step and no token to refresh.

Authorization: Bearer sspa_live_your_key_here

Rate limits

Full catalog pull1 per day standard, plus 1 per account per day
SKU lookups (single and batch)500 per day
Stock lookups5,000 per day

Both reset at midnight US Eastern. Exceeding either returns 429 with a Retry-Afterheader giving the seconds until reset. Pull a full catalog occasionally and use SKU lookups for day-to-day quoting. If those limits don't fit your use case, talk to us rather than working around them.

Endpoints

GET/pricing/v1/catalog

The whole catalog at standard pricing: what a dealer with no negotiated agreement pays. Open once your first dealer is connected and has agreed, and it stays open from then on. Around 11,200 products, roughly 5.7 MB uncompressed and about 780 KB gzipped. Once per day. Send Accept-Encoding: gzip.

{
  "pricing": "standard",
  "pricedAt": "2026-09-22T14:22:10.000Z",
  "count": 11239,
  "products": [
    {
      "sku": "DSC-HSM2108",
      "mpn": "HSM2108",
      "name": "DSC | 8-Hardwired Zone Expander Module | HSM2108",
      "brand": "DSC",
      "brandId": 18,
      "productId": 50,
      "categoryIds": [1, 818],
      "price": 42.20,
      "currency": "USD",
      "unitOfMeasure": "each",
      "imageUrl": "https://cdn11.bigcommerce.com/.../hsm2108.jpg",
      "productUrl": "https://shop.ssandsi.com/dsc-8-hardwired-zone-expander-module-hsm2108/",
      "specSheetUrl": null
    }
  ]
}
GET/pricing/v1/price?sku=XF1640

One product at standard pricing. Remember to URL-encode the SKU, as many of ours contain spaces and slashes.

{
  "pricing": "standard",
  "pricedAt": "2026-09-22T14:22:10.000Z",
  "sku": "XF1640",
  "mpn": "MGT1640",
  "name": "16.5VAC 40VA TRANSFORMER",
  "brand": "MG Electronics",
  "price": 28.74,
  "currency": "USD",
  "unitOfMeasure": "each"
}
POST/pricing/v1/prices

Up to 250 SKUs at standard pricing, body { "skus": [...] }. Duplicates are ignored.

{
  "pricing": "standard",
  "pricedAt": "2026-09-22T14:22:10.000Z",
  "found": [ { "sku": "XF1640", "mpn": "MGT1640", "price": 28.74, "…": "…" } ],
  "notFound": ["NOT-A-REAL-SKU"]
}
GET/pricing/v1/accounts

Every dealer you are authorised to query. Empty until the first one agrees, which is normal and not an error. Start here rather than hard-coding references, since an account disappears from this list if access is withdrawn.

{
  "accounts": [
    { "ref": "acct_pi7rfqsfpgbsm666fp39", "name": "Coastal Protection, Inc." }
  ]
}
POST/pricing/v1/stock

Live availability for up to 250 SKUs, body { "skus": [...] }. Status, quantity and incoming, nothing else. Its own allowance of 5,000 a day, because a stock answer needs no pricing context and is far cheaper for us to serve than a price. Use this rather than spending priced lookups on availability.

{
  "found": [
    { "sku": "XF1640", "status": "in_stock", "quantity": 24, "incoming": 0,
      "asOf": "2026-10-16T14:05:00.000Z" }
  ],
  "notFound": ["NOT-A-REAL-SKU"]
}
GET/pricing/v1/categories

The category tree behind every categoryIds array. Flat, with parentId on each row and null on the roots, limited to categories a visible product is actually filed under. It changes a few times a year, so cache it. No daily limit.

{
  "count": 162,
  "categories": [
    { "id": 580, "name": "Access Control", "parentId": null },
    { "id": 586, "name": "Readers", "parentId": 580 }
  ]
}
GET/pricing/v1/accounts/{ref}/price?sku=DSC-PG9911B%20BATT

The same product at one dealer's price. Identical shape to the standard endpoint, with account in place of pricing.

{
  "account": "acct_pi7rfqsfpgbsm666fp39",
  "pricedAt": "2026-09-22T14:22:10.000Z",
  "sku": "DSC-PG9911B BATT",
  "mpn": "PG9911B BATT",
  "name": "DSC | PowerG 915MHz wireless outdoor siren",
  "brand": "DSC",
  "price": 181.23,
  "currency": "USD"
}
POST/pricing/v1/accounts/{ref}/prices

The same batch lookup at one dealer's prices. Up to 250 SKUs per request.

{
  "account": "acct_pi7rfqsfpgbsm666fp39",
  "pricedAt": "2026-09-22T14:22:10.000Z",
  "found": [
    { "sku": "DSC-PG9911B BATT", "mpn": "PG9911B BATT", "price": 181.23, "…": "…" }
  ],
  "notFound": ["NOT-A-REAL-SKU"]
}
GET/pricing/v1/accounts/{ref}/catalog

Everything that dealer can buy, at their prices. Same shape as the standard catalog. Note a dealer restricted to certain categories returns fewer products than the standard catalog does. Once per account per day.

{
  "account": "acct_pi7rfqsfpgbsm666fp39",
  "pricedAt": "2026-09-22T14:22:10.000Z",
  "count": 11093,
  "products": [
    { "sku": "XF1640", "mpn": "MGT1640", "name": "16.5VAC 40VA TRANSFORMER", "price": 28.74, "…": "…" }
  ]
}

The fields

skuOur SKU. The identifier to order against
mpnManufacturer's part number. Null where we do not hold one
nameProduct name as it appears on our storefront
brand / brandIdBrand name, and an id that survives a rename
productIdOur product id. Stable across SKU and name changes
categoryIdsCategory ids, most general first. A product can sit in several
price / currencyUSD, excluding tax and shipping
unitOfMeasureAlways each
imageUrlPrimary product image. On about 48% of products
productUrlThe product's page on our storefront
specSheetUrlManufacturer spec sheet. On about 10% of products

Our SKU is not the manufacturer part number. Ours is a house SKU, usually the manufacturer's number behind a brand prefix: DSC-HSM2108 is manufacturer part HSM2108. Do not derive one from the other. There are brands whose real part numbers legitimately begin with what looks like our prefix, and brands whose SKUs bear no relationship to the part number at all. Use mpn, and read null as “we do not know” rather than “same as the SKU”.

mpn is on about 94% of the visible catalog. Every one is a value entered against the product in our ERP or our store, never derived from our own SKU, so null means we genuinely do not hold one. It is not unique in principle: two SKUs can share a part number where we sell the same part in different configurations. categoryIds are ids rather than names; /pricing/v1/categories has the tree. specSheetUrl points at the manufacturer, so it is subject to their URL changes.

unitOfMeasure is always each. Every product we sell is sold as a single item, and multi-packs are their own SKU at their own price rather than a quantity against a base part, which is why there is no conversion factor. The field is published so you do not have to assume one.

Errors

Every error has the same shape:

{ "error": { "code": "no_consent", "message": "Account not available." } }
400Malformed request. Check the message
401Key missing, invalid or revoked
403no_active_account: no dealer of yours has connected and agreed yet. Otherwise: the account exists but is not currently available to you
404Unknown account reference, or a SKU this account cannot buy
429Daily limit reached. See Retry-After
A SKU we don't sell and a SKU this dealer can't buy both return 404. We don't distinguish them, because the difference is a fact about that dealer's agreement.
An account reference that isn't yours returns 404, not 403. You can't tell an unknown reference from someone else's.

Notes on the data

Prices change

Correct at the moment you fetch them. pricedAt on every response says when. Confirm at checkout for anything binding.

Not every product is visible to every dealer

Some accounts are restricted to particular categories. Those are absent, not returned at a different price. Catalog sizes legitimately differ.

SKUs are strings, not identifiers

They contain spaces, slashes and mixed case. Match them exactly as returned; lookup matching is case-insensitive.

Prices are USD

Excludes tax and shipping.

Good practice

Cache the catalog, refresh with lookups

Pull the full catalog once a day, then use single or batch lookups for anything you're about to quote.

Handle 429 by waiting, not retrying

Retry-After is exact. Retrying sooner will not work.

Don't hard-code account references

Read /accountsand key off what comes back, so access changes don't break you.

Store the key somewhere secret

If it leaks, tell us and we'll revoke and re-issue immediately.

Example

KEY="sspa_live_your_key_here"
API="https://api.ssandsi.com/pricing/v1"

# The whole catalog at standard pricing.
curl -s --compressed -H "Authorization: Bearer $KEY" "$API/catalog"

# One price, standard
curl -s -H "Authorization: Bearer $KEY" "$API/price?sku=XF1640"

# Live stock for up to 250 SKUs
curl -s -X POST -H "Authorization: Bearer $KEY"   -H "Content-Type: application/json"   -d '{"skus":["XF1640","DSC-HSM2108"]}'   "$API/stock"

# The category tree
curl -s -H "Authorization: Bearer $KEY" "$API/categories"

# Which dealers can I see?
curl -s -H "Authorization: Bearer $KEY" "$API/accounts"

# Several SKUs at that dealer's pricing
curl -s -X POST -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -d '{"skus":["XF1640","DSC-PG9911B BATT"]}' \
  "$API/accounts/acct_xxx/prices"

# The same catalog at one dealer's pricing
curl -s --compressed -H "Authorization: Bearer $KEY" \
  "$API/accounts/acct_xxx/catalog"

Being listed

We publish a Third Party Integrationspage on our storefront, listing the software our dealers can connect to SS&Si. A dealer who already uses you can switch you on from there themselves, with no setup call and nothing waiting on us. You also get a page of your own at /integrations/your-name, which you are welcome to send prospects to: it carries your logo, your own description and nobody else's.

It is entirely optional. Tell us on the access request form whether you are a public product or something private, and if public, whether you want to be listed. Private and in-house integrations never appear, and a public one only appears if you ask. Either way your access works exactly the same.

Partners on the page are ones we have already approved, so it is a directory of people we work with rather than an open door. Send us a logo, a short description and the link you want it to point at, and we will set it up. Changed your mind later, either way? Tell us.

Support

Contact your SS&Si representative. When reporting a problem, include the endpoint, the account reference, and roughly when it happened. Every request is logged on our side, so that's usually enough to find it.

Call us on 866-364-0030.