Cisco IOS RESTCONF Basics — Query and Configure with REST API

Every configuration command you type in the Cisco IOS CLI has an equivalent representation as structured data in a YANG model — a machine-readable schema that describes exactly what fields exist, what types they hold, and what constraints apply. RESTCONF (RFC 8040) is the HTTP API that exposes this YANG-modelled data over standard REST calls: a GET retrieves the current configuration or state, a PUT replaces it, a PATCH modifies it, and a DELETE removes it. Every request and response uses JSON (or XML) — structured data that tools like Python, Postman, Ansible, and CI/CD pipelines can consume directly without parsing CLI text. For an overview of YANG, JSON, and XML in networking see JSON, XML & YANG, and for the broader automation landscape see Network Automation Overview.

Where Netmiko automates the CLI (screen-scraping text output), RESTCONF speaks to the device's data model directly: you get structured JSON back instead of text you must parse. The trade-off is that RESTCONF requires IOS-XE 16.x or later, HTTPS must be enabled, and you need to know the correct YANG model path for the data you want. This lab enables RESTCONF on NetsTuts-R1, retrieves full interface data in JSON using both Postman and Python requests, and then performs a configuration change via HTTP PATCH. For the SSH and HTTPS prerequisites, see SSH Configuration. For AAA credentials used by RESTCONF, see AAA TACACS+ Configuration.

1. RESTCONF Architecture — How It Works

The Stack: YANG → NETCONF → RESTCONF

Both NETCONF and RESTCONF sit on top of YANG data models but use different transports and semantics. See NETCONF & RESTCONF Overview for a side-by-side comparison.

  ┌─────────────────────────────────────────────────────────────────┐
  │                      YANG Data Models                           │
  │  Machine-readable schemas defining every configurable object.   │
  │  e.g. ietf-interfaces.yang: defines interface name, IP,        │
  │  enabled state, type, statistics — as structured types          │
  └──────────────────────┬──────────────────────────────────────────┘
                         │  same data, different transport
          ┌──────────────┴──────────────┐
          ▼                             ▼
  NETCONF (RFC 6241)           RESTCONF (RFC 8040)
  SSH-based XML RPC            HTTP/HTTPS + JSON or XML
  Port 830                     Port 443 (HTTPS)
  Older, more feature-rich     Simpler, REST-native
  Full transaction support     Stateless HTTP requests
  Used by Ansible/NSO          Used by Postman/Python/web tools

  RESTCONF URL Structure:
  https://[device-ip]/restconf/data/[yang-module]:[container]/[list-key]

  Example — retrieve all interfaces:
  GET https://192.168.10.1/restconf/data/ietf-interfaces:interfaces

  Example — retrieve one specific interface:
  GET https://192.168.10.1/restconf/data/ietf-interfaces:interfaces/interface=GigabitEthernet1

  Example — retrieve IP address config on one interface:
  GET https://192.168.10.1/restconf/data/ietf-interfaces:interfaces/interface=GigabitEthernet1/ietf-ip:ipv4
  

RESTCONF HTTP Methods Mapped to CRUD Operations

RESTCONF uses standard HTTP verbs. For a broader look at REST API methods and conventions see REST API Methods and REST API Overview.

HTTP Method RESTCONF Operation Cisco IOS Equivalent Effect
GET Read configuration or operational state show running-config / show commands Returns JSON/XML of the requested YANG node — no changes made
PUT Create or replace a resource entirely Full interface reconfiguration (replaces all sub-fields) Replaces the entire resource at the URL with the request body. Existing sub-fields not in the body are deleted
PATCH Merge-update — modify specific fields only Targeted interface config changes without touching other settings Merges the request body into the existing resource — only specified fields are changed, others untouched. Safest for partial updates
POST Create a new resource inside a container Adding a new loopback, a new VLAN, a new static route Creates a new child resource. Fails with 409 Conflict if the resource already exists
DELETE Remove a resource no interface Loopback0 / no ip address Removes the specified resource and all its children from the configuration

Key YANG Models Used in This Lab

YANG Module Data It Describes Base URL Path
ietf-interfaces All interfaces: name, type, enabled state, description /restconf/data/ietf-interfaces:interfaces
ietf-ip IPv4/IPv6 addressing augmented onto interfaces /restconf/data/ietf-interfaces:interfaces/interface={name}/ietf-ip:ipv4
Cisco-IOS-XE-native All Cisco-specific IOS-XE configuration (hostname, VLANs, routing, etc.) /restconf/data/Cisco-IOS-XE-native:native
Cisco-IOS-XE-interface-common Cisco-specific interface extensions (duplex, speed, negotiation) Augments ietf-interfaces paths
ietf-yang-library List of all YANG modules supported by this device /restconf/data/ietf-yang-library:modules-state
Standard vs Cisco-native YANG models. IETF standard models (ietf-interfaces, ietf-ip) provide a vendor-neutral view of common data. They work on Cisco, Juniper, and Arista with the same URL paths — enabling vendor-agnostic automation. Cisco-native models (Cisco-IOS-XE-native) expose every IOS-XE-specific feature not covered by IETF standards. For maximum portability, use IETF models where they exist. For Cisco-specific features (e.g., EIGRP, specific QoS policies), use Cisco-native models.

