part2kicad
Type an LCSC part number and get a KiCad part you can hand to a fab house: the official KiCad symbol where one exists, a standard footprint that was measured before it was swapped in, real pin types so KiCad's electrical rule check has something to work with — and a report that says, field by field, where every value came from.
pip install part2kicad
cd my-board
part2kicad add C93119
LM393M (C93119) → my-board.kicad_sym
8 pins in 3 units
Footprint my-board:SOIC-8_L4.9-W3.9-P1.27-LS6.0-BL, 8 pads
3D model ${KIPRJMOD}/my-board.3dshapes/SOIC-8_L4.9-W3.9-P1.27-LS6.0-BL.step
Fetched on 2026-08-15
14 rules checked, 6 violated, 0 added
! Taking the official symbol over also exchanged 8 pin names — the numbers agree on
both sides, so nothing moves electrically: 1 '1' → '', 2 '2' → '-', 3 '3' → '+', …
Open the schematic, press A, and the part is in the list. No download folder, no library table to edit, no files to copy around.
The six violations in that last line are not a failure to hide — they are the reason to use this tool at all. All six sit on the footprint, which this part keeps because the official candidate misses its pads by 0.2498 mm and 0.20 mm is what the tolerance allows. The report says exactly that, with the figures.
What a part looks like before and after
C93119 is a plain LM393 comparator, and it is the whole argument in one part. This is
what changes when it goes through part2kicad — measured on 2026-08-15, not estimated:
| as the source delivers it | after part2kicad add |
|
|---|---|---|
| Units | one flat block, 8 pins | 3 units — two comparators and a supply block |
| Pin types | 8 × unspecified |
2 × power_in, 4 × input, 2 × open_collector |
| Rule violations | 10 of 14 | 6 of 14, all of them on the footprint |
| Electrical rule check | nothing to check | catches a comparator output tied to +3V3 |
That last row was run by hand in KiCad, in the order that makes it mean something: the
rule check was first made silent with PWR_FLAG on both supply nets, and only then was
the deliberate mistake introduced. Without that step the check only proves that KiCad
says something.
An RP2040 in a QFN-56 tells the same story from the other end. Its converted footprint
broke 6 of 9 footprint rules; after the official QFN-56-1EP_7x7mm_P0.4mm was measured
against it and swapped in, the count is 0. Its symbol arrives with 51 of 57 pins off the
2.54 mm grid, and comes out with none.
What it does
It takes the official KiCad part over when there is one. For the symbol it looks for
the manufacturer part number in your installed KiCad libraries — exact name first, then
the name with a suffix trimmed, then the package family. extends is resolved, so you get
the real body and not a stub. The taken-over symbol keeps your name and your provenance
fields, and the report names the library, the symbol, whatever suffix was cut off, and the
KiCad version it came from.
It swaps the footprint only when the geometry agrees. The package name chooses the candidate; the measurement vetoes it. Pad hull, lead span, largest pad and pad pitch are compared, and anything off by more than 0.20 mm keeps the source drawing. Drill diameter and grid are hard gates with no tolerance at all — a 10 mm electrolytic that matched its official candidate to 0.0000 mm was still turned away, because the official hole is 0.2 mm too small for its lead. A footprint that is taken over is copied into your project, not linked.
It redraws IC symbols. Where no official symbol exists, the symbol is drawn afresh from the pin data instead of repairing the source artwork: every pin on the 2.54 mm grid, one pin length, one text size, inputs left, outputs right, supply up, ground down. Twice the same input gives twice the same symbol.
It gives pins real electrical types. Types come from the taken-over official symbol
first, then from name rules you can edit. Over the parts measured so far, 58 of 81 pin
names are classifiable that way. A pin that cannot be classified is reported as not
determined and left unspecified — never guessed, because a wrong power_in is worse
than an honest blank.
It writes down what it did. Every field carries its origin, and the report names the fourteen KLC rules it checked with each result, plus seven more it does not yet check, each with the reason why not.
It merges multi-unit parts properly. A quad op-amp becomes one symbol with units
U1A to U1D, not four parts. Shared pins land in unit 0.
It improves parts you already have. part2kicad refine re-runs the footprint match,
the pin types and the rule check against a part that is already in your library, without
making a single network request.
It never overwrites by accident. A part already in the library is reported, not
replaced; --force is the only way past that.
Installation
pip install part2kicad
That is all it takes on any machine with Python 3.11 or newer. The package is a pure
py3-none-any wheel, so nothing is compiled and no build toolchain is needed. If you use
uv, uv pip install part2kicad does the same thing; uv tool install part2kicad or
pipx install part2kicad keep it out of your project environment.
You also need KiCad 10.0 or newer. The tool calls kicad-cli, which ships with KiCad.
On Windows that program is normally not on the search path, so part2kicad looks in the
usual installation folders first. If it still cannot find it, say where it is:
set PART2KICAD_KICAD_CLI=C:\Program Files\KiCad\10.0\bin\kicad-cli.exe
In PowerShell the same line reads:
$env:PART2KICAD_KICAD_CLI = "C:\Program Files\KiCad\10.0\bin\kicad-cli.exe"
To install from a wheel you build yourself, which is the route while working on the tool:
uv build
uv pip install dist/part2kicad-*.whl
Publishing to PyPI was a deliberate step of its own rather than a side effect of development. It cannot be taken back: it claims the name permanently and turns the command layout into a published interface that scripts and tutorials will anchor themselves to. A released version can be yanked, but its number can never be reused.
Your first part
-
Open a terminal in your KiCad project folder — the one holding the
.kicad_profile.part2kicadfinds the project the way git finds a repository: from the working directory upwards. If there is none, it stops instead of writing somewhere arbitrary. -
Take a part in. If you know the LCSC number:
part2kicad add C93119If you do not, search by manufacturer part number or by description:
part2kicad search lm393You get a numbered list with package, price, stock and whether CAD data exists at all. Pick a number and that part is taken straight in — search and import are one command.
-
Open the schematic in KiCad and press A to place a symbol. The library is named after your project and is already registered; you do not have to add anything under Preferences → Manage Symbol Libraries.
-
Check the 3D model. Open the board, press Alt+3. The model lives inside the project, so the folder still works on a machine that has never seen
part2kicad.
If KiCad was already open while the part was written, use Preferences → Manage Symbol Libraries → OK or restart it. KiCad reads the library tables once, at start.
The report
--report prints the full report and files a copy next to the part, under
<project>.reports/. It is Markdown, so it is still readable in a year without this tool
installed. Here is the middle of the real one for the LM393 above — where the symbol came
from, and what the rule check found, violations included:
## Where the symbol comes from (QUAL-04)
| Fact | Value |
| --- | --- |
| Way | taken over from the official KiCad library |
| Why | the official symbol name is the manufacturer part number with a suffix trimmed |
| Official library | Comparator |
| Official symbol | LM393 |
| Trimmed off the part number | M |
| Resolved from | LM2903 |
| KiCad version | 10.0.5 |
## Checked rules
| Rule | Subject | Demands | Result | Details |
| --- | --- | --- | --- | --- |
| KLC F5.1 (reference text) | footprint | the reference text of the footprint is 'REF**' | violated | found '', expected 'REF**' |
| KLC F5.2 (drawing on the fabrication layer) | footprint | at least one graphic lies on F.Fab or B.Fab | violated | no graphic lies on a fabrication layer; the 4 entries there are text only: fp_text (1x), property (3x) |
| KLC F5.3 (courtyard on the grid) | footprint | every courtyard coordinate is a multiple of the courtyard grid | violated | 4 of 4 courtyard coordinates are not multiples of 0.01 mm: -3.7499, -2.449, 2.449, 3.7499 |
| KLC S4.1 (pin grid) | symbol | every pin sits on the 2.54 mm grid | passed | — |
| KLC S4.4 (pin types) | symbol | not every pin carries the electrical type 'unspecified' | passed | — |
The full report has seven sections: the facts about the part, where the symbol came from, which footprint it got and why, the pin types with the source of each one, the origin of every single field, the rules that were checked, and the rules that were deliberately left out with the reason for each. Every judgement carries its figures — "0.2498 mm off, and 0.20 mm is what is allowed", not "close enough".
--json gives the same content as a machine-readable structure, out of the same source.
There is no second, parallel truth.
Everyday use
part2kicad add C2040 # take a part in by number
part2kicad search rp2040 # search, pick, take it in — one command
part2kicad add C2040 --report # print the full report and file it beside the part
part2kicad add C2040 --force # replace a part that is already there
part2kicad add C2040 --symbol redraw # force one of the four symbol routes
part2kicad add C2040 --project ../my-board # name the project explicitly
part2kicad add C2040 --json # machine-readable output, asks nothing
part2kicad add C2040 --verbose # one line per fetch on stderr
part2kicad refine C93119 # improve a part already in the library
part2kicad refine --all # go over the whole project library
Choosing how the symbol is made
--symbol takes auto, redraw, repair or keep. auto is the default and decides
for itself: take over the official symbol where there is one, otherwise draw the symbol
afresh from the pin data, otherwise repair the source drawing. The other three force one
route. Whichever way it goes, the report names the route and the reason — including,
when it falls back, why the preferred route was not available.
Refining what you already have
part2kicad refine works on a part that is already in your project library and makes no
network request at all — that is not a promise in prose, it is a bolt in the test suite
that fails the build if a fetch ever appears on that path. It re-runs the footprint match,
the pin types and the rule check against the files on disk.
Like add, it reports before it writes:
refine my-board.kicad_sym
0 would change · 1 unchanged · 0 skipped · 0 failed
LM393M (C93119) unchanged
! Price and stock are neither added nor changed by refine: both would need exactly the
fetch this command avoids.
--force performs the change. --all walks the whole project library; it can be
interrupted, a single failing part does not stop the run, and symbols without a Source
field — anything you drew yourself — are left alone and named as left alone.
Taking the same part in twice
If the part is already in the project library, part2kicad says so along with the date of
the first fetch and writes nothing. Anyone who has touched up a part by hand does not
lose that work to a careless second command.
--force replaces it. A symbol already placed in the schematic then has to be brought up
to date there, via Tools → Update Symbols from Library.
What ends up on the part
| Group | Fields |
|---|---|
| Provenance | Supplier Part, Manufacturer, Manufacturer Part, Datasheet, Source, Fetch Date |
| Commercial data | Price and Stock, each with the date it was fetched |
| Technical parameters | whatever the source delivers, under its unchanged field names |
| Assembly | JLCPCB Part Class (Basic/Extended) — it decides the assembly cost |
Every field except Reference and Value is created hidden, so the schematic stays
readable. Price and stock only come with part2kicad search; a fetch by part number does
not carry them, and part2kicad does not invent a price.
The Datasheet link is checked before it is written: one HEAD request asks the server
what it would actually deliver. Many links that end in .pdf return a web page, and the
field says so rather than pretending. Where an official KiCad symbol was taken over, its
datasheet link wins and the source link is kept beside it as Source Datasheet.
When something goes wrong
| What you see | What it means | What to do |
|---|---|---|
kicad-cli not found |
KiCad is not installed, or not where the tool looked | Set PART2KICAD_KICAD_CLI to the full path, see Installation |
no KiCad project found |
You are not inside a project folder | cd into the folder holding the .kicad_pro file, or pass --project |
this part has no CAD data |
The supplier has the part but no symbol or footprint for it | The message names the part and links its datasheet; draw it by hand or pick another part |
already in the library |
The part was taken in before | That is the safeguard, not an error. --force replaces it |
| The library does not show in KiCad | KiCad read its library tables at start | Preferences → Manage Symbol Libraries → OK, or restart KiCad |
| Everything hangs for two minutes | The supplier is throttling you | Leave it running; it waits and retries. Ctrl-C stops it cleanly at any time |
What it cannot do yet
The list stays here, and it stays complete, because a tool whose whole point is honest reporting cannot be coy about itself:
- A local result store does not exist yet, so a second
add … --forcefetches the same geometry again. It arrives with phase 5, together with offline search and filtering by value and price. refinedoes not take over an official symbol. On an existing part it keeps the symbol it finds and improves footprint, pin types and report around it. That is deliberate — an earlier version redrew a taken-over symbol and threw the good one away — but it means the official-symbol route is available on import only.refinesays nothing about a footprint candidate it rejected. The import report names the candidate and the deviation in figures; the refine report only lists changes, so a rejection leaves no line at all.- The field names of the technical parameters stay as the source delivers them — so, inconsistent. Unifying them is a step of its own and has not been taken.
- The symbol naming scheme and the target library name are not configurable. Everything
else about the drawing and matching lives in
recipe.toml; these two are still hard-wired to the part number and the project name. - macOS is untested, and nobody has run this on Linux yet. Development and testing
happen on Windows. For Linux the platform assumptions are pinned down statically — no
hard-wired path, no shell invocation, no file access without a stated encoding,
configuration folder from
platformdirs— but a run is still outstanding. - Two concurrent invocations on the same library can lose a symbol. The swap of the finished file is atomic; the read-modify-write before it is not. For an interactive single-workstation tool, a file lock has deliberately not been built in.
- Price and stock are only available via
part2kicad search, see above.
How this tool treats the data sources
part2kicad queries foreign servers that have no contract with us to serve it. What it
does while doing so is written out here in full, because a user is entitled to know what
runs in their name.
Endpoints used
All the addresses live in src/part2kicad/recipe.toml and not in the program code. If
the counterpart changes, the repair is an edit there — not a program update.
| Purpose | Address | When |
|---|---|---|
| Search | https://pro.easyeda.com/api/eda/product/search |
once per search command |
| Geometry (symbol and footprint) | https://easyeda.com/api/products/{lcsc}/components |
once per part taken in |
| 3D model (STEP) | https://modules.easyeda.com/…/{uuid} |
once per part taken in, if one is on file |
| Datasheet check | whichever address the response names — measured, atta., item. and so.szlcsc.com |
one HEAD request per part taken in |
Symbol and footprint come from one fetch; there is no second one for the footprint.
The datasheet check is a HEAD request: it asks what the server would deliver and reads no
body. If a part is already in the library, no fetch happens at all without --force. And
part2kicad refine touches none of these addresses — it is the one command that is
guaranteed to stay silent on the wire.
Identification
part2kicad identifies itself as
part2kicad/<version> (+https://pypi.org/project/part2kicad/)
An honest identification that names the product. Explicitly not: a faked browser user agent, or bending name resolution to get around a block. This is not decoration — measured, the honest route gets through, and the dishonest one would not be acceptable even if it were necessary.
The identification can be changed without touching the program: your own recipe.toml in
the platform's configuration folder (Windows %LOCALAPPDATA%\part2kicad\, Linux
~/.config/part2kicad/) overrides individual values selectively:
[identity]
user_agent = "my-identification/1.0 (+https://example.example)"
Minimum intervals and behaviour when blocked
Every endpoint has its own time budget — the block we measured is tied to the path, and a shared budget would slow the search down as soon as a geometry fetch is due.
| Endpoint | Minimum interval | Maximum attempts |
|---|---|---|
| Search | 2.5 s | 3 |
| Geometry | 3.0 s | 3 |
| 3D model | 1.0 s | 3 |
| Datasheet check | 1.0 s | 3 |
The counterpart starts blocking after roughly 45 fetches within a few minutes —
cumulatively, not by peak rate. It then answers with a bare HTTP 403, without
Retry-After and without HTTP 429; the block therefore cannot be predicted, only
recognised. It clears by itself after about two minutes.
In that case part2kicad waits, starting at 120 seconds and doubling per attempt, for at
most three attempts — 120 + 240 = six minutes in total. More would be out of
proportion: measured, the block clears after around two minutes, and if it is still there
after three times that, it is not the ordinary rate limit; waiting longer is then no better
a bet than telling the user. The wait is shown as a status line — two minutes of silence
could not be told apart from a crash — and Ctrl-C ends it at any time with a message
instead of a stack trace. If the counterpart still will not let up, the command stops with
a message saying that it is not your machine's fault. Nothing is written along the way: as
long as not all the pieces are together, the library stays untouched.
The numbers all live in recipe.toml and can be changed in your own user file — a limit
hard-coded in the program would take that option away.
The counterpart's exclusion list
easyeda.com/robots.txt disallows //api/ paths — and with them the geometry endpoint.
That is named openly here rather than passed over:
- The route is used deliberately, because there is no other one that would yield a part's CAD data.
- It is minimised: one fetch per part, never a full catalogue, none at all for a part
that is already present, and none at all for
part2kicad refine. - A local cache that would also spare the repeated fetch of the same part is still
missing — it arrives with phase 5. So a second
part2kicad add C2040 --forcefetches the geometry a second time. This is stated here because a disclosure that lists a measure not yet taken is not a disclosure. - The search traffic — that is, the volume — goes to
pro.easyeda.com, whose own exclusion list names only/loginand a set of named SEO bots, not/api/.
Anyone who judges this differently can leave part2kicad add alone and use the tool for
searching only.
Development
git clone https://github.com/AltF4Quit/part2kicad
uv sync
| Run | Command |
|---|---|
| Fast lane, without KiCad | pytest -m "not kicad and not live" |
| Full run (default) | pytest -m "not live" |
| Live tests against the real counterpart | pytest -m live --live |
Live tests never run by accident: they need both the marker and the --live switch.
Tests that need an installed KiCad skip themselves with a visible reason when none is
found.
License
GPL-3.0-or-later, see LICENSE.
Metadata
Release files for part2kicad 0.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| part2kicad-0.4.0.tar.gz | 590.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| part2kicad-0.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 813.3 kB
Release files / part2kicad-0.4.0.tar.gz
| Download URL | part2kicad-0.4.0.tar.gz |
|---|---|
| Size | 590.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
752eee10f561f1b65ce817d6c8b106b9f0d7217ebbe0d18ec13f82e92c592d12
|
|
BLAKE2b-256 checksum How to use checksums |
4eeccdb59fa5086b8c6a21b09611b531379c45aaf6d618d4e8d1c31800604722
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / part2kicad-0.4.0-py3-none-any.whl
| Download URL | part2kicad-0.4.0-py3-none-any.whl |
|---|---|
| Size | 222.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
efe3d178673100c29e62c3e6e9c60f35e4e2fcfa002745a4dcf24cde320dfd2e
|
|
BLAKE2b-256 checksum How to use checksums |
e499a47a12d8a46d90fcbcfbe044a429b959c87c45be09e0cebdd1a8e6fc5011
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|