Part search, matching and ordering with popular suppliers
Project description
SupplierScout
SupplierScout is an InvenTree plugin that automatically finds, matches, and imports supplier parts for your inventory. Given any purchaseable part in InvenTree, it derives a search query from the part's name, parameters, IPN, MPN, and category, then searches configured supplier APIs, ranks the results, and lets you add or update supplier parts and price breaks with one click.
Features
- Automatic query derivation — builds a supplier search query from the part's name, Internal Part Number (IPN), Manufacturer Part Number (MPN), parameters, and category hierarchy, with normalisation for passive component values (capacitance, resistance, EIA codes, engineering shorthand).
- Candidate ranking — scores results using a configurable mix of text-match similarity, stock availability, and unit price; supports balanced, availability-first, and price-first strategies.
- One-click import — select one or more candidates and add or update supplier parts, manufacturer parts, and price breaks directly from the part detail page.
- Scheduled resync — periodically refreshes existing supplier-part metadata and price breaks in the background, with per-supplier interval and batch-size controls.
- Response caching — caches API responses to reduce quota usage and improve responsiveness.
- API usage tracking — per-supplier request counters and daily-limit enforcement with a dashboard widget showing live metrics.
- Per-user supplier credentials — users can store their own supplier credentials (API key or OAuth2 client credentials), overriding global settings for their own searches.
- Token debug endpoint — inspect exactly which tokens were extracted from a part and how the final search query was constructed.
Supported Suppliers
| Supplier | Search | Scheduled Resync | Notes |
|---|---|---|---|
| DigiKey | ✅ | ✅ | Uses DigiKey OAuth2 client credentials (client_id + client_secret) for authenticated API access; response caching |
| Mouser Electronics | ✅ | ✅ | Part-number and keyword search; response caching; per-user API keys |
Additional suppliers can be added by implementing a BaseSupplierAdapter subclass.
Installation
Via the InvenTree Plugin Manager (recommended)
- Open InvenTree → Settings → Plugins.
- Click Install Plugin.
- Enter the package name:
inventree-supplier-scout - Click Install and then Activate.
Via the Command Line
pip install inventree-supplier-scout
After installation, restart InvenTree and activate the plugin in Settings → Plugins.
From Source
git clone https://github.com/getpwnam/inventree-supplier-scout.git
cd inventree-supplier-scout
pip install -e .
Initial Setup
After activating the plugin, you must configure at least one supplier before you can search:
- Open Settings → Plugins and click on Supplier Scout → Plugin Settings.
- Set either DigiKey Supplier ID (
DIGIKEY_PK) or Mouser Supplier ID (MOUSER_PK) to the primary key of your supplier company record in InvenTree. - Set credentials for your chosen supplier:
- DigiKey:
DIGIKEY_CLIENT_IDandDIGIKEY_CLIENT_SECRET - Mouser:
MOUSER_APIKEY_SEARCH
- DigiKey:
- Save. The Supplier Match action will now appear on every purchaseable part. If you only set API credentials without a supplier company ID, the action will stay hidden because the plugin has not been registered against a supplier record yet.
Usage
Searching and Matching Parts
- Navigate to any purchaseable part in InvenTree.
- Click the Supplier Match action button (🔍) that appears in the primary actions bar.
- A panel opens showing:
- The auto-derived search query (editable).
- A supplier selector.
- Optional quantity range controls for price selection (Min Qty / Max Qty).
- Click Find Matches. SupplierScout queries the supplier API and shows ranked candidates with part number, description, availability, and price.
- Select one or more candidates using the checkboxes.
- Click Add Selected to create or update supplier parts and import price breaks.
Dashboard Widget
A Supplier Scout Metrics card appears on the InvenTree dashboard (if at least one supplier is registered). It shows, for each configured supplier:
- Total and successful query counts.
- API usage (daily calls remaining, reset time).
- Mouser cache status (enabled, file count, size, TTL).
API Endpoints
The plugin exposes several JSON endpoints under the plugin base URL (/plugin/supplierscout/):
| Endpoint | Method | Permission | Description |
|---|---|---|---|
searchcandidates |
POST | Part write | Search one or all configured suppliers for ranked candidate matches |
applycandidates |
POST | Part write | Create or update supplier parts and refresh part pricing |
runresync |
POST | Part write or Admin | Run or queue a manual supplier resync |
clearcache |
POST | Admin | Clear response caches for one supplier or all suppliers |
ratelimitstatus |
GET or POST | Part write | Return current API usage and daily quota status |
dashboardmetrics |
GET | None | Return dashboard diagnostics for registered suppliers |
tokendebug |
GET or POST | Part write | Inspect token extraction and query planning for a part |
Notes:
- Every endpoint also accepts a
.jsonsuffix, for example/plugin/supplierscout/runresync.json. - Browser requests must still satisfy the normal InvenTree session and CSRF requirements.
Part writemeans the user must have add, change, or delete permission for parts.Adminmeans InvenTree staff or superuser access.
Endpoint Details
POST /plugin/supplierscout/searchcandidates
Search supplier APIs for a part and return ranked candidates.
Request body:
{
"pk": 123,
"supplier": 7,
"query": "10k 0603 resistor",
"top_n": 10,
"min_qty": 1,
"max_qty": 100
}
Request fields:
pkrequired: InvenTree part primary key.supplieroptional: supplier company primary key. Omit it, set it to"",0,"all", or"*"to search all configured suppliers.queryoptional: explicit search string. If omitted or empty, Supplier Scout builds a query from part metadata.top_noptional: maximum ranked results returned after scoring. Defaults to the user setting or10.min_qtyoptional: lower quantity bound used when selecting price breaks.max_qtyoptional: preferred upper quantity bound used when selecting price breaks.
Response highlights:
message,query,count, andcandidates.- Each candidate includes supplier metadata, rank score, and whether it already matches an existing supplier part.
debugincludes token sources, semantic hints, numeric constraints, supplier attempts, and supplier failures.
Example success response:
{
"message": "OK",
"query": "10k 0603 resistor",
"count": 2,
"candidates": [
{
"supplier_part_number": "RC0603FR-0710KL",
"manufacturer_part_number": "RC0603FR-0710KL",
"_supplier_pk": 7,
"_supplier_key": "mouser",
"_supplier_name": "Mouser",
"existing_supplier_part": true,
"existing_supplier_part_pk": 456,
"action": "update"
}
],
"debug": {
"supplier_failures": [],
"supplier_attempts": []
}
}
POST /plugin/supplierscout/applycandidates
Create or update supplier parts from candidate payloads returned by searchcandidates.
Request body:
{
"pk": 123,
"supplier": 7,
"candidates": [
{
"supplier_part_number": "RC0603FR-0710KL",
"manufacturer_part_number": "RC0603FR-0710KL",
"description": "10 kOhm Thick Film Resistor 0603",
"price_breaks": [
{ "quantity": 1, "price": 0.02 },
{ "quantity": 100, "price": 0.01 }
]
}
]
}
Response highlights:
created,updated,errors, and per-candidateresults.- Part pricing is refreshed automatically when any candidate import succeeds.
Example success response:
{
"message": "OK",
"created": 0,
"updated": 1,
"errors": 0,
"results": [
{
"status": "updated",
"supplier_part_pk": 456
}
]
}
POST /plugin/supplierscout/runresync
Trigger a manual resync of existing supplier parts.
Request body:
{
"supplier": 7,
"part_pk": 123,
"async": true
}
Request fields:
supplierrequired: supplier company primary key.part_pkoptional: when present, resync only supplier parts attached to this InvenTree part.asyncoptional: truthy values1,true,yes,on, oryqueue the work through the InvenTree background task worker.actionoptional: set toreset_cursorto reset the round-robin cursor for scheduled supplier-wide resync.
Permission rules:
- Any user with part write permission may resync a single part by passing
part_pk. - Supplier-wide resync without
part_pkrequires admin access. action = reset_cursorrequires admin access.
Synchronous response example:
{
"message": "OK",
"scope": "supplier",
"action": "resync",
"supplier_pk": 7,
"processed": 5,
"updated": 5,
"created": 0,
"failed": 0,
"skipped": 0,
"cursor_before": 120,
"cursor_after": 125
}
Asynchronous response example:
{
"message": "Queued",
"queued": true,
"scope": "supplier",
"action": "resync",
"supplier_pk": 7,
"task_id": "b526758b2baf433eb71ac6994918065f",
"task_url": "/api/background-task/b526758b2baf433eb71ac6994918065f/"
}
Cursor reset response example:
{
"message": "OK",
"scope": "supplier",
"action": "reset_cursor",
"supplier_pk": 7,
"cursor_before": 125,
"cursor_after": 0
}
Operational note:
- Async resync requires the InvenTree worker to be running from the main checkout with
cd /home/inventree && source dev/venv/bin/activate && invoke worker.
POST /plugin/supplierscout/clearcache
Clear cached supplier API responses.
Request body for one supplier:
{
"supplier": 7
}
Request body for all suppliers:
{}
Response highlights:
- Supplier-specific response returns
scope = supplierplus acacheobject. - Global response returns
scope = allplus asupplierslist. - Cache paths are intentionally sanitized before being returned.
Example supplier response:
{
"message": "OK",
"scope": "supplier",
"supplier_pk": 7,
"supplier_key": "mouser",
"cache": {
"enabled": true,
"cache_backend": "filesystem",
"cache_ttl_seconds": 3600,
"cache_path": "~/.cache/inventree_mouser",
"cache_file_count": 12,
"cache_size_bytes": 48219,
"cleared_file_count": 12,
"failed_file_count": 0
}
}
GET|POST /plugin/supplierscout/ratelimitstatus
Return current API usage state for one supplier or all configured suppliers.
Inputs:
GET /plugin/supplierscout/ratelimitstatus?supplier=7POSTwith body{ "supplier": 7 }- Omit
supplierto return all registered suppliers.
Response highlights:
updated_tstimestamp.- Per-supplier usage with
configured,rate_limit_per_second,daily_limit,daily_count,daily_remaining,daily_percent_used, anddaily_reset_at.
GET /plugin/supplierscout/dashboardmetrics
Return dashboard diagnostics for every registered supplier.
Response highlights:
query_metricsfor historical request totals.api_usagefor current rate-limit counters.cache_statusfor dashboard-visible cache diagnostics.
This endpoint is used by the dashboard card and currently does not require part-write permission.
GET|POST /plugin/supplierscout/tokendebug
Inspect how Supplier Scout derived the search query for a part.
Inputs:
GET /plugin/supplierscout/tokendebug?pk=123POSTwith body{ "pk": 123 }
Response highlights:
- Top-level
part_pkandquery. debug.tokens,debug.token_sources, anddebug.token_attribution.debug.semantic_hintsfor inferred component type and extracted values.debug.query_debugforname_mode,has_structured_tokens,include_name_tokens,source_token_map, andfinal_query_tokens.
Example response:
{
"message": "OK",
"part_pk": 123,
"query": "capacitor 100nF 0402 16V",
"debug": {
"query_debug": {
"name_mode": "fallback",
"has_structured_tokens": true,
"include_name_tokens": false,
"final_query_tokens": ["capacitor", "100nF", "0402", "16V"]
}
}
}
Common Error Responses
Most endpoints return a JSON payload with a message field on validation or permission failures.
Common cases:
400for invalid supplier or part identifiers.403for missing part-write permission or admin-only operations.404for unknown suppliers or missing parts.500for unexpected internal failures.503fromrunresyncwhen async queue submission fails.
How Search Tokens Are Derived
SupplierScout builds a keyword search query from a part's structured data rather than relying on the part name alone. The goal is to generate a query that a supplier's API will match against real component listings.
Token Sources
Tokens are extracted from the following sources, in priority order:
| Source | Description |
|---|---|
| Manufacturer Part Number (MPN) | Highest-signal identifier; used first if available |
| IPN | Internal Part Number |
| SKU | Supplier-facing stock-keeping unit |
| Part parameters | Parameter values (e.g., 100nF, 10kΩ, 0402), with unit template attached |
| Part category names | Direct category and every ancestor up the tree (configurable) |
| Part name / description | Fallback when no structured tokens exist (configurable) |
Text Tokenisation
Each source value is split on non-alphanumeric boundaries (spaces, dashes, slashes, underscores). Sub-tokens are also extracted from compound tokens. The following normalisation rules are applied to every fragment:
| Rule | Input example | Output tokens |
|---|---|---|
| Raw chunk | 100nF |
100nF |
| Split sub-token | MLCC-0402 |
MLCC, 0402 |
| Shorthand expansion | 4.7n |
4.7nf, 4.7nF |
| Shorthand expansion | 10k |
10kohm, 10kOhm |
| Capacitance normalisation | 4n7 |
4.7nF |
| Resistance normalisation | 4R7 |
4.7ohm |
| EIA capacitor code | 104 |
100nF |
| Unitised parameter | value=100, unit=nF |
100nF, 100 nF |
Tokens shorter than two characters are discarded. Duplicate tokens (case-insensitive) are removed.
Semantic Hints and Query Plan
After token extraction, SupplierScout inspects the tokens for semantic clues:
- Component type — inferred from the part name prefix (
C_/C-→ capacitor,R_/R-→ resistor,L_/L-→ inductor) or from parameter names containing capacit, resist, induct. - Electrical characteristics — capacitance, resistance, inductance, package, tolerance, voltage, and current values are extracted from parameters and tokens.
The final query is assembled from:
- Component type hint (e.g.,
capacitor) - Electrical characteristic values (e.g.,
100nF,0402,10%,25V) - Structured tokens (MPN → IPN → SKU → parameters → category)
- Name/description tokens — included always, never, or only as fallback when no structured tokens exist (controlled by
TOKEN_NAME_MODE)
The query is capped at ten tokens before being sent to the supplier API.
Numeric Constraints
Voltage and current parameter values are also extracted as hard constraints. Candidates whose spec attributes violate these constraints (e.g., rated voltage below the required minimum) receive a score penalty, so they appear lower in the ranked list.
Inspecting Token Extraction
Use the Token Debug endpoint to see exactly what SupplierScout extracted from a part:
GET /plugin/supplierscout/tokendebug?pk=<part_pk>
The response includes the full token list, per-source breakdown, semantic hints, and the final query token sequence.
Configuration Reference
Settings are managed through the InvenTree plugin settings UI (Settings → Plugins → Supplier Scout). Scope labels below indicate where each setting is stored:
- Global — applies to all users; set by an administrator in plugin settings.
- User — per-user override; each user sets their own value in their personal plugin settings. An empty value falls back to the global setting.
DigiKey
| Setting key | Scope | Default | Description |
|---|---|---|---|
DIGIKEY_PK |
Global | — | Primary key of the DigiKey supplier company record in InvenTree. Must be set before search works. |
DIGIKEY_CLIENT_ID |
Global, User | Global: — / User: — |
DigiKey OAuth2 client ID. User scope overrides global when set. |
DIGIKEY_CLIENT_SECRET |
Global, User | Global: — / User: — |
DigiKey OAuth2 client secret (stored encrypted). User scope overrides global when set. |
DIGIKEY_MAX_CANDIDATES |
Global | 40 |
Maximum number of raw DigiKey results fetched before ranking. |
DIGIKEY_MIN_PRICE_QUANTITY |
Global, User | Global: 1 / User: — |
Minimum quantity used when selecting the best price break. User scope overrides global when set. |
DIGIKEY_MAX_PRICE_QUANTITY |
Global, User | Global: (empty) / User: — |
Upper bound for price-break quantity selection. User scope overrides global when set. |
DIGIKEY_CACHE_TTL |
Global | 3600 |
How long (in seconds) to cache DigiKey API responses on disk. Set to 0 to disable caching. Cache files are stored in ~/.cache/inventree_digikey/. |
DigiKey Scheduled Resync
| Setting key | Scope | Default | Description |
|---|---|---|---|
DIGIKEY_RESYNC_ENABLED |
Global | False |
Enable periodic background refresh of existing DigiKey supplier parts. |
DIGIKEY_RESYNC_INTERVAL_MINUTES |
Global | 1440 |
How often to run a DigiKey resync (in minutes). Default is once per day. |
DIGIKEY_RESYNC_BATCH_SIZE |
Global | 100 |
Maximum number of existing DigiKey supplier parts to refresh per scheduled run. Uses a round-robin cursor to spread work across runs. |
DIGIKEY_API_RATE_LIMIT_PER_SECOND |
Global | 1 |
Maximum DigiKey API requests per second. Set to 0 to disable rate limiting. |
DIGIKEY_API_DAILY_LIMIT |
Global | 1000 |
Maximum DigiKey API requests per day. Requests beyond this limit raise an error until midnight UTC. Set to 0 for no limit. |
Mouser Electronics
| Setting key | Scope | Default | Description |
|---|---|---|---|
MOUSER_PK |
Global | — | Primary key of the Mouser supplier company record in InvenTree. Must be set before search works. |
MOUSER_APIKEY_SEARCH |
Global, User | Global: — / User: — |
Mouser Part Search API key (stored encrypted). Obtain from the Mouser API Hub. User scope overrides global when set. |
MOUSER_MAX_CANDIDATES |
Global | 40 |
Maximum number of raw Mouser results fetched before ranking. Higher values improve match quality at the cost of more API calls. |
MOUSER_MIN_PRICE_QUANTITY |
Global, User | Global: 1 / User: — |
Minimum quantity used when selecting the best price break (e.g., 1 for single-unit prices, 10 for tape-and-reel). User scope overrides global when set. |
MOUSER_MAX_PRICE_QUANTITY |
Global, User | Global: (empty) / User: — |
Upper bound for price-break quantity selection. Leave empty to use the smallest available price break above the minimum. User scope overrides global when set. |
MOUSER_CACHE_TTL |
Global | 3600 |
How long (in seconds) to cache Mouser API responses on disk. Set to 0 to disable caching. Cache files are stored in ~/.cache/inventree_mouser/. |
Mouser Scheduled Resync
| Setting key | Scope | Default | Description |
|---|---|---|---|
MOUSER_RESYNC_ENABLED |
Global | False |
Enable periodic background refresh of existing Mouser supplier parts. |
MOUSER_RESYNC_INTERVAL_MINUTES |
Global | 1440 |
How often to run a Mouser resync (in minutes). Default is once per day. |
MOUSER_RESYNC_BATCH_SIZE |
Global | 100 |
Maximum number of existing Mouser supplier parts to refresh per scheduled run. Uses a round-robin cursor to spread work across runs. |
MOUSER_API_RATE_LIMIT_PER_SECOND |
Global | 1 |
Maximum Mouser API requests per second. Set to 0 to disable rate limiting. |
MOUSER_API_DAILY_LIMIT |
Global | 1000 |
Maximum Mouser API requests per day. Requests beyond this limit raise an error until midnight UTC. Set to 0 for no limit. |
General Scheduler
| Setting key | Scope | Default | Description |
|---|---|---|---|
RESYNC_SCHEDULER_TICK_MINUTES |
Global | 15 |
How often the background scheduler checks whether any supplier is due for a resync. The per-supplier interval settings control the actual refresh frequency; this is just the polling granularity. |
Candidate Ranking
| Setting key | Scope | Default | Description |
|---|---|---|---|
RANKING_STRATEGY |
Global, User | Global: balanced / User: (empty) |
Candidate ranking strategy. balanced weights match similarity (45 %), availability (35 %), and price (20 %). availability prioritises stock (50 %). price prioritises cost (50 %). User scope overrides global when set. |
TOP_N_CANDIDATES |
User | (empty) | Number of ranked candidates displayed in the search panel. Leave empty to use the default of 10. |
Token Generation
| Setting key | Scope | Default | Description |
|---|---|---|---|
TOKEN_PARAMETER_NAMES |
Global | (empty) | Comma- or newline-separated list of parameter template names to include in token extraction. Leave empty to use all parameters. Example: Capacitance, Voltage Rating, Package. |
TOKEN_INCLUDE_CATEGORY_NAMES |
Global | True |
When enabled, the part's direct category name and every ancestor category name are added as token sources. Disable if category names interfere with search results. |
TOKEN_NAME_MODE |
Global, User | Global: fallback / User: (empty) |
Controls when the part name and description are included as search tokens. fallback — only when no structured tokens (MPN, IPN, parameters, categories) are available. always — always append name tokens. never — never include name tokens. User scope overrides global when set. |
Testing and Coverage
See development/TESTING.md for test commands, local CI-equivalent checks, and coverage details.
Development Docs
Developer-focused documentation and helper scripts live under development/:
- development/README.md - workflow, sourcemaps, branch-switch guidance.
- development/TESTING.md - backend/frontend checks and coverage.
- development/frontend.md - frontend-specific setup and build notes.
- development/TODO.md - engineering backlog and design notes.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file inventree_supplier_scout-0.1.1.tar.gz.
File metadata
- Download URL: inventree_supplier_scout-0.1.1.tar.gz
- Upload date:
- Size: 66.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fcd205fed7ee9b7386160f9426363b2443f7d40d301d4217b23e7ca90af49531
|
|
| MD5 |
9e819d9b05eacffd5e7ce60bb1348c5d
|
|
| BLAKE2b-256 |
8320676d244d91d41a5d61dcf2b27728b2ed2033b403c89ed18f83341fefc0a9
|
File details
Details for the file inventree_supplier_scout-0.1.1-py3-none-any.whl.
File metadata
- Download URL: inventree_supplier_scout-0.1.1-py3-none-any.whl
- Upload date:
- Size: 74.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
43f92d2de4cc170663eaa53b2f6ebf7d207c9af535a4302ef3dcbdd1a8cac967
|
|
| MD5 |
96ee0f452e8af12609e8f02c35e450ec
|
|
| BLAKE2b-256 |
1f23e1931cafa41b14f073bb958d82e0edf55501ac0ade5b2067912d4789633b
|