API reference

Interchange API reference

Version 1.0.0-draft.1 of the specification, in OpenAPI 3.1.0. This page is generated from the specification when the site is built, so it cannot disagree with it.

Overview

Interchange maps every brand's part number for the same specification to one canonical item with a permanent IX ID, for example IX-CAM-C-200-AL. This API returns those mappings, the specification behind each, who stocks them, and the confidence and review history of every claim.

  • Answers are the same for everyone. Plan level changes how much you may call and which extra features you can use. It never changes which parts match, in what order suppliers appear, or how a correction is handled.
  • We would rather return nothing than a wrong match. When no reviewed mapping exists the API says so (no_verified_match) and does not return the nearest part.
  • Nothing machine-suggested is returned. Only reviewed, published mappings are visible.
  • Safety information is data. Known confusions, such as BSPP against NPT threads, come back as safety_flags. If you show a result to a person, show its safety flags and its usage.disclaimer.
  • You build and run your own integration. Interchange publishes this specification, the documentation and a sandbox. See the support boundary and the API terms.

Authentication

Read endpoints work without a key, at a low per-address rate. Send an API key as a bearer token for higher limits and for the keyed endpoints. Keys beginning ix_test_ reach the sandbox data set only; keys beginning ix_live_ reach reviewed data.

Data sets

Every response carries meta.data_set. While Interchange is being built it is sandbox: illustrative data that must not be relied on for a purchase. It becomes reviewed when human-reviewed data is published.

Versioning and change

The version is in the path (/v1). Within a version we only add: new endpoints, new optional parameters, new response fields and new enum values. Treat unknown fields as ignorable and unknown enum values as "other". Response enumerations are marked x-extensible-enum: they list the values that exist today and may gain members. Only request bodies reject unknown fields. Removing or changing the meaning of anything needs a new version, with the notice period in the support boundary document.

Caching

Responses carry Cache-Control. You may cache a response for up to 12 hours. A correction or withdrawal must reach what you show within 24 hours of the change, so honour the header and, if you subscribe to the change feed or webhooks, apply events as they arrive. The 12-hour limit leaves the other 12 hours for the change to reach your cache and your screens.

Request IDs

Every response has an Interchange-Request-Id header. Quote it when you contact support.

Rate limits

Calls without a key are limited per address. Calls with a key are limited per key, and the limit depends on the plan. A plan changes how much you may call, never what an answer says.

Over the limit for your address or key. Wait for Retry-After seconds. Limits and what remains are in the RateLimit-* headers on every response.

Every response carries the headers below, and a 429 also carries Retry-After. Read the limits from the headers and do not hard-code them: the numbers are not fixed yet, and they will be published here with the sandbox.

Rate-Limit-Limit
Requests allowed in the current window for your address or key.
Rate-Limit-Remaining
Requests left in the current window.
Rate-Limit-Reset
Seconds until the window resets.
Retry-After
Seconds to wait. Sent with a 429.

Servers

  • Production will be at https://api.interchangeparts.com.au. Reviewed data once published, sandbox data before that.
  • Sandbox will be at https://sandbox.api.interchangeparts.com.au. Illustrative data only, safe to build against.

Use the sandbox while you build.

Errors

Errors are RFC 9457 problem details with the content type application/problem+json. Branch on code, which is stable, not on title or detail, which are wording. Treat a code you do not know as an error of its status class.

Error codes
CodeStatusMeaningWhat to do
invalid_request400The request is malformed: a missing or badly formed parameter or body.Fix the request. Retrying the same request will fail the same way.
unknown_attribute400An attribute key is not in the family's vocabulary.Read the vocabulary from the family endpoint and use its keys.
invalid_attribute_value400An attribute value is outside what the family accepts.Use one of the values the family endpoint lists for that attribute.
not_found404No item has this ID.Check the ID. An ID that has never existed is a 404. A withdrawn one is a 410.
withdrawn410The item was withdrawn, usually because its mapping was found to be wrong or unsafe.Stop showing the item. If replaced_by is present, look that item up instead.
unauthorized401A key is required, or the key sent is not valid.Send a valid key as a bearer token. If it was valid before, it may have been revoked.
plan_does_not_include403The key is valid but its plan does not include this endpoint.Do not retry. The plan decides which keyed endpoints you may call, never what an answer says.
rate_limited429Over the limit for your address or key.Wait for the number of seconds in Retry-After, then try again.
method_not_allowed405That address does not accept this HTTP method.Use the method the reference lists for this operation. The Allow header names the ones it accepts.
payload_too_large413The request body is larger than the limit for this operation.Send fewer rows per request and repeat the call for the rest.
internal_error500Something went wrong on our side.Retry with backoff. If it persists, send us the Interchange-Request-Id.

Every error carries request_id, the same value as the Interchange-Request-Id header. Quote it when you write to us.

Items

One canonical item, its equivalents and its suppliers.

Get one item

GET /v1/items/{ix_id} operationId getItem

The specification, attributes and safety flags of one canonical item. A retired item still resolves and points to its successor in replaced_by. A withdrawn item does not resolve: it answers 410 Gone and must no longer be shown.

Works without a key. A key raises the limits.

Parameters

Parameters of Get one item
NameInRequiredTypeDescriptionExample
ix_idpathYesIxIdAn Interchange ID. Treat it as an opaque key: store it and send it back, but never parse it to make decisions. Length 0 to 32. Pattern ^IX(-[A-Z0-9]+)+$IX-CAM-C-200-AL

