REST API
REST API
Section titled “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.
Overview
Section titled “Overview”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.
Sub-menu
Section titled “Sub-menu”/ip/service
Key Capabilities
Section titled “Key Capabilities”- 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
Enabling the API
Section titled “Enabling the API”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.
Enable HTTPS (recommended)
Section titled “Enable HTTPS (recommended)”/ip service set www-ssl disabled=no port=443Assign a certificate to the HTTPS service:
/ip service set www-ssl certificate=your-server-certEnable HTTP (plain, lab/testing only)
Section titled “Enable HTTP (plain, lab/testing only)”/ip service set www disabled=no port=80Restrict access by source address
Section titled “Restrict access by source address”Limit which hosts can reach the API (best practice):
/ip service set www-ssl address=192.168.88.0/24Verify service status
Section titled “Verify service status”/ip service printAuthentication
Section titled “Authentication”The REST API uses HTTP Basic Authentication. Include credentials as a Base64-encoded Authorization header, or pass them directly with curl using -u.
curl -k -u admin:password https://192.168.88.1/rest/system/identityAvoid 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.
Creating a dedicated API user (recommended)
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-readonlyURL Structure
Section titled “URL Structure”REST endpoints follow the pattern:
https://<router-ip>/rest/<menu-path>RouterOS menu paths use forward slashes, matching the CLI hierarchy:
| RouterOS CLI menu | REST 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 |
Referencing specific items
Section titled “Referencing specific items”Items are referenced by their .id value (prefixed with *):
/rest/interface/*1/rest/ip/address/*3HTTP Methods
Section titled “HTTP Methods”| Method | Operation | Description |
|---|---|---|
| GET | Read / list | Retrieve one or all items |
| PATCH | Update | Modify properties of an existing item |
| PUT | Create | Add a new item |
| DELETE | Remove | Delete an item by .id |
| POST | Command | Execute a RouterOS CLI command |
GET — Reading Data
Section titled “GET — Reading Data”List all items
Section titled “List all items”# List all interfacescurl -k -u admin:password \ https://192.168.88.1/rest/interfaceGet a specific item by ID
Section titled “Get a specific item by ID”# Get interface with ID *1curl -k -u admin:password \ https://192.168.88.1/rest/interface/*1Filter results with query parameters
Section titled “Filter results with query parameters”Append .proplist to limit returned fields:
curl -k -u admin:password \ 'https://192.168.88.1/rest/interface?.proplist=name,type,running'Filter by property value using ?<property>=<value>:
# List only running interfacescurl -k -u admin:password \ 'https://192.168.88.1/rest/interface?running=true'Query routes
Section titled “Query routes”curl -k -u admin:password \ https://192.168.88.1/rest/ip/routeFilter for active routes only:
curl -k -u admin:password \ 'https://192.168.88.1/rest/ip/route?active=true'Query IP addresses
Section titled “Query IP addresses”curl -k -u admin:password \ https://192.168.88.1/rest/ip/addressExample JSON response
Section titled “Example JSON response”[ { ".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": "" }]PUT — Creating Items
Section titled “PUT — Creating Items”Use PUT with a JSON body to add a new item:
# Add an IP address to an interfacecurl -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"}'# Add a static routecurl -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"}'Response
Section titled “Response”On success, RouterOS returns the created object including its assigned .id:
{ ".id": "*5", "address": "10.0.0.1/24", "interface": "ether2", "disabled": "false"}PATCH — Updating Items
Section titled “PATCH — Updating Items”Use PATCH with the item’s .id in the URL path:
# Update interface commentcurl -k -u admin:password \ -H "Content-Type: application/json" \ -X PATCH https://192.168.88.1/rest/interface/*1 \ -d '{"comment":"WAN uplink"}'# Disable a firewall rulecurl -k -u admin:password \ -H "Content-Type: application/json" \ -X PATCH https://192.168.88.1/rest/ip/firewall/filter/*A \ -d '{"disabled":"true"}'DELETE — Removing Items
Section titled “DELETE — Removing Items”# Delete a firewall rule by IDcurl -k -u admin:password \ -X DELETE https://192.168.88.1/rest/ip/firewall/filter/*A# Remove an IP addresscurl -k -u admin:password \ -X DELETE https://192.168.88.1/rest/ip/address/*5A successful DELETE returns HTTP 204 with no body.
POST — Executing Commands
Section titled “POST — Executing Commands”POST maps to RouterOS CLI commands. The URL path is the command path, and the JSON body contains named parameters.
# Change admin passwordcurl -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"}'# Ping from the routercurl -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"}'# Export configuration sectioncurl -k -u admin:password \ -H "Content-Type: application/json" \ -X POST https://192.168.88.1/rest/ip/address/print \ -d '{}'Practical Examples
Section titled “Practical Examples”Check system identity
Section titled “Check system identity”curl -k -u admin:password \ https://192.168.88.1/rest/system/identity{"name": "MikroTik"}List DHCP leases
Section titled “List DHCP leases”curl -k -u admin:password \ https://192.168.88.1/rest/ip/dhcp-server/leaseGet router resource stats
Section titled “Get router resource stats”curl -k -u admin:password \ https://192.168.88.1/rest/system/resourceEnable an interface
Section titled “Enable an interface”curl -k -u admin:password \ -H "Content-Type: application/json" \ -X PATCH https://192.168.88.1/rest/interface/*2 \ -d '{"disabled":"false"}'Add a DNS record
Section titled “Add a DNS record”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"}'Python Scripting Examples
Section titled “Python Scripting Examples”Use the requests library for REST API automation from Python scripts. Always set a timeout to prevent scripts from hanging on unresponsive routers.
Fetch interface traffic counters
Section titled “Fetch interface traffic counters”import requestsimport 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"), )Check DHCP leases
Section titled “Check DHCP leases”import requestsimport 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", ""), )Add a firewall filter rule
Section titled “Add a firewall filter rule”import requestsimport 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"))Reusable session helper
Section titled “Reusable session helper”For scripts that make multiple calls, use a requests.Session to reuse the TCP connection:
import requestsimport 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()
# Usages = make_session("192.168.88.1", "api-user", "strong-password")identity = get(s, "/system/identity")print(identity)Rate Limiting and Best Practices
Section titled “Rate Limiting and Best Practices”RouterOS does not publish a documented rate limit for the REST API, but the following practices prevent overload and maintain router stability.
Connection and request behaviour
Section titled “Connection and request behaviour”- 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.
Polling intervals
Section titled “Polling intervals”| Use case | Recommended interval |
|---|---|
| Interface traffic counters | ≥ 30 seconds |
| DHCP lease table | ≥ 60 seconds |
| System resource stats | ≥ 30 seconds |
| Firewall rule checks | On-demand only |
Polling faster than 10 seconds on resource-constrained routers (RB, hAP series) can noticeably increase CPU load.
Reduce payload size
Section titled “Reduce payload size”Use .proplist to fetch only the fields you need:
curl -k -u admin:password \ 'https://192.168.88.1/rest/interface?.proplist=name,rx-byte,tx-byte,running'Timeouts
Section titled “Timeouts”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)Avoid polling; prefer scripted triggers
Section titled “Avoid polling; prefer scripted triggers”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.
HTTP Status Codes
Section titled “HTTP Status Codes”| Code | Meaning |
|---|---|
200 OK | Successful GET or POST |
201 Created | Item created (PUT) |
204 No Content | Successful DELETE |
400 Bad Request | Invalid JSON or missing required property |
401 Unauthorized | Invalid or missing credentials |
404 Not Found | Unknown endpoint or item ID |
500 Internal Server Error | RouterOS command failed |
Troubleshooting
Section titled “Troubleshooting”401 Unauthorized
Section titled “401 Unauthorized”Verify the username and password, and confirm the user group has api policy:
/user group print/user printConnection refused
Section titled “Connection refused”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=443SSL certificate error
Section titled “SSL certificate error”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-certUnexpected Content-Type
Section titled “Unexpected Content-Type”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.
Item not found (404)
Section titled “Item not found (404)”Retrieve item IDs first with a GET, then use the exact .id value (including the * prefix) in subsequent requests:
# Step 1: find the IDcurl -k -u admin:password \ 'https://192.168.88.1/rest/interface?.proplist=.id,name'
# Step 2: use the IDcurl -k -u admin:password \ https://192.168.88.1/rest/interface/*3