Skip to content

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/data is the datastore root — configuration plus operational data.
  • /restconf/operations invokes 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=1 gives 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, and requests.
  • The URI shape is /restconf/data/<module:container>/<list>=<key>; RPCs live under /restconf/operations. Discover the root via /.well-known/host-meta rather than hard-coding /restconf.
  • Verbs map to actions: PUT replaces, PATCH merges — the distinction that most often bites people.
  • Use the media type application/yang-data+json on both Accept and Content-Type; plain application/json is 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, and with-defaults instead of pulling everything and filtering client-side.

Sources: RFC 8040 — RESTCONF Protocol, Cisco IOS XE Programmability Configuration Guide — RESTCONF Protocol.