What Type Of API Is Cisco Modeling Labs Built On: Complete Guide

11 min read

What if you could spin up a whole network lab in minutes, snap a screenshot of the topology, and then pull the same configuration into a script?
That’s the promise of Cisco Modeling Labs (CML).
But underneath the slick UI, there’s a whole stack of APIs making the magic happen.

What Is Cisco Modeling Labs

CML is Cisco’s official network‑simulation platform. Think of it as a sandbox where you can drag‑and‑drop routers, switches, firewalls and even cloud‑native VMs, then run real Cisco IOS, NX‑OS, IOS‑XR, or ASA images. The result is a fully functional virtual network that behaves just like the real thing—except you can reset, clone, or destroy it with a click Practical, not theoretical..

Counterintuitive, but true.

In practice, CML is a web‑based service that talks to a back‑end engine (the “simulation engine”) via a set of RESTful endpoints. Those endpoints expose everything from lab creation to node power‑control, and they’re the reason you can automate CML from Python, Ansible, or even a simple curl command Simple, but easy to overlook..

The Core Piece: The CML API

When people ask “what type of API is CML built on?”, the short answer is: a REST API built on HTTP/HTTPS with JSON payloads. Cisco wrapped the simulation engine in a Flask‑based web service that follows the typical CRUD (Create, Read, Update, Delete) pattern.

But there’s a twist: CML also offers a WebSocket interface for real‑time console streaming, and a limited gRPC endpoint for internal telemetry. Those aren’t the primary way most users interact, but they’re there if you need low‑latency, bidirectional streams.

Why It Matters

You might wonder why the underlying API matters at all. Here’s the thing — the API determines how you can extend CML beyond the browser.

  • Automation – With a REST API you can script lab builds, tear‑downs, and even run validation checks as part of a CI/CD pipeline.
  • Integration – Want to pull lab topology into NetBox or a documentation generator? The API makes that painless.
  • Scalability – REST is stateless, so you can spin up multiple CML instances behind a load balancer without worrying about session affinity.

When people ignore the API, they end up manually clicking through the UI, which is fine for a one‑off lab but a nightmare for repeatable testing. In large enterprises, that manual step becomes a hidden cost.

How It Works

Below is a step‑by‑step walk‑through of the CML API stack, from the moment you fire up a request to the point the simulated node boots Most people skip this — try not to..

1. Authentication

CML uses token‑based authentication. You POST your username and password to /api/v0/authenticate and receive a JWT (JSON Web Token) And it works..

curl -k -X POST https://cml.example.com/api/v0/authenticate \
  -d '{"username":"admin","password":"s3cr3t"}' \
  -H "Content-Type: application/json"

The token lives for 30 minutes by default; you can refresh it with /api/v0/authenticate/refresh.

2. Lab Lifecycle

Create a Lab

POST to /api/v0/labs with a JSON body that defines the lab name, description, and optional tags.

{
  "title": "BGP Test Lab",
  "description": "Simple 2‑router BGP",
  "tags": ["demo","bgp"]
}

The response includes a lab_id you’ll use for every subsequent call.

Add Nodes

Nodes are added via /api/v0/labs/{lab_id}/nodes. So each node payload references an image ID (e. Day to day, g. , iosv for IOSv) and a position on the canvas Easy to understand, harder to ignore..

{
  "node_definition": "iosv",
  "label": "R1",
  "x": 100,
  "y": 150
}

Connect Interfaces

Links are created with /api/v0/labs/{lab_id}/links. g.Now, you supply the source and destination node IDs plus the interface names (e. , Gig0/0) Simple, but easy to overlook..

{
  "src_node": "node-1",
  "src_interface": "Gig0/0",
  "dst_node": "node-2",
  "dst_interface": "Gig0/0"
}

Power Control

Once the topology is assembled, you start the lab with a POST to /api/v0/labs/{lab_id}/start. The engine provisions Docker containers (or LXC on older versions) for each node, mounts the IOS image, and boots the process That's the whole idea..

3. Real‑Time Console Access

The REST API can give you a URL for a WebSocket console stream:

wss://cml.example.com/api/v0/nodes/{node_id}/console

You open this with a WebSocket client (e.g., websocat) and you get a live terminal, just like the built‑in web console. This is handy for automated config pushes via expect‑style scripts.

4. Telemetry & Stats

CML exposes a /api/v0/labs/{lab_id}/stats endpoint that returns CPU, memory, and network I/O for each node in JSON. Internally, this data is gathered via a gRPC service that talks to the container runtime, but you never need to touch gRPC unless you’re building a custom dashboard Simple as that..

5. Cleanup

When you’re done, DELETE /api/v0/labs/{lab_id} removes the lab and tears down all containers. Always clean up; orphaned containers waste CPU cycles on your host.

