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.

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.

Creating the zone

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

from pcbnew import ZONE, F_Cu

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

With the IPC API, creation and layer selection happen in a single call:

from kicad import KiCad

kicad = KiCad()
board = kicad.get_board()
zone = board.create_zone(layer="F.Cu")

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 just a list of points in millimeters:

from kicad.geometry import Vector2

points = [(0, 0), (50, 0), (50, 30), (0, 30)]
zone.outline = [Vector2.from_mm(x, y) for x, y in points]
board.update_items(zone)

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, with a global fill command on the IPC API side:

zone.net = board.get_net("GND")
board.update_items(zone)
board.refill_zones()

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.