Skip to content

REST API

The RouterOS REST API provides a JSON-based HTTP interface to all RouterOS configuration and monitoring data. Available since RouterOS v7.1beta4, it maps every RouterOS menu path directly to a URL under /rest, allowing you to read, create, update, and delete configuration using standard HTTP methods.

The REST API is served by the RouterOS web service (www / www-ssl). Each RouterOS menu translates to a URL path — for example, /ip/address becomes https://router/rest/ip/address. All requests and responses use JSON.

/ip/service

  • Full read/write access to all RouterOS menus
  • Standard HTTP methods (GET, POST, PATCH, PUT, DELETE)
  • JSON request and response bodies
  • HTTP Basic authentication
  • HTTPS support with certificate validation
  • CLI command execution via POST

The REST API is enabled by activating the web service on the router. For production use, HTTPS is strongly recommended to protect credentials in transit.

/ip service set www-ssl disabled=no port=443

Assign a certificate to the HTTPS service:

/ip service set www-ssl certificate=your-server-cert
/ip service set www disabled=no port=80

Limit which hosts can reach the API (best practice):

/ip service set www-ssl address=192.168.88.0/24
/ip service print

The REST API uses HTTP Basic Authentication. Include credentials as a Base64-encoded Authorization header, or pass them directly with curl using -u.

Terminal window
curl -k -u admin:password https://192.168.88.1/rest/system/identity

Avoid using plain HTTP (www) in production — credentials are transmitted in cleartext. Use HTTPS (www-ssl) with a valid certificate, or at minimum accept the self-signed cert with -k in controlled environments.

Section titled “Creating a dedicated API user (recommended)”
/user group add name=api-readonly policy=read,api,!local,!telnet,!ssh,!ftp,!reboot,!write,!policy,!test,!winbox,!password,!web,!sniff,!sensitive,!romon
/user add name=api-user password=strong-password group=api-readonly

REST endpoints follow the pattern:

https://<router-ip>/rest/<menu-path>

RouterOS menu paths use forward slashes, matching the CLI hierarchy:

RouterOS CLI menuREST endpoint
/ip/address/rest/ip/address
/ip/route/rest/ip/route
/interface/rest/interface
/ip/firewall/filter/rest/ip/firewall/filter
/system/identity/rest/system/identity

Items are referenced by their .id value (prefixed with *):