Common Mistakes / What Most People Get Wrong

  1. Using the UI for automation – People often try to “scrape” the web UI with Selenium. It works, but it’s fragile. The API is stable and versioned; stick to it The details matter here..

  2. Skipping token refresh – The JWT expires. If you schedule a long‑running job (say, a 12‑hour lab), you’ll get a 401 error halfway through. Implement a simple refresh loop Worth keeping that in mind..

  3. Assuming all images are the same – CML ships with a handful of Cisco images, but you can upload your own. Forgetting to map the correct node_definition leads to “image not found” errors that are hard to debug Small thing, real impact..

  4. Mixing HTTP and HTTPS – The UI forces HTTPS, but the API defaults to HTTP if you don’t specify https://. In a production environment that’s a security risk And that's really what it comes down to. Practical, not theoretical..

  5. Neglecting resource limits – Each node consumes CPU and RAM. If you launch a 50‑node lab on a modest VM, the simulation will thrash. Use the /stats endpoint to monitor and scale the host accordingly.

Practical Tips / What Actually Works

  • Wrap the API in a thin Python library – A few helper functions for auth, lab creation, and node power‑control cut the boilerplate down to a dozen lines.
import requests, json, time

class CML:
    def __init__(self, host, user, pwd):
        self.Practically speaking, base = f"https://{host}/api/v0"
        self. token = self.

    def _login(self, user, pwd):
        r = requests.Here's the thing — post(f"{self. base}/authenticate",
                          json={"username": user, "password": pwd},
                          verify=False)
        return r.

- **use Ansible’s uri module** – If your team already uses Ansible, a simple playbook can spin up labs as part of a test suite.  

- **Store lab definitions as JSON files** – Treat the lab spec like code. Version‑control the JSON, and you can replay any scenario on demand.  

- **Use the console WebSocket for config pushes** – Instead of SSH’ing into each node (which isn’t exposed), pipe a configuration file into the WebSocket stream. It’s faster and avoids extra network setup.  

- **Batch API calls** – CML supports bulk node creation via `/labs/{lab_id}/nodes/bulk`. Bundle your node list into one request to shave off latency.  

- **Monitor host health** – Pair the `/stats` endpoint with a Grafana dashboard. You’ll see when you’re about to hit a CPU ceiling before the lab crashes.  

## FAQ  

**Q: Does CML support SOAP or XML APIs?**  
A: No. CML is built on a pure REST/JSON model. Older Cisco tools used SOAP, but CML never did.  

**Q: Can I use the API from a non‑Python language?**  
A: Absolutely. Since it’s just HTTP with JSON, any language with an HTTP client (Go, JavaScript, PowerShell, etc.) can talk to it.  

**Q: Is there a rate limit on the API?**  
A: By default, CML allows 100 requests per minute per user token. You can adjust this in the `cml.conf` if you need higher throughput.  

**Q: How do I retrieve the list of available node images?**  
A: GET `/api/v0/node_definitions` returns a catalog of all uploaded IOS, NX‑OS, and third‑party images, including their IDs and supported platforms.  

**Q: What’s the difference between the REST API and the WebSocket console?**  
A: The REST API handles lab orchestration (create, start, stop). The WebSocket provides a live, interactive console stream for a specific node, letting you type commands in real time.  

---

That’s the lowdown on the API that powers Cisco Modeling Labs. On top of that, once you start scripting CML, you’ll wonder how you ever lived without it. Knowing it is more than a curiosity—it’s the key to turning a point‑and‑click lab into a repeatable, automated part of your network‑engineer workflow. Happy building!

## A Quick‑Start Example

Below is a minimal, end‑to‑end Python script that pulls everything together: it authenticates, creates a lab, adds two nodes, starts the lab, streams console output, and finally tears everything down.  Feel free to copy, paste, and tweak it for your own experiments.