2. Lab Environment & Prerequisites

  Admin Workstation                          NetsTuts-R1
  Windows / Linux / macOS                    Cisco ISR 4321
  Postman installed                          IOS-XE 16.09.05
  Python 3.8+ with requests library          HTTPS enabled (port 443)
  IP: 192.168.10.5                           Mgmt IP: 192.168.10.1
        │                                          │
        └──────── HTTPS TCP/443 (RESTCONF) ────────┘
                    192.168.10.0/24

  IOS-XE Prerequisites (must be configured before RESTCONF works):
  ┌──────────────────────────────────────────────────────────────────┐
  │  hostname NetsTuts-R1                                            │
  │  ip domain-name netstuts.com                                     │
  │  crypto key generate rsa modulus 2048                            │
  │  ip http secure-server          ← HTTPS must be enabled          │
  │  ip http authentication local   ← use local username database    │
  │  restconf                       ← enables the RESTCONF subsystem │
  │  username restapi privilege 15 secret RestAPI2026!               │
  └──────────────────────────────────────────────────────────────────┘

  Router Interfaces:
  ┌───────────────────────────────────────────────────────────────┐
  │  GigabitEthernet1   192.168.10.1/24  (management — up/up)    │
  │  GigabitEthernet2   192.168.20.1/24  (LAN segment — up/up)   │
  │  GigabitEthernet3   unassigned       (admin down)             │
  │  Loopback0          10.255.255.1/32  (router ID — up/up)      │
  └───────────────────────────────────────────────────────────────┘
  
IOS-XE vs classic IOS. RESTCONF requires IOS-XE 16.x or later. Classic IOS (12.x, 15.x on ISR G1/G2 routers) does not support RESTCONF. In Cisco Packet Tracer (used for CCNA labs), RESTCONF is not available — use Cisco DevNet Sandbox (free, at devnetsandbox.cisco.com) or a GNS3/EVE-NG lab with an IOS-XE image instead. The RESTCONF subsystem also requires the HTTPS server to be running: ip http secure-server is mandatory. ip http server (plain HTTP, port 80) is optionally available but should be disabled in production as it transmits credentials in cleartext.

3. Step 1 — Enable RESTCONF on IOS-XE

NetsTuts-R1>en
NetsTuts-R1#conf t

! ── RSA key (required for HTTPS — skip if already generated for SSH) ──
NetsTuts-R1(config)#crypto key generate rsa modulus 2048
% You already have RSA keys defined named NetsTuts-R1.netstuts.com.
% Do you really want to replace them? [yes/no]: yes
The name for the keys will be: NetsTuts-R1.netstuts.com
% The key modulus size is 2048 bits

! ── Enable HTTPS server ────────────────────────────────────────────
NetsTuts-R1(config)#ip http secure-server
NetsTuts-R1(config)#ip http authentication local

! ── OPTIONAL: Enable plain HTTP (NOT recommended for production) ──
! NetsTuts-R1(config)#ip http server

! ── Create the RESTCONF API user (privilege 15 required) ──────────
NetsTuts-R1(config)#username restapi privilege 15 secret RestAPI2026!

! ── Enable RESTCONF subsystem ─────────────────────────────────────
NetsTuts-R1(config)#restconf

! ── Verify RESTCONF and HTTPS are running ─────────────────────────
NetsTuts-R1(config)#end
NetsTuts-R1#show platform software yang-management process

confd            : Running
nesd             : Running
syncfd           : Running
ncsshd           : Running
dmiauthd         : Running
nginx            : Running         ← HTTPS server (serves RESTCONF)
ndbmand          : Running
pubd             : Running

NetsTuts-R1#show ip http server status

HTTP server status: Disabled
HTTP server port: 80
HTTP server authentication method: local

HTTPS server status: Enabled       ← must show Enabled
HTTPS server port: 443
HTTPS server authentication method: local
  
The restconf command activates the IOS-XE YANG management subsystem. The nginx process shown in show platform software yang-management process is the HTTPS web server that handles incoming RESTCONF requests. All eight processes showing "Running" confirms the full YANG stack is operational. If any process shows "Not Running," restart with no restconf followed by restconf. The RESTCONF user must have privilege 15 — lower privilege levels cannot read or write YANG data via RESTCONF.

4. Step 2 — Discover Device Capabilities

Before querying specific data, query the RESTCONF root to confirm the API is responding and to discover the device's capabilities document — the list of all supported YANG modules:

RESTCONF Root and Capabilities Endpoints

Endpoint Purpose Key Information Returned
GET /restconf RESTCONF root — confirms the API is live Links to /restconf/data and /restconf/operations
GET /restconf/data/ietf-yang-library:modules-state Full list of supported YANG modules with revision dates All module names and versions the device supports — essential for knowing which paths are valid
GET /restconf/data Root datastore — top-level containers of all supported models Links to all top-level YANG containers (interfaces, native, routing, etc.)

Postman: First Request — RESTCONF Root

For a guide on setting up Postman and Ansible for network automation see Postman & Ansible for Network Automation.

  ── Postman Setup for all RESTCONF requests ─────────────────────

  1. New Request → Method: GET
     URL: https://192.168.10.1/restconf

  2. Authorization tab:
     Type: Basic Auth
     Username: restapi
     Password: RestAPI2026!

  3. Headers tab — add:
     Accept: application/yang-data+json
     Content-Type: application/yang-data+json

  4. Settings tab:
     ✓ Disable SSL Certificate Verification
       (Self-signed cert on the router — required for lab use)

  5. Send → Expected response: 200 OK

  Response body:
  {
    "ietf-restconf:restconf": {
      "data": {},
      "operations": {},
      "yang-library-version": "2016-06-21"
    }
  }
  
The Accept and Content-Type headers are mandatory for RESTCONF. The MIME type application/yang-data+json tells the router to respond with JSON-formatted YANG data (as opposed to XML, which uses application/yang-data+xml). Without these headers, IOS-XE returns a 406 Not Acceptable error. SSL certificate verification must be disabled in Postman for lab use because the router uses a self-signed RSA certificate generated by the crypto key generate rsa command — not a certificate signed by a trusted CA. In Python, the equivalent is verify=False in the requests call. In production, deploy a proper certificate signed by your organisation's CA and enable verification.

5. Step 3 — GET All Interfaces (Postman)

