Overview
KiCad uses S-expression based text formats for all design files. This makes them human-readable, version-control friendly, and easy to parse programmatically. Understanding these formats enables custom tool development and advanced automation.File Format Philosophy
KiCad’s file formats follow these principles:- Human-readable: Text-based S-expressions
- Self-documenting: Tag names describe content
- Forward-compatible: Unknown tags are preserved
- Hierarchical: Nested structure reflects logical relationships
- Version-tracked: Format version in header
PCB File Format (.kicad_pcb)
File Structure
Based on/demos/cm5_minima/CM5_MINIMA_3.kicad_pcb:
(kicad_pcb
(version 20250513)
(generator "pcbnew")
(generator_version "9.99")
(general
(thickness 1.5296)
(legacy_teardrops no)
)
(paper "A5")
(title_block
(title "My Board")
(date "2024-01-15")
(rev "1.0")
(comment 1 "Author Name")
)
(layers ...)
(setup ...)
(net ...)
(footprint ...)
(gr_line ...)
(segment ...)
(via ...)
(zone ...)
)
Layer Definition
(layers
(0 "F.Cu" signal)
(4 "In1.Cu" signal)
(6 "In2.Cu" signal)
(2 "B.Cu" signal)
(5 "F.SilkS" user "F.Silkscreen")
(7 "B.SilkS" user "B.Silkscreen")
(1 "F.Mask" user)
(3 "B.Mask" user)
(13 "F.Paste" user)
(15 "B.Paste" user)
(25 "Edge.Cuts" user)
(27 "Margin" user)
(31 "F.CrtYd" user "F.Courtyard")
(29 "B.CrtYd" user "B.Courtyard")
(35 "F.Fab" user)
(33 "B.Fab" user)
(39 "User.1" user)
)
(id "name" type ["display_name"])
Stackup Definition
(setup
(stackup
(layer "F.SilkS"
(type "Top Silk Screen")
)
(layer "F.Paste"
(type "Top Solder Paste")
)
(layer "F.Mask"
(type "Top Solder Mask")
(thickness 0.01)
)
(layer "F.Cu"
(type "copper")
(thickness 0.035)
)
(layer "dielectric 1"
(type "prepreg")
(thickness 0.2104)
(material "FR4")
(epsilon_r 4.5)
(loss_tangent 0.02)
)
(layer "In1.Cu"
(type "copper")
(thickness 0.0152)
)
(copper_finish "None")
(dielectric_constraints no)
)
)
Footprint Definition
(footprint "Resistor_SMD:R_0805_2012Metric"
(layer "F.Cu")
(uuid "12345678-1234-1234-1234-123456789abc")
(at 100 50 90)
(descr "Resistor SMD 0805")
(tags "resistor")
(property "Reference" "R1"
(at 0 -1.65 90)
(layer "F.SilkS")
(uuid "abcd1234-5678-90ab-cdef-1234567890ab")
(effects
(font
(size 1 1)
(thickness 0.15)
)
)
)
(property "Value" "10k"
(at 0 1.65 90)
(layer "F.Fab")
(uuid "efgh5678-90ab-cdef-1234-567890abcdef")
(effects
(font
(size 1 1)
(thickness 0.15)
)
)
)
(fp_line
(start -0.227064 -0.735)
(end 0.227064 -0.735)
(stroke
(width 0.12)
(type solid)
)
(layer "F.SilkS")
(uuid "wxyz-0123-4567-89ab-cdef01234567")
)
(pad "1" smd rect
(at -0.9125 0 90)
(size 1.025 1.4)
(layers "F.Cu" "F.Paste" "F.Mask")
(net 1 "GND")
(uuid "1111-2222-3333-4444-555566667777")
)
(pad "2" smd rect
(at 0.9125 0 90)
(size 1.025 1.4)
(layers "F.Cu" "F.Paste" "F.Mask")
(net 2 "+3V3")
(uuid "8888-9999-aaaa-bbbb-ccccddddeeee")
)
)
Track and Via
(segment
(start 100 50)
(end 120 50)
(width 0.25)
(layer "F.Cu")
(net 1)
(uuid "track-uuid-here")
)
(via
(at 120 50)
(size 0.8)
(drill 0.4)
(layers "F.Cu" "B.Cu")
(net 1)
(uuid "via-uuid-here")
)
Zone (Copper Pour)
(zone
(net 1)
(net_name "GND")
(layer "F.Cu")
(uuid "zone-uuid")
(hatch edge 0.508)
(connect_pads
(clearance 0.508)
)
(min_thickness 0.254)
(filled_areas_thickness no)
(keepout
(tracks not_allowed)
(vias not_allowed)
(pads not_allowed)
(copperpour not_allowed)
(footprints allowed)
)
(fill
(thermal_gap 0.508)
(thermal_bridge_width 0.508)
)
(polygon
(pts
(xy 90 40)
(xy 130 40)
(xy 130 60)
(xy 90 60)
)
)
)
Schematic File Format (.kicad_sch)
Basic Structure
(kicad_sch
(version 20231120)
(generator "eeschema")
(uuid "root-sheet-uuid")
(paper "A4")
(title_block
(title "My Schematic")
(date "2024-01-15")
(rev "1.0")
)
(lib_symbols ...)
(symbol ...)
(wire ...)
(junction ...)
(label ...)
(global_label ...)
)
Symbol Instance
(symbol
(lib_id "Device:R")
(at 100 100 0)
(unit 1)
(exclude_from_sim no)
(in_bom yes)
(on_board yes)
(uuid "symbol-uuid")
(property "Reference" "R1"
(at 102 99 0)
(effects
(font (size 1.27 1.27))
(justify left)
)
)
(property "Value" "10k"
(at 102 101 0)
(effects
(font (size 1.27 1.27))
(justify left)
)
)
(property "Sim.Device" "R"
(at 0 0 0)
(effects (font (size 1.27 1.27)) hide)
)
(property "Sim.Params" "r=10k"
(at 0 0 0)
(effects (font (size 1.27 1.27)) hide)
)
(pin "1" (uuid "pin1-uuid"))
(pin "2" (uuid "pin2-uuid"))
)
Wire and Connections
(wire
(pts
(xy 100 100)
(xy 120 100)
)
(stroke
(width 0)
(type default)
)
(uuid "wire-uuid")
)
(junction
(at 120 100)
(diameter 0)
(color 0 0 0 0)
(uuid "junction-uuid")
)
(label "SIGNAL"
(at 110 100 0)
(effects
(font (size 1.27 1.27))
(justify left bottom)
)
(uuid "label-uuid")
)
Project File Format (.kicad_pro)
{
"board": {
"design_settings": {
"defaults": {
"board_outline_line_width": 0.1,
"copper_line_width": 0.2,
"copper_text_size_h": 1.5,
"copper_text_size_v": 1.5
},
"diff_pair_dimensions": [
{
"gap": 0.25,
"via_gap": 0.25,
"width": 0.2
}
],
"drc_exclusions": [],
"rules": {
"min_copper_edge_clearance": 0.0,
"solder_mask_clearance": 0.0,
"solder_mask_min_width": 0.0
},
"track_widths": [0.25, 0.5, 1.0],
"via_dimensions": [
{
"diameter": 0.8,
"drill": 0.4
}
]
},
"layers": {
"boardThickness": 1.6,
"copperLayers": 2
}
},
"schematic": {
"legacy_lib_dir": "",
"legacy_lib_list": []
},
"sheets": [
["root", "Main Sheet"]
],
"text_variables": {}
}
Parsing Files with Python
S-Expression Parser
import re
from typing import List, Union, Any
SExpression = Union[str, List[Any]]
def parse_sexpr(text: str) -> List[SExpression]:
"""
Parse KiCad S-expression format
Args:
text: S-expression string
Returns:
Parsed nested list structure
"""
# Tokenize
tokens = re.findall(r'\(|\)|"[^"]*"|[^\s()]+', text)
def parse_tokens(tokens, pos=0):
result = []
while pos < len(tokens):
token = tokens[pos]
if token == '(':
# Start of new list
sublist, pos = parse_tokens(tokens, pos + 1)
result.append(sublist)
elif token == ')':
# End of current list
return result, pos
else:
# Atom (string or number)
if token.startswith('"'):
result.append(token[1:-1]) # Remove quotes
elif token.replace('.', '').replace('-', '').isdigit():
result.append(float(token) if '.' in token else int(token))
else:
result.append(token)
pos += 1
return result, pos
parsed, _ = parse_tokens(tokens)
return parsed
def find_sections(sexpr: List, tag: str) -> List[List]:
"""
Find all sections with a specific tag
Args:
sexpr: Parsed S-expression
tag: Tag name to search for
Returns:
List of matching sections
"""
results = []
def search(node):
if isinstance(node, list):
if len(node) > 0 and node[0] == tag:
results.append(node)
for item in node:
search(item)
search(sexpr)
return results
# Usage
with open('board.kicad_pcb', 'r') as f:
content = f.read()
parsed = parse_sexpr(content)
# Find all footprints
footprints = find_sections(parsed, 'footprint')
for fp in footprints:
print(f"Footprint: {fp[1]}") # Library reference
# Find all nets
nets = find_sections(parsed, 'net')
for net in nets:
net_code = net[1]
net_name = net[2]
print(f"Net {net_code}: {net_name}")
Extract Board Information
class KiCadBoardParser:
def __init__(self, filepath):
with open(filepath, 'r') as f:
content = f.read()
self.sexpr = parse_sexpr(content)[0] # Root kicad_pcb node
def get_version(self):
"""Get file format version"""
for item in self.sexpr:
if isinstance(item, list) and item[0] == 'version':
return item[1]
return None
def get_layers(self):
"""Extract layer definitions"""
layers = find_sections(self.sexpr, 'layers')
if not layers:
return []
layer_list = []
for item in layers[0][1:]:
if isinstance(item, list):
layer_id = item[0]
layer_name = item[1]
layer_type = item[2] if len(item) > 2 else None
layer_list.append({
'id': layer_id,
'name': layer_name,
'type': layer_type
})
return layer_list
def get_stackup(self):
"""Extract stackup configuration"""
stackup = find_sections(self.sexpr, 'stackup')
if not stackup:
return None
layers = []
for item in stackup[0][1:]:
if isinstance(item, list) and item[0] == 'layer':
layer_info = {'name': item[1]}
for prop in item[2:]:
if isinstance(prop, list):
layer_info[prop[0]] = prop[1]
layers.append(layer_info)
return layers
def get_footprints(self):
"""Get all footprints with positions"""
footprints = find_sections(self.sexpr, 'footprint')
result = []
for fp in footprints:
fp_data = {
'library': fp[1],
'layer': None,
'position': None,
'rotation': 0,
'reference': None,
'value': None
}
for item in fp[2:]:
if isinstance(item, list):
if item[0] == 'layer':
fp_data['layer'] = item[1]
elif item[0] == 'at':
fp_data['position'] = (item[1], item[2])
if len(item) > 3:
fp_data['rotation'] = item[3]
elif item[0] == 'property' and item[1] == 'Reference':
fp_data['reference'] = item[2]
elif item[0] == 'property' and item[1] == 'Value':
fp_data['value'] = item[2]
result.append(fp_data)
return result
# Usage
parser = KiCadBoardParser('my_board.kicad_pcb')
print(f"Version: {parser.get_version()}")
print(f"\nLayers: {len(parser.get_layers())}")
for layer in parser.get_layers():
print(f" {layer['id']}: {layer['name']} ({layer['type']})")
print(f"\nFootprints: {len(parser.get_footprints())}")
for fp in parser.get_footprints()[:5]: # First 5
print(f" {fp['reference']}: {fp['library']} at {fp['position']}")
Modifying Files
Generate Custom Footprint
def generate_footprint(name, pads):
"""
Generate footprint S-expression
Args:
name: Footprint name
pads: List of (number, x, y, width, height) tuples
Returns:
S-expression string
"""
import uuid
fp_uuid = str(uuid.uuid4())
lines = [
f'(footprint "{name}"',
' (layer "F.Cu")',
f' (uuid "{fp_uuid}")',
' (at 0 0)',
]
for i, (num, x, y, w, h) in enumerate(pads):
pad_uuid = str(uuid.uuid4())
lines.extend([
f' (pad "{num}" smd rect',
f' (at {x} {y})',
f' (size {w} {h})',
' (layers "F.Cu" "F.Paste" "F.Mask")',
f' (uuid "{pad_uuid}")',
' )'
])
lines.append(')')
return '\n'.join(lines)
# Generate 4-pad footprint
pads = [
("1", -1.0, -1.0, 0.6, 0.6),
("2", 1.0, -1.0, 0.6, 0.6),
("3", 1.0, 1.0, 0.6, 0.6),
("4", -1.0, 1.0, 0.6, 0.6),
]
footprint = generate_footprint("Custom_QFN_4", pads)
print(footprint)
Best Practices
- Preserve UUIDs: Never reuse or modify existing UUIDs
- Maintain version: Keep format version up to date
- Validate syntax: Ensure balanced parentheses
- Backup before editing: Always keep originals
- Use version control: Track changes with git
- Test incrementally: Verify file loads after each change
- Follow conventions: Match KiCad’s formatting style
See Also
- Python Scripting - Programmatic access
- IPC API - Modern API interface
- Action Plugins - Interactive tools