Skip to content

Create a MySQL database and user on web hosting

6 steps · Checked against the API code 3 November 2026

A guide does one task from start to finish. Every endpoint, with all its fields and cases, is in the API reference.

A website or app on Hostup web hosting keeps its data in a MySQL database. The API cannot create the database itself. You create it in the hosting control panel. Everything around it works through the API: the database user, its access to the database and the places it may connect from.

The AI app cannot give the user access to a database. Do step 5 through the API.

  • An API key with the scopes write:hosting and console:services. write:hosting also covers read:hosting. Keys and scopes
  • The requests read the key from $HOSTUP_API_KEY, as set in the Quickstart.
  • A browser for step 3.

List web hosting accounts with include=domains returns your web hosting accounts and the domains on each.

GET /api/v2/shared-hosting
curl "https://cloud.hostup.se/api/v2/shared-hosting?include=domains" \
-H "Authorization: Bearer $HOSTUP_API_KEY"
Response 200
{
"data": [
{
"id": "acct_01hxa3b4c5d6e7f8g9h0j1k2m3",
"primaryDomain": "example.com",
"domains": ["example.com"],
"serviceStatus": "active",
"controlPanel": { "type": "cpanel" },
"…": "…"
}
],
"hasMore": false,
"nextCursor": null
}

Find the account whose domains has the site the database is for. Pick an account with serviceStatus active and controlPanel.type cpanel. Keep its id:

export ACCOUNT_ID="acct_…" # data[].id of that account

The list comes 20 per page. When hasMore is true, send nextCursor as cursor for the next page. Pagination

2. Check that the plan has room for a database

Section titled “2. Check that the plan has room for a database”

List hosting databases shows the databases on the account and whether the plan allows one more.

GET /api/v2/shared-hosting/{accountId}/databases
curl "https://cloud.hostup.se/api/v2/shared-hosting/$ACCOUNT_ID/databases" \
-H "Authorization: Bearer $HOSTUP_API_KEY"
Response 200
{
"databases": [
{ "id": "db_01hxa3b4c5d6e7f8g9h0j1k2m3", "name": "abcde_wp", "…": "…" }
],
"quota": { "used": 1, "limit": null, "remaining": null },
"actions": {
"canCreate": { "allowed": true, "reason": null }
}
}

When canCreate.allowed is false, reason says why, for example “Databases are not available on your current plan.” quota.limit is the number of databases the plan allows. null means no limit.

3. Create the database in the control panel

Section titled “3. Create the database in the control panel”

Create a control panel login link gives you a login to the account’s control panel. The client area opens the same control panel.

POST /api/v2/shared-hosting/{accountId}/actions/sso
curl -X POST "https://cloud.hostup.se/api/v2/shared-hosting/$ACCOUNT_ID/actions/sso" \
-H "Authorization: Bearer $HOSTUP_API_KEY" \
-H "Content-Type: application/json"
Response 200
{
"loginUrl": "https://server1.example.com/login/session/…",
"expiresAt": null,
"singleUse": true
}

Open loginUrl in a browser. It works once. Don’t store it. In the control panel, open MySQL Databases in the Databases section and create the database. Then list the databases again as in step 2. The new database is there with its full name, for example abcde_shop.

When the name starts with a prefix, such as abcde_ in abcde_shop, give the user in step 4 the same prefix. Some servers put the account’s prefix on every database and database user name. The API does not add it for you.

Create database user adds a MySQL user to the account.

POST /api/v2/shared-hosting/{accountId}/database-users
curl -X POST "https://cloud.hostup.se/api/v2/shared-hosting/$ACCOUNT_ID/database-users" \
-H "Authorization: Bearer $HOSTUP_API_KEY" \
-H "Content-Type: application/json" \
-d '{"username": "abcde_shopuser", "password": "Amber-Ladder-41-Orchard"}'
Response 201
{
"id": "dbu_01hxa3b4c5d6e7f8g9h0j1k2m3",
"username": "abcde_shopuser",
"host": null,
"databases": []
}
  • username: letters, digits and underscores. Any other character returns 400 invalid_request. The database server takes at most 32 characters, the prefix included. A longer name fails with 502 upstream_failed. A name that already exists fails the same way. List database users shows the names in use.
  • password: 8 to 128 characters with at least one uppercase letter, one lowercase letter and one digit. It must also be hard to guess. A password close to the user name fails with password_too_weak. Generate a new one. Never use the one in this example.

The response never repeats the password. Keep it where your app reads it. Keep id too. The calls on this user accept it in place of the user name.

export DB_USER_ID="dbu_…" # id above

Grant database privileges links the user to one database. Use the database name from step 3.

POST /api/v2/shared-hosting/{accountId}/database-users/{username}/privileges
curl -X POST "https://cloud.hostup.se/api/v2/shared-hosting/$ACCOUNT_ID/database-users/$DB_USER_ID/privileges" \
-H "Authorization: Bearer $HOSTUP_API_KEY" \
-H "Content-Type: application/json" \
-d '{"database": "abcde_shop", "privileges": ["ALL PRIVILEGES"]}'
Response 200
{
"username": "abcde_shopuser",
"database": "abcde_shop",
"privileges": ["ALL PRIVILEGES"]
}