Postman Request

  Method:  GET
  URL:     https://192.168.10.1/restconf/data/ietf-interfaces:interfaces

  Headers:
    Accept:       application/yang-data+json
    Content-Type: application/yang-data+json

  Authorization: Basic Auth — restapi / RestAPI2026!
  SSL Verify: Disabled

  → Send
  

JSON Response — 200 OK

{
  "ietf-interfaces:interfaces": {
    "interface": [
      {
        "name": "GigabitEthernet1",
        "description": "Management Interface - DO NOT SHUTDOWN",
        "type": "iana-if-type:ethernetCsmacd",
        "enabled": true,
        "ietf-ip:ipv4": {
          "address": [
            {
              "ip": "192.168.10.1",
              "prefix-length": 24
            }
          ]
        }
      },
      {
        "name": "GigabitEthernet2",
        "description": "LAN Segment",
        "type": "iana-if-type:ethernetCsmacd",
        "enabled": true,
        "ietf-ip:ipv4": {
          "address": [
            {
              "ip": "192.168.20.1",
              "prefix-length": 24
            }
          ]
        }
      },
      {
        "name": "GigabitEthernet3",
        "description": "",
        "type": "iana-if-type:ethernetCsmacd",
        "enabled": false,
        "ietf-ip:ipv4": {}
      },
      {
        "name": "Loopback0",
        "description": "Router ID Loopback",
        "type": "iana-if-type:softwareLoopback",
        "enabled": true,
        "ietf-ip:ipv4": {
          "address": [
            {
              "ip": "10.255.255.1",
              "prefix-length": 32
            }
          ]
        }
      }
    ]
  }
}
  

Understanding the Response Structure

JSON Field YANG Source CLI Equivalent
"name" ietf-interfaces:interfaces/interface/name — the list key Interface name in show ip interface brief
"description" ietf-interfaces:interfaces/interface/description description command under interface config
"type" ietf-interfaces:interfaces/interface/type — IANA interface type identity Implicit from interface GigabitEthernet / interface Loopback
"enabled" ietf-interfaces:interfaces/interface/enabled no shutdown = true, shutdown = false (see show interfaces)
"ietf-ip:ipv4" Augmented by ietf-ip module onto the interface list entry ip address command under interface
"prefix-length" ietf-ip:ipv4/address/prefix-length — integer (CIDR notation) Subnet mask (24 = 255.255.255.0, 32 = 255.255.255.255)

GET a Single Interface

  Method: GET
  URL:    https://192.168.10.1/restconf/data/ietf-interfaces:interfaces/interface=GigabitEthernet1

  Note: the list key (interface name) is appended after = in the URL path.
  Spaces in interface names must be URL-encoded as %20, but Cisco IOS-XE
  interface names (GigabitEthernet1, Loopback0) contain no spaces.
  
{
  "ietf-interfaces:interface": {
    "name": "GigabitEthernet1",
    "description": "Management Interface - DO NOT SHUTDOWN",
    "type": "iana-if-type:ethernetCsmacd",
    "enabled": true,
    "ietf-ip:ipv4": {
      "address": [
        {
          "ip": "192.168.10.1",
          "prefix-length": 24
        }
      ]
    }
  }
}
  
Note the subtle difference in the top-level key: querying the list container returns "ietf-interfaces:interfaces" (plural, containing an "interface" array), while querying a specific list entry returns "ietf-interfaces:interface" (singular, containing the object directly). This is standard RESTCONF behaviour: container nodes return their full content, individual list entry nodes return the entry object without the array wrapper.

6. Step 4 — GET Interfaces with Python requests

The same request made in Postman translates directly to Python using the requests library. Install it with pip install requests if not already available. For a broader introduction to Python for networking see Python Networking Overview and Python Scripts for Network Engineers:

# restconf_get_interfaces.py
# Retrieve all interfaces from NetsTuts-R1 via RESTCONF GET

import requests
import json
import urllib3

# Suppress SSL certificate warnings (self-signed cert in lab)
# Remove this line and set verify=True in production with a real cert
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

# ── Connection parameters ─────────────────────────────────────────
BASE_URL  = "https://192.168.10.1/restconf"
AUTH      = ("restapi", "RestAPI2026!")  # (username, password) tuple for Basic Auth
HEADERS   = {
    "Accept":       "application/yang-data+json",
    "Content-Type": "application/yang-data+json",
}

# ── Build the RESTCONF URL ────────────────────────────────────────
url = f"{BASE_URL}/data/ietf-interfaces:interfaces"

# ── Send the GET request ──────────────────────────────────────────
response = requests.get(
    url,
    auth=AUTH,
    headers=HEADERS,
    verify=False   # Disable SSL verification for self-signed cert
)

# ── Check HTTP status code ────────────────────────────────────────
print(f"Status: {response.status_code}")  # 200 = success

if response.status_code == 200:
    # ── Parse the JSON response ───────────────────────────────────────
    data = response.json()
    interfaces = data["ietf-interfaces:interfaces"]["interface"]

    # ── Print a summary table ─────────────────────────────────────────
    print(f"\n{'Interface':<22} {'IP Address':<18} {'Prefix':>7}  {'Enabled'}")
    print("-" * 60)

    for iface in interfaces:
        name    = iface.get("name", "")
        enabled = iface.get("enabled", False)

        # IP address is nested inside ietf-ip:ipv4 → address list
        ipv4_block = iface.get("ietf-ip:ipv4", {})
        addresses  = ipv4_block.get("address", [])

        if addresses:
            ip     = addresses[0]["ip"]
            prefix = addresses[0]["prefix-length"]
        else:
            ip, prefix = "unassigned", "-"

        status = "✓ up" if enabled else "✗ down"
        print(f"{name:<22} {ip:<18} {str(prefix):>7}  {status}")

