Super simple importer for (mostly) passives into the Partsbox management system using the API.
Find a file
2026-03-28 19:08:53 -07:00
.ralph-tui Merge ralph-session/0eac4417: US-003 interactive storage prompt for missing storage 2026-02-20 12:22:34 -08:00
examples New version with close to full functionality 2026-03-28 19:08:53 -07:00
src/partsbox New version with close to full functionality 2026-03-28 19:08:53 -07:00
templates feat: partsbox-import-1zl.6 - US-006: Sample import templates (XLSX) 2026-02-18 20:28:09 -08:00
tests New version with close to full functionality 2026-03-28 19:08:53 -07:00
.env.example US-002: Implement core API client with authentication and rate limiting 2026-02-18 11:36:48 -08:00
.gitignore feat: partsbox-import-3uj.1 - US-001: Remove --storage CLI flag 2026-02-20 12:05:41 -08:00
.python-version Initial version with basic functionality and limited error checking 2026-01-03 20:30:30 -08:00
AGENTS.md initial setup bd goo 2026-02-18 10:44:19 -08:00
AI_USAGE.md add AI disclaimer 2026-02-18 13:44:47 -08:00
LICENSE Initial version with basic functionality and limited error checking 2026-01-03 20:30:30 -08:00
parts_list.ods Initial version with basic functionality and limited error checking 2026-01-03 20:30:30 -08:00
parts_list.xlsx Initial version with basic functionality and limited error checking 2026-01-03 20:30:30 -08:00
pyproject.toml US-002: Implement core API client with authentication and rate limiting 2026-02-18 11:36:48 -08:00
README.md New version with close to full functionality 2026-03-28 19:08:53 -07:00
uv.lock feat: partsbox-import-s3r.1 - US-008: Refactor GroupKey to Support Variable Characteristics 2026-02-18 14:19:52 -08:00

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

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):

  1. CLI flag--api-key your_key_here on any command
  2. Environment variableexport PARTSBOX_API_KEY=your_key_here
  3. .env file — 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:

  1. 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.
  2. Create any storage locations that don't already exist (with a single confirmation prompt per new location).
  3. Create the part and add the specified quantity of stock.
  4. 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:

  1. In PartsBox, go to Settings > CAD Integration and generate a .kicad_httplib file (requires an API key).
  2. Place the .kicad_httplib file in KiCad's user library directory and add it as a symbol library.
  3. 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:

  1. MPN column — if your BOM has an MPN column, it's matched against inventory MPNs (normalized: dashes, dots, and spaces are stripped before comparing, so 500901-0801 matches 5009010801).
  2. Value as name — the KiCad Value field is matched against inventory part names.
  3. Description — the Description field is matched against inventory part names.
  4. Value as MPN — the Value field 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 MPN field in each KiCad symbol. In the schematic editor, right-click a component → Properties, and add a field named MPN with the manufacturer part number (e.g. GRM155R71H104KA88D). When you export the BOM, the MPN column is included and matching is fully automatic.

  • Fallback — Generic value strings. Passives described as 10uF 16V X5R 0603 rather 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.