ALL PRIVILEGES gives the user full rights on this one database. For a narrower user, list privileges such as SELECT, INSERT, UPDATE and DELETE. Grant database privileges lists every one.

Where your code runs decides what it needs.

Connect to localhost with the user and password from step 4 and the database name from step 3. PHP (mysqli and PDO) then uses the local socket. The socket is always allowed. Nothing more is needed.

From Node.js or another TCP client on the same account

Section titled “From Node.js or another TCP client on the same account”

A client that connects over TCP is seen as 127.0.0.1. When it resolves localhost to IPv6, it is seen as ::1. Those addresses are refused until you allow them. There are two ways:

  • Connect through the socket instead: /var/lib/mysql/mysql.sock. In Node.js mysql2, set it as socketPath.
  • Allow 127.0.0.1 and ::1 as hosts, as in the next case. localhost itself is refused with 400 invalid_request.

Put the public IP address of that server or computer on the account’s list of allowed hosts. Allow a database access host adds one.

POST /api/v2/shared-hosting/{accountId}/database-access-hosts
curl -X POST "https://cloud.hostup.se/api/v2/shared-hosting/$ACCOUNT_ID/database-access-hosts" \
-H "Authorization: Bearer $HOSTUP_API_KEY" \
-H "Content-Type: application/json" \
-d '{"host": "192.0.2.10"}'
Response 201
{
"host": "192.0.2.10",
"note": null
}

host is an IP address, an IP range with % as the wildcard (192.0.2.%), a hostname or % for every host. The list is account-wide. Every database user on the account may connect from every host on it. With %, a password is the only thing that protects the databases. Prefer one address.

Then get the server’s hostname from Get connection details for web hosting.

GET /api/v2/shared-hosting/{accountId}/account-details
curl "https://cloud.hostup.se/api/v2/shared-hosting/$ACCOUNT_ID/account-details" \
-H "Authorization: Bearer $HOSTUP_API_KEY"
Response 200
{
"id": "acct_01hxa3b4c5d6e7f8g9h0j1k2m3",
"access": {
"username": "abcde",
"serverHostname": "server1.example.com",
"…": "…"
},
"…": "…"
}

Connect to access.serverHostname on port 3306 with the user and the password from step 4. Use the hostname, not your domain. The domain may point somewhere else.

mysql -h server1.example.com -P 3306 -u abcde_shopuser -p abcde_shop

The calls below use $DB_USER_ID from step 4. The user name works in its place too.

Send the new password as newPassword. The same rules as in step 4 apply. Apps that still use the old password stop connecting until you update them.

PUT /api/v2/shared-hosting/{accountId}/database-users/{username}/password
curl -X PUT "https://cloud.hostup.se/api/v2/shared-hosting/$ACCOUNT_ID/database-users/$DB_USER_ID/password" \
-H "Authorization: Bearer $HOSTUP_API_KEY" \
-H "Content-Type: application/json" \
-d '{"newPassword": "Silver-Kettle-29-Harvest"}'
Response 200
{
"username": "abcde_shopuser"
}

Revoke database privileges takes every privilege the user has on one database. The user and the database stay.

DELETE /api/v2/shared-hosting/{accountId}/database-users/{username}/privileges
curl -X DELETE "https://cloud.hostup.se/api/v2/shared-hosting/$ACCOUNT_ID/database-users/$DB_USER_ID/privileges" \
-H "Authorization: Bearer $HOSTUP_API_KEY" \
-H "Content-Type: application/json" \
-d '{"database": "abcde_shop"}'

The response is 204 with no body.

Remove a database access host takes a host off the list. URL-encode the host in the path: % is %25.

DELETE /api/v2/shared-hosting/{accountId}/database-access-hosts/{host}
curl -X DELETE "https://cloud.hostup.se/api/v2/shared-hosting/$ACCOUNT_ID/database-access-hosts/192.0.2.10" \
-H "Authorization: Bearer $HOSTUP_API_KEY"

The response is 204 with no body, also when the host was not on the list.

Every call here returns at once. There is no job to follow. No e-mail goes out and nothing is invoiced.

  • 400 password_too_weak: the password breaks a rule. detail says which. When it is too easy to guess, suggestions lists ways to make it stronger. Send a longer generated password.
  • 502 upstream_failed: the control panel refused the change. detail does not say why. The cause is often in the request itself. Sending it again unchanged then fails again. In step 4 the usual cause is a user name that exists already, is longer than 32 characters or lacks the account’s prefix on a server that uses prefixes. In step 5 it is a database name that does not exist on the account. Check the names with List database users and List hosting databases. Then try again.
  • 403 forbidden: with requiredScopes, the key lacks a scope (Keys and scopes). Without it, the account is not active. Check serviceStatus from step 1.
  • 409 account_not_eligible_cpanel: the account is on Hostup’s previous hosting platform. Contact support. Hostup moves the account to cPanel at no extra charge. No new hosting plan is needed. Such an account shows an empty list in step 2, with canCreate.allowed true all the same.

Next: Create an email account