else:
    print(f"Error: HTTP {response.status_code}")
    print(response.text)
  

Script Output

Status: 200

Interface              IP Address         Prefix  Enabled
------------------------------------------------------------
GigabitEthernet1       192.168.10.1           24  ✓ up
GigabitEthernet2       192.168.20.1           24  ✓ up
GigabitEthernet3       unassigned              -  ✗ down
Loopback0              10.255.255.1           32  ✓ up
  
The response.json() method deserialises the JSON response body directly into a Python dictionary — no manual JSON parsing needed. The nested access pattern data["ietf-interfaces:interfaces"]["interface"] follows the exact JSON key hierarchy shown in the Postman response. Using .get(key, default) instead of direct bracket access ([key]) prevents KeyError exceptions when optional YANG fields are absent from the response (e.g., an interface with no IP address has no "address" list in its "ietf-ip:ipv4" block).

7. Step 5 — PATCH: Update Interface Description

A PATCH request makes a partial update — only the fields specified in the request body are changed. This is the safest method for configuration changes as it leaves all other interface settings untouched. The example adds a description to GigabitEthernet2 and enables GigabitEthernet3:

Postman PATCH — Update Description

  Method:  PATCH
  URL:     https://192.168.10.1/restconf/data/ietf-interfaces:interfaces/interface=GigabitEthernet2

  Headers:
    Accept:       application/yang-data+json
    Content-Type: application/yang-data+json

  Authorization: Basic Auth — restapi / RestAPI2026!

  Body (raw → JSON):
  {
    "ietf-interfaces:interface": {
      "name": "GigabitEthernet2",
      "description": "LAN Segment — Updated via RESTCONF PATCH"
    }
  }

  → Send → Expected response: 204 No Content
  
HTTP 204 No Content is the standard success response for RESTCONF PATCH and PUT operations — it means the change was applied successfully. There is no response body. Only 2xx status codes indicate success. Verify the change took effect with a follow-up GET request to the same URL, or on the router with show running-config interface GigabitEthernet2. A 400 Bad Request typically means the JSON body is malformed or contains a field the YANG model does not accept. A 409 Conflict means the change violates a constraint (e.g., adding a duplicate list key).

PATCH in Python — Update Description + Enable Interface

# restconf_patch.py
# PATCH GigabitEthernet3: add description and bring it up (no shutdown)

import requests
import json
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

BASE_URL = "https://192.168.10.1/restconf"
AUTH     = ("restapi", "RestAPI2026!")
HEADERS  = {
    "Accept":       "application/yang-data+json",
    "Content-Type": "application/yang-data+json",
}

# ── URL targets GigabitEthernet3 list entry ────────────────────────
url = f"{BASE_URL}/data/ietf-interfaces:interfaces/interface=GigabitEthernet3"

# ── Payload: only the fields being changed ─────────────────────────
# "enabled": true  →  equivalent to "no shutdown" in CLI
# "description"    →  sets the interface description
# The IP address and other settings are untouched by PATCH
payload = {
    "ietf-interfaces:interface": {
        "name":        "GigabitEthernet3",
        "description": "WAN Link — Enabled via RESTCONF",
        "enabled":     True   # Python True → JSON true → no shutdown
    }
}

response = requests.patch(
    url,
    auth=AUTH,
    headers=HEADERS,
    json=payload,   # requests serialises dict to JSON and sets Content-Type
    verify=False
)

print(f"PATCH status: {response.status_code}")

if response.status_code == 204:
    print("Success! GigabitEthernet3 description set and interface enabled.")
else:
    print(f"Error: {response.text}")

# ── Verify: GET the interface back and confirm the change ──────────
verify_response = requests.get(url, auth=AUTH, headers=HEADERS, verify=False)
updated = verify_response.json()["ietf-interfaces:interface"]
print(f"\nVerification — GigabitEthernet3:")
print(f"  Description : {updated.get('description', '(none)')}")
print(f"  Enabled     : {updated.get('enabled', False)}")
  

Script Output

PATCH status: 204
Success! GigabitEthernet3 description set and interface enabled.

Verification — GigabitEthernet3:
  Description : WAN Link — Enabled via RESTCONF
  Enabled     : True
  

8. Step 6 — POST: Create a New Loopback Interface

POST creates a new resource inside a container. To add a new Loopback interface with an IP address, POST to the interfaces container:

# restconf_post_loopback.py
# Create a new Loopback1 interface with an IP address via RESTCONF POST

import requests
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

BASE_URL = "https://192.168.10.1/restconf"
AUTH     = ("restapi", "RestAPI2026!")
HEADERS  = {
    "Accept":       "application/yang-data+json",
    "Content-Type": "application/yang-data+json",
}

# POST to the interfaces container — creates a new entry in the list
url = f"{BASE_URL}/data/ietf-interfaces:interfaces"

payload = {
    "ietf-interfaces:interface": {
        "name":        "Loopback1",
        "description": "Secondary Loopback — Created via RESTCONF",
        "type":        "iana-if-type:softwareLoopback",
        "enabled":     True,
        "ietf-ip:ipv4": {
            "address": [
                {
                    "ip":            "10.255.255.2",
                    "prefix-length": 32
                }
            ]
        }
    }
}

response = requests.post(url, auth=AUTH, headers=HEADERS, json=payload, verify=False)

print(f"POST status: {response.status_code}")
if response.status_code == 201:  # 201 Created = success for POST
    print("Loopback1 created successfully.")
elif response.status_code == 409:  # 409 Conflict = already exists
    print("Loopback1 already exists — use PATCH to modify it.")
else:
    print(f"Error: {response.text}")
  
POST returns 201 Created on success, unlike PATCH/PUT which return 204 No Content. The Location header in the response contains the URL of the newly created resource: Location: https://192.168.10.1/restconf/ data/ietf-interfaces:interfaces/interface=Loopback1. If you run the POST script twice, the second run returns 409 Conflict because Loopback1 already exists — use PATCH to update an existing resource, POST only to create a new one.

