Skip to content

9Login API: proxies#

Manage the shared proxy list of the account: add, check, change and delete proxies, and write, test and save proxy scripts. See Proxies for how the list works.

GET /v1/proxies#

The shared proxy list, filtered and paged. Passwords are never returned; a script entry shows hasScript, and GET /v1/proxies/{id}/script reads its script. Entries come newest first, the order the Proxy Manager opens in; sort and order pick another column, as clicking a column header there does. usedBy counts the profiles on this machine that use the entry; lastCheck is the last Check IP made on this machine (null = never; error holds the reason of a failed one, as the app shows it). pending is true when this machine holds changes the account does not have yet: they are sent at the next sign-in. Changes made on another machine show up here after 9Login signs in again.

MCP tool: list_proxies. Access: read only.

Field Type Required Description
search string no The same search as the box of the Proxy Manager, case-insensitive: label, server, user, type, last IPs and country.
type string no Only entries of this type; "custom" = script entries. Values: http, socks4, socks5, ssh, custom.
used boolean no true = only entries some profile uses, false = only unused ones.
sort string no Sort by when the proxy was added (created), its label, type, endpoint (server), how many profiles use it (used), or its last check (country, then IP). Entries added in one call share one time and keep the order of their lines. Values: created, label, type, server, used, lastCheck. Default "created".
order string no Sort direction. Values: asc, desc. Default "desc".
page integer no Page number, from 1. Min 1. Default 1.
limit integer no Entries per page; 1000 holds the whole list of any plan. Min 1, max 1000. Default 100.
Request
curl -H "Authorization: Bearer YOUR_API_KEY" "http://127.0.0.1:37286/v1/proxies?search=us&limit=2"
Response
{
  "ok": true,
  "data": {
    "total": 5,
    "page": 1,
    "limit": 2,
    "pending": false,
    "items": [
      {
        "id": "k3m9q2x7v4b8n6c1",
        "label": "US 1",
        "type": "http",
        "server": "1.2.3.4:8080",
        "user": "u1",
        "hasPassword": true,
        "hasScript": false,
        "createdAt": 1760000000000,
        "usedBy": 3,
        "lastCheck": {
          "ipV4": "1.2.3.4",
          "ipV6": "",
          "country": "US",
          "error": "",
          "checkedAt": 1760000100000
        }
      },
      {
        "id": "p9w2e5r8t1y4u7i0",
        "label": "US rotating",
        "type": "custom",
        "server": "",
        "user": "",
        "hasPassword": false,
        "hasScript": true,
        "createdAt": 1760000000500,
        "usedBy": 0,
        "lastCheck": null
      }
    ]
  }
}

GET /v1/proxies/{id}/script#

The saved script of a script (custom) entry of the proxy list. Read it before changing it with POST /v1/proxies/update. A proxy script usually holds the user's key for a provider: show or copy it only where the user asked. bytes is its size in UTF-8 (at most 16 KB); usedBy counts the profiles on this machine that use the entry. GET /v1/proxies/script-docs describes what a script can call.

MCP tool: get_proxy_script. Access: read only.

No parameters.

Request
curl -H "Authorization: Bearer YOUR_API_KEY" "http://127.0.0.1:37286/v1/proxies/s7k2m9q4w1e8r5t3/script"
Response
{
  "ok": true,
  "data": {
    "id": "s7k2m9q4w1e8r5t3",
    "label": "Provider X, US",
    "script": "SetResult({ Type: \"socks5\", Server: \"gate.example.com:\" + RandomInt(10000, 10099) });",
    "bytes": 85,
    "usedBy": 3,
    "createdAt": 1760000000500
  }
}

POST /v1/proxies/add#

Add proxies from text, one per line, as the Import tab of the Proxy Manager does. Blank lines and lines starting with # are skipped. A line may start with its type (http, socks4, socks5, ssh); other lines take type. Accepted forms: host:port, host:port:user:pass, user:pass@host:port, host:port;user;pass, host:port|user|pass, host port user pass. IPv6 hosts go in brackets. A missing port becomes 22 for ssh and 80 otherwise. A proxy already in the list (same type, server, user and password), or repeated in the text, is skipped. invalidLines lists the 1-based lines that are not a proxy. Send many lines in one call rather than one call per proxy. A script (custom) entry is added with POST /v1/proxies/add-script instead.

MCP tool: add_proxies. Access: needs "Allow changes", needs the sub-user permission "manageProxy".