Responses

200 Success

The item.

Body: ItemResponse

Headers: Interchange-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

Fields of the 200 response
FieldTypeRequiredDescription
metaMetaYes
itemItemYes
usageUsageYes
200 response
{
  "meta": {
    "api_version": "v1",
    "data_set": "sandbox"
  },
  "item": {
    "ix_id": "IX-CAM-C-200-AL",
    "name": "Camlock Type C, 2\" (50 mm), aluminium",
    "family": "camlock",
    "status": "published",
    "attributes": {
      "type": {
        "value": "C",
        "label": "Type",
        "detail": "Female coupler x hose tail"
      },
      "size": {
        "value": 50,
        "unit": "mm",
        "label": "Nominal size"
      },
      "bodyMaterial": {
        "value": "aluminium",
        "label": "Body material"
      },
      "grooveStandard": {
        "value": "A-A-59326",
        "label": "Groove standard"
      }
    },
    "safety_flags": [],
    "reviewed": "2026-10-28",
    "url": "https://interchangeparts.com.au/items/IX-CAM-C-200-AL"
  },
  "usage": {
    "attribution": "Source: Interchange (interchangeparts.com.au)",
    "disclaimer": "Cross-reference only. Check the manufacturer's current specification, rating and media compatibility before use."
  }
}
404 Error

No item has this ID.

Body: Problem (as application/problem+json)

404 response
{
  "type": "https://interchangeparts.com.au/problems/not-found",
  "title": "Item not found",
  "status": 404,
  "code": "not_found",
  "detail": "No item has the ID IX-NOPE-X-000-ZZ.",
  "request_id": "req_7c1f3a"
}
410 Error

The item was withdrawn, usually because its mapping was found to be wrong or unsafe. It is not returned and must no longer be shown. If a corrected item replaces it, replaced_by names it.

Body: Problem (as application/problem+json)

410 response
{
  "type": "https://interchangeparts.com.au/problems/withdrawn",
  "title": "Item withdrawn",
  "status": 410,
  "code": "withdrawn",
  "detail": "This item was withdrawn after a correction.",
  "replaced_by": "IX-CAM-C-200-AL",
  "request_id": "req_7c1f3b"
}
429 Error

Over the limit for your address or key. Wait for Retry-After seconds. Limits and what remains are in the RateLimit-* headers on every response.

Body: Problem (as application/problem+json)

Headers: Retry-After

Code examples

These calls will work when the sandbox opens. Until then they show the shape of a request and the answer you will get. Examples use the values from this operation’s specification.

curl: Get one item
curl -s "https://api.interchangeparts.com.au/v1/items/IX-CAM-C-200-AL"
JavaScript (fetch): Get one item
const response = await fetch("https://api.interchangeparts.com.au/v1/items/IX-CAM-C-200-AL");
const body = await response.json();
console.log(response.status, body);
Python (requests): Get one item
import requests

response = requests.get("https://api.interchangeparts.com.au/v1/items/IX-CAM-C-200-AL")
print(response.status_code, response.json())

Every brand's part mapped to an item

GET /v1/items/{ix_id}/equivalents operationId getEquivalents

Exact equivalents are parts that map to this item. Near matches are different items that fit the same way but differ in a stated attribute, such as material, and are always listed apart from exact equivalents. Equivalents are sorted by brand name, A to Z. They are never ordered by plan or payment.

Works without a key. A key raises the limits.

Parameters

Parameters of Every brand's part mapped to an item
NameInRequiredTypeDescriptionExample
ix_idpathYesIxIdAn Interchange ID. Treat it as an opaque key: store it and send it back, but never parse it to make decisions. Length 0 to 32. Pattern ^IX(-[A-Z0-9]+)+$IX-CAM-C-200-AL
include_nearqueryNobooleanAlso return near matches. Default false.

Responses

200 Success

Equivalents and, if asked for, near matches.

Body: EquivalentsResponse

Headers: Interchange-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

Fields of the 200 response
FieldTypeRequiredDescription
metaMetaYes
ix_idIxIdYesLength 0 to 32. Pattern ^IX(-[A-Z0-9]+)+$
namestringYes
statusstring: matched, no_verified_match, and others may be addedYesno_verified_match when the item exists but has no reviewed equivalents yet.
reasonstringNo
replaced_byIxId or nullNo
equivalentsarray of EquivalentYesSorted by brand, A to Z.
near_matchesarray of NearMatchNoPresent only when include_near is true.
safety_flagsarray of SafetyFlagYes
suppliers_urlstring (uri)No
usageUsageYes
200 response
{
  "meta": {
    "api_version": "v1",
    "data_set": "sandbox"
  },
  "ix_id": "IX-CAM-C-200-AL",
  "name": "Camlock Type C, 2\" (50 mm), aluminium",
  "status": "matched",
  "equivalents": [
    {
      "brand": "Alfagomma",
      "part_number": "CC-200-AL",
      "confidence": "verified",
      "reviewed": "2026-10-28",
      "evidence": "standard"
    },
    {
      "brand": "Dixon",
      "part_number": "ALDC200C",
      "confidence": "verified",
      "reviewed": "2026-10-28",
      "evidence": "standard"
    }
  ],
  "near_matches": [
    {
      "ix_id": "IX-CAM-C-200-SS6",
      "name": "Camlock Type C, 2\" (50 mm), 316 stainless",
      "same_fit": true,
      "differences": [
        {
          "attribute": "bodyMaterial",
          "this_value": "aluminium",
          "other_value": "stainless-316"
        }
      ]
    }
  ],
  "safety_flags": [],
  "suppliers_url": "https://interchangeparts.com.au/items/IX-CAM-C-200-AL#suppliers",
  "usage": {
    "attribution": "Source: Interchange (interchangeparts.com.au)",
    "disclaimer": "Cross-reference only. Check the manufacturer's current specification, rating and media compatibility before use."
  }
}
404 Error

