Post

Getting Started with Scrapli - Python Network Automation

Getting Started with Scrapli - Python Network Automation

In today post, I introduce an old but lesser known Python library named scrapli, to automate network devices. There are so many module/libraries in Python for network engineers to automate networking tasks. So the question is why scrapli vs netmiko. According to ChatGPT:

Netmiko is like the veteran — reliable, proven, and widely used for network automation over SSH. Scrapli came later as a modern alternative, built to be faster, more flexible, and developer-friendly. It handles both synchronous and asynchronous operations, works smoothly with structured data, and is easier to extend for different platforms, which makes it attractive if you want speed and scalability without giving up reliability.

Scrapli - Introduction

According to the Scrapli docs, it is a Python library for connecting to network devices like routers, switches, and firewalls over SSH or Telnet. The name comes from “scrape CLI.” Its goal is speed, flexibility, and simplicity, with a clean API that works in both sync and async modes, backed by solid testing and documentation.

Setup Scrapli and Devices

In most cases, installing via pip is the easiest and recommended way to install Scrapli. It’s best to use a virtual environment to avoid system issues or conflicts with other modules.

1
2
3
4
5
6
7
8
$ pip install scrapli

Collecting scrapli
  Downloading scrapli-2025.1.30-py3-none-any.whl.metadata (11 kB)