Field Type Required Description
text string yes The proxies, one per line.
type string no Type of the lines that do not start with one. Values: http, socks4, socks5, ssh. Default "http".
label string no Name given to each added proxy, as the Name box of the Import tab. {INDEX} numbers them in the order they are added: US_{INDEX|1} gives US_1, US_2, US_3; {INDEX|10} starts at 10; {INDEX|1|3} pads to 001. Proxies skipped as already listed take no number. Without {INDEX} every added proxy gets the same name. {DATE}, {TIME} and {RAND|8} work as in profile titles.
onConflict string no When the proxy list was changed on another machine first: "fail" answers conflict and changes nothing; "reload" takes that version and applies this change on top of it. A reload drops changes this machine kept while offline. Values: reload, fail. Default "fail".
Request
curl -X POST "http://127.0.0.1:37286/v1/proxies/add" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "text": "1.2.3.4:8080:user:pass\nsocks5 5.6.7.8:1080\nhttp://9.9.9.9:3128",
  "type": "http",
  "label": "US_{INDEX|1}"
}'
Response
{
  "ok": true,
  "data": {
    "added": [
      {
        "id": "k3m9q2x7v4b8n6c1",
        "label": "US_1",
        "type": "http",
        "server": "1.2.3.4:8080",
        "user": "user",
        "hasPassword": true
      },
      {
        "id": "p9w2e5r8t1y4u7i0",
        "label": "US_2",
        "type": "socks5",
        "server": "5.6.7.8:1080",
        "user": "",
        "hasPassword": false
      }
    ],
    "skipped": 0,
    "invalidLines": [
      3
    ],
    "cloud": "saved"
  }
}

POST /v1/proxies/add-script#

Add a script (custom) entry to the proxy list, as Custom script on the Add tab of the Proxy Manager does. The script runs each time a profile that uses the entry starts and decides its proxy; GET /v1/proxies/script-docs describes what it can call. Try it with POST /v1/proxies/test-script first. Profiles use the new entry with proxy {"type": "mine", "proxyId": ...}. A script with a syntax error is refused; one that never calls SetResult is saved with a noSetResult warning. GET /v1/proxies/{id}/script reads the script back.

MCP tool: add_proxy_script. Access: needs "Allow changes", needs the sub-user permission "manageProxy".

Field Type Required Description
label string no Name of the entry, shown in the Proxy Manager and when picking a proxy for a profile.
script string yes The proxy script: JavaScript run as the body of an async function, up to 16 KB (UTF-8). GET /v1/proxies/script-docs describes what it can call.
onConflict string no When the proxy list was changed on another machine first: "fail" answers conflict and changes nothing; "reload" takes that version and applies this change on top of it. A reload drops changes this machine kept while offline. Values: reload, fail. Default "fail".
Request
curl -X POST "http://127.0.0.1:37286/v1/proxies/add-script" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "label": "Provider X, US",
  "script": "const r = await HttpRequest(\"https://api.example.com/proxy?country=us\", null, 15000);\nif (r.status !== 200) return SetResult({ Error: \"Provider error: \" + (r.error || r.status) });\nconst p = JSON.parse(r.body);\nSetResult({ Type: \"http\", Server: p.ip + \":\" + p.port, User: p.user, Password: p.pass });"
}'
Response
{
  "ok": true,
  "data": {
    "added": {
      "id": "s7k2m9q4w1e8r5t3",
      "label": "Provider X, US",
      "type": "custom"
    },
    "cloud": "saved"
  }
}

POST /v1/proxies/check#

Check the exit IP of proxies, as the Check IP button of the Proxy Manager does. Returns a job. Pass ids or all. Five proxies are checked at a time and one check job runs at a time; each proxy can take 15 seconds or more (ssh and script entries longer). A result has the exit IPs, place, ISP and time zone, and the TCP fingerprint of the exit: tcp.os is the kernel family its SYN packet shows (windows, mac, linux or other; Android exits read as linux, iOS as mac) and tcp.confidence runs from 0 to 100. tcp is null when the TCP probe gave no answer. profiles lists each OS of this machine's profiles that use the entry, with a verdict: match, mismatch (sites that read TCP fingerprints see another OS than the profile claims), or unknown when the probe gave no answer or could not tell the OS (other with confidence 0). The last check shown in the list is updated as in the app. A script (custom) entry is checked with one run of its script; a script that hands out a different endpoint per profile can resolve differently at launch. Values come from public lookups made through the proxy, so treat them as what the proxy reports.

MCP tool: check_proxies. Access: needs "Allow changes".

