Creating a new net by script and assigning it with KiCad's Python API

Published on September 28, 2026 by leZ

Create a net that doesn't exist yet on the board and assign it to a via or a pad — with the tested trap that silently drops it.

New MY_NEW_NET net created by script and assigned to pad 1 of a resistor in KiCad
Pad 1 now carries the new MY_NEW_NET net.

This site’s via and track tutorials always attach their objects to a net already present on the board (board.FindNet("GND")). But a procedural generation script — a per-zone ground pour, a grid of stitching vias on a test net — sometimes needs to create the net itself before using it. That extra step hides a tested trap on both APIs: a brand-new net doesn’t always survive being saved.

Creating the net

A net is a full object, NETINFO_ITEM, which needs to be added to the board before use:

import pcbnew
from pcbnew import NETINFO_ITEM

board = pcbnew.GetBoard()

new_net = NETINFO_ITEM(board)
new_net.SetNetname("MY_NEW_NET")
board.Add(new_net)

On the IPC side, there’s no dedicated create_net method on Board: Net(name=...) is a plain value object, created on the fly the moment you assign it to a board item:

from kipy.board_types import Net

new_net = Net(name="MY_NEW_NET")

⚠️ Assigning it to a via: the persistence trap

⚠️ Tested: a via created and attached to the new net with via.SetNet(new_net) then board.Add(via) correctly shows the right net in memory (via.GetNetname() returns "MY_NEW_NET", the net count goes from 112 to 113) right before SaveBoard. But after SaveBoard followed by LoadBoard on the generated file, the via is back on its previous net ("GND") — a grep "MY_NEW_NET" on the .kicad_pcb file finds nothing at all. The net simply isn’t written: a via alone isn’t enough to make a net persist.

⚠️ Tested, with different behavior: via.net = Net(name="MY_NEW_NET"); board.create_items(via) does make the net show up in board.get_nets() (112 → 113 nets) — but if the via physically touches existing copper on a different net (a ground pour, a track), a plain board.get_vias() right after already shows its net reverted to the touched copper’s net (GND in our tests), with no save involved at all. KiCad’s connectivity engine runs continuously on an open instance and reassigns an isolated item’s net based on what it physically touches.

Placed in an area of the board with no nearby copper at all, the same via keeps its net after a fresh read (board.get_vias()) and after a board.save() — confirmed with grep on the generated file, which finds (net "MY_NEW_NET").

Assigning it to a pad

Same scenario, but attaching the new net to a real footprint’s pad (pad.SetNet(new_net)) instead of a via: after SaveBoard/LoadBoard, grep "MY_NEW_NET" finds (net "MY_NEW_NET") in the file. Unlike a lone via, a net attached to a pad persists correctly — KiCad prunes nets not attached to any pad on save, but keeps the ones that are.

⚠️ Tested, and more restrictive than pcbnew: reassigning an already-existing pad’s net to a brand-new net name, via pad.net = Net(name=...); board.update_items(pad), always reverts on the very next read (board.get_pads()) — even with no save involved, and even if the pad isn’t touching any other copper. The new net doesn’t even show up in board.get_nets() in that case. On an open KiCad instance with a full project (schematic + PCB), a pad’s net stays driven by schematic-derived connectivity — forcing it by script isn’t enough to make it stick. Creating a fresh net and assigning it to a pad already on the board therefore doesn’t work at all on the IPC API; only an isolated via (see above) can hold a genuinely new net.

Going further

The tested rule holds on both APIs: a freshly created net only survives if it’s attached to something KiCad treats as “real” — a pad on the pcbnew side, an isolated item (typically a via, not a pad already tied to the schematic) on the IPC side. If your script generates a full copper pour on this new net (see the copper zone tutorial), attach the net to at least one pad or via that satisfies this rule before trusting it to still be there after save.