subconverter's whole API is a GET with the subscription URL in the query string
Utility to convert between various subscription format
At a glance
- What is it?
- subconverter is a C++ utility that converts proxy subscriptions between fourteen client formats in both directions, driven by a single HTTP endpoint on port 25500 where the upstream subscription is a query parameter that also accepts a local file path. It can also write to a GitHub Gist when asked, using a token kept in a plaintext ini file.
- Who is it for?
- subconverter fits someone with several proxy clients and one upstream subscription who wants a client-specific file without maintaining a converter per client. It does not fit a shared or public deployment as the file presents it, because the upstream subscription travels as a query parameter, the same parameter accepts a local file path, and the automatic Gist upload is triggered by a plain request while the token sits in an ini file in the repository root.
- Can I use it commercially?
- Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
- Is it still maintained?
- Yes. The repository last received commits 128 days ago.
- What is it written in?
- Mainly C++, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.
Star history
- tindy2013/subconverter17,118 ★ · +48
| Repository | 2026-09-21 | 2026-11-14 |
|---|---|---|
| tindy2013/subconverter | 17.1k | 17.1k |
48 Stars in the last 54 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
The whole interface is a GET, and the subscription is a query parameter
The access interface is one line:
http://127.0.0.1:25500/sub?target=%TARGET%&url=%URL%&config=%CONFIG%Three arguments, two of them required. The target is the format you want, the url is the subscription to convert, and the config is an optional external configuration file. Both the subscription and the configuration are described as accepting URLs and file paths, and both need percent-encoding before you paste them in. Two consequences follow directly from that shape. The first is that the upstream subscription, which is itself a credential, travels in the query string of a GET, so it is written into access logs, proxy logs, monitoring that records paths, and browser history on both ends. The second is that a parameter accepting a local file path turns the endpoint into a file-read surface on whatever machine runs it. Both are design choices rather than bugs, and both are worth understanding before this runs anywhere but your own machine.
Fourteen formats convert both ways, and the target name is not the type name
The supported types table has four columns, and the fourth is the one that matters for the query string. Fourteen formats convert in both directions: Clash, ClashR, Quantumult, Quantumult X, Loon, Shadowsocks in the SIP002 form and the Android form, SSD, SSR, Surfboard, Surge in versions 2, 3 and 4, and V2Ray. The target names are a different vocabulary from the type names, which is where mistakes happen. Quantumult is quan, Quantumult X is quanx, the Android Shadowsocks form is sssub, and the three Surge versions share one scheme with a version parameter. The fifteenth row, Telegram-style HTTP and Socks 5 links, is source only, with no target name at all, so anything you cannot name in that table can be an input but never an output. There is also a client-specific note: Shadowrocket users are told to use ss, ssr or v2ray as the target, which is a hint that the mapping is not clean for every client.
Automatic Gist upload is a token in a plaintext file, triggered by a request
The auto upload feature is short to describe and worth reading twice. You put a GitHub personal access token into gistconf.ini in the root directory, in this shape:
[common]
;uncomment the following line and enter your token to enable upload function
token = xxxxxxxxxxxxxxxxxxxxxxxx(Your Personal Access Token)Then you add upload=true to the local subscription link, and when someone accesses that link the program updates the content of the Gist repository. So the trigger is an ordinary request to the subscription link, while the write is performed with a credential sitting in gistconf.ini in the root directory. The README describes no access control for that request, so assume anything that can reach the endpoint can cause a write with your token. That combination is fine on a laptop and is the reason not to expose this service, and if you do need it, the token belongs in a file outside the source tree and the endpoint belongs on loopback.
Merging subscriptions produces an encoded blob nobody can read
To convert more than one subscription you join them with a pipe character before encoding, and the file walks through it step by step: two subscription links, the two joined with a vertical bar, that whole string percent-encoded into a single percent-encoded value, and then the placeholders in the access interface filled in with the real target and the real encoded value. The mechanism is trivial and the consequence is not. After the encoding step, the thing in your template is a single opaque string that no human can read, check or edit, so a subscription that stops working is debugged by decoding the query string rather than by looking at a configuration file. Two subscriptions become one unreadable token, three become an unreadable one with an embedded pipe, and the error you get from a client is about the output file rather than about the input. Write the merged value down somewhere outside the URL if you intend to use it more than once.
The rules your client receives are data files, not code
The quick usage section is explicit that the path shown uses the default groups and rulesets configuration directly, without changing any settings, and the top of the tree holds a base directory. The optional config argument points at an external configuration file instead, accepts a URL or a local path, and the file points to a separate repository that collects worked examples. So the groups, rules and proxies your client ends up with are configuration data that ships alongside the program rather than logic compiled into it. That is the right design for a converter whose formats change faster than its code, and it has a consequence worth planning around: upgrading the binary can change the output for reasons that have nothing to do with your subscriptions, and pinning the binary does not pin the rules. Keep the configuration under your own control if the rules matter to you.
Two notices carry most of the practical knowledge
Under the supported types table there are two notices, and between them they are the useful part of this file. The first is the Shadowrocket instruction, telling you which target to use for that client, which is a workaround for a mapping the table does not express. The second is about naming: you can add a remark parameter to Telegram-style HTTP and Socks 5 links to set a name for the node, and the file gives both deep-link forms for it, one beginning with the app scheme and one with the site's HTTPS path, carrying the same server, port, user, password and remark. A remark is a way to keep one node recognisable across the clients you convert for. Beyond that, the file hands advanced usage to a separate document, and the Docker route has its own document too.
No release since April 2024, while the branch took commits in July 2026
The version facts are worth having in front of you. The tagged releases are v0.9.0 from 2024-04-08, v0.8.1 from 2023-10-12 and v0.8.0 from 2023-10-09, the default branch is master, and the last push recorded for the repository is 2026-07-09. So the released artefact is more than two years older than the tip of the branch, and whether that matters depends on whether the changes since then touch the formats you care about. Two other details say something about the same age. The top level still carries a Travis file even though the README's build badge links to GitHub Actions, and the practical documentation is split across a separate Docker document and a separate Chinese document, with the latter carrying the advanced usage. Nothing here is broken, but a tool that sits between you and your subscription deserves a version check before you trust a build of it.
Editorial conclusion
subconverter fits someone with several proxy clients and one upstream subscription who wants a client-specific file without maintaining a converter per client. It does not fit a shared or public deployment as the file presents it, because the upstream subscription travels as a query parameter, the same parameter accepts a local file path, and the automatic Gist upload is triggered by a plain request while the token sits in an ini file in the repository root. Before you deploy it, read the argument table twice: both the subscription and the configuration arguments accept URLs and file paths, so the endpoint is a file-read surface as well as a fetch proxy. And check what you are installing, because the last tagged release is v0.9.0 from 2024-04-08 while the last recorded push is 2026-07-09, so a release is over two years behind the branch. If you need the advanced behaviour, the file points you to the Chinese documentation, which is where that material lives.
Frequently asked questions
subconverter alternative
The project file does not name any alternative. What it offers instead is a separate repository of worked configuration examples, a separate Docker document, and a Chinese document that carries the advanced usage. The supported types table is the closest thing to a map of the ecosystem, listing fourteen formats that convert in both directions.
How do I run subconverter?
The access interface is an HTTP GET on port 25500 at the sub path, with target, url and config arguments, used with the default groups and rulesets without changing settings. The target value comes from the Target Name column of the supported types table, and the url and config values both need percent-encoding.
Which subscription formats can subconverter produce?
Both as source and as target: Clash, ClashR, Quantumult, Quantumult X, Loon, Shadowsocks in the SIP002 form, the Android Shadowsocks form, SSD, SSR, Surfboard, Surge 2, Surge 3, Surge 4 and V2Ray. Telegram-style HTTP and Socks 5 links are accepted as source only, with no target.
How does subconverter's automatic Gist upload work?
You put a GitHub personal access token into gistconf.ini in the root directory, add upload=true to the local subscription link, and accessing that link makes the program update the content of the Gist repository. The token is a plaintext value in a configuration file, and the trigger is the request itself.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/tindy2013-subconverter)Star history chart for your README
[](https://hysenlabs.com/projects/tindy2013-subconverter)Was this useful?