No item has this ID.

Body: Problem (as application/problem+json)

404 response
{
  "type": "https://interchangeparts.com.au/problems/not-found",
  "title": "Item not found",
  "status": 404,
  "code": "not_found",
  "detail": "No item has the ID IX-NOPE-X-000-ZZ.",
  "request_id": "req_7c1f3a"
}
410 Error

The item was withdrawn, usually because its mapping was found to be wrong or unsafe. It is not returned and must no longer be shown. If a corrected item replaces it, replaced_by names it.

Body: Problem (as application/problem+json)

410 response
{
  "type": "https://interchangeparts.com.au/problems/withdrawn",
  "title": "Item withdrawn",
  "status": 410,
  "code": "withdrawn",
  "detail": "This item was withdrawn after a correction.",
  "replaced_by": "IX-CAM-C-200-AL",
  "request_id": "req_7c1f3b"
}
429 Error

Over the limit for your address or key. Wait for Retry-After seconds. Limits and what remains are in the RateLimit-* headers on every response.

Body: Problem (as application/problem+json)

Headers: Retry-After

Code examples

These calls will work when the sandbox opens. Until then they show the shape of a request and the answer you will get. Examples use the values from this operation’s specification.

curl: Every brand's part mapped to an item
curl -s "https://api.interchangeparts.com.au/v1/items/IX-CAM-C-200-AL/equivalents"
JavaScript (fetch): Every brand's part mapped to an item
const response = await fetch("https://api.interchangeparts.com.au/v1/items/IX-CAM-C-200-AL/equivalents");
const body = await response.json();
console.log(response.status, body);
Python (requests): Every brand's part mapped to an item
import requests

response = requests.get("https://api.interchangeparts.com.au/v1/items/IX-CAM-C-200-AL/equivalents")
print(response.status_code, response.json())

Suppliers who list an item

GET /v1/items/{ix_id}/suppliers operationId listSuppliers

Suppliers are always sorted by name, A to Z, and a supplier's plan is never exposed or used for ordering. Stock and price are per branch, so a branch in one state never shows stock held in another. They appear only where the supplier chooses to share them.

Works without a key. A key raises the limits.

Parameters

Parameters of Suppliers who list an item
NameInRequiredTypeDescriptionExample
ix_idpathYesIxIdAn Interchange ID. Treat it as an opaque key: store it and send it back, but never parse it to make decisions. Length 0 to 32. Pattern ^IX(-[A-Z0-9]+)+$IX-CAM-C-200-AL
statequeryNoAuStateRestrict to suppliers with a branch in this state or territory.
limitqueryNointegerPage size. 1 to 25. Default 10.
cursorqueryNostringThe next_cursor of the previous page. Length 0 to 500.

Responses

200 Success

One page of suppliers.

Body: SuppliersResponse

Headers: Interchange-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

Fields of the 200 response
FieldTypeRequiredDescription
metaMetaYes
ix_idIxIdYesLength 0 to 32. Pattern ^IX(-[A-Z0-9]+)+$
suppliersarray of SupplierYesSorted by name, A to Z.
next_cursorstring or nullNo
usageUsageYes
404 Error

No item has this ID.

Body: Problem (as application/problem+json)

404 response
{
  "type": "https://interchangeparts.com.au/problems/not-found",
  "title": "Item not found",
  "status": 404,
  "code": "not_found",
  "detail": "No item has the ID IX-NOPE-X-000-ZZ.",
  "request_id": "req_7c1f3a"
}
410 Error

The item was withdrawn, usually because its mapping was found to be wrong or unsafe. It is not returned and must no longer be shown. If a corrected item replaces it, replaced_by names it.

Body: Problem (as application/problem+json)

410 response
{
  "type": "https://interchangeparts.com.au/problems/withdrawn",
  "title": "Item withdrawn",
  "status": 410,
  "code": "withdrawn",
  "detail": "This item was withdrawn after a correction.",
  "replaced_by": "IX-CAM-C-200-AL",
  "request_id": "req_7c1f3b"
}
429 Error

Over the limit for your address or key. Wait for Retry-After seconds. Limits and what remains are in the RateLimit-* headers on every response.

Body: Problem (as application/problem+json)

Headers: Retry-After

Code examples

These calls will work when the sandbox opens. Until then they show the shape of a request and the answer you will get. Examples use the values from this operation’s specification.

curl: Suppliers who list an item
curl -s "https://api.interchangeparts.com.au/v1/items/IX-CAM-C-200-AL/suppliers"
JavaScript (fetch): Suppliers who list an item
const response = await fetch("https://api.interchangeparts.com.au/v1/items/IX-CAM-C-200-AL/suppliers");
const body = await response.json();
console.log(response.status, body);
Python (requests): Suppliers who list an item
import requests

response = requests.get("https://api.interchangeparts.com.au/v1/items/IX-CAM-C-200-AL/suppliers")
print(response.status_code, response.json())

Matching

Match what you know about a part, in bulk or one at a time.

Match what you know about a part