9. Step 7 — DELETE: Remove a Loopback Interface

# restconf_delete.py
# Delete Loopback1 — equivalent to "no interface Loopback1" in CLI

import requests
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

BASE_URL = "https://192.168.10.1/restconf"
AUTH     = ("restapi", "RestAPI2026!")
HEADERS  = {
    "Accept":       "application/yang-data+json",
    "Content-Type": "application/yang-data+json",
}

url = f"{BASE_URL}/data/ietf-interfaces:interfaces/interface=Loopback1"

response = requests.delete(url, auth=AUTH, headers=HEADERS, verify=False)

print(f"DELETE status: {response.status_code}")
if response.status_code == 204:
    print("Loopback1 deleted successfully.")
elif response.status_code == 404:
    print("Loopback1 not found — may have already been deleted.")
  

10. Step 8 — Querying the Cisco-Native Model

The Cisco-IOS-XE-native model exposes the full IOS-XE running configuration as a YANG tree. The hostname and other global settings live here:

# restconf_native.py
# Query hostname and retrieve interface config from Cisco-native YANG model

import requests
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

BASE_URL = "https://192.168.10.1/restconf"
AUTH     = ("restapi", "RestAPI2026!")
HEADERS  = {
    "Accept":       "application/yang-data+json",
    "Content-Type": "application/yang-data+json",
}

# ── GET hostname via Cisco-native model ───────────────────────────
r = requests.get(
    f"{BASE_URL}/data/Cisco-IOS-XE-native:native/hostname",
    auth=AUTH, headers=HEADERS, verify=False
)
hostname = r.json()["Cisco-IOS-XE-native:hostname"]
print(f"Hostname: {hostname}")

# ── PATCH hostname via Cisco-native model ─────────────────────────
patch_r = requests.patch(
    f"{BASE_URL}/data/Cisco-IOS-XE-native:native",
    auth=AUTH,
    headers=HEADERS,
    json={"Cisco-IOS-XE-native:native": {"hostname": "NetsTuts-R1-Updated"}},
    verify=False
)
print(f"Hostname PATCH: {patch_r.status_code}")  # 204 = applied
  
The Cisco-IOS-XE-native:native path is the root of the entire Cisco-specific configuration tree. Under it, every IOS-XE configuration section has a YANG path: /native/hostname, /native/ip/route (static routes), /native/router/ospf (OSPF config), /native/vlan/vlan-list (VLAN database). This model is the most complete view of IOS-XE configuration but is Cisco-specific — the same paths do not exist on Juniper or Arista. Use IETF standard models for portable automation and Cisco-native models for Cisco-specific features.

11. Verification

Router-Side Verification

! ── Verify RESTCONF is running ────────────────────────────────────
NetsTuts-R1#show platform software yang-management process state

YANG Management Processes:
  confd           : Running
  nesd            : Running
  syncfd          : Running
  ncsshd          : Running
  dmiauthd        : Running
  nginx           : Running
  ndbmand         : Running
  pubd            : Running

! ── Verify HTTPS is enabled on port 443 ───────────────────────────
NetsTuts-R1#show ip http server status | include HTTPS
HTTPS server status: Enabled
HTTPS server port: 443

! ── Check active RESTCONF sessions ───────────────────────────────
NetsTuts-R1#show platform software yang-management process state

! ── Verify a PATCH was applied (check running-config) ─────────────
NetsTuts-R1#show running-config interface GigabitEthernet3
Building configuration...

Current configuration : 89 bytes
!
interface GigabitEthernet3
 description WAN Link — Enabled via RESTCONF
 no ip address
!                ← Note: "shutdown" is gone — PATCH "enabled":true worked
  

RESTCONF HTTP Status Code Reference

HTTP Code Meaning When You See It
200 OK Request succeeded — body contains the requested data Successful GET requests
201 Created New resource was created successfully Successful POST requests
204 No Content Change applied successfully — no response body Successful PATCH, PUT, DELETE requests
400 Bad Request Malformed JSON body or YANG constraint violation Wrong JSON structure, missing required field, invalid value type
401 Unauthorized Authentication failed — wrong username or password Incorrect credentials in Basic Auth header
403 Forbidden Authenticated but not authorized to perform this operation User has insufficient privilege level (must be 15)
404 Not Found The YANG path in the URL does not exist on this device Querying an interface that does not exist, wrong YANG path, or RESTCONF not enabled
406 Not Acceptable Accept header missing or not set to yang-data+json or yang-data+xml Forgot to add the Accept header, or used an incorrect MIME type
409 Conflict POST failed because the resource already exists POST to create a resource that was already created previously

12. Troubleshooting RESTCONF Issues

