Partner API · v1

Bring SourceSheets sourcing data into your own product

One REST call that tells you which suppliers in the SourceSheets network carry a given ASIN — or a whole batch of them — how many, and at what price, with full supplier detail unlocked for any mutual customer who has connected their account.

Overview

The Partner API has one job right now: given an ASIN, or a batch of them, tell you whether SourceSheets' supplier network has a match, and how good a deal it is. Every request is authenticated as your organization using a partner key. Requests can optionally also carry a customer token — a credential one of your mutual customers generates from their own SourceSheets account and hands to you — which unlocks that customer's actual supplier detail instead of an aggregate summary.

Neither side is required to connect for the other to work. You can show "3 suppliers carry this ASIN, from £4.50" to every user of your product using nothing but your partner key. A customer token only upgrades the response for the specific customer who chose to connect one.

Authentication

Two credentials, sent as headers on every request:

HeaderRequiredIdentifies
x-partner-keyYesYour organization. Issued once per key, can be scoped and revoked independently.
x-customer-tokenNoOne specific SourceSheets customer, generated by them and pasted into your product.

Both use the same shape — a public identifier and a secret, joined by a period:

Credential shape
x-partner-key:    pak_5f2c9a1b7e0d4c3f.9b1e7a2d4c6f8091a3b5c7d9e1f30284
x-customer-token: pct_8a41d0f2c6b93e77.1d3f5b7a9c0e2416385a7c9e0b2d4f68

A customer token is only ever valid alongside the partner key of the organization it was generated for — pairing it with a different partner's key doesn't upgrade the response, it's treated the same as not sending one at all.

Rate limits

60 requests per minute, tracked per API key rather than per IP address — several of your customers can sit behind a shared outbound IP without affecting each other's limit, and your sandbox key never eats into your live key's budget. Requests over the limit receive a 429 response. A bulk ASIN Search call counts as one request however many ASINs it carries, so batch where you can. Get in touch if your integration needs more than that — the limit can be lifted entirely for a partner's keys.

Getting credentials

Your partner key

Partner keys are issued directly by the SourceSheets team, not self-serve. Reach out to your SourceSheets contact with the scopes your integration needs — asin-search for the endpoint below, plus supplier-url only if you need links to suppliers' own sites (see Supplier URL scope below; most integrations don't) — and you'll be issued a key shown to you exactly once at creation time.

A customer's token

Each mutual customer generates their own token from Settings → Integrations in their SourceSheets account, then pastes it into your product wherever you store third-party credentials. There's nothing for you to request or provision on your end — it arrives the same way an API key from any other connected service would.

Sandbox mode

Ask for a sandbox key alongside your live one — same auth, same endpoints, same shapes, but every response is fixed mock data. No real supplier data is touched, and x-customer-token isn't validated against a real customer — any non-empty value resolves as "connected." Build and test your whole integration before you have a single real customer connected.

EndpointSandbox behavior
GET /partner/asin/:asinAny valid-shaped ASIN returns the same 3-supplier fixture — base tier without a customer token, enriched tier with any non-empty one. A sandbox key holding the supplier-url scope also gets a fixture supplierUrl on each enriched entry.
POST /partner/asin/bulkEvery ASIN in the batch returns that same fixture, under results, in the order you sent them.
GET /partner/customer/verifyAny non-empty x-customer-token returns { valid: true, connectedAt: "2026-01-15T09:00:00.000Z" }.
Request
curl https://api.sourcesheets.co.uk/v1/partner/asin/B0ANYTHING1 \
  -H "x-partner-key: pak_1a2b3c4d5e6f7890.sandboxsecretexampleexampleexampleexample" \
  -H "x-customer-token: anything-non-empty"
GEThttps://api.sourcesheets.co.uk/v1/partner/asin/:asin

Returns whatever tier of detail your credentials unlock for the given ASIN. The ASIN is matched case-insensitively and normalized to uppercase before lookup.

Path parameterTypeNotes
asinstring10-character alphanumeric ASIN, e.g. B0EXAMPLE1. Anything else returns a 400.

Base tier — partner key only

Real cost, total cost, profit, and ROI for every matching supplier — just with no name, ID, or link attached, so you can show the deal without revealing who it's from.

