- Python 100%
| .ralph-tui | ||
| examples | ||
| src/partsbox | ||
| templates | ||
| tests | ||
| .env.example | ||
| .gitignore | ||
| .python-version | ||
| AGENTS.md | ||
| AI_USAGE.md | ||
| LICENSE | ||
| parts_list.ods | ||
| parts_list.xlsx | ||
| pyproject.toml | ||
| README.md | ||
| uv.lock | ||
PartsBox Import
A Python CLI tool and client library for importing parts into PartsBox via the API. PartsBox doesn't offer a native bulk import, so this fills the gap — especially useful for importing large quantities of passive components from supplier orders.
AI Disclosure: This project was heavily enhanced using AI. Read more here.
Table of Contents
- Quick Start
- Installation
- Configuration
- Importing Parts
- Import Formats
- Native KiCad Integration
- BOM Import
- Metaparts
- Reports
- Other Commands
- Troubleshooting
- Development
Quick Start
git clone https://codeberg.org/jtperry/partsbox-import.git
cd partsbox-import
uv sync
export PARTSBOX_API_KEY=your_key_here
# Preview what would be imported
uv run partsbox import parts.tsv --dry-run
# Import for real
uv run partsbox import parts.tsv
Installation
Prerequisites
- Python 3.13 or newer
- UV (recommended) or any Python tool that honors
pyproject.toml - A PartsBox API key (found under Settings > API in the PartsBox web app)
Install
git clone https://codeberg.org/jtperry/partsbox-import.git
cd partsbox-import
uv sync
After uv sync, the partsbox command is available via uv run partsbox.
Configuration
The tool needs your PartsBox API key. Provide it in one of three ways (highest priority first):
- CLI flag —
--api-key your_key_hereon any command - Environment variable —
export PARTSBOX_API_KEY=your_key_here .envfile — create one from the included example:
cp .env.example .env
# Edit .env and add your key
| Variable | Required | Default | Description |
|---|---|---|---|
PARTSBOX_API_KEY |
Yes | — | Your PartsBox API key |
PARTSBOX_API_URL |
No | https://api.partsbox.com/api/1 |
API base URL (rarely needed) |
All commands also accept --verbose (INFO logging) or --debug (DEBUG logging) for troubleshooting.
Importing Parts
The import command reads a CSV or TSV file and creates parts in PartsBox. It automatically detects the file format from column headers, and the delimiter from the file extension (.csv → comma, .tsv/.txt → tab).
uv run partsbox import parts.tsv
uv run partsbox import lcsc_order.csv
For each row in the file, the importer will:
- Look up the part by MPN first, then by name (case-insensitive). If it already exists, stock is added and any missing metadata is filled in rather than creating a duplicate.
- Create any storage locations that don't already exist (with a single confirmation prompt per new location).
- Create the part and add the specified quantity of stock.
- After all rows are processed, group similar parts into metaparts (unless disabled).
| Option | Description |
|---|---|
--format / --fmt |
Force a format: lcsc, digikey, mouser, arrow, or generic. Omit to auto-detect. |
--dry-run |
Preview the import plan without making any API calls. |
--storage NAME |
Default storage location for rows with no storage value. Created if it doesn't exist. |
--no-storage |
Skip storage assignment for all parts (no prompts). |
--no-metaparts |
Skip automatic metapart creation after import. |
--track-prices / --no-track-prices |
Store unit price, currency, and order number in lot entries. On by default for supplier formats. |
Dry Run
Always a good idea before your first import. Shows exactly what the tool would do without touching your PartsBox account:
uv run partsbox import parts.tsv --dry-run
Assigning Storage
If your file has a Storage column, per-row values are used directly. If a location name in the file doesn't exist in PartsBox, the tool asks once before creating it:
Storage 'Wall1' not found. Create it? [Y/n]:
If your file has no Storage column (or some rows have an empty storage value), use --storage to specify a fallback location:
# All parts with no storage value go to "SMD Shelf"
uv run partsbox import lcsc_order.csv --storage "SMD Shelf"
To skip all storage assignment without any prompts:
uv run partsbox import lcsc_order.csv --no-storage
Real LCSC and Digi-Key exports already include a Storage column, so you typically won't need --storage for those.
Import Formats
The importer auto-detects the format by checking column headers. You can override detection with --format. Files must be UTF-8 encoded; the delimiter is detected from the file extension.
Generic Format
For your own parts lists. Prepare a CSV or TSV with these columns:
| Column | Required | Description |
|---|---|---|
Name |
Yes | Part name or part number |
Quantity |
Yes | Number of units to import |
Description |
No | Part description |
Manufacturer |
No | Manufacturer name |
MPN |
No | Manufacturer part number |
Footprint |
No | Package or footprint (e.g. 0402, SOT-23) |
Storage |
No | Storage location (created automatically if needed) |
Unit Price |
No | Unit cost |
Currency |
No | Currency code (e.g. USD) |
Order # |
No | Order or invoice reference |
LCSC Format
Detected by the presence of LCSC Part Number and Manufacture Part Number columns. Export your LCSC order as CSV and import directly:
uv run partsbox import "LCSC__WM2512280052_20260219123533.csv"
Real LCSC exports include a Storage column — per-row storage values are used automatically. Rows with an empty storage value are imported without a storage assignment (or fall back to --storage if provided).
The part name and MPN are both set from the Manufacture Part Number column. Unit prices from the Unit Price($) column are recorded in lot entries.
Digi-Key Format
Detected by Digi-Key Part Number and Manufacturer Part Number columns. Export your Digi-Key order as CSV and import:
uv run partsbox import parts-digikey.csv
Real Digi-Key exports include a Storage column — per-row storage values are used automatically. The part name and MPN are both set from the Manufacturer Part Number column.
Mouser Format
Detected by Mouser No and Mfr. No columns.
uv run partsbox import mouser_order.tsv
The part name and MPN are both set from the Mfr. No column.
Arrow Format
Detected by the Arrow Part # column.
uv run partsbox import arrow_order.tsv
Native KiCad Integration
PartsBox offers a first-class KiCad integration via the KiCad HTTP Library protocol. Once configured, KiCad connects live to your PartsBox parts database, letting you browse and place components directly from your inventory while drawing the schematic.
How it works:
- In PartsBox, go to Settings > CAD Integration and generate a
.kicad_httplibfile (requires an API key). - Place the
.kicad_httplibfile in KiCad's user library directory and add it as a symbol library. - KiCad now shows your PartsBox inventory as a searchable symbol library. When you place a component, the ID Anything™ code — a stable, unique PartsBox identifier — is stored as a symbol property.
The ID Anything™ code never changes even if the part's name or MPN is updated later in PartsBox. This makes the identity of each placed component unambiguous and permanent.
Available on all plans, including the free Hobbyist/Maker tier.
Relationship to bom import: When you use the HTTP library, every placed component already exists in your PartsBox inventory. By the time you run bom import, the parts are there and the names/MPNs are an exact match — matching is reliable with no manual intervention needed.
If your design pre-dates the integration, or uses generic KiCad symbols without PartsBox IDs, the bom import workflow below still handles it.
BOM Import
Import a KiCad BOM into a PartsBox project. The importer matches BOM rows to existing parts by MPN first, then by value/name, and prompts you for any unmatched parts.
uv run partsbox bom import bom.csv --project "My PCB Rev 1"
| Option | Description |
|---|---|
--project NAME |
PartsBox project name to import into (required). |
--check-stock |
Show stock coverage summary after import. |
--dry-run |
Preview without making API calls. |
Expected KiCad BOM CSV columns: Reference, Value, Footprint, Datasheet, Quantity Per PCB. Optional: MPN, Manufacturer.
Matching Logic
The importer uses these strategies in order:
- MPN column — if your BOM has an
MPNcolumn, it's matched against inventory MPNs (normalized: dashes, dots, and spaces are stripped before comparing, so500901-0801matches5009010801). - Value as name — the KiCad
Valuefield is matched against inventory part names. - Description — the
Descriptionfield is matched against inventory part names. - Value as MPN — the
Valuefield is also tried as a normalized MPN, in case the designer typed an MPN directly into the Value field.
Best Results
There is a hierarchy of approaches, from most to least reliable:
-
Best — Use the PartsBox KiCad HTTP library. Parts placed from the HTTP library are already in your inventory by name and MPN. When you run
bom import, matching is automatic and high-confidence. See Native KiCad Integration. -
Good — Populate the
MPNfield in each KiCad symbol. In the schematic editor, right-click a component → Properties, and add a field namedMPNwith the manufacturer part number (e.g.GRM155R71H104KA88D). When you export the BOM, theMPNcolumn is included and matching is fully automatic. -
Fallback — Generic value strings. Passives described as
10uF 16V X5R 0603rather than by MPN will not match automatically. The importer will prompt for manual matching or part creation for each one. That is expected behavior.
Interactive Prompt for Unmatched Parts
When no automatic match is found, the importer prompts you. If there are close matches in your inventory (by substring on normalized name/MPN), they are offered as candidates:
No match for: MicroSD Slot (Connector_Card:microSD_AMP_693072010801)
Designators: J_SDCARD1
Possible matches in inventory:
1) Molex MicroSD Connector (MPN: 5009010801)
2) Create new part
3) Skip
Your choice [3]:
For passives with generic value strings like 10uF 16V X5R 0603, no candidates will surface — the prompt goes directly to create/skip. That is expected: without an MPN, there is no reliable way to match parametric values to specific parts.
Metaparts
Metaparts in PartsBox group equivalent parts together — for example, a 10kΩ 0402 resistor from two different manufacturers becomes one logical part with two alternates.
Automatic Grouping
After import, the tool analyzes all parts and groups them by:
- Component type — resistor, capacitor, inductor, LED, diode, etc. (detected from descriptions and common part number prefixes like
RC,GRM,ERJ) - Value — the electrical value (e.g.
10kΩ,100nF,4.7uH) - Package — the physical footprint (e.g.
0402,SOT-23,SOIC-8) - Extra characteristics — tolerance and power rating for resistors; tolerance and current rating for inductors
A metapart is created only when two or more parts share all of the above. The auto-generated name follows the pattern: 10kΩ 0402 0.1W 1% Resistor.
Ambiguous Parts
Some parts can be partially identified but are missing characteristics needed for safe grouping. For example, a resistor with no power rating listed. When this happens, you'll see an interactive prompt:
════════════════════════════════════════════════════
Ambiguous Part Detected
════════════════════════════════════════════════════
Part: RC0402FR-0710KL
Description: Resistor 10kohm 1%
Extracted: component_type=resistor, value=10kΩ, tolerance=1%
Missing: power_rating
Related existing metaparts:
1) 10kΩ 0402 Resistor
2) Create a new metapart
3) Skip metapart linking for this part
Your choice [3]:
You can link it to an existing metapart, create a new one, or skip it.
Disabling Metaparts
If you prefer to manage metaparts manually, skip the automatic step:
uv run partsbox import parts.tsv --no-metaparts
Reports
Generate a JSON or HTML inventory report:
# JSON only
uv run partsbox report
# JSON + HTML dashboard
uv run partsbox report --html
# Custom output path
uv run partsbox report --output my-inventory.json --html
The HTML dashboard shows per-storage stock levels and per-part lot details.
Other Commands
Beyond importing, the CLI provides commands for inspecting and managing your PartsBox data.
Parts
# List all parts
uv run partsbox parts list
# Get full details for a part (JSON output)
uv run partsbox parts get <PART_ID>
# Update part fields
uv run partsbox parts update <PART_ID> --name "New Name" --mpn "ABC123" --description "..." --manufacturer "..." --footprint "0402" --notes "..."
# Delete a part
uv run partsbox parts delete <PART_ID>
uv run partsbox parts delete <PART_ID> --yes # skip confirmation
Storage
# List all storage locations
uv run partsbox storage list
# Create a single storage area
uv run partsbox storage create "Drawer A1"
uv run partsbox storage create "Drawer A1" --notes "SMD passives" --color "#ff0000"
# Import multiple storage areas from a TSV file (columns: name, notes, color)
uv run partsbox storage import storage_areas.tsv
# Rename a storage location
uv run partsbox storage rename <STORAGE_ID> "New Name"
# Archive / restore
uv run partsbox storage archive <STORAGE_ID>
uv run partsbox storage restore <STORAGE_ID>
Stock
# Add stock to a part at a storage location
uv run partsbox stock add <PART_ID> <STORAGE_ID> <QUANTITY>
uv run partsbox stock add <PART_ID> <STORAGE_ID> <QUANTITY> --price 0.05 --currency USD --order "INV-123"
# Remove stock from a lot
uv run partsbox stock remove <LOT_ID> <QUANTITY>
# Move stock to a different storage location
uv run partsbox stock move <LOT_ID> <QUANTITY> <TARGET_STORAGE_ID>
uv run partsbox stock move <LOT_ID> <QUANTITY> <TARGET_STORAGE_ID> --comments "Reorganized"
Metaparts Commands
# List all metaparts
uv run partsbox metaparts list
# Link parts to a metapart
uv run partsbox metaparts link <METAPART_ID> <PART_ID> [<PART_ID> ...]
# Unlink parts from a metapart
uv run partsbox metaparts unlink <METAPART_ID> <PART_ID> [<PART_ID> ...]
Projects
# List all projects
uv run partsbox projects list
# Create a project
uv run partsbox projects create "My PCB" --description "Rev 1" --notes "First spin"
# Get full details for a project (JSON output)
uv run partsbox projects get <PROJECT_ID>
# Delete a project
uv run partsbox projects delete <PROJECT_ID>
uv run partsbox projects delete <PROJECT_ID> --yes # skip confirmation
Troubleshooting
"No API key provided" — Set PARTSBOX_API_KEY via environment variable, .env file, or --api-key flag. See Configuration.
"Could not detect format from headers" — Your file's column headers don't match any known format. Either rename columns to match the Generic Format or force detection with --format. Make sure the file is UTF-8 encoded and uses the correct delimiter for its extension (.csv → comma, .tsv → tab).
"HTTP 401" or "HTTP 403" — Your API key is invalid or expired. Generate a new one from Settings > API in PartsBox.
"HTTP 429" — Rate limit hit. The tool limits itself to 5 requests/second, but running multiple instances simultaneously can exceed server-side limits. Wait a moment and retry.
Parts not appearing — Use --dry-run first to confirm the plan looks correct. Check that the file is UTF-8 encoded.
Duplicate parts — The importer checks for existing parts by MPN first, then by name (case-insensitive). If a match is found, stock is added to the existing part and any missing metadata fields are filled in.
Storage locations not found during dry run — Expected behavior. New storage locations are created during the actual import but won't exist during --dry-run.
Development
Setup
git clone https://codeberg.org/jtperry/partsbox-import.git
cd partsbox-import
uv sync
Running Tests
uv run pytest
Tests use the responses library to mock HTTP calls — no live API key is needed.
Linting
uv run ruff check
Project Structure
src/partsbox/
├── __init__.py # Exports PartsBoxClient
├── cli.py # Click CLI definitions
├── client.py # API client (PartsBoxClient)
├── config.py # Configuration loading
├── exceptions.py # Exception hierarchy
├── models.py # Data models (Part, StorageLocation, Stock, etc.)
├── report.py # Inventory report generation
└── importers/
├── importer.py # CSV/TSV parsing and import workflow
├── bom_kicad.py # KiCad BOM import
└── metaparts.py # Metapart grouping logic
Using the Client Library
The PartsBoxClient can be used directly in your own scripts:
from partsbox import PartsBoxClient
with PartsBoxClient(api_key="your_key") as client:
parts = client.get_all_parts()
for part in parts:
print(part.name)
The client covers parts, storage, stock, lots, and projects. See src/partsbox/client.py for the full API.
Dependencies
| Package | Purpose |
|---|---|
click |
CLI framework |
python-dotenv |
.env file loading |
requests |
HTTP client |
requests-ratelimiter |
Rate limiting (5 req/s) |
Dev dependencies: pytest, responses, ruff.
License
See LICENSE for details.