Problem Symptom Cause Fix
Connection refused on port 443 Postman or Python returns "Connection refused" or "Failed to connect" for any RESTCONF URL ip http secure-server is not configured, or the RSA key has not been generated, or the restconf command is missing On the router: show ip http server status — HTTPS server status must show Enabled. If not, run ip http secure-server. Verify RSA key exists: show crypto key mypubkey rsa. Verify RESTCONF: show running-config | include restconf. Verify nginx process is running: show platform software yang-management process
401 Unauthorized on every request Every RESTCONF request returns HTTP 401 regardless of credentials used Wrong username or password in Basic Auth, username not configured on the router, or ip http authentication local not configured (router is using AAA/TACACS and rejecting local credentials) Test credentials manually: show running-config | section username to confirm the username and check its privilege level. Verify ip http authentication local is configured. If AAA is active, either add the user to the TACACS/RADIUS server or add ip http authentication aaa with the correct method list
406 Not Acceptable on all requests Every request returns 406 even with correct credentials and URL The Accept header is missing from the request, or it contains an incorrect MIME type (e.g., application/json instead of application/yang-data+json) In Postman: confirm the Accept header is set to exactly application/yang-data+json. In Python: confirm HEADERS = {"Accept": "application/yang-data+json", "Content-Type": "application/yang-data+json"} and that headers=HEADERS is passed in the requests call. The exact MIME type string is required — abbreviated forms like application/json are not accepted by IOS-XE RESTCONF
404 Not Found for a valid-looking URL GET to /restconf/data/ietf-interfaces:interfaces returns 404 even though interfaces exist on the router RESTCONF subsystem is not running (nginx not started), or the YANG model path is wrong (typo in module name or container name), or the specific YANG module is not supported on this IOS-XE version Verify nginx is running: show platform software yang-management process. Test the root endpoint first: GET /restconf — if this returns 404, RESTCONF is not running. Check module support: GET /restconf/data/ietf-yang-library:modules-state and search the response for the module name. Verify the exact URL path against Cisco YANG Explorer or developer.cisco.com
400 Bad Request on PATCH or POST PATCH or POST returns 400 with an error body referencing a specific field or constraint The JSON payload is malformed (trailing comma, missing bracket), a required field is missing, a field has the wrong type (string instead of integer for prefix-length), or the top-level JSON key does not match the YANG module name Read the 400 error body carefully — it contains a specific error message identifying the problem field. Common issues: "prefix-length" must be an integer (24), not a string ("24"). The top-level key must match the module: "ietf-interfaces:interface" not "interface". The "name" key in the payload must match the name in the URL path. Use a JSON validator to check for syntax errors before sending
PATCH applied but changes not in running-config PATCH returns 204 successfully but show running-config on the router does not show the change RESTCONF changes the running configuration but not the startup configuration — a router reload would lose the changes. Or the change was applied to a YANG leaf that maps to a different CLI command than expected After making RESTCONF changes, save the configuration: either via CLI (write memory / copy running-config startup-config) or via RESTCONF PATCH to the Cisco-IOS-XE-native:native model with a save RPC. Verify the YANG-to-CLI mapping by checking the running-config after the PATCH and comparing with the intended change

Key Points & Exam Tips

  • RESTCONF (RFC 8040) is an HTTP-based API for reading and modifying network device configuration and state. It uses standard HTTP methods (GET, PUT, PATCH, POST, DELETE) and returns data in JSON or XML format based on YANG data models. It requires IOS-XE 16.x or later and is not available on classic IOS.
  • YANG (Yet Another Next Generation, RFC 6020) is the data modelling language used to define the structure of all RESTCONF data. Every RESTCONF URL corresponds to a path in a YANG model tree. YANG models come in two flavours: IETF standard models (vendor-agnostic, e.g., ietf-interfaces) and Cisco-native models (Cisco-IOS-XE-native) for Cisco-specific features.
  • The RESTCONF URL structure is: https://[device]/restconf/data/[module]:[container]/[list-key]. The module name prefix (e.g., ietf-interfaces:) identifies which YANG model the path belongs to. List entries are accessed by appending =key-value to the list name (e.g., interface=GigabitEthernet1).
  • The two mandatory headers for all RESTCONF requests are Accept: application/yang-data+json and Content-Type: application/yang-data+json. Missing or wrong headers cause 406 Not Acceptable errors. Authentication uses HTTP Basic Auth (username:password Base64-encoded in the Authorization header).
  • HTTP status codes: 200 = successful GET, 201 = successful POST (resource created), 204 = successful PATCH/PUT/DELETE (no body), 400 = bad request (malformed JSON or YANG violation), 401 = wrong credentials, 404 = path not found, 409 = POST conflict (resource already exists).
  • The critical difference between PATCH and PUT: PATCH merges the request body into the existing resource (only specified fields change); PUT replaces the entire resource with the request body (unspecified fields are deleted). Always use PATCH for partial updates to avoid accidentally deleting configuration.
  • The key YANG field mappings: "enabled": true = no shutdown; "enabled": false = shutdown; "prefix-length": 24 = /24 subnet mask; "ietf-ip:ipv4" contains the IP addressing augmented by the ietf-ip module onto the interface.
  • RESTCONF changes the running configuration only — just like typing commands in the CLI. The startup configuration is not updated automatically. Use write memory on the CLI after RESTCONF changes, or automate the save step via the RESTCONF RPC interface.
  • For CCNA automation objectives: know the difference between NETCONF (SSH, XML, port 830, older) and RESTCONF (HTTPS, JSON/XML, port 443, simpler REST interface), what YANG models are (schemas that define the data structure), and the HTTP verbs used for CRUD operations. Know that restconf is the IOS-XE command to enable the subsystem and that ip http secure-server must be configured first.
  • Compared to Netmiko (CLI screen-scraping, works on any IOS), RESTCONF provides structured data natively (no regex parsing), is stateless (each HTTP call is independent, no session management), and is standardised (RFC 8040). The trade-off is that it requires IOS-XE 16.x, knowledge of YANG paths, and proper HTTPS/certificate configuration.
Next Steps: RESTCONF is the simpler of the two model-driven APIs on IOS-XE. NETCONF (RFC 6241) operates over SSH on port 830, uses XML, and provides full transaction semantics (commit/rollback) that RESTCONF lacks — making it the choice for complex multi-step configuration changes that must be atomic. See NETCONF with ncclient (Python) for the hands-on NETCONF lab. For the Netmiko CLI-based alternative to RESTCONF covered in this lab, see Python Netmiko — Connect and Run Show Commands. For the SSH and HTTPS configuration that RESTCONF depends on, see SSH Configuration. For the AAA credential control over the RESTCONF user, see AAA TACACS+ Configuration. For Ansible-based automation that can complement RESTCONF workflows, see Ansible IOS Configuration.

