Migrating your pcbnew scripts to the IPC API before KiCad 11

Published on October 18, 2026 by leZ

pcbnew is being removed in KiCad 11: a pcbnew → kipy mapping table, the key differences, and a method to port your KiCad Python scripts.

The pcbnew Python bindings (SWIG) have been deprecated since KiCad 9, and the official KiCad documentation states that their removal is planned for KiCad 11. Every script starting with import pcbnew will then have to go through the IPC API and its Python client, kipy. This guide collects the mappings verified across this site’s tutorials, so you can port a script without guesswork.

What really changes

pcbnew IPC API (kipy)
Where the script runs Inside KiCad’s process, or with the Python bundled with KiCad In any Python, talking to KiCad over a socket
Installation Ships with KiCad pip install kicad-python
KiCad must be open No (headless, LoadBoard is enough) Yes, or started headless by kipy (KiCad 11)
Editing an object Immediate effect Edit the local copy, then board.update_items(...)
Creating an object board.Add(item) board.create_items(item)
Units FromMM() / ToMM() from_mm() / to_mm() (nanometres)

The most confusing point: with kipy, the objects you read are copies. Changing fp.position does nothing until you call board.update_items(fp).

Mapping table

Task pcbnew kipy
Open board board = pcbnew.GetBoard() board = KiCad().get_board()
Save pcbnew.SaveBoard(path, board) board.save()
Find a footprint board.FindFootprintByReference("U1") next(f for f in board.get_footprints() if f.reference_field.text.value == "U1")
Position fp.SetPosition(VECTOR2I(FromMM(30), FromMM(20))) fp.position = Vector2.from_xy_mm(30, 20)
Orientation fp.SetOrientationDegrees(90) fp.orientation = Angle.from_degrees(90)
Apply changes (immediate) board.update_items(fp)
Flip to the back fp.Flip(fp.GetPosition(), True) board.flip_items(fp)
Track PCB_TRACK(board) + board.Add(track) Track() + board.create_items(track)
Via PCB_VIA(board), SetWidth(via.TopLayer(), w), SetDrill(d) Via(), via.diameter = w, via.drill_diameter = d
Layer pcbnew.F_Cu board.get_layer_by_name("F.Cu")
Net board.FindNet("GND") Net(name="GND")
Zone ZONE(board) + SHAPE_POLY_SET outline Zone() + zone.outline = PolygonWithHoles
Lists board.GetFootprints(), board.Zones(), board.GetTracks() get_footprints(), get_zones(), get_tracks(), get_vias()
Gerber export PLOT_CONTROLLER(board) board.export_gerbers(...) (KiCad 11 + kicad-python 0.9)
Redraw pcbnew.Refresh() (automatic)

Each row is covered in detail, with both versions side by side, in the matching tutorial: positioning a footprint, creating a track, creating a via, creating a zone, exporting Gerbers.

Example: the same script in both APIs

Moving and rotating U1 with pcbnew:

import pcbnew
from pcbnew import VECTOR2I, FromMM

board = pcbnew.GetBoard()
fp = board.FindFootprintByReference("U1")
fp.SetPosition(VECTOR2I(FromMM(30), FromMM(20)))
fp.SetOrientationDegrees(90)
pcbnew.Refresh()

The same thing with kipy:

from kipy import KiCad
from kipy.geometry import Angle, Vector2

board = KiCad().get_board()
fp = next(f for f in board.get_footprints() if f.reference_field.text.value == "U1")
fp.position = Vector2.from_xy_mm(30, 20)
fp.orientation = Angle.from_degrees(90)
board.update_items(fp)
  1. Inventory: search your scripts for every pcbnew. and board. call to know what needs porting.
  2. Isolate board access in a single module (reading footprints, creating tracks…): it’s the only place to rewrite.
  3. Port line by line with the table above, without forgetting update_items() and create_items().
  4. Compare: run the old and new versions on a copy of the same board, then diff the files or run the DRC with kicad-cli.

First of all, enable the IPC API in KiCad’s preferences: it’s off by default.

Going further

Both APIs coexist in KiCad 9 and 10: you can migrate gradually, script by script, without waiting for KiCad 11. Every tutorial on the site has a switcher at the top to compare both versions.