Network Automation Reference

Meraki API Branch Automation in Python: Clone Networks, Fix VLANs, Update MX Firewall Rules

This guide puts the Meraki Dashboard API calls I use for branch rollouts in one place: create or clone a network, fix the VLAN subnet the clone brings with it, and update MX Layer 3 firewall rules without wiping the existing ones. Every script below is a template. The IDs, API key and subnets are placeholders, so run it against a test network first and check the result in the Dashboard before you point it at production.

Before you start

  • API key: generate it in the Dashboard under your profile (My profile, API access). The key needs write access to the organization. Keep it out of the script: export it as an environment variable, MERAKI_API_KEY.
  • Organization ID and Network ID: see how to find your Meraki Organization ID and Network ID.
  • Python: either requests for raw calls or the official SDK (pip install meraki). The SDK retries on HTTP 429 for you.
  • Rate limit: Meraki allows roughly 10 calls per second per organization. A script that creates a network, updates a VLAN and rewrites firewall rules back to back can hit it.

1. Plan the IP schema first

Cloning a network copies its subnets. If two sites in an Auto VPN topology advertise the same subnet, routing breaks, so work out each branch’s address block before you create anything. A simple scheme is one /23 per branch, derived from a branch number:

BranchSubnetGateway
110.200.0.0/2310.200.0.1
210.200.2.0/2310.200.2.1
310.200.4.0/2310.200.4.1

A /23 gives 510 usable addresses, enough for data, voice, guest and IoT in one block for a typical branch. Doing the math in the script means two branches can never get the same subnet.

def calculate_branch_subnet(branch_id):
    third_octet = (branch_id - 1) * 2
    subnet = f"10.200.{third_octet}.0/23"
    appliance_ip = f"10.200.{third_octet}.1"
    return subnet, appliance_ip

2. Create or clone a network

Creating a network gives you a blank network with default settings. Cloning, with copyFromNetworkId, copies the configuration of an existing “golden” network: VLANs, SSIDs, firewall rules. Both use the same endpoint:

POST /organizations/{organizationId}/networks

Supported product types are appliance (MX), switch (MS), wireless (MR), camera (MV), sensor (MT) and cellularGateway (MG). productTypes must be a list, not a string.

The JSON payload

{
  "name": "New_Retail_Branch_05",
  "timeZone": "America/New_York",
  "productTypes": ["appliance", "switch", "wireless"],
  "copyFromNetworkId": "N_1234567890123456"
}

Python with requests

import os
import requests

api_key = os.environ["MERAKI_API_KEY"]
org_id = "YOUR_ORGANIZATION_ID"
url = f"https://api.meraki.com/api/v1/organizations/{org_id}/networks"

headers = {
    "X-Cisco-Meraki-API-Key": api_key,
    "Content-Type": "application/json",
    "Accept": "application/json",
}

payload = {
    "name": "New_Retail_Branch_05",
    "timeZone": "America/New_York",
    "productTypes": ["appliance", "switch", "wireless"],
    "copyFromNetworkId": "N_1234567890123456",
}

response = requests.post(url, headers=headers, json=payload)

if response.status_code == 201:
    print("Network created:", response.json()["id"])
else:
    print("Failed:", response.status_code, response.text)

Python with the meraki SDK

import os
import meraki

dashboard = meraki.DashboardAPI(os.environ["MERAKI_API_KEY"], suppress_logging=True)

try:
    new_network = dashboard.organizations.createOrganizationNetwork(
        organizationId="YOUR_ORGANIZATION_ID",
        name="New_Retail_Branch_05",
        productTypes=["appliance", "switch", "wireless"],
        timeZone="America/New_York",
        copyFromNetworkId="N_1234567890123456",
    )
    print("New Network ID:", new_network["id"])
except meraki.APIError as e:
    print(f"Meraki API Error: {e}")

3. Fix the VLAN subnet after cloning

This is the step people miss. The clone is an exact copy, including the source site’s subnet, DHCP scope and fixed IP assignments. Update the VLAN straight away with the subnet you calculated, and clear the cloned static assignments so they do not conflict with the new range:

target_subnet, gateway_ip = calculate_branch_subnet(2)

dashboard.appliance.updateNetworkApplianceVlan(
    new_network["id"],
    vlanId="1",
    subnet=target_subnet,
    applianceIp=gateway_ip,
    fixedIpAssignments={},
    reservedIpRanges=[],
)

If this call fails with a 400, read the error text. Meraki’s messages are specific, for example that the appliance IP must be inside the subnet.

Same VLAN update with raw requests

Without the SDK the call is the same. Send only the fields you are changing. Replacing subnet without clearing the old fixed IP assignments and reserved ranges is the usual cause of an HTTP 400, because every address in the payload has to fit inside the new subnet.

import os
import json
import requests

API_KEY = os.environ["MERAKI_API_KEY"]
NETWORK_ID = "YOUR_NETWORK_ID"
VLAN_ID = "10"

url = f"https://api.meraki.com/api/v1/networks/{NETWORK_ID}/appliance/vlans/{VLAN_ID}"
headers = {"X-Cisco-Meraki-API-Key": API_KEY, "Content-Type": "application/json", "Accept": "application/json"}

# Look at the current VLAN first
current = requests.get(url, headers=headers)
current.raise_for_status()
print(json.dumps(current.json(), indent=2))