Field Type Required Description
ids string[] no Proxy ids, as GET /v1/proxies lists them. Leave out and pass all instead to check the whole list.
all boolean no Check every proxy in the list.
wait boolean no Wait for the job to finish, at most waitSec, before answering. false answers at once with the job, to follow with GET /v1/jobs/{id}. Default true.
waitSec integer no How long wait may hold the request. Min 1, max 100. Default 60.
Request
curl -X POST "http://127.0.0.1:37286/v1/proxies/check" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "ids": [
    "k3m9q2x7v4b8n6c1"
  ],
  "wait": true
}'
Response
{
  "ok": true,
  "data": {
    "id": "j0123456789abcdef",
    "kind": "proxyCheck",
    "state": "done",
    "createdAt": 1760000000000,
    "finishedAt": 1760000003000,
    "progress": {
      "done": 1,
      "total": 1
    },
    "error": null,
    "result": {
      "checked": [
        {
          "id": "k3m9q2x7v4b8n6c1",
          "ip": "1.2.3.4",
          "ipV4": "1.2.3.4",
          "ipV6": "",
          "country": "United States",
          "countryCode": "US",
          "city": "Ashburn",
          "isp": "Example Hosting",
          "timezone": "America/New_York",
          "tcp": {
            "os": "linux",
            "confidence": 90,
            "mss": 1460
          },
          "profiles": [
            {
              "os": "windows",
              "count": 2,
              "verdict": "mismatch"
            },
            {
              "os": "android",
              "count": 1,
              "verdict": "match"
            }
          ]
        }
      ],
      "failed": []
    }
  }
}

POST /v1/proxies/test-script#

Run a proxy script once without saving it, as Run test in the Proxy Manager does. Returns a job. Pass script to try new code, or id to run the saved script of a script (custom) entry. The result says whether the script handed back a proxy (ok, proxy with type, server, user and hasPassword; the password itself never comes back) or why it did not (error), with what it printed with Log (logs, the last 200 lines; logsDropped counts the earlier ones) and how long it took (ms). Nothing is saved, and the proxy is not checked: POST /v1/proxies/check does that for a saved entry. The script really runs: its HttpRequest calls reach the network, and it shares Global with every other proxy script run while 9Login is open. Profile is the profile named by profileId (its id, title and position in the list, as a start of it would see), or { Id: "test", Title: "", Index: 0 }; index overrides Index. One test job runs at a time.

MCP tool: test_proxy_script. Access: needs "Allow changes", needs the sub-user permission "manageProxy".

Field Type Required Description
script string no The script to run, up to 16 KB. Leave out and pass id instead to run a saved entry.
id string no Id of a script (custom) entry, as GET /v1/proxies lists it, whose saved script runs.
profileId string no Run as a start of this profile would: Profile.Id, Profile.Title and Profile.Index are its own.
index integer no Profile.Index to run with: the position of a profile in the list. Default: that of profileId, or 0. Min 0, max 100000.
timeoutSec integer no Give up after this many seconds. A real start always waits 120. Min 1, max 120. Default 120.
wait boolean no Wait for the job to finish, at most waitSec, before answering. false answers at once with the job, to follow with GET /v1/jobs/{id}. Default true.
waitSec integer no How long wait may hold the request. Min 1, max 100. Default 60.
Request
curl -X POST "http://127.0.0.1:37286/v1/proxies/test-script" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "script": "const r = await HttpRequest(\"https://api.example.com/proxy\", null, 15000);\nLog(\"status \" + r.status);\nconst p = JSON.parse(r.body);\nSetResult({ Type: \"http\", Server: p.ip + \":\" + p.port, User: p.user, Password: p.pass });",
  "wait": true
}'
Response
{
  "ok": true,
  "data": {
    "id": "j0123456789abcdef",
    "kind": "proxyScriptTest",
    "state": "done",
    "createdAt": 1760000000000,
    "finishedAt": 1760000000400,
    "progress": null,
    "error": null,
    "result": {
      "ok": true,
      "proxy": {
        "type": "http",
        "server": "203.0.113.7:8000",
        "user": "u123",
        "hasPassword": true
      },
      "error": null,
      "logs": [
        "status 200"
      ],
      "logsDropped": 0,
      "ms": 412
    }
  }
}

POST /v1/proxies/update#

Change the label, endpoint or credentials of proxies in the list, or the script of a script entry. server takes the same forms as the editor of the Proxy Manager. User and password written in server are used, unless user or password are passed too; when server carries none, the current ones are kept. The type cannot change (add a new entry instead). Script (custom) entries take label and script; the others take label, server, user and password. A new script replaces the old one whole: read the current one with GET /v1/proxies/{id}/script, and try the new one with POST /v1/proxies/test-script first; one with a syntax error is refused, one that never calls SetResult is saved with a noSetResult warning. A change that would make an entry equal to another one (same type, server, user and password) is refused as duplicate. All items are saved in one go. Profiles that use an entry follow the change at their next start.