POST /v1/match/attributes operationId matchAttributes

For a client that has read a fitting, for example from a photo, and wants the item it could be. Send the attributes you are sure of. The response never fills a gap by guessing: where the answer depends on an attribute you did not send, status is needs_confirmation and confirm holds the questions to put to the person. Ask them and call again with the answers.

Works without a key. A key raises the limits.

Request body

application/json, MatchAttributesRequest, required

Fields of the Match what you know about a part request body
FieldTypeRequiredDescription
familystringYesPattern ^[a-z][a-z0-9-]{1,30}$
attributesobject of string or number or booleanYesOnly what you are sure of. Keys and values are listed at /v1/families/{family}.
limitintegerNo1 to 25. Default 10.
Request body
{
  "family": "camlock",
  "attributes": {
    "type": "C",
    "size": 50,
    "bodyMaterial": "aluminium"
  }
}

Responses

200 Success

Candidates, questions, or an explicit no verified match.

Body: MatchAttributesResponse

Headers: Interchange-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

Fields of the 200 response
FieldTypeRequiredDescription
metaMetaYes
statusstring: matched, needs_confirmation, no_verified_match, and others may be addedYesmatched: one item fits everything sent and nothing else is needed. needs_confirmation: the answer depends on attributes in confirm. no_verified_match: nothing reviewed fits. reason says why.
reasonstringNo
candidatesarray of CandidateYes
confirmarray of QuestionYes
safety_flagsarray of SafetyFlagYes
usageUsageYes
200 response
{
  "meta": {
    "api_version": "v1",
    "data_set": "sandbox"
  },
  "status": "needs_confirmation",
  "candidates": [
    {
      "ix_id": "IX-CAM-C-200-AL",
      "name": "Camlock Type C, 2\" (50 mm), aluminium",
      "safety_flags": [],
      "depends_on": [
        "grooveStandard",
        "endSize"
      ]
    }
  ],
  "confirm": [
    {
      "key": "grooveStandard",
      "question": "Is the groove standard (A-A-59326) or 68 degree?",
      "options": [
        {
          "value": "A-A-59326",
          "label": "Standard"
        },
        {
          "value": "68deg",
          "label": "68 degree"
        }
      ]
    },
    {
      "key": "endSize",
      "question": "What is the nominal size of the other end, the hose tail for a Type C?",
      "options": [
        {
          "value": 50,
          "label": "50 mm"
        }
      ]
    }
  ],
  "safety_flags": [],
  "usage": {
    "attribution": "Source: Interchange (interchangeparts.com.au)",
    "disclaimer": "Cross-reference only. Check the manufacturer's current specification, rating and media compatibility before use."
  }
}
400 Error

The request is malformed or a value is outside what the family accepts.

Body: Problem (as application/problem+json)

429 Error

Over the limit for your address or key. Wait for Retry-After seconds. Limits and what remains are in the RateLimit-* headers on every response.

Body: Problem (as application/problem+json)

Headers: Retry-After

Code examples

These calls will work when the sandbox opens. Until then they show the shape of a request and the answer you will get. Examples use the values from this operation’s specification.

curl: Match what you know about a part
curl -s -X POST "https://api.interchangeparts.com.au/v1/match/attributes" \
  -H "Content-Type: application/json" \
  -d '{
  "family": "camlock",
  "attributes": {
    "type": "C",
    "size": 50,
    "bodyMaterial": "aluminium"
  }
}'
JavaScript (fetch): Match what you know about a part
const response = await fetch("https://api.interchangeparts.com.au/v1/match/attributes", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "family": "camlock",
    "attributes": {
      "type": "C",
      "size": 50,
      "bodyMaterial": "aluminium"
    }
  }),
});
const body = await response.json();
console.log(response.status, body);
Python (requests): Match what you know about a part
import requests

response = requests.post(
    "https://api.interchangeparts.com.au/v1/match/attributes",
    json={
        "family": "camlock",
        "attributes": {
            "type": "C",
            "size": 50,
            "bodyMaterial": "aluminium",
        },
    },
)
print(response.status_code, response.json())

Match a list of your part numbers

POST /v1/match operationId matchBatch

Maps up to 1,000 of your own part numbers or descriptions to IX IDs in one call. Needs an API key on a plan that includes batch matching. Each row has its own status, so one unmatched row does not fail the batch. Your rows are used to answer this request and are not added to the public catalogue.

Needs an API key on a plan that includes it.

Request body

application/json, MatchBatchRequest, required

Fields of the Match a list of your part numbers request body
FieldTypeRequiredDescription
rowsarray of objectYes1 to 1000 items.
Request body: Two rows from a price list
{
  "rows": [
    {
      "ref": "row-1",
      "text": "ALDC200C"
    },
    {
      "ref": "row-2",
      "text": "2 inch type C aluminium camlock",
      "brand": "Dixon"
    }
  ]
}

Responses

200 Success

One result per row, in the order sent.

Body: MatchBatchResponse

Headers: Interchange-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

Fields of the 200 response
FieldTypeRequiredDescription
metaMetaYes
resultsarray of objectYes
usageUsageYes
400 Error

The request is malformed or a value is outside what the family accepts.

Body: Problem (as application/problem+json)

401 Error

A key is required, or the key sent is not valid.

Body: Problem (as application/problem+json)

403 Error

The key is valid but its plan does not include this endpoint.

Body: Problem (as application/problem+json)

429 Error

