What the Clash External Controller Can Automate
Clash is often used as a manual node switcher: open the client, select a proxy group, and click the server that appears to be fastest. That workflow is convenient for occasional use, but it becomes inefficient when node quality changes throughout the day. A server that performs well in the morning may develop packet loss in the evening, while another node may become faster after a provider refreshes its routes.
The External Controller API turns node selection into a programmable workflow. Instead of depending entirely on the graphical interface, a local script can inspect proxy groups, measure latency, choose a candidate, and tell Clash to use it. You can then add scheduled checks, retry rules, structured logs, and safety limits without changing the underlying subscription configuration.
This guide focuses on Clash clients and cores that expose the familiar controller interface, including Mihomo, Clash Verge Rev, and compatible desktop clients. The exact menu names may differ between applications, but the API concepts are largely the same. The goal is not to select a node based on one impressive ping result. It is to create a repeatable decision process that avoids unstable nodes and fails safely when the controller or network is unavailable.
Automation Goal
Measure eligible nodes, select a reliable candidate, update one proxy group, and keep every change observable and reversible.
1Configure and Protect the External Controller
The external controller is an HTTP API served by the Clash core. It normally listens on a local address and accepts requests for reading runtime state or changing selections. Before writing any automation, confirm that your client actually exposes the controller. In Clash Verge Rev, this is commonly configured through the active profile or the client settings. In a Mihomo YAML profile, the relevant options look like this:
The address 127.0.0.1 restricts access to the same computer. This is the safest default for a personal automation script because the API cannot be reached directly from another device on the network. The port can be changed if another service is already using 9090, but remember to update the script and any firewall rule accordingly.
The secret protects the controller with an authorization token. Do not leave the controller exposed without a secret, especially if you change the bind address to 0.0.0.0 or a LAN address. An unauthenticated controller may allow another device to inspect your proxy names, view runtime information, or switch traffic to a node of its choice.
Test the API Before Automating It
After applying the profile, restart or reload the Clash core if required. Then send a harmless read-only request from the same machine. The API generally uses the Authorization header with a bearer token:
A successful response should contain a JSON object with proxy names and runtime information. If you receive 401 Unauthorized, check the secret and make sure the header is spelled correctly. A connection-refused error usually means the controller is disabled, the port is incorrect, or the core has not reloaded the configuration.
Security Warning
Never publish the controller port to the public internet. If remote administration is necessary, use a private tunnel or firewall allowlist, keep a strong secret, and avoid placing the token directly in a shared script repository.
Identify the Correct Proxy Group
Clash distinguishes between individual proxies and proxy groups. Your automation should normally update a group such as Proxy, 节点选择, or Auto, not an individual node. Use the response from /proxies to find the group name and inspect its current selection. A group entry usually includes a now field and a list of available all members.
Names may contain spaces, punctuation, emoji, or non-Latin characters. Treat them as opaque strings. Do not reconstruct a name by trimming or replacing characters, and always URL-encode the name when it appears in a request path. This prevents a valid node from failing simply because its subscription label contains a slash, question mark, or whitespace.
2Measure Latency and Filter Bad Candidates
A node selector needs a consistent measurement method. The controller commonly exposes a delay endpoint for a specific proxy, such as /proxies/{proxy-name}/delay. The request includes a test URL and a timeout value:
The response normally contains a delay value in milliseconds. A low number is useful, but it is not a complete definition of quality. Some nodes answer the test URL quickly and then fail during sustained downloads, WebSocket connections, or UDP traffic. For that reason, use latency as one filter rather than the only ranking factor.
Choose a stable HTTPS endpoint that is expected to return quickly. Avoid a site that requires a login, presents a large page, or changes behavior frequently. The test URL should be reachable through the same type of traffic that matters to you. If the automation is intended for general web access, a small HTTPS response is reasonable. If you need streaming or gaming stability, supplement the test with separate application-specific checks.
Use Thresholds Instead of Blindly Picking the Fastest Node
A practical selection process can apply these rules in order:
- Ignore entries that are actually groups,
DIRECT,REJECT, or other control options. - Reject a candidate when the delay request times out or returns an invalid value.
- Reject nodes above a maximum threshold, such as 800 or 1200 milliseconds, depending on your location and use case.
- Prefer candidates that pass more than one measurement instead of trusting a single sample.
- Keep the current node when every alternative is worse or no candidate reaches the minimum quality requirement.
Repeated measurements are particularly important on mobile or congested connections. A node with results of 180, 190, and 210 milliseconds is usually more useful than one with results of 90, 700, and 120 milliseconds. You can calculate a median or a trimmed average, removing the highest result before ranking. This reduces the chance that one temporary packet delay causes an unnecessary switch.
Separate Testing from Switching
Do not change the active group after every successful delay request. First collect all measurements, then rank the candidates, and only make one switch at the end. This prevents the script from rapidly changing the active route while it is still testing. It also makes the log easier to understand and avoids unnecessary reconnections for applications that maintain long-lived sessions.
Pro Tip: Add Hysteresis
Require the new candidate to be at least 20 percent faster than the current node, or better by a fixed margin such as 80 milliseconds. This prevents constant switching when two nodes have nearly identical performance.
3Update a Proxy Group Through the API
Once a candidate has passed your filters, update the proxy group with a PUT request. The group name is part of the URL, while the selected proxy is sent as JSON. A typical request has this shape:
Both the group name in the path and the node name in the JSON body must match the values returned by the controller. If the group contains a selector nested inside another group, update the group that actually controls your rules. Changing a group that is not referenced by any rule will produce a successful API response but no visible traffic change.
A Reliable Selection Workflow
The following pseudocode shows the order that a dependable script should follow. It intentionally separates discovery, measurement, decision-making, and mutation:
Verification after the PUT request is essential. A request may return successfully while the selected name is not the one you expected because of a typo, a nested group, or a client-specific behavior. Read the group state again and compare the returned now field with the intended node. If verification fails, report the error and avoid repeatedly sending the same mutation.
Retries Without Creating a Switching Loop
Transient failures are normal. The controller may briefly be unavailable while the core reloads, and an individual delay request may fail because a node is overloaded. Use a small retry count, such as two or three attempts, with a short increasing delay between attempts. Retry network timeouts and temporary connection errors, but do not endlessly retry authentication failures or malformed requests.
Every retry should use the same request identifier or log context so that one automation run can be reconstructed later. When a switch fails, leave the current working node untouched. A safe script should prefer a known route over an unverified route, even if that means the run finishes without making a change.
4Scheduling, Logging, and Operational Safety
Automatic node switching becomes useful when it runs at an appropriate interval. Running a test every few seconds creates needless traffic, may trigger provider rate limits, and can interrupt active sessions. For ordinary browsing, a check every 15 to 30 minutes is usually enough. You can also run the workflow when the computer starts, after waking from sleep, or only during a known busy period.
Windows users can schedule a PowerShell or Python script with Task Scheduler. On macOS and Linux, cron or a system service can invoke the script. Keep the schedule outside the script itself so that the selection logic remains easy to test manually. Before enabling a recurring task, run it in dry-run mode and print the proposed switch without sending the PUT request.
Log the Decision, Not the Secret
A useful log entry should include the timestamp, target group, current node, candidate measurements, selected node, and the reason for the decision. For example, record whether the switch happened because the current node exceeded a threshold, because it failed repeated tests, or because a significantly faster candidate was found. Never write the controller token into the log, command output, screenshots, or error reports.
Use log rotation if the workflow runs continuously. A simple text file is adequate for personal use, while JSON Lines can be better when you want to analyze node performance over time. Historical measurements can reveal patterns that a single run cannot, such as a node that is fast during local daytime but unstable during evening congestion.
Protect the Local Automation Surface
Store the controller token in an environment variable or an operating-system credential store rather than embedding it in the script. Restrict the script file permissions so other local accounts cannot read it. If you use a configuration file, keep secrets in a separate file that is excluded from backups or source control. When sharing troubleshooting output, replace the token and consider masking provider-specific node labels.
Finally, provide a manual override. Users should be able to disable the scheduled task, select a node in the Clash client, or run the script with a --dry-run option. Automation should reduce routine work, not remove control. A good implementation switches only when the evidence is strong, records what it did, and leaves the existing configuration intact.
Operational Checklist
Bind the controller locally, require a secret, test multiple candidates, add a switching margin, verify every update, retry only temporary failures, and keep a clear audit log.
With these safeguards in place, the Clash External Controller API becomes more than a way to click a different node remotely. It becomes a controlled automation layer that can adapt to changing network conditions while preserving predictable behavior and secure administration.