Request
curl https://api.sourcesheets.co.uk/v1/partner/asin/B0EXAMPLE1 \
  -H "x-partner-key: pak_5f2c9a1b7e0d4c3f.9b1e7a2d4c6f8091a3b5c7d9e1f30284"
200 Response
{
  "asin": "B0EXAMPLE1",
  "amazonTitle": "Wireless Mechanical Keyboard, 87-Key",
  "amazonImageUrl": "https://images.example.com/b0example1.jpg",
  "matches": 3,
  "suppliers": [
    { "cost": 5.1, "profit": 3.2, "roi": 62.7, "isProfitable": true },
    { "cost": 5.85, "profit": 2.9, "roi": 49.6, "isProfitable": true },
    { "cost": 10.75, "profit": 1.4, "roi": 13.0, "isProfitable": true }
  ]
}

No matches found returns 200 with matches: 0, null title/image, and an empty suppliers array — not a 404. Entries are sorted cheapest first.

Enriched tier — with a customer token

Same shape, but each supplier entry gains an identity and a link — scoped to exactly what that customer's own SourceSheets account can see, the same set they'd get in the SourceSheets Viewer.

Request
curl https://api.sourcesheets.co.uk/v1/partner/asin/B0EXAMPLE1 \
  -H "x-partner-key: pak_5f2c9a1b7e0d4c3f.9b1e7a2d4c6f8091a3b5c7d9e1f30284" \
  -H "x-customer-token: pct_8a41d0f2c6b93e77.1d3f5b7a9c0e2416385a7c9e0b2d4f68"
200 Response
{
  "asin": "B0EXAMPLE1",
  "amazonTitle": "Wireless Mechanical Keyboard, 87-Key",
  "amazonImageUrl": "https://images.example.com/b0example1.jpg",
  "matches": 3,
  "suppliers": [
    {
      "supplierId": "3e7a1c9d-2b4f-4e8a-9c1d-7f2b5a8e0d3c",
      "supplierName": "Northgate Wholesale",
      "cost": 5.1,
      "profit": 3.2,
      "roi": 62.7,
      "isProfitable": true,
      "supplierLogoUrl": "https://cdn.sourcesheets.co.uk/supplier/northgate-wholesale.png",
      "supplierViewerUrl": "https://app.sourcesheets.co.uk/viewer/3e7a1c9d-2b4f-4e8a-9c1d-7f2b5a8e0d3c?search=B0EXAMPLE1"
    }
  ]
}
An invalid, expired, or revoked customer token never fails the request. It's ignored and you get the base-tier response back instead of an error — so a customer's lapsed connection degrades gracefully rather than breaking your integration.

Response fields

FieldTypeNotes
asinstringNormalized to uppercase.
amazonTitlestring | nullNull when there are no matches.
amazonImageUrlstring | null
matchesnumberCount of distinct suppliers carrying this ASIN.
suppliersarrayAlways present, cheapest first. Shape depends on tier — see below.

Supplier entry fields

FieldTypeTierNotes
costnumber | nullBothTotal cost for the unit quantity on the sheet.
profitnumber | nullBoth
roinumber | nullBoth
isProfitablebooleanBothTrue only when profit is a known positive number — a quick condition if you don't want to inspect profit yourself.
supplierIdstringEnriched
supplierNamestring | nullEnriched
supplierLogoUrlstring | nullEnrichedThe supplier's logo, served from our CDN.
supplierViewerUrlstringEnrichedDeep link into the SourceSheets Viewer, pre-filtered to this ASIN — not a link to the supplier's own site. The visitor needs to be logged into a SourceSheets account; if not, they're sent to log in first and land back here.
supplierUrlstring | nullEnriched + scopeDirect link to the listing on the supplier's own site. Only present when your key holds the supplier-url scope — absent entirely otherwise, not null. Null when the supplier has no URL on file for this listing.
POSThttps://api.sourcesheets.co.uk/v1/partner/asin/bulk

The same lookup for up to 100 ASINs in one call — same scope, same two tiers, and each entry in results has exactly the shape of a single ASIN Search response. Send a JSON body with an asins array: ASINs are case-insensitive, duplicates are collapsed, and results come back in the order you sent them. An ASIN with no matches is still present with matches: 0, never omitted.