Over the limit for your address or key. Wait for Retry-After seconds. Limits and what remains are in the RateLimit-* headers on every response.

Body: Problem (as application/problem+json)

Headers: Retry-After

Code examples

These calls will work when the sandbox opens. Until then they show the shape of a request and the answer you will get. Examples use the values from this operation’s specification.

curl: Match a list of your part numbers
curl -s -X POST "https://api.interchangeparts.com.au/v1/match" \
  -H "Authorization: Bearer $INTERCHANGE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "rows": [
    {
      "ref": "row-1",
      "text": "ALDC200C"
    },
    {
      "ref": "row-2",
      "text": "2 inch type C aluminium camlock",
      "brand": "Dixon"
    }
  ]
}'
JavaScript (fetch): Match a list of your part numbers
const response = await fetch("https://api.interchangeparts.com.au/v1/match", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.INTERCHANGE_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "rows": [
      {
        "ref": "row-1",
        "text": "ALDC200C"
      },
      {
        "ref": "row-2",
        "text": "2 inch type C aluminium camlock",
        "brand": "Dixon"
      }
    ]
  }),
});
const body = await response.json();
console.log(response.status, body);
Python (requests): Match a list of your part numbers
import os
import requests

response = requests.post(
    "https://api.interchangeparts.com.au/v1/match",
    headers={"Authorization": f"Bearer {os.environ['INTERCHANGE_API_KEY']}"},
    json={
        "rows": [
            {
                "ref": "row-1",
                "text": "ALDC200C",
            },
            {
                "ref": "row-2",
                "text": "2 inch type C aluminium camlock",
                "brand": "Dixon",
            },
        ],
    },
)
print(response.status_code, response.json())

Reference

The vocabulary the data uses.

Product families

GET /v1/families operationId listFamilies

The families that exist. Use the keys with getFamily and matchAttributes.

Works without a key. A key raises the limits.

Responses

200 Success

Families.

Body: FamiliesResponse

Headers: Interchange-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

Fields of the 200 response
FieldTypeRequiredDescription
metaMetaYes
usageUsageYes
familiesarray of objectYes
429 Error

Over the limit for your address or key. Wait for Retry-After seconds. Limits and what remains are in the RateLimit-* headers on every response.

Body: Problem (as application/problem+json)

Headers: Retry-After

Code examples

These calls will work when the sandbox opens. Until then they show the shape of a request and the answer you will get. Examples use the values from this operation’s specification.

curl: Product families
curl -s "https://api.interchangeparts.com.au/v1/families"
JavaScript (fetch): Product families
const response = await fetch("https://api.interchangeparts.com.au/v1/families");
const body = await response.json();
console.log(response.status, body);
Python (requests): Product families
import requests

response = requests.get("https://api.interchangeparts.com.au/v1/families")
print(response.status_code, response.json())

A family's attribute vocabulary

GET /v1/families/{family} operationId getFamily

The attributes of a family and the values each accepts, with the role each plays in matching. Use these terms and values when you call matchAttributes.

Works without a key. A key raises the limits.

Parameters

Parameters of A family's attribute vocabulary
NameInRequiredTypeDescriptionExample
familypathYesstringPattern ^[a-z][a-z0-9-]{1,30}$camlock

Responses

200 Success

The family.

Body: FamilyResponse

Headers: Interchange-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

Fields of the 200 response
FieldTypeRequiredDescription
metaMetaYes
usageUsageYes
familyFamilyYes
404 Error

No item has this ID.

Body: Problem (as application/problem+json)

404 response
{
  "type": "https://interchangeparts.com.au/problems/not-found",
  "title": "Item not found",
  "status": 404,
  "code": "not_found",
  "detail": "No item has the ID IX-NOPE-X-000-ZZ.",
  "request_id": "req_7c1f3a"
}
429 Error

Over the limit for your address or key. Wait for Retry-After seconds. Limits and what remains are in the RateLimit-* headers on every response.

Body: Problem (as application/problem+json)

Headers: Retry-After

Code examples

These calls will work when the sandbox opens. Until then they show the shape of a request and the answer you will get. Examples use the values from this operation’s specification.

curl: A family's attribute vocabulary
curl -s "https://api.interchangeparts.com.au/v1/families/camlock"
JavaScript (fetch): A family's attribute vocabulary
const response = await fetch("https://api.interchangeparts.com.au/v1/families/camlock");
const body = await response.json();
console.log(response.status, body);
Python (requests): A family's attribute vocabulary
import requests

response = requests.get("https://api.interchangeparts.com.au/v1/families/camlock")
print(response.status_code, response.json())

Changes

Keep your copy current.

What has changed since you last looked

GET /v1/changes operationId listChanges

Items that were published, corrected, retired or withdrawn after a point in time. Use this, or webhooks, to keep a copy current. Needs an API key.

First call: send since. Every response has a next_cursor. Keep it and send it as cursor on the next call, without since. When changes is empty you have caught up, and the cursor is still the place to resume from.

Needs an API key on a plan that includes it.

Parameters

Parameters of What has changed since you last looked
NameInRequiredTypeDescriptionExample
sincequeryNostring (date-time)Return changes after this time. Send it on your first call. Afterwards send cursor instead. One of the two is required.2026-10-28T00:00:00Z
limitqueryNointegerPage size. 1 to 25. Default 10.
cursorqueryNostringThe next_cursor of the previous page. Length 0 to 500.

Responses

200 Success

One page of changes, oldest first.

Body: ChangesResponse

Headers: Interchange-Request-Id, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset

Fields of the 200 response
FieldTypeRequiredDescription
metaMetaYes
usageUsageYes
changesarray of ChangeEventYes
next_cursorstringYesAlways present. Keep it and send it as cursor on your next call, instead of since.
400 Error

The request is malformed or a value is outside what the family accepts.

Body: Problem (as application/problem+json)

401 Error

A key is required, or the key sent is not valid.

Body: Problem (as application/problem+json)

403 Error

The key is valid but its plan does not include this endpoint.

Body: Problem (as application/problem+json)

429 Error

Over the limit for your address or key. Wait for Retry-After seconds. Limits and what remains are in the RateLimit-* headers on every response.

Body: Problem (as application/problem+json)

Headers: Retry-After

Code examples

These calls will work when the sandbox opens. Until then they show the shape of a request and the answer you will get. Examples use the values from this operation’s specification.

curl: What has changed since you last looked
curl -s "https://api.interchangeparts.com.au/v1/changes?since=2026-10-28T00%3A00%3A00Z" \
  -H "Authorization: Bearer $INTERCHANGE_API_KEY"
JavaScript (fetch): What has changed since you last looked
const response = await fetch("https://api.interchangeparts.com.au/v1/changes?since=2026-10-28T00%3A00%3A00Z", {
  headers: {
    Authorization: `Bearer ${process.env.INTERCHANGE_API_KEY}`,
  },
});
const body = await response.json();
console.log(response.status, body);
Python (requests): What has changed since you last looked
import os
import requests

response = requests.get(
    "https://api.interchangeparts.com.au/v1/changes",
    params={
        "since": "2026-10-28T00:00:00Z",
    },
    headers={"Authorization": f"Bearer {os.environ['INTERCHANGE_API_KEY']}"},
)
print(response.status_code, response.json())

Webhooks

A webhook tells you an item changed, so you do not have to poll. It is sent to an endpoint you register, for a key on a plan that includes webhooks. Verify the signature on every delivery.

item.updated: An item you follow changed

POST to your endpoint

Sent to the endpoint you register for a key on a plan that includes webhooks.

Verify every delivery before you trust it. The Interchange-Signature header looks like t=1761874000,v1=3f9a…. t is the Unix time we sent it. v1 is the hex HMAC-SHA256, with your signing secret, of the string t, a full stop, and the raw request body. Recompute it over the exact bytes you received, compare in constant time, and reject the delivery if it does not match or if t is more than 5 minutes from your clock. The timestamp is inside the signature, so a captured delivery cannot be replayed later.

Respond with any 2xx within 10 seconds. Failed deliveries are retried with backoff for 24 hours, and a retry carries a fresh t and signature, so handlers must be idempotent: use event_id.

Headers

Parameters of the item.updated webhook
NameInRequiredTypeDescriptionExample
Interchange-SignatureheaderYesstringt=<unix seconds>,v1=<hex HMAC-SHA256 of "<t>.<raw body>">t=1761874000,v1=3f9a0c5e8b1d4a7f92c6e03d5b8a1f4c7e9d2b6a0c3f5e8d1b4a7c9e2f6d0a3b

Body

ChangeEvent

Fields of the item.updated body
FieldTypeRequiredDescription
event_idstringYesUnique and stable. Use it to ignore a delivery you have already handled.
typestring: item.published, item.updated, item.retired, item.withdrawn, and others may be addedYes
occurred_atstring (date-time)Yes
ix_idIxIdYesLength 0 to 32. Pattern ^IX(-[A-Z0-9]+)+$
replaced_byIxId or nullNo
reasonstring: new_item, correction, specification_change, safety, other, and others may be addedYes
urlstring (uri)No
item.updated body
{
  "event_id": "evt_01J9ZQ3K8M",
  "type": "item.withdrawn",
  "occurred_at": "2026-10-30T02:14:00Z",
  "ix_id": "IX-CAM-C-200-AL-RETIRED",
  "replaced_by": "IX-CAM-C-200-AL",
  "reason": "correction",
  "url": "https://interchangeparts.com.au/items/IX-CAM-C-200-AL"
}

Your response

Respond with any 2xx within 10 seconds.

Verify a delivery

The Interchange-Signature header carries a timestamp and a signature, such as t=1761874000,v1=3f9a…. The timestamp is part of what is signed, so a captured delivery cannot be replayed later. For each delivery:

  1. Read the raw request body as bytes, before any JSON parsing.
  2. Split the header into t and v1.
  3. Compute the hex HMAC-SHA256, with your signing secret, of t, a full stop, then the raw body.
  4. Compare it with v1 in constant time.
  5. Reject the delivery if it does not match, or if t is more than 5 minutes from your clock.
  6. Only then act on it, and use event_id to ignore a delivery you have already handled: a retry carries a fresh t and signature, so the same event can arrive more than once.
JavaScript: verify a webhook signature
import { createHmac, timingSafeEqual } from "node:crypto";

const TOLERANCE_SECONDS = 5 * 60;

// rawBody is the exact bytes received, as a Buffer or string. Do not parse and re-serialise it first.
export function verifyInterchangeSignature(rawBody, header, secret, nowSeconds = Math.floor(Date.now() / 1000)) {
  const parts = {};
  for (const piece of header.split(",")) {
    const i = piece.indexOf("=");
    if (i > 0) parts[piece.slice(0, i).trim()] = piece.slice(i + 1).trim();
  }
  if (!/^\d+$/.test(parts.t ?? "") || !parts.v1) return false;
  if (Math.abs(nowSeconds - Number(parts.t)) > TOLERANCE_SECONDS) return false;

  const expected = createHmac("sha256", secret).update(parts.t + ".").update(rawBody).digest();
  const given = Buffer.from(parts.v1, "hex");
  return given.length === expected.length && timingSafeEqual(given, expected);
}
Python: verify a webhook signature
import hashlib
import hmac
import time