# Change the subnet and clear the cloned fixed IPs
payload = {
    "subnet": "10.200.4.0/24",
    "applianceIp": "10.200.4.1",
    "fixedIpAssignments": {},
    "reservedIpRanges": [],
}
response = requests.put(url, headers=headers, json=payload)
print(response.status_code)
print(response.text)

A 404 means the VLAN ID does not exist on that network, so create it first with a POST and then update it. To check that a new subnet and gateway fit together, use the free Subnet Calculator.

Same VLAN update with raw requests

Without the SDK the call is the same. Send only the fields you are changing. Replacing subnet without clearing the old fixed IP assignments and reserved ranges is the usual cause of an HTTP 400, because every address in the payload has to fit inside the new subnet.

import os
import json
import requests

API_KEY = os.environ["MERAKI_API_KEY"]
NETWORK_ID = "YOUR_NETWORK_ID"
VLAN_ID = "10"

url = f"https://api.meraki.com/api/v1/networks/{NETWORK_ID}/appliance/vlans/{VLAN_ID}"
headers = {"X-Cisco-Meraki-API-Key": API_KEY, "Content-Type": "application/json", "Accept": "application/json"}

# Look at the current VLAN first
current = requests.get(url, headers=headers)
current.raise_for_status()
print(json.dumps(current.json(), indent=2))

# Change the subnet and clear the cloned fixed IPs
payload = {
    "subnet": "10.200.4.0/24",
    "applianceIp": "10.200.4.1",
    "fixedIpAssignments": {},
    "reservedIpRanges": [],
}
response = requests.put(url, headers=headers, json=payload)
print(response.status_code)
print(response.text)

A 404 means the VLAN ID does not exist on that network, so create it first with a POST and then update it. To check that a new subnet and gateway fit together, use the free Subnet Calculator.

4. Update MX Layer 3 firewall rules without deleting the rest

The endpoint is PUT /networks/{networkId}/appliance/firewall/l3FirewallRules. There is no PATCH for adding one rule, so PUT replaces the whole list. Send one rule to a firewall that has 50 and the 50 are gone. The safe order is always: GET the current rules, change the one you need in Python, PUT the full list back.

import os
import requests

API_KEY = os.environ["MERAKI_API_KEY"]
NETWORK_ID = "L_1234567890123456"
URL = f"https://api.meraki.com/api/v1/networks/{NETWORK_ID}/appliance/firewall/l3FirewallRules"

headers = {
    "X-Cisco-Meraki-API-Key": API_KEY,
    "Content-Type": "application/json",
    "Accept": "application/json",
}

def update_firewall_rules():
    # 1. Get the existing rules
    response = requests.get(URL, headers=headers)
    response.raise_for_status()
    rules = response.json().get("rules", [])

    # 2. Change one rule locally, matched by its comment
    new_source_cidr = "10.50.10.0/24"
    for rule in rules:
        if rule.get("comment") == "Allow Printers to Internal":
            rule["srcCidr"] = new_source_cidr
            break
    else:
        print("Target rule not found, nothing changed.")
        return

    # 3. Put the complete list back
    put_response = requests.put(URL, headers=headers, json={"rules": rules})
    put_response.raise_for_status()
    print("Rules updated, HTTP", put_response.status_code)

if __name__ == "__main__":
    update_firewall_rules()

A rule is an object with comment, policy, protocol, srcCidr, srcPort, destCidr and destPort. The change applies in the background like clicking Save in the Dashboard. The SDK equivalent is dashboard.appliance.updateNetworkApplianceFirewallL3FirewallRules().

If a rule you pushed opens a port to the outside, confirm it is reachable with our free Port Checker, not only accepted by the API.

5. Putting it together

A branch rollout is the four steps in order, with a short pause between them if you are running many sites:

  1. Calculate the subnet and gateway from the branch number.
  2. Create the network by cloning the golden template.
  3. Update the VLAN subnet and clear the cloned fixed IP assignments.
  4. Update the firewall rules so the new subnet is allowed or restricted the way the policy requires.

Wrap SDK calls in try/except meraki.APIError and print the error. If a call keeps returning 400, paste the payload your script generates into Postman. That tells you quickly whether the problem is the Python or the Meraki network.

Troubleshooting

ErrorUsual cause
400 Bad RequestMalformed payload. productTypes and rules must be lists. After a clone, stale fixed IP assignments also cause 400 on a VLAN update.
401 UnauthorizedAPI key missing, wrong, or without write access to the organization or network.
404 Not FoundWrong Organization ID or Network ID in the URL.
429 Too Many RequestsRate limit. Use the SDK, or add a short time.sleep() between calls.

FAQ

What is the difference between creating and cloning a Meraki network?

Creating gives a blank network with default settings. Cloning with copyFromNetworkId copies the source network’s configuration, including VLAN subnets, so you must update the subnets afterwards.

Why did my API call delete all my other firewall rules?

The Layer 3 firewall endpoint uses PUT, which overwrites the whole list. Always GET first, edit the list, and PUT all of it back.

Does changing firewall rules by API cause downtime?

The change applies in the background, the same as saving in the Dashboard. Still test on a non-production network first.

Postman or Python?

Postman is good for exploring endpoints and testing a single payload. Python is for looping over sites and chaining calls.

Related guides: Python NETCONF with ncclient on Cisco IOS-XE

Advertisement

Leave a Comment

Sign in with Google to comment โ€” verifies you're a real person. No WordPress account is created; nothing beyond your name and email is used.

โœ“ Signed in โ€” you can comment below.