Automating cPanel with UAPI: Tokens, Batching, and the Gotchas Nobody Mentions
cPanel's UAPI is the supported path for automating hosting accounts — but tokens vs. passwords, the non-transactional batch endpoint, and LTS version gaps are the details that make or break an integration.
02 Mar 2026, 04:09 UTC

If you manage more than a handful of cPanel accounts, clicking through the UI stops being a workflow and starts being a liability. The moment you need to provision fifty email accounts, rotate SSL installs, or wire deployments into CI, you need the API. cPanel's Unified API (UAPI) has been the supported programmatic interface since it replaced the legacy API 1/2 back in cPanel 11.42, and it's genuinely usable — but the docs don't always surface the operational details that bite you in production.
This post covers the decisions that matter: how to authenticate, how to stay under rate limits, and where UAPI's behavior diverges from what you'd expect from a modern REST API.
UAPI in one paragraph
UAPI speaks JSON over HTTPS on port 2083. Calls are namespaced by module and function — Email::add_pop, Mysql::create_database, Git::create_repository — and every response carries errors and warnings arrays alongside a data payload. Always check the errors array; a 200 HTTP status does not mean the call succeeded. Modules are versioned per cPanel release, so a function available on a CURRENT-tier server may not exist on your LTS box — the changelog at api.docs.cpanel.net is the authoritative reference for availability.
Authenticate with tokens, not passwords
UAPI supports three auth methods: session cookies (for browser-driven tooling), HTTP Basic Auth with the account password, and API tokens. For automation, tokens win decisively. They're created in the cPanel UI (or WHM for root/reseller tokens), can be revoked without a password rotation, and — critically — they keep working when two-factor authentication is enabled on the account, whereas password-based Basic Auth does not.
A quick smoke test, run from any machine that can reach the server on 2083 — no special privileges needed beyond a valid token:
curl -H 'Authorization: cpanel CPUSER:YOUR_TOKEN' \
'https://host.example.com:2083/execute/Email/list_pops?domain=example.com'
Replace CPUSER with the cPanel username and YOUR_TOKEN with the token string. A successful call returns JSON with "errors": [] and a data array of mailboxes. A 401 means bad credentials; a 403-style error like "You do not have access to this API function" means the account's feature list (WHM → Feature Manager) doesn't include the Email feature — a distinction that trips up a lot of first-time integrations.
One least-privilege note: user-level tokens inherit that user's feature list, which is usually what you want. WHM-level tokens can impersonate any user via a user parameter but require broad ACLs — reserve those for genuine reseller tooling.
Respect the rate limit — or better, batch
cPanel enforces per-account UAPI rate limiting through the max_uapi_calls tweak setting (30 calls/second by default). Exceed it and you get HTTP 429 with a Retry-After header. A naive provisioning loop that creates a database, then a user, then a grant, then a mailbox, per customer, will hit this fast at any real scale.
The supported answer is the batch endpoint: POST /execute/batch combines up to 50 UAPI calls into a single HTTP request. Here's the shape of it:
curl -H 'Authorization: cpanel CPUSER:YOUR_TOKEN' \\n -d 'batch=[{"namespace":"Mysql","function":"create_database","args":{"name":"cpuser_shop"}},{"namespace":"Mysql","function":"create_user","args":{"name":"cpuser_shopapp","password":"REPLACE_ME"}}]' \\n 'https://host.example.com:2083/execute/batch'
Now the important caveat: batch is not a transaction. Each call in the batch returns its own result object with its own errors array, and partial failure is entirely possible — the database gets created, the user creation fails on password policy, and you're left with an orphan database. Your client code must inspect every element of the response and be prepared to run compensating deletes (e.g., Mysql::delete_database) to roll back partial work. Treat UAPI like a set of independent operations, not a unit of work.
A practical pattern: Git-driven deployment
A concrete use case that ties this together: cPanel's Git Version Control feature (available since v72) is exposed through UAPI — Git::create_repository and Git::update_repository. A CI pipeline holding a user-scoped token can push a deploy by calling Git::update_repository on the target account, pulling the latest commit into the user's directory. No SSH keys to manage, no FTP credentials in the CI secrets store, and revoking the token kills the pipeline's access cleanly. The prerequisite is that the Git feature is enabled in the account's feature list — verify before you build on it.
Version pinning is a real trade-off
Many hosts pin servers to an LTS tier (110 LTS, 114 LTS) for stability. Reasonable — but LTS receives security backports only. New UAPI functions land in STABLE/CURRENT first, so automation written against the latest docs may call functions that simply don't exist on your LTS fleet. Check the server's actual version before assuming a function is present:
/usr/local/cpanel/cpanel -V
# or, via UAPI itself:
uapi --user=cpuser --output=jsonpretty Variables get_server_information
The first command runs on the server as root via SSH; the second runs locally if you have the uapi CLI wrapper available, or remotely over HTTPS. Either way, record the version and cross-reference the UAPI changelog before shipping automation against newer modules.
What to take away
UAPI is stable, well-documented, and old enough that the sharp edges are known. Use tokens scoped to individual users, batch aggressively but handle partial failures explicitly, verify feature-list dependencies when you see access errors, and pin your code to the cPanel version you actually run — not the one the docs describe. If you do those four things, UAPI stops being a quirky hosting-panel API and becomes a dependable part of your infrastructure tooling.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.