TOLERANCE_SECONDS = 5 * 60


def verify_interchange_signature(raw_body: bytes, header: str, secret: str, now=None) -> bool:
    # raw_body is the exact bytes received. Do not parse and re-serialise it first.
    parts = {}
    for piece in header.split(","):
        key, sep, value = piece.partition("=")
        if sep:
            parts[key.strip()] = value.strip()
    t, v1 = parts.get("t", ""), parts.get("v1", "")
    if not t.isdigit() or not v1:
        return False
    if abs((time.time() if now is None else now) - int(t)) > TOLERANCE_SECONDS:
        return False

    expected = hmac.new(secret.encode(), t.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, v1)

Schemas

Every object in a request or a response. Field types link to the schema they refer to.

IxId

Interchange ID. Upper-case letters, digits and hyphens, at most 32 characters. Permanent and never reused.

Type: string Length 0 to 32. Pattern ^IX(-[A-Z0-9]+)+$

Meta

Fields of Meta
FieldTypeRequiredDescription
api_versionstringYes
data_setstring: sandbox, reviewed, and others may be addedYessandbox is illustrative and must not be relied on. reviewed has been checked by a person.

Usage

Carried on every answer so that it survives being passed along.

Fields of Usage
FieldTypeRequiredDescription
attributionstringYes
disclaimerstringYes

Confidence

verified: reviewed by Interchange against a standard or full specification. supplier_confirmed: confirmed by the supplier in its match report. under_review: a correction has been reported and the mapping is being checked. Machine-suggested mappings are never returned.

Type: string: verified, supplier_confirmed, under_review, and others may be added

Evidence

The kind of evidence behind a mapping, strongest first.

Type: string: standard, specification, supplier_data, manufacturer_table, and others may be added

AuState

Type: string: ACT, NSW, NT, QLD, SA, TAS, VIC, WA

SafetyFlag

Fields of SafetyFlag
FieldTypeRequiredDescription
codestringYesStable machine-readable code. New codes may be added.
severitystring: info, caution, warning, and others may be addedYes
messagestringYesPlain-language text you can show to a person as written.
attributesarray of stringNoThe attributes the flag concerns.
relatedarray of IxIdNoOther items the flag refers to, for example the part it is commonly confused with.

AttributeValue

Fields of AttributeValue
FieldTypeRequiredDescription
valuestring or number or booleanYes
unitstringNo
labelstringYes
detailstringNo

Item

Fields of Item
FieldTypeRequiredDescription
ix_idIxIdYesLength 0 to 32. Pattern ^IX(-[A-Z0-9]+)+$
namestringYes
familystringYes
statusstring: published, retired, and others may be addedYesA retired item was replaced after a correction. See replaced_by.
replaced_byIxId or nullNo
attributesobject of AttributeValueYesAttributes by key. The vocabulary for a family is at /v1/families/{family}.
safety_flagsarray of SafetyFlagYes
reviewedstring (date)YesWhen a person last reviewed this item.
urlstring (uri)YesThe item's permanent page. Link to it when you show a result.

ItemResponse

Fields of ItemResponse
FieldTypeRequiredDescription
metaMetaYes
itemItemYes
usageUsageYes

SearchResult

Fields of SearchResult
FieldTypeRequiredDescription
ix_idIxIdYesLength 0 to 32. Pattern ^IX(-[A-Z0-9]+)+$
namestringYes
identified_bystring: exact_code, pattern, text, and others may be addedYesHow the query led here. exact_code means a mapped part number matched. text is the weakest.
matched_onobjectNoThe mapped part that matched, when identified_by is exact_code.
confidenceConfidenceYes
safety_flagsarray of SafetyFlagYesKnown hazards for this item. Show them wherever a person sees this result.

SearchResponse

Fields of SearchResponse
FieldTypeRequiredDescription
metaMetaYes
querystringYes
statusstring: matched, no_verified_match, and others may be addedYes
reasonstringNoPresent when status is no_verified_match.
resultsarray of SearchResultYes
usageUsageYes

Equivalent

Fields of Equivalent
FieldTypeRequiredDescription
brandstringYes
part_numberstringYes
confidenceConfidenceYes
reviewedstring (date)Yes
evidenceEvidenceYes

AttributeDifference

Fields of AttributeDifference
FieldTypeRequiredDescription
attributestringYes
this_valuestring or number or booleanYes
other_valuestring or number or booleanYes

NearMatch

Fields of NearMatch
FieldTypeRequiredDescription
ix_idIxIdYesLength 0 to 32. Pattern ^IX(-[A-Z0-9]+)+$
namestringYes
same_fitbooleanYesTrue when the two parts connect the same way and differ only in a non-fit attribute such as material.
differencesarray of AttributeDifferenceYes

EquivalentsResponse