/rest/interface/*1
/rest/ip/address/*3
MethodOperationDescription
GETRead / listRetrieve one or all items
PATCHUpdateModify properties of an existing item
PUTCreateAdd a new item
DELETERemoveDelete an item by .id
POSTCommandExecute a RouterOS CLI command
Terminal window
# List all interfaces
curl -k -u admin:password \
https://192.168.88.1/rest/interface
Terminal window
# Get interface with ID *1
curl -k -u admin:password \
https://192.168.88.1/rest/interface/*1

Append .proplist to limit returned fields:

Terminal window
curl -k -u admin:password \
'https://192.168.88.1/rest/interface?.proplist=name,type,running'

Filter by property value using ?<property>=<value>:

Terminal window
# List only running interfaces
curl -k -u admin:password \
'https://192.168.88.1/rest/interface?running=true'
Terminal window
curl -k -u admin:password \
https://192.168.88.1/rest/ip/route

Filter for active routes only:

Terminal window
curl -k -u admin:password \
'https://192.168.88.1/rest/ip/route?active=true'
Terminal window
curl -k -u admin:password \
https://192.168.88.1/rest/ip/address
[
{
".id": "*1",
"address": "192.168.88.1/24",
"network": "192.168.88.0",
"interface": "bridge",
"actual-interface": "bridge",
"invalid": "false",
"dynamic": "false",
"disabled": "false",
"comment": ""
}
]

Use PUT with a JSON body to add a new item:

Terminal window
# Add an IP address to an interface
curl -k -u admin:password \
-H "Content-Type: application/json" \
-X PUT https://192.168.88.1/rest/ip/address \
-d '{"address":"10.0.0.1/24","interface":"ether2"}'
Terminal window
# Add a static route
curl -k -u admin:password \
-H "Content-Type: application/json" \
-X PUT https://192.168.88.1/rest/ip/route \
-d '{"dst-address":"0.0.0.0/0","gateway":"10.0.0.254"}'

On success, RouterOS returns the created object including its assigned .id:

{
".id": "*5",
"address": "10.0.0.1/24",
"interface": "ether2",
"disabled": "false"
}

Use PATCH with the item’s .id in the URL path:

Terminal window
# Update interface comment
curl -k -u admin:password \
-H "Content-Type: application/json" \
-X PATCH https://192.168.88.1/rest/interface/*1 \
-d '{"comment":"WAN uplink"}'
Terminal window
# Disable a firewall rule
curl -k -u admin:password \
-H "Content-Type: application/json" \
-X PATCH https://192.168.88.1/rest/ip/firewall/filter/*A \
-d '{"disabled":"true"}'
Terminal window
# Delete a firewall rule by ID
curl -k -u admin:password \
-X DELETE https://192.168.88.1/rest/ip/firewall/filter/*A
Terminal window
# Remove an IP address
curl -k -u admin:password \
-X DELETE https://192.168.88.1/rest/ip/address/*5

A successful DELETE returns HTTP 204 with no body.

POST maps to RouterOS CLI commands. The URL path is the command path, and the JSON body contains named parameters.

Terminal window
# Change admin password
curl -k -u admin:password \
-H "Content-Type: application/json" \
-X POST https://192.168.88.1/rest/password \
-d '{"old-password":"oldpass","new-password":"newpass","confirm-new-password":"newpass"}'
Terminal window
# Ping from the router
curl -k -u admin:password \
-H "Content-Type: application/json" \
-X POST https://192.168.88.1/rest/ping \
-d '{"address":"8.8.8.8","count":"3"}'
Terminal window
# Export configuration section
curl -k -u admin:password \
-H "Content-Type: application/json" \
-X POST https://192.168.88.1/rest/ip/address/print \
-d '{}'
Terminal window
curl -k -u admin:password \
https://192.168.88.1/rest/system/identity
{"name": "MikroTik"}
Terminal window
curl -k -u admin:password \
https://192.168.88.1/rest/ip/dhcp-server/lease
Terminal window
curl -k -u admin:password \
https://192.168.88.1/rest/system/resource
Terminal window
curl -k -u admin:password \
-H "Content-Type: application/json" \
-X PATCH https://192.168.88.1/rest/interface/*2 \
-d '{"disabled":"false"}'
Terminal window
curl -k -u admin:password \
-H "Content-Type: application/json" \
-X PUT https://192.168.88.1/rest/ip/dns/static \
-d '{"name":"myhost.local","address":"192.168.88.50"}'

Use the requests library for REST API automation from Python scripts. Always set a timeout to prevent scripts from hanging on unresponsive routers.

import requests
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
ROUTER = "https://192.168.88.1"
AUTH = ("api-user", "strong-password")
r = requests.get(
f"{ROUTER}/rest/interface",
auth=AUTH,
verify=False,
timeout=10,
)
r.raise_for_status()
for iface in r.json():
print(
iface.get("name"),
"rx:", iface.get("rx-byte"),
"tx:", iface.get("tx-byte"),
)
import requests
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
ROUTER = "https://192.168.88.1"
AUTH = ("api-user", "strong-password")
r = requests.get(
f"{ROUTER}/rest/ip/dhcp-server/lease",
auth=AUTH,
verify=False,
timeout=10,
)
r.raise_for_status()
for lease in r.json():
if lease.get("status") == "bound":
print(
lease.get("address"),
lease.get("mac-address"),
lease.get("host-name", ""),
)
import requests
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
ROUTER = "https://192.168.88.1"
AUTH = ("api-user", "strong-password")
payload = {
"chain": "input",
"action": "accept",
"protocol": "tcp",
"dst-port": "8291",
"src-address": "192.168.88.0/24",
"comment": "Allow Winbox from LAN",
}
r = requests.put(
f"{ROUTER}/rest/ip/firewall/filter",
auth=AUTH,
json=payload,
verify=False,
timeout=10,
)
r.raise_for_status()
print("Created:", r.json().get(".id"))

For scripts that make multiple calls, use a requests.Session to reuse the TCP connection:

import requests
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
def make_session(router_ip, username, password):
s = requests.Session()
s.auth = (username, password)
s.verify = False
s.base_url = f"https://{router_ip}/rest"
return s
def get(session, path):
r = session.get(f"{session.base_url}{path}", timeout=10)
r.raise_for_status()
return r.json()
def patch(session, path, data):
r = session.patch(f"{session.base_url}{path}", json=data, timeout=10)
r.raise_for_status()
return r.json()
# Usage
s = make_session("192.168.88.1", "api-user", "strong-password")
identity = get(s, "/system/identity")
print(identity)

RouterOS does not publish a documented rate limit for the REST API, but the following practices prevent overload and maintain router stability.

  • RouterOS handles REST requests sequentially per connection. Avoid sending many parallel requests from separate connections simultaneously.
  • Each HTTP request opens a new TCP connection unless you reuse a session (Python requests.Session, curl --keepalive).
  • Long-running POST commands (e.g., ping, traceroute) block the connection until the router responds.
Use caseRecommended interval
Interface traffic counters≥ 30 seconds
DHCP lease table≥ 60 seconds
System resource stats≥ 30 seconds
Firewall rule checksOn-demand only

Polling faster than 10 seconds on resource-constrained routers (RB, hAP series) can noticeably increase CPU load.

Use .proplist to fetch only the fields you need:

Terminal window
curl -k -u admin:password \
'https://192.168.88.1/rest/interface?.proplist=name,rx-byte,tx-byte,running'

Always set a request timeout in scripts. RouterOS can take several seconds to respond when the device is under load:

requests.get(url, auth=auth, verify=False, timeout=15)

Where possible, use RouterOS scripting with schedulers or event triggers to push changes, rather than polling the REST API from an external system at high frequency.

CodeMeaning
200 OKSuccessful GET or POST
201 CreatedItem created (PUT)
204 No ContentSuccessful DELETE
400 Bad RequestInvalid JSON or missing required property
401 UnauthorizedInvalid or missing credentials
404 Not FoundUnknown endpoint or item ID
500 Internal Server ErrorRouterOS command failed

Verify the username and password, and confirm the user group has api policy:

/user group print
/user print

Check that the web service is enabled and the port is reachable:

/ip service print where name=www-ssl
/ip firewall filter print where dst-port=443

If using a self-signed cert, pass -k to curl to skip verification during testing. For production, install a trusted certificate or add the router’s CA to your trust store:

/certificate print
/certificate export-certificate your-ca-cert

Some RouterOS versions return text/plain; charset=utf-8 instead of application/json. Parse the response body as JSON regardless of the Content-Type header. This is a known interoperability quirk reported in RouterOS 7.x.

Retrieve item IDs first with a GET, then use the exact .id value (including the * prefix) in subsequent requests:

Terminal window
# Step 1: find the ID
curl -k -u admin:password \
'https://192.168.88.1/rest/interface?.proplist=.id,name'
# Step 2: use the ID
curl -k -u admin:password \
https://192.168.88.1/rest/interface/*3