Creating a copper zone (ground plane) with pcbnew's Python API

Published on September 9, 2026 by leZ

Generate a copper zone connected to a net and trigger its fill by script, with pcbnew or KiCad's IPC API.

Filled 50 × 30 mm rectangular GND copper zone on F.Cu, created by a pcbnew Python script in KiCad
The GND copper zone, filled by ZONE_FILLER.

A ground plane drawn and filled by hand has to be redone after every routing change. By scripting the zone’s creation and fill, you get a reproducible ground plane that can be regenerated with a single command after each change.

Seven copper zones of different shapes (circle, star, ring with a hole, hatched ground plane…) drawn by script, filmed live in KiCad, then checked with the DRC (1:26):

Creating the zone

Instantiate a ZONE object, pick its layer, then add it to the board:

import pcbnew

board = pcbnew.GetBoard()
from pcbnew import ZONE, F_Cu

zone = ZONE(board)
zone.SetLayer(F_Cu)
board.Add(zone)

With the IPC API, you instantiate a blank Zone object, then set its type (copper):

from kipy import KiCad
from kipy.board_types import Zone, ZoneType

kicad = KiCad()
board = kicad.get_board()
zone = Zone()
zone.type = ZoneType.ZT_COPPER

Defining the outline

The outline is a SHAPE_POLY_SET you append the polygon’s vertices to, in internal units:

from pcbnew import SHAPE_POLY_SET, FromMM

outline = SHAPE_POLY_SET()
outline.NewOutline()
for x, y in [(0, 0), (50, 0), (50, 30), (0, 30)]:
    outline.Append(FromMM(x), FromMM(y))
zone.SetOutline(outline)

On the IPC API side, the outline is built from a PolyLine of nodes (in nanometers, converted with from_mm), wrapped in a PolygonWithHoles:

from kipy.geometry import PolyLine, PolyLineNode
from kipy.common_types import PolygonWithHoles
from kipy.util.units import from_mm

outline = PolyLine()
for x, y in [(0, 0), (50, 0), (50, 30), (0, 30), (0, 0)]:  # last point closes the loop
    outline.append(PolyLineNode.from_xy(from_mm(x), from_mm(y)))

polygon = PolygonWithHoles()
polygon.outline = outline

zone.outline = polygon
zone.layers = [board.get_layer_by_name("F.Cu")]

Try it below: adjust width and height to see the zone’s outline change on the board.

outline.NewOutline()
for x, y in [(0, 0), (50, 0), (50, 30), (0, 30)]:
    outline.Append(FromMM(x), FromMM(y))
50
30
GND

Connecting the net and filling

Attach the zone to a net, then trigger the fill through ZONE_FILLER:

from pcbnew import ZONE_FILLER

zone.SetNet(board.FindNet("GND"))

filler = ZONE_FILLER(board)
filler.Fill(board.Zones())

Same idea for attaching the net. This is also the point, once every property is set, where the zone is actually created on the board — there’s no confirmed method for triggering a fill programmatically via the IPC API yet:

from kipy.board_types import Net

zone.net = Net(name="GND")
board.create_items(zone)

Going further

This skeleton is easy to reuse to generate a full per-layer ground plane, carve out keep-out zones around sensitive components, or automatically re-trigger the fill after every routing change instead of clicking “Fill All Zones” by hand.