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 |
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) │
└───────────────────────────────────────────────────────────────┘
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
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"
}
}
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
}
]
}
}
}
"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
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
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}")
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
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-valueto the list name (e.g.,interface=GigabitEthernet1). - The two mandatory headers for all RESTCONF requests are
Accept: application/yang-data+jsonandContent-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 theietf-ipmodule 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 memoryon 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
restconfis the IOS-XE command to enable the subsystem and thatip http secure-servermust 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.