Repositioning footprints in bulk from a CSV file with pcbnew's Python API

Published on September 22, 2026 by leZ

Read a list of references and coordinates from a CSV file, then reposition every matching footprint in a single script — with pcbnew or KiCad's IPC API.

Before / after: U1 and R1 repositioned from a CSV file (U1 at (20, 15), 0°; R1 at (48, 15), 90°) in KiCad
Footprints before, then placed from the CSV.

Coming from Eagle or Fusion 360, it’s common to prepare a coordinate table in a spreadsheet and replay it in one shot to reposition every component at once, instead of dragging each one by hand. The same workflow scripts easily in KiCad from a CSV file: reference, X/Y position, and rotation, applied in a single pass.

The CSV format

One row per footprint to reposition, with its reference and position in millimeters:

ref,x_mm,y_mm,rotation_deg
U1,20,15,0
R1,48,15,90
C1,48,30,90

Reading the file

This step is the same regardless of which KiCad API comes next: the standard library’s csv module is enough, nothing extra to install:

import csv

with open("placements.csv") as f:
    rows = list(csv.DictReader(f))

Repositioning each footprint

This script runs fully headless (see the installation tutorial): LoadBoard opens the .kicad_pcb file in memory without launching KiCad, then SaveBoard writes the result back — handy for replaying a full placement in a single command:

import pcbnew

board = pcbnew.LoadBoard("my_board.kicad_pcb")

for row in rows:
    fp = board.FindFootprintByReference(row["ref"])
    if fp is None:
        continue
    fp.SetPosition(pcbnew.VECTOR2I(pcbnew.FromMM(float(row["x_mm"])), pcbnew.FromMM(float(row["y_mm"]))))
    fp.SetOrientationDegrees(float(row["rotation_deg"]))

pcbnew.SaveBoard("my_board.kicad_pcb", board)

The same code works just as well from KiCad’s scripting console: just replace the LoadBoard/SaveBoard lines with board = pcbnew.GetBoard(), as in the footprint placement tutorial.

With the IPC API (the kipy package), changes are grouped into an explicit commit, so the whole batch can be undone at once with Ctrl+Z if the result isn’t right:

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

board = KiCad().get_board()
footprints = board.get_footprints()
commit = board.begin_commit()

updated = []
for row in rows:
    fp = next((f for f in footprints if f.reference_field.text.value == row["ref"]), None)
    if fp is None:
        continue
    fp.position = Vector2.from_xy_mm(float(row["x_mm"]), float(row["y_mm"]))
    fp.orientation = Angle.from_degrees(float(row["rotation_deg"]))
    updated.append(fp)

board.update_items(updated)
board.push_commit(commit, "Move footprints via script")

Try it below: change the X/Y position for each reference to see all three footprints move on the board, just like editing rows in the CSV.

for row in rows:
    fp = board.FindFootprintByReference(row["ref"])
    fp.SetPosition(VECTOR2I(FromMM(20), FromMM(15)))
U1
R1
C1
U1R1C1

Going further

This approach generalizes to any external coordinate source — a panelization tool’s export, another ECAD tool, or a homemade spreadsheet — as long as it produces one reference and one position per row. Add a layer column to the CSV if you also need to flip some footprints to the back side (see the footprint flip tutorial).