TEST WHAT YOU LEARNED

1. What are the three IOS-XE commands that must all be configured before a RESTCONF GET request can succeed, and what does each one do?

Correct answer is C. All three commands serve distinct roles in the RESTCONF stack. ip http secure-server starts the HTTPS service on TCP/443 — without this, port 443 is not listening and any connection attempt is refused immediately. ip http authentication local tells IOS-XE to validate HTTPS (and therefore RESTCONF) credentials against the local username database configured with username commands. Without this, the HTTPS server has no authentication method and may reject all connections or use a different (non-local) method. restconf activates the YANG management subsystem — this starts the nginx process, confd, syncfd, and the other YANG-management processes visible in show platform software yang-management process, and registers the /restconf URL path on the HTTPS server. Without restconf, the HTTPS server is running but has no /restconf endpoint — every RESTCONF request returns 404. Note that crypto key generate rsa modulus 2048 is also a prerequisite (HTTPS requires a certificate), but this is typically already done if SSH is configured. The RSA key generates a self-signed certificate used by the HTTPS server.

2. A PATCH request to update an interface description returns HTTP 204. A follow-up GET to the same interface returns the old description — the change did not persist. What is the most likely explanation?

Correct answer is A. This is the most important operational consideration for RESTCONF: it modifies the running configuration immediately and synchronously (204 means the change is applied, not queued). However, like all IOS-XE configuration changes, it does not automatically update the startup configuration (NVRAM). A router reload would lose all RESTCONF-applied changes. The fix is to save after making changes, either via CLI (write memory or copy running-config startup-config) or via a RESTCONF POST to the Cisco-IOS-XE-native:native save RPC. If the GET is immediately returning old data after a 204, the most likely cause is a URL path mismatch — the interface name in the PATCH URL must be an exact, case-sensitive match of the interface name as IOS-XE stores it. For example, patching interface=gigabitethernet3 (lowercase) may return 404 or 204 without effect if the router stores it as GigabitEthernet3. Always use the exact name format returned by a prior GET request. RESTCONF does not use a commit model — changes are immediate, unlike NETCONF's candidate datastore workflow.

3. What is the fundamental difference between using PUT and PATCH to update an interface, and why is PATCH generally safer for partial updates?

Correct answer is D. This distinction is critical for safe automation. PUT implements "replace semantics" per RFC 8040: the resource at the target URL is entirely replaced with the content of the request body. If you PUT a minimal payload containing only {"ietf-interfaces:interface": {"name": "GigabitEthernet1", "description": "New"}}, the router will replace the entire GigabitEthernet1 configuration with this minimal object — deleting the IP address, removing the enabled state (defaulting the interface to shutdown), and removing all other previously configured attributes. PATCH implements "merge semantics": the request body is merged into the existing resource. Only the fields in the request body are updated; all other existing fields are left as-is. For operational safety, always default to PATCH for updates unless you specifically intend to replace the entire resource. Use PUT when you want a clean slate and are providing a complete, authoritative replacement for the entire resource. A practical example where PUT is appropriate: replacing a complete access-list with a new version where you want all old entries deleted and replaced with the new set.

4. Why must the Accept header be set to application/yang-data+json rather than simply application/json, and what error occurs if it is missing?

Correct answer is B. RESTCONF is defined by RFC 8040 which specifies application/yang-data+json as the correct MIME type for JSON-encoded YANG data. The +json suffix follows the structured syntax suffix convention from RFC 6838 — it means the base format is JSON but the structure is further defined by YANG. This MIME type tells both the client and server that the JSON keys will use YANG module namespace prefixes (like "ietf-interfaces:interfaces"), that the structure follows YANG schema rules (required fields, typed values, list semantics), and that values like true/false map to YANG boolean leaves. IOS-XE's RESTCONF implementation performs strict content negotiation: if the Accept header is missing or contains an unrecognised type, the server returns 406 Not Acceptable because it cannot confirm the client can handle YANG-structured JSON. The same applies to Content-Type on PATCH/PUT/POST requests — the body must be declared as application/yang-data+json for the server to accept and parse it correctly as YANG-structured JSON rather than arbitrary JSON.

5. What is the difference between the ietf-interfaces YANG model and the Cisco-IOS-XE-native model, and when should each be used?

Correct answer is C. Both models can be used simultaneously on the same device — they provide different views of overlapping data. The IETF models (ietf-interfaces, ietf-ip, ietf-routing) are defined by the IETF in RFCs and represent a vendor-neutral abstraction layer. The same URL /restconf/data/ietf-interfaces:interfaces with the same JSON structure works on Cisco IOS-XE, Juniper JunOS (with RESTCONF enabled), and Arista EOS. This is the foundation of vendor-agnostic network automation: write one script against IETF models and run it against any compliant device. The Cisco-native model exposes the complete IOS-XE configuration as a YANG tree — every feature has a path including EIGRP routing, Cisco-specific QoS policies, platform-specific hardware settings, and features not standardised by IETF. Nothing in the IETF model covers EIGRP, for example — you must use Cisco-IOS-XE-native:native/router/eigrp. In practice, most automation frameworks use a combination: IETF models for common operations (interface management, IP addressing, routing table reads) and vendor-native models for platform-specific features. The Cisco Yang Explorer tool (available on DevNet) provides an interactive browser of all supported YANG paths.

6. A POST request to create Loopback1 succeeds with 201 Created. Running the same POST again immediately returns 409 Conflict. What should be done to modify Loopback1 instead, and why does POST not work for updates?

