Network Automation with RESTCONF (RFC 8040)¶
RESTCONF is an HTTP-based protocol, defined in RFC 8040,
for reading and writing the YANG-modeled configuration and operational data on a network device. This
article covers the pieces you actually use: the URI structure, the HTTP verb mapping, media types and
headers, enabling it on Cisco IOS XE, worked curl/Python examples, and the query parameters that keep
responses manageable.
RESTCONF, YANG, and where NETCONF fits¶
If NETCONF is the heavyweight XML-over-SSH protocol, RESTCONF is its REST-friendly cousin: it maps the
familiar HTTP verbs (GET, POST, PUT, PATCH, DELETE) onto YANG data so you can drive a device with
ordinary web tooling — curl, Postman, or Python requests. It was standardized to expose the same
YANG-modeled data as NETCONF over plain HTTPS with JSON, trading NETCONF's advanced features (candidate
datastores, locking, multi-datastore transactions) for approachability.
You cannot use RESTCONF without a little YANG. YANG is a vendor-neutral modeling language that describes a device's data as containers, lists, and leaves. RESTCONF exposes that model over HTTP; the JSON body you send has to match the shape of the model you target. Three model families show up on IOS XE:
| Model family | Example | Trade-off |
|---|---|---|
| Native (Cisco) | Cisco-IOS-XE-native |
Every feature, but not portable across vendors |
| IETF standard | ietf-interfaces |
Portable, but a common subset only |
| OpenConfig | openconfig-interfaces |
Vendor-neutral, common subset |
A useful rule of thumb: reach for RESTCONF when you want quick, RESTful access and easy tooling, and for NETCONF when you need candidate/commit workflows, configuration locking, or all-or-nothing changes across datastores. Both read the same models, so the data you manipulate is identical — only the transport and transactional guarantees differ.
| Aspect | RESTCONF | NETCONF |
|---|---|---|
| Transport | HTTP(S) / REST | SSH (port 830) |
| Encoding | JSON or XML | XML |
| Transactions | Per-request | Candidate + commit, locking |
| Tooling | curl, requests, browser |
ncclient, dedicated clients |
| Best for | Quick RESTful access | Careful transactional change |
The RESTCONF URI structure¶
Every request targets a resource through a structured URL. Reading that URL is half the battle:
https://<device>/restconf/data/<module:container>/<list>=<key>
# The interfaces container (IETF model)
https://192.0.2.11/restconf/data/ietf-interfaces:interfaces
# One interface — a list element selected by its key
https://192.0.2.11/restconf/data/ietf-interfaces:interfaces/interface=GigabitEthernet2
# A native-model container
https://192.0.2.11/restconf/data/Cisco-IOS-XE-native:native/vlan
/restconf/datais the datastore root — configuration plus operational data./restconf/operationsinvokes YANG RPC actions.- A list element is selected by appending
=key; multiple keys are comma-separated, and any reserved characters in a key must be percent-encoded.
Discover the root, don't hard-code it
RFC 8040 does not fix the API root at /restconf. A client is supposed to GET
/.well-known/host-meta, which returns an
XRD document (application/xrd+xml) with a Link element carrying the restconf relation pointing at
the real root path. IOS XE uses /restconf, but discovering it keeps your client portable.
HTTP methods: PUT replaces, PATCH merges¶
The verb you choose is the operation:
| Method | Action |
|---|---|
GET |
Retrieve data |
POST |
Create a new child resource, or invoke an RPC |
PUT |
Create or fully replace the target |
PATCH |
Merge — a partial update that leaves untouched fields alone |
DELETE |
Remove the target |
The PUT-vs-PATCH distinction is the one to internalize. If you want a resource to become exactly the
body you send, use PUT; if you want to change one field without disturbing the rest of the resource,
use PATCH. Getting this wrong is how an innocent-looking update wipes out configuration you never
mentioned.
Media types, headers, and enabling RESTCONF¶
RESTCONF defines its own media types: application/yang-data+json or application/yang-data+xml. Set the
Accept header to choose the response encoding and Content-Type to declare the body encoding. JSON is by
far the most common in automation.
Plain application/json is not the RESTCONF media type
A frequent mistake is sending Content-Type: application/json. RESTCONF requires
application/yang-data+json (or +xml); the wrong media type gets your request rejected with a
400-class error. Set it on both Accept and Content-Type.
On IOS XE, RESTCONF rides on the secure HTTP server, so you enable both — and set up AAA so the API can authenticate (Cisco IOS XE Programmability Configuration Guide — RESTCONF):
! Enable the secure HTTP server and RESTCONF
ip http secure-server
restconf
!
! AAA so the API can authenticate users
aaa new-model
aaa authentication login default local
aaa authorization exec default local
username automation privilege 15 secret S3cret!
Worked examples¶
Read interfaces with curl¶
The -k flag skips certificate verification for a lab device with a self-signed certificate — never
use it against production.
curl -k -u automation:S3cret! \
-H "Accept: application/yang-data+json" \
https://192.0.2.11/restconf/data/ietf-interfaces:interfaces
Create or replace a VLAN with Python (native model)¶
PUT makes the target match the body exactly — ideal for declaring a VLAN's full definition.
# restconf_vlan.py — PUT creates or replaces VLAN 10
import requests
from requests.auth import HTTPBasicAuth
requests.packages.urllib3.disable_warnings() # lab only: silence self-signed cert warnings
base = "https://192.0.2.11/restconf/data"
headers = {
"Accept": "application/yang-data+json",
"Content-Type": "application/yang-data+json",
}
auth = HTTPBasicAuth("automation", "S3cret!")
url = base + "/Cisco-IOS-XE-native:native/vlan/vlan-list=10"
payload = {"Cisco-IOS-XE-native:vlan-list": [{"id": 10, "name": "SALES"}]}
r = requests.put(url, headers=headers, auth=auth, json=payload, verify=False)
print(r.status_code) # 201 Created, or 204 No Content if it already existed
Merge fields on an interface (IETF model, PATCH)¶
PATCH updates only the leaves you send, so the interface keeps every other setting it had.
# restconf_interface.py — merge a description and admin state
url = base + "/ietf-interfaces:interfaces/interface=GigabitEthernet2"
payload = {
"ietf-interfaces:interface": {
"name": "GigabitEthernet2",
"description": "Uplink to core",
"type": "iana-if-type:ethernetCsmacd",
"enabled": True,
}
}
r = requests.patch(url, headers=headers, auth=auth, json=payload, verify=False)
print(r.status_code) # 204 on success
OSPF, ACLs, and everything else follow the same recipe: pick the model (native is the most complete on
IOS XE), build JSON that matches the YANG tree, and choose PUT to replace or PATCH to merge.
Status codes worth recognizing¶
RESTCONF speaks standard HTTP status codes. These are the ones you will see most:
| Code | Meaning in RESTCONF |
|---|---|
200 OK |
GET succeeded; the response has a body |
201 Created |
A new resource was created via POST/PUT |
204 No Content |
PUT/PATCH/DELETE succeeded, no body returned |
400 Bad Request |
Malformed or invalid YANG data |
401 Unauthorized |
Authentication failed |
404 Not Found |
The resource path does not exist |
409 Conflict |
Data already exists (e.g., POST of an existing item) |
Query parameters: shaping the response¶
A plain GET against a large container can return an overwhelming amount of data. RESTCONF defines query
parameters that shape the response server-side — better for performance and for getting exactly the view
you need. Append them after a ? and combine them with &:
depth=N— limit how many levels of the tree are returned.depth=1gives just the immediate children, handy for discovering structure without pulling everything.fields=a;b/c— return only the named leaves, dramatically shrinking the payload when you want two values out of a huge object.content=config|nonconfig|all— choose writable configuration, read-only operational state, or both.with-defaults=report-all|trim— control whether leaves sitting at their YANG default value are included or omitted.
GET /restconf/data/ietf-interfaces:interfaces/interface=GigabitEthernet1?fields=enabled;oper-status&content=all
Accept: application/yang-data+json
Missing live status? Check content
content=config returns only what you could write back; content=nonconfig returns operational state
such as counters and oper-status. If a GET unexpectedly omits live status, the content parameter
is usually the reason.
Key takeaways¶
- RESTCONF (RFC 8040) exposes YANG-modeled data over HTTP — perfect for
curl, Postman, andrequests. - The URI shape is
/restconf/data/<module:container>/<list>=<key>; RPCs live under/restconf/operations. Discover the root via/.well-known/host-metarather than hard-coding/restconf. - Verbs map to actions:
PUTreplaces,PATCHmerges — the distinction that most often bites people. - Use the media type
application/yang-data+jsonon bothAcceptandContent-Type; plainapplication/jsonis rejected. - On IOS XE, enable with
restconf+ip http secure-server+ AAA, and pick a model family: native (complete, Cisco-specific) or IETF/OpenConfig (portable subsets). - Shape large responses with
depth,fields,content, andwith-defaultsinstead of pulling everything and filtering client-side.
Sources: RFC 8040 — RESTCONF Protocol, Cisco IOS XE Programmability Configuration Guide — RESTCONF Protocol.