Quick answer: Paramiko is a low-level Python implementation of the SSH protocol; Netmiko is a higher-level library built on top of Paramiko that understands network device prompts, paging and configuration modes for Cisco, Juniper, Arista and more than a hundred other platforms. Use Netmiko for routers and switches; drop down to Paramiko when you need raw SSH control or are talking to Linux servers. Netmiko also handles legacy Telnet through its _telnet device types.
Almost every network automation script begins with “connect to the device and run a command”. This guide shows how to do that with both libraries, explains exactly what Netmiko adds on top of Paramiko, covers Telnet for the old gear still in your racks, and lists the connection mistakes that cause hanging scripts and locked accounts.
Paramiko versus Netmiko: what each one does
SSH is a general-purpose protocol. A Linux server gives you a shell, runs your command, and returns when it finishes. A Cisco switch behaves differently: it shows a prompt like Switch> or Switch#, paginates long output with --More--, has an enable mode and a configuration mode, and never signals “the command finished” β it just prints the prompt again.
Paramiko handles the SSH part β key exchange, authentication, channels β and leaves the prompt handling to you. Netmiko handles the network-device part: it detects the prompt, disables paging, waits for output to finish, and knows the commands to enter configuration mode on each vendor.
| Feature | Paramiko | Netmiko |
|---|---|---|
| Protocol | SSH only (SSHv2) | SSH, plus Telnet and serial via dedicated device types |
| Prompt detection | Manual | Automatic |
Disable paging (terminal length 0) |
You send it | Automatic per platform |
| Enable / config mode | You script it | enable(), send_config_set() |
| Structured output | No | TextFSM, TTP and Genie parsing built in |
| Vendor knowledge | None | 100+ device types |
| Best for | Linux servers, SFTP, custom protocols | Routers, switches, firewalls |
Install both with pip install netmiko β Netmiko lists Paramiko as a dependency, so you get it automatically. If you are still deciding whether automation is worth the learning curve, Why Use Python for Network Engineering? makes the case before you write a line of code.
Hands-on: SSH with Paramiko
This example connects to a Cisco IOS device, opens an interactive shell, disables paging and runs a command. Notice how much manual work is involved compared with the Netmiko version that follows.
import os
import time
import paramiko
HOST = "192.168.1.1"
client = paramiko.SSHClient()
client.load_system_host_keys()
# RejectPolicy is the secure default; AutoAddPolicy only in a trusted lab
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(
HOST,
username=os.environ["NET_USER"],
password=os.environ["NET_PASS"],
look_for_keys=False,
allow_agent=False,
timeout=10,
)
shell = client.invoke_shell()
shell.send("terminal length 0\n")
time.sleep(1)
shell.send("show ip interface brief\n")
time.sleep(2) # crude: wait for output
output = shell.recv(65535).decode("utf-8")
print(output)
client.close()
The time.sleep() calls are the weak point. Too short and you miss output; too long and the script crawls. Production Paramiko code loops on shell.recv_ready() and looks for the prompt β which is exactly the logic Netmiko packages for you. Paramiko’s exec_command() method is cleaner for Linux hosts but many network operating systems do not support it.
Hands-on: SSH with Netmiko
import os
from netmiko import ConnectHandler
router = {
"device_type": "cisco_ios",
"host": "192.168.1.1",
"username": os.environ["NET_USER"],
"password": os.environ["NET_PASS"],
"secret": os.environ.get("NET_ENABLE", ""),
"conn_timeout": 10,
}
with ConnectHandler(**router) as conn:
conn.enable()
# Raw text, paging already disabled
print(conn.send_command("show ip interface brief"))
# Structured data via TextFSM
interfaces = conn.send_command("show ip interface brief", use_textfsm=True)
for intf in interfaces:
print(f"{intf['interface']:25} {intf['ip_address']:16} {intf['status']}")
# Configuration change
result = conn.send_config_set([
"interface Loopback100",
" description Managed by Netmiko",
" ip address 10.255.0.1 255.255.255.255",
])
print(result)
conn.save_config()
Five lines of Paramiko boilerplate became a dictionary and a context manager. Netmiko waits for the prompt rather than sleeping, handles enable mode, and returns the exact output you would see in a terminal. The with block guarantees disconnect() is called even when an exception is raised.
Device types to remember: cisco_ios, cisco_xe, cisco_nxos, cisco_asa, juniper_junos, arista_eos, hp_procurve, fortinet, paloalto_panos and linux. If you are unsure, from netmiko import SSHDetect can guess the platform for you.
Telnet connections for legacy devices
Telnet sends passwords in clear text and should be disabled wherever possible, but older switches and lab equipment sometimes leave you no choice. Netmiko supports it with the same API β only the device_type changes:
import os
from netmiko import ConnectHandler
legacy_switch = {
"device_type": "cisco_ios_telnet", # note the _telnet suffix
"host": "192.168.1.20",
"username": os.environ["NET_USER"],
"password": os.environ["NET_PASS"],
"port": 23,
}
with ConnectHandler(**legacy_switch) as conn:
print(conn.send_command("show version | include Version"))
# First automation task on a Telnet device: turn on SSH
conn.send_config_set([
"ip domain-name lab.local",
"crypto key generate rsa modulus 2048",
"ip ssh version 2",
"line vty 0 15",
" transport input ssh",
])
conn.save_config()
Paramiko does not do Telnet at all. If you need Telnet without Netmiko, Python’s standard library telnetlib was removed in Python 3.13, so use the telnetlib3 package instead.
Connection errors you will meet and what they mean
NetmikoTimeoutExceptionβ TCP connection failed. Check reachability, firewall rules, and that SSH is actually enabled on the device.NetmikoAuthenticationExceptionβ wrong username or password, or the account lacks SSH privilege. On Cisco, make surelogin localor AAA is configured on the VTY lines.paramiko.ssh_exception.SSHException: Incompatible ssh peerβ the device offers only legacy key-exchange or cipher algorithms. Newer Paramiko and OpenSSH disable these; passdisabled_algorithmsor, better, upgrade the device firmware.ReadTimeoutβ the command took longer than Netmiko expected (for exampleshow tech-support). Increaseread_timeoutonsend_command()or usesend_command_timing().- Script hangs forever β usually a prompt Netmiko did not recognise, such as a confirmation question. Use
send_command(cmd, expect_string=r"\[confirm\]")and then send the answer.
Six connection mistakes to avoid
AutoAddPolicy()everywhere. It accepts any host key, which defeats SSH’s man-in-the-middle protection. Fine in a lab; in production load a known-hosts file.- Passwords in the script. Use
os.environ,getpass.getpass(), or a secrets manager. Netmiko also supportsuse_keys=Truewithkey_filefor SSH key authentication. - No timeouts. A single unreachable device blocks a 200-device run indefinitely. Set
conn_timeoutand catch the exceptions above. - Forgetting
disconnect(). Devices have a VTY line limit (often five on Cisco IOS). Leaked sessions lock everyone out until they time out. Always use thewithblock. - Running config commands with
send_command(). It never enters configuration mode. Usesend_config_set()orsend_config_from_file(). - Parsing with string positions. Column widths change between software versions. Prefer
use_textfsm=Trueor regular expressions β see Parsing Network Device Output with Regular Expressions in Python.
Frequently asked questions
Can Netmiko connect to Linux servers?
Yes, with device_type: "linux". For heavy server automation, though, plain Paramiko with exec_command() or a tool such as Fabric or Ansible is usually a better fit.
Does Netmiko support SSH keys instead of passwords?
Yes. Add "use_keys": True and "key_file": "~/.ssh/id_ed25519" to the device dictionary. Key-based authentication is both more secure and easier to automate.
How many devices can I connect to at once?
Netmiko connections are blocking, so use concurrent.futures.ThreadPoolExecutor or move to Nornir, which manages a thread pool and inventory for you. Twenty to fifty concurrent sessions is a common safe ceiling, limited mostly by the devices and your AAA server.
Is Scrapli a replacement for Netmiko?
Scrapli is a newer alternative with async support and very fast transports. Netmiko remains the most widely used and documented option, and the concepts transfer directly between the two.
Key takeaways
- Paramiko gives you raw SSH; Netmiko adds everything network devices need β prompts, paging, config mode and 100+ platforms.
- Reach for Netmiko first for routers and switches; keep Paramiko for servers and custom SSH work.
- Telnet still works through Netmiko’s
_telnetdevice types, but use it mainly to enable SSH. - Always set timeouts, handle exceptions, use the
withblock, and keep credentials out of code.
Want to master Python from the basics through to real automation and data projects? Our Python & Data Science course includes mentor support, hands-on labs and placement assistance. Prefer learning by watching? Subscribe to our YouTube channel for step-by-step demos.