MCP tool: update_proxies. Access: needs "Allow changes", needs the sub-user permission "manageProxy".

Field Type Required Description
items object[] yes One object per proxy: id (required); label (up to 200 characters); server, the endpoint in any form the Proxy Manager editor takes, credentials included (host:port, host:port:user:pass, user:pass@host:port...); user and password (up to 255 characters each, "" clears one); script, the new script of a script (custom) entry (up to 16 KB, see GET /v1/proxies/script-docs). Fields left out keep their value.
onConflict string no When the proxy list was changed on another machine first: "fail" answers conflict and changes nothing; "reload" takes that version and applies this change on top of it. A reload drops changes this machine kept while offline. Values: reload, fail. Default "fail".
Request
curl -X POST "http://127.0.0.1:37286/v1/proxies/update" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "items": [
    {
      "id": "k3m9q2x7v4b8n6c1",
      "label": "US 1 (new)",
      "server": "1.2.3.5:8080"
    },
    {
      "id": "p9w2e5r8t1y4u7i0",
      "password": "n3w-pass"
    },
    {
      "id": "s7k2m9q4w1e8r5t3",
      "script": "SetResult({ Type: \"socks5\", Server: \"gate.example.com:\" + RandomInt(10000, 10099) });"
    }
  ]
}'
Response
{
  "ok": true,
  "data": {
    "updated": [
      "k3m9q2x7v4b8n6c1",
      "p9w2e5r8t1y4u7i0",
      "s7k2m9q4w1e8r5t3"
    ],
    "failed": [],
    "cloud": "saved"
  }
}

POST /v1/proxies/delete#

Delete proxies from the shared list. Needs "Allow deleting" on the API & MCP screen. There is no undo. A proxy that profiles use is refused (inUse) unless unlinkProfiles is true; then those profiles are left without a proxy (profiles.updated lists them) and will not start until another one is picked; one already running keeps its connection until it stops. Profiles are only unlinked once the list is saved; profiles.failed lists any that still point at a deleted proxy.

MCP tool: delete_proxies. Access: needs "Allow deleting", needs the sub-user permission "manageProxy".

Field Type Required Description
ids string[] yes Proxy ids, as GET /v1/proxies lists them.
unlinkProfiles boolean no Also delete proxies that profiles use, and clear the proxy of those profiles. Default false.
onConflict string no When the proxy list was changed on another machine first: "fail" answers conflict and changes nothing; "reload" takes that version and applies this change on top of it. A reload drops changes this machine kept while offline. Values: reload, fail. Default "fail".
Request
curl -X POST "http://127.0.0.1:37286/v1/proxies/delete" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "ids": [
    "k3m9q2x7v4b8n6c1",
    "p9w2e5r8t1y4u7i0"
  ]
}'
Response
{
  "ok": true,
  "data": {
    "deleted": [
      "k3m9q2x7v4b8n6c1"
    ],
    "failed": [
      {
        "id": "p9w2e5r8t1y4u7i0",
        "code": "inUse",
        "message": "Used by 2 profiles. Pass unlinkProfiles true to delete it anyway and leave those profiles without a proxy."
      }
    ],
    "profiles": {
      "updated": [],
      "failed": [],
      "error": null
    },
    "cloud": "saved"
  }
}

GET /v1/proxies/script-docs#

Reference of proxy scripts: what the script of a script (custom) entry can call, its rules, errors and examples. Read it before writing or fixing a proxy script. A proxy script is not a macro: 9Login runs it before the browser opens and it has its own small API, so the macro reference (GET /v1/macro/docs) does not apply to it.

MCP tool: proxy_script_docs. Access: answers even when signed out or on a free plan.

No parameters.

Request
curl -H "Authorization: Bearer YOUR_API_KEY" "http://127.0.0.1:37286/v1/proxies/script-docs"
Response
{
  "ok": true,
  "data": {
    "functions": [
      {
        "name": "SetResult",
        "signature": "SetResult(result)",
        "description": "Hands the proxy to the profile being started, or cancels the start."
      }
    ],
    "variables": [
      {
        "name": "Global",
        "description": "One object shared by every run of every proxy script while 9Login is open."
      }
    ],
    "limits": {
      "maxBytes": 16384,
      "timeoutSec": 120
    }
  }
}