Downloading scrapli-2025.1.30-py3-none-any.whl (145 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 145.7/145.7 kB 704.5 kB/s eta 0:00:00
Installing collected packages: scrapli
Successfully installed scrapli-2025.1.30

The next step is testing Scrapli with network devices. To get your feet wet with automation, the easiest way is to use netlab.tools along with Containerlab.

Here’s a minimal topology file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
---
name: lab
provider: clab

defaults:
  devices:
    iol.clab.image: vrnetlab/cisco_iol:17.12.01
    ioll2.clab.image: vrnetlab/cisco_iol:l2-17.12.01

nodes:
  R1:
    device: iol
  S1:
    device: ioll2

links:
  - R1-S1

👉 For lab setup guides, see this netlab tutorial and Containerlab walkthrough.

A Simple Example

Here’s a short Python script using Scrapli to connect to a Cisco device and run a command:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from scrapli.driver.core import IOSXEDriver

device = {
    "host": "192.168.121.101",
    "auth_username": "admin",
    "auth_password": "admin",
    "auth_strict_key": False,
}

conn = IOSXEDriver(**device)
conn.open()
response = conn.send_command("show ip interface brief")
print(response.result)
conn.close()

Upon running the script, we get the following output:

1
2
3
4
5
6
7
$ python show_cmd.py
Interface              IP-Address      OK? Method Status                Protocol
Ethernet0/0            192.168.121.101 YES TFTP   up                    up
Ethernet0/1            10.1.0.1        YES manual up                    up
Ethernet0/2            unassigned      YES unset  administratively down down
Ethernet0/3            unassigned      YES unset  administratively down down
Loopback0              10.0.0.1        YES manual up                    up

This confirms that Scrapli successfully connected to the Cisco device and retrieved the CLI output of the show ip interface brief command.

Using with Statement for Cleaner Connections

The previous example works, but it requires us to manually open and close the connection. A cleaner way is to use a with statement, which automatically handles connection setup and teardown.

1
2
3
4
5
6
7
8
9
10
11
12
from scrapli.driver.core import IOSXEDriver

device = {
    "host": "192.168.121.101",
    "auth_username": "admin",
    "auth_password": "admin",
    "auth_strict_key": False,
}

with IOSXEDriver(**device) as conn:
    response = conn.send_command("show ip interface brief")
print(response.result)

This is better than the previous example because the connection is automatically closed when the with block finishes, making the code safer and easier to manage.

You can check the available methods of Scrapli objects in a few simple ways using IPython:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
In [1]: from scrapli.driver.core import IOSXEDriver
   ...:
   ...: device = {
   ...:     "host": "192.168.121.101",
   ...:     "auth_username": "admin",
   ...:     "auth_password": "admin",
   ...:     "auth_strict_key": False,
   ...: }
   ...:
   ...: conn = IOSXEDriver(**device)

In [2]: print(dir(conn))

['acquire_priv', 'auth_bypass', 'auth_password', 'auth_private_key', 'auth_private_key_passphrase', 'auth_secondary', 'auth_strict_key', 'auth_username', 'channel', 'close', 'commandeer', 'comms_prompt_pattern', 'comms_prompt_search_depth', 'comms_return_char', 'comms_roughly_match_inputs', 'default_desired_privilege_level', 'failed_when_contains', 'genie_platform', 'get_prompt', 'host', 'isalive', 'logger', 'on_close', 'on_init', 'on_open', 'open', 'port', 'privilege_levels', 'read_callback', 'send_and_read', 'send_command', 'send_commands', 'send_commands_from_file', 'send_config', 'send_configs', 'send_configs_from_file', 'send_interactive', 'ssh_config_file', 'ssh_known_hosts_file', 'textfsm_platform', 'timeout_ops', 'timeout_socket', 'timeout_transport', 'transport', 'transport_name', 'update_privilege_levels']

This will list all methods and attributes for the connection object.

TextFSM/NTC-Templates Integration

Scrapli supports parsing CLI output into structured data using TextFSM and ntc-templates. This requires installing the TextFSM extra:

1
2
3
4
5
# for zsh
$ pip install "scrapli[textfsm]"

# for bash
$ pip install scrapli[textfsm]

If TextFSM parsing succeeds, Scrapli returns a structured result (list of dictionaries). If parsing fails, it simply returns an empty list.

Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from scrapli.driver.core import IOSXEDriver

device = {
    "host": "192.168.121.101",
    "auth_username": "admin",
    "auth_password": "admin",
    "auth_strict_key": False,
}

with IOSXEDriver(**device) as conn:
    response = conn.send_command("show ip interface brief")
    structured_result = response.textfsm_parse_output()

print(structured_result)

Output

1
2
$ python show_cmd.py
[{'interface': 'Ethernet0/0', 'ip_address': '192.168.121.101', 'status': 'up', 'proto': 'up'}, {'interface': 'Ethernet0/1', 'ip_address': '10.1.0.1', 'status': 'up', 'proto': 'up'}, {'interface': 'Ethernet0/2', 'ip_address': 'unassigned', 'status': 'administratively down', 'proto': 'down'}, {'interface': 'Ethernet0/3', 'ip_address': 'unassigned', 'status': 'administratively down', 'proto': 'down'}, {'interface': 'Loopback0', 'ip_address': '10.0.0.1', 'status': 'up', 'proto': 'up'}]

As you can see, instead of raw CLI output, Scrapli + TextFSM gives you structured JSON-like data that’s much easier to use in automation scripts.

Pretty JSON Output

For easier reading, you can format the structured result with Python’s json module:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import json
from scrapli.driver.core import IOSXEDriver

device = {
    "host": "192.168.121.101",
    "auth_username": "admin",
    "auth_password": "admin",
    "auth_strict_key": False,
}

with IOSXEDriver(**device) as conn:
    response = conn.send_command("show ip interface brief")
    structured_result = response.textfsm_parse_output()

print(json.dumps(structured_result, indent=2))

Output

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
$ python show_cmd.py
[
  {
    "interface": "Ethernet0/0",
    "ip_address": "192.168.121.101",
    "status": "up",
    "proto": "up"
  },
  {
    "interface": "Ethernet0/1",
    "ip_address": "10.1.0.1",
    "status": "up",
    "proto": "up"
  },
  {
    "interface": "Ethernet0/2",
    "ip_address": "unassigned",
    "status": "administratively down",
    "proto": "down"
  },
  {
    "interface": "Ethernet0/3",
    "ip_address": "unassigned",
    "status": "administratively down",
    "proto": "down"
  },
  {
    "interface": "Loopback0",
    "ip_address": "10.0.0.1",
    "status": "up",
    "proto": "up"
  }
]

This makes the output much easier to read, especially when working with larger command results.

Let’s expand the example to handle two devices — one at 192.168.121.101 and another at 192.168.121.102. We’ll loop through both, run the command, and print results.

Multiple Devices Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import json
from scrapli.driver.core import IOSXEDriver

devices = [
    {
        "host": "192.168.121.101",
        "auth_username": "admin",
        "auth_password": "admin",
        "auth_strict_key": False,
    },
    {
        "host": "192.168.121.102",
        "auth_username": "admin",
        "auth_password": "admin",
        "auth_strict_key": False,
    },
]

for device in devices:
    with IOSXEDriver(**device) as conn:
        response = conn.send_command("show ip interface brief")
        structured_result = response.textfsm_parse_output()
    print(f"\n=== Results from {device['host']} ===")
    print(json.dumps(structured_result, indent=2))

Sample Output

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
=== Results from 192.168.121.101 ===
[
  {
    "interface": "Ethernet0/0",
    "ip_address": "192.168.121.101",
    "status": "up",
    "proto": "up"
  },
  {
    "interface": "Ethernet0/1",
    "ip_address": "10.1.0.1",
    "status": "up",
    "proto": "up"
  },
  ...
]

=== Results from 192.168.121.102 ===
[
  {
    "interface": "Ethernet0/0",
    "ip_address": "192.168.121.102",
    "status": "up",
    "proto": "up"
  },
  {
    "interface": "Ethernet0/1",
    "ip_address": "10.2.0.1",
    "status": "up",
    "proto": "up"
  },
  ...
]

This way, you can scale the same script to handle many devices just by adding them to the devices list.

Sending Configurations

Scrapli makes sending configs simple. You can push a single block with send_config, multiple lines with send_configs, or load them directly from a file using send_configs_from_file. Each option automatically handles privilege escalation, so you can focus on the commands instead of the access level.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from scrapli.driver.core import IOSXEDriver

device = {
    "host": "192.168.121.101",
    "auth_username": "admin",
    "auth_password": "admin",
    "auth_strict_key": False,
}

with IOSXEDriver(**device) as conn:
    response = conn.send_configs([
        "interface loopback123",
        "description configured by scrapli",
    ])
print(response.result)

Output

1
2
3
$ python config_cmd.py
interface loopback123
description configured by scrapli

Scrapli doesn’t exit config mode after sending configs. That’s intentional — the network drivers automatically return to the correct privilege level before running any new command, so you don’t need to manually exit config mode.

Conclusion

Scrapli is a modern, fast, and flexible alternative to Netmiko that makes network automation smoother. It works well for both commands and configurations, integrates with TextFSM for structured output, and scales easily across multiple devices. With its clean API, async support, and built-in handling of privilege levels, it’s a solid tool for Python network automation.

This post is licensed under CC BY 4.0 by the author.