Fields of EquivalentsResponse
FieldTypeRequiredDescription
metaMetaYes
ix_idIxIdYesLength 0 to 32. Pattern ^IX(-[A-Z0-9]+)+$
namestringYes
statusstring: matched, no_verified_match, and others may be addedYesno_verified_match when the item exists but has no reviewed equivalents yet.
reasonstringNo
replaced_byIxId or nullNo
equivalentsarray of EquivalentYesSorted by brand, A to Z.
near_matchesarray of NearMatchNoPresent only when include_near is true.
safety_flagsarray of SafetyFlagYes
suppliers_urlstring (uri)No
usageUsageYes

Money

Fields of Money
FieldTypeRequiredDescription
amountstringYesDecimal string, to avoid rounding. Pattern ^\d+(\.\d{1,2})?$
currencystring: AUD, and others may be addedYes
ex_gstboolean, always trueYes

Stock

Fields of Stock
FieldTypeRequiredDescription
statusstring: in_stock, low, out_of_stock, unknown, and others may be addedYes
quantityintegerNo0 to any.
priceMoneyNo
as_ofstring (date-time)Yes

Supplier

Fields of Supplier
FieldTypeRequiredDescription
idstringYes
namestringYes
profile_urlstring (uri)Yes
quote_urlstring (uri)NoWhere a person asks this supplier for a quote. Link to it when you show a result.
branchesarray of objectYes

SuppliersResponse

Fields of SuppliersResponse
FieldTypeRequiredDescription
metaMetaYes
ix_idIxIdYesLength 0 to 32. Pattern ^IX(-[A-Z0-9]+)+$
suppliersarray of SupplierYesSorted by name, A to Z.
next_cursorstring or nullNo
usageUsageYes

MatchAttributesRequest

Fields of MatchAttributesRequest
FieldTypeRequiredDescription
familystringYesPattern ^[a-z][a-z0-9-]{1,30}$
attributesobject of string or number or booleanYesOnly what you are sure of. Keys and values are listed at /v1/families/{family}.
limitintegerNo1 to 25. Default 10.

Question

Fields of Question
FieldTypeRequiredDescription
keystringYesThe attribute to send back with the answer.
questionstringYesPlain-language question to put to the person.
optionsarray of objectNo

Candidate

Fields of Candidate
FieldTypeRequiredDescription
ix_idIxIdYesLength 0 to 32. Pattern ^IX(-[A-Z0-9]+)+$
namestringYes
safety_flagsarray of SafetyFlagYesKnown hazards for this item. Show them wherever a person sees this candidate.
depends_onarray of stringNoAttributes that must be confirmed before this candidate can be treated as the answer.

MatchAttributesResponse

Fields of MatchAttributesResponse
FieldTypeRequiredDescription
metaMetaYes
statusstring: matched, needs_confirmation, no_verified_match, and others may be addedYesmatched: one item fits everything sent and nothing else is needed. needs_confirmation: the answer depends on attributes in confirm. no_verified_match: nothing reviewed fits. reason says why.
reasonstringNo
candidatesarray of CandidateYes
confirmarray of QuestionYes
safety_flagsarray of SafetyFlagYes
usageUsageYes

MatchBatchRequest

Fields of MatchBatchRequest
FieldTypeRequiredDescription
rowsarray of objectYes1 to 1000 items.

MatchBatchResponse

Fields of MatchBatchResponse
FieldTypeRequiredDescription
metaMetaYes
resultsarray of objectYes
usageUsageYes

AttributeDef

Fields of AttributeDef
FieldTypeRequiredDescription
keystringYes
labelstringYes
rolestring: identity, variant, info, and others may be addedYesidentity: must match exactly for two parts to be the same item. variant: splits variants within one item and can be filtered on. info: shown only, never matched.
requiredbooleanYes
typestring: enum, number, boolean, text, and others may be addedYes
valuesarray of stringNoAccepted values, when type is enum.
unitstringNo

Family

Fields of Family
FieldTypeRequiredDescription
keystringYes
labelstringYes
attributesarray of AttributeDefNo

FamiliesResponse

Fields of FamiliesResponse
FieldTypeRequiredDescription
metaMetaYes
usageUsageYes
familiesarray of objectYes

FamilyResponse

Fields of FamilyResponse
FieldTypeRequiredDescription
metaMetaYes
usageUsageYes
familyFamilyYes

ChangeEvent

Fields of ChangeEvent
FieldTypeRequiredDescription
event_idstringYesUnique and stable. Use it to ignore a delivery you have already handled.
typestring: item.published, item.updated, item.retired, item.withdrawn, and others may be addedYes
occurred_atstring (date-time)Yes
ix_idIxIdYesLength 0 to 32. Pattern ^IX(-[A-Z0-9]+)+$
replaced_byIxId or nullNo
reasonstring: new_item, correction, specification_change, safety, other, and others may be addedYes
urlstring (uri)No

ChangesResponse

Fields of ChangesResponse
FieldTypeRequiredDescription
metaMetaYes
usageUsageYes
changesarray of ChangeEventYes
next_cursorstringYesAlways present. Keep it and send it as cursor on your next call, instead of since.

Problem

An error, as RFC 9457 problem details.

Fields of Problem
FieldTypeRequiredDescription
typestring (uri)Yes
titlestringYes
statusintegerYes
detailstringNo
codestring: invalid_request, unknown_attribute, invalid_attribute_value, not_found, withdrawn, unauthorized, plan_does_not_include, rate_limited, method_not_allowed, payload_too_large, internal_error, and others may be addedYesStable machine-readable code.
request_idstringNo
replaced_byIxIdNoLength 0 to 32. Pattern ^IX(-[A-Z0-9]+)+$
errorsarray of objectNo