Request
curl -X POST https://api.sourcesheets.co.uk/v1/partner/asin/bulk \
  -H "x-partner-key: pak_5f2c9a1b7e0d4c3f.9b1e7a2d4c6f8091a3b5c7d9e1f30284" \
  -H "x-customer-token: pct_8a41d0f2c6b93e77.1d3f5b7a9c0e2416385a7c9e0b2d4f68" \
  -H "content-type: application/json" \
  -d '{ "asins": ["B0EXAMPLE1", "B0EXAMPLE2"] }'
200 Response
{
  "results": [
    {
      "asin": "B0EXAMPLE1",
      "amazonTitle": "Wireless Mechanical Keyboard, 87-Key",
      "amazonImageUrl": "https://images.example.com/b0example1.jpg",
      "matches": 3,
      "suppliers": [ ... ]
    },
    {
      "asin": "B0EXAMPLE2",
      "amazonTitle": null,
      "amazonImageUrl": null,
      "matches": 0,
      "suppliers": []
    }
  ]
}

The whole batch is validated up front: an empty list, more than 100 entries, or any malformed ASIN fails the request with a 400 naming the problem, rather than silently dropping the bad entries. A bulk call counts as a single request against your rate limit.

Supplier URL scope

Most integrations only ever need supplierViewerUrl, which keeps the customer inside SourceSheets. If yours needs to send them to the supplier's own product page instead, ask for the supplier-url scope on top of asin-search — it's granted case by case rather than by default. A key holding it gets one extra field on every enriched-tier supplier entry:

Enriched supplier entry — key with supplier-url scope
{
  "supplierId": "3e7a1c9d-2b4f-4e8a-9c1d-7f2b5a8e0d3c",
  "supplierName": "Northgate Wholesale",
  "cost": 5.1,
  "profit": 3.2,
  "roi": 62.7,
  "isProfitable": true,
  "supplierLogoUrl": "https://cdn.sourcesheets.co.uk/supplier/northgate-wholesale.png",
  "supplierViewerUrl": "https://app.sourcesheets.co.uk/viewer/3e7a1c9d-2b4f-4e8a-9c1d-7f2b5a8e0d3c?search=B0EXAMPLE1",
  "supplierUrl": "https://www.northgatewholesale.example/products/wireless-mechanical-keyboard-87"
}

Without the scope the field is absent entirely rather than null, so its presence is a reliable signal of what your key can do. It is never included in the base tier — a supplier's URL identifies the supplier, and the base tier never does — and it's null when the supplier has no listing URL on file for that ASIN.

Verify Customer Token

GEThttps://api.sourcesheets.co.uk/v1/partner/customer/verify

Check a customer token the moment it's pasted into your product, instead of waiting for their first ASIN search to discover it was wrong. No scope required — any active partner key can call this. Doesn't count toward the token's own usage stats and reveals no customer identity, just whether it's connected.

Request
curl https://api.sourcesheets.co.uk/v1/partner/customer/verify \
  -H "x-partner-key: pak_5f2c9a1b7e0d4c3f.9b1e7a2d4c6f8091a3b5c7d9e1f30284" \
  -H "x-customer-token: pct_8a41d0f2c6b93e77.1d3f5b7a9c0e2416385a7c9e0b2d4f68"
200 Response
{
  "valid": true,
  "connectedAt": "2026-07-14T09:32:00.000Z"
}

An unrecognized, revoked, or cross-partner token isn't an error — you get 200 with { valid: false, connectedAt: null }. A missing x-customer-token header is the one case that's a 400, since that's a caller mistake rather than a token to check.

Errors

Errors return a JSON body of the shape { message: string }.

StatusMeaning
400Malformed request — an invalid ASIN, a missing required header, or a bulk list that's empty, over 100 entries, or contains a malformed ASIN.
401x-partner-key is missing, unrecognized, revoked, or belongs to a suspended partner.
403Your key doesn't have the scope required for this endpoint.
429Rate limit exceeded — back off and retry.

x-customer-token problems are never in this table — see the callout above, they degrade silently instead.

Versioning

Every route is versioned in the URL — everything here lives under /v1. Breaking changes ship as a new version rather than altering /v1 in place, so an integration written against this page keeps working indefinitely.

Questions? Reach out to your SourceSheets account contact.