Correct answer is D. This is the standard REST CRUD pattern applied to RESTCONF. POST targets a parent container and creates a new child resource — it is inherently a "create new" operation. When POST detects that a resource with the same key already exists, it correctly returns 409 Conflict rather than silently overwriting existing data (which could be dangerous in network automation). The correct URL targets differ: POST goes to the container (/restconf/data/ietf-interfaces:interfaces), while PATCH/PUT/DELETE/GET for individual resources go to the resource URL (/restconf/data/ietf-interfaces:interfaces/interface=Loopback1). A common automation pattern for idempotent operations (where you want "ensure this config exists" regardless of current state): (1) try PUT to the resource URL — if it returns 201, the resource was created; if 204, it was replaced; (2) alternatively, try PATCH first — if it returns 404 (resource doesn't exist), switch to POST to create it. Some automation frameworks abstract this with "upsert" logic: attempt PATCH, if 404 then POST.

7. Why is verify=False used in Python requests for RESTCONF in a lab environment, and what should be done in production instead?

Correct answer is A. HTTPS works by the server presenting a certificate that the client verifies against a trusted CA. Python's requests library includes a bundle of trusted CA certificates (from the certifi package). A Cisco router's self-signed certificate is created by the router itself and is not signed by any of these trusted CAs — it is essentially a certificate the router issued to itself. When verify=True, requests sees an untrusted certificate and raises ssl.SSLCertVerificationError, refusing the connection. verify=False disables this verification entirely — the connection is still TLS-encrypted (your traffic is not readable in transit), but you have no cryptographic assurance that you are talking to the actual router rather than a man-in-the-middle presenting a fake certificate. In production environments: the correct solution is to have your PKI (Public Key Infrastructure) sign a certificate for the router, or to use IOS-XE's trustpoint mechanism to install a CA-signed certificate. For internal corporate networks, the internal CA's root certificate can be added to the requests verification path: verify='/etc/ssl/certs/corporate-ca.pem'. This provides both encryption and authentication. In Postman, "Disable SSL Certificate Verification" is the equivalent setting.

8. How does RESTCONF compare to the Netmiko CLI approach for network automation, and when would you choose RESTCONF over Netmiko?

Correct answer is C. This is the practical engineering decision most automation engineers face. Netmiko: works via SSH, sends CLI commands as text, receives text responses that must be parsed with regex or TextFSM. Works on any Cisco IOS version (even 12.x on ancient hardware), any vendor supported by Netmiko drivers, and requires no special device configuration beyond SSH access. The weakness is fragility: output format differences between IOS versions can break regex parsers, and the unstructured text approach lacks the data validation that a schema-based system provides. RESTCONF: works via HTTPS with standardised JSON, returns structured data aligned to YANG schemas with no parsing required, enables idempotent operations (you describe the desired state; the device validates and applies it), and supports vendor-agnostic automation through IETF models. The weakness is platform requirements: IOS-XE 16.x or later only, requires HTTPS configuration and certificate management, and you must know the correct YANG paths (which have a learning curve). In practice, large-scale production automation increasingly uses RESTCONF/NETCONF for modern devices while maintaining Netmiko for legacy device support. Many tools (Ansible network modules, NAPALM) abstract this choice by using the best available protocol for each device type automatically.

9. In the JSON response for interface data, the IP address is located at data["ietf-interfaces:interfaces"]["interface"][0]["ietf-ip:ipv4"]["address"][0]["ip"]. Why is the path so deeply nested, and what would cause a KeyError at the "ietf-ip:ipv4" level?

Correct answer is B. The deep nesting in RESTCONF JSON responses is not arbitrary — every level corresponds to a structural element in the YANG data model. ietf-interfaces:interfaces: the root container of the ietf-interfaces module, prefixed with the module name. interface: a YANG list node (always an array in JSON). Each element is a list entry keyed by "name". ietf-ip:ipv4: this is the key insight — the IPv4 address data is not defined in the ietf-interfaces module itself. The ietf-ip module (RFC 8343) augments the ietf-interfaces module, adding IPv4 and IPv6 addressing nodes to each interface list entry. YANG augmentation is reflected in JSON by including the augmenting module's namespace prefix in the key name: "ietf-ip:ipv4" means "the ipv4 container contributed by the ietf-ip module." address: another YANG list — an interface can have multiple IP addresses (primary + secondaries). The KeyError occurs because IOS-XE omits optional YANG leaves from the JSON response when they have no configured value. An interface with no IP address simply lacks the "ietf-ip:ipv4" key entirely — there is no "ietf-ip:ipv4": {} placeholder. Defensive coding with .get("ietf-ip:ipv4", {}) and .get("address", []) handles this correctly.

10. A network engineer runs a RESTCONF GET against a Cisco Catalyst 9300 switch using the URL /restconf/data/ietf-interfaces:interfaces and receives a valid JSON response. They then run the exact same GET against a Juniper EX switch and also receive a valid JSON response with the same top-level structure. What principle does this demonstrate, and what is the significance for network automation?

Correct answer is D. This scenario illustrates the single biggest architectural advantage of RESTCONF over CLI automation. With Netmiko (CLI-based), the output of show interfaces on a Cisco device looks completely different from show interfaces on a Juniper device — different field names, different formatting, different TextFSM templates required. You need vendor-specific code for every vendor. With RESTCONF and IETF YANG models: GET /restconf/data/ietf-interfaces:interfaces returns a JSON object with "ietf-interfaces:interfaces": {"interface": [...]} structure on every compliant device. The field names ("name", "enabled", "ietf-ip:ipv4", "prefix-length") are defined by the RFC and are identical across vendors. One Python function that processes this JSON structure works on all of them. The practical significance: enterprise network automation scripts can be written once against IETF models and deployed across a mixed-vendor infrastructure. When a vendor is replaced (e.g., migrating from Cisco to Juniper for access switching), the automation scripts targeting IETF models require little or no modification. Vendor-specific features still require vendor-specific models, but the majority of common operations (interface management, IP addressing, basic routing) are covered by IETF standards.