```python
#!/usr/bin/env python3
"""
Simple CML automation demo
Author: Your Name
"""

import json, time, requests, websocket, ssl

# ---------- Configuration ----------
HOST     = "cml.example.com"
USER     = "admin"
PASS     = "cmladmin"
LAB_NAME = "demo-lab"
NODE_IDS = ["cisco-2911", "cisco-2911"]  # IDs from /node_definitions

# ---------- Helper classes ----------
class CML:
    def __init__(self, host, user, pwd):
        self.base = f"https://{host}/api/v0"
        self.token = self._login(user, pwd)

    def _login(self, user, pwd):
        r = requests.post(f"{self.Because of that, base}/authenticate",
                          json={"username": user, "password": pwd},
                          verify=False)
        r. raise_for_status()
        return r.

    def _headers(self):
        return {"Authorization": f"Bearer {self.token}",
                "Content-Type": "application/json"}

    def create_lab(self, name):
        r = requests.That's why post(f"{self. So base}/labs",
                          json={"name": name},
                          headers=self. _headers(),
                          verify=False)
        r.raise_for_status()
        return r.

    def add_nodes(self, lab_id, node_ids):
        payload = {"nodes": [{"node_definition_id": nid} for nid in node_ids]}
        r = requests.post(f"{self.base}/labs/{lab_id}/nodes/bulk",
                          json=payload,
                          headers=self._headers(),
                          verify=False)
        r.raise_for_status()
        return [n["id"] for n in r.

    def start_lab(self, lab_id):
        r = requests.post(f"{self.base}/labs/{lab_id}/start",
                          headers=self._headers(),
                          verify=False)
        r.

    def stop_lab(self, lab_id):
        r = requests.post(f"{self.That's why base}/labs/{lab_id}/stop",
                          headers=self. _headers(),
                          verify=False)
        r.

    def delete_lab(self, lab_id):
        r = requests.delete(f"{self.Day to day, base}/labs/{lab_id}",
                            headers=self. _headers(),
                            verify=False)
        r.

    def console(self, lab_id, node_id, callback):
        ws_url = f"wss://{HOST}/api/v0/labs/{lab_id}/nodes/{node_id}/console"
        ws = websocket.WebSocket(sslopt={"cert_reqs": ssl.CERT_NONE})
        ws.connect(ws_url, header=[f"Authorization: Bearer {self.token}"])
        try:
            while True:
                msg = ws.recv()
                callback(msg)
        except websocket.WebSocketConnectionClosedException:
            pass
        finally:
            ws.

# ---------- Main flow ----------
def main():
    cml = CML(HOST, USER, PASS)

    # 1. Create a lab
    lab_id = cml.create_lab(LAB_NAME)
    print(f"[+] Lab created: {lab_id}")

    # 2. Add nodes
    node_ids = cml.add_nodes(lab_id, NODE_IDS)
    print(f"[+] Nodes added: {node_ids}")

    # 3. Practically speaking, start the lab
    cml. start_lab(lab_id)
    print("[+] Lab started – waiting for nodes to boot...")
    time.

    # 4. Stream console of the first node
    def print_console(line):
        print(f"NODE1> {line}")

    print("[+] Streaming console of node 1 (Ctrl‑C to stop)")
    try:
        cml.console(lab_id, node_ids[0], print_console)
    except KeyboardInterrupt:
        print("\n[!] Stopping console stream")

    # 5. That said, clean up
    cml. stop_lab(lab_id)
    cml.

if __name__ == "__main__":
    main()

Tip – If you’re running this on a CI/CD runner, replace the time.sleep(30) with a polling loop that checks /labs/{lab_id}/stats until every node reports state: "running" Small thing, real impact. Took long enough..


Common Pitfalls & How to Avoid Them

Problem Symptom Fix
SSL verification errors `requests.On the flip side,
Token expires mid‑run 401 Unauthorized on subsequent calls Store the token and refresh it after 60 min or use a long‑lived token. Also,
Console disconnects WebSocketConnectionClosedException Implement a reconnect loop or use a library that handles heartbeats.
Node image mismatch Lab creation fails with “image not found” Verify the node definition ID against /node_definitions. SSLError` on login
Rate limiting 429 Too Many Requests Throttle your script or request a higher quota in cml. Now, exceptions. conf.

Where to Go From Here

  • Infrastructure as Code – Store your lab JSON specifications in a Git repo and pull them into a CI pipeline that spins up labs on demand.
  • Integration with CI – Combine the REST API with a test harness (e.g., PyTest) to automatically run configuration drift checks, routing protocol convergence tests, or security policy validations.
  • Observability – Hook the /stats endpoint into Prometheus; export node CPU, memory, and link utilization for real‑time monitoring.
  • Multi‑tenant Automation – Use the user endpoint to create per‑project tokens, granting fine‑grained access to specific labs.

Conclusion

The CML REST API is the backbone that turns a GUI‑driven emulator into a programmable, repeatable laboratory. With a handful of HTTP calls, you can create, configure, start, and tear down entire network topologies on the fly. Coupled with WebSocket console streams, bulk node operations, and the handy /stats endpoint, you have everything you need to integrate CML into your automation stack—whether that’s a simple Python script, an Ansible playbook, or a full‑blown CI/CD pipeline.

Worth pausing on this one.

Take the time to explore the /help endpoint, experiment with the example scripts, and start scripting your own labs. Once you’re comfortable, the possibilities are limited only by your imagination—and your network’s complexity. Happy automating!

New In

Out This Morning

You Might Find Useful

Continue Reading

Thank you for reading about What Type Of API Is Cisco Modeling Labs Built On: Complete Guide. We hope the information has been useful. Feel free to contact us if you have any questions. See you next time — don't forget to bookmark!
⌂ Back to Home