System administrators, web developers, or agencies often need to deploy multiple WordPress applications simultaneously or in quick succession. Whether you’re launching client sites, building staging environments, or deploying templated sites, doing it manually wastes time and invites errors.
This guide will show you how to programmatically create WordPress applications in bulk using the RunCloud API. By preparing a simple CSV (Comma Separated Values) file containing the necessary details for each website (such as its internal name, desired domain name, site title, and administrator credentials), you can instruct the script to automate the repetitive creation tasks.
This method saves considerable time and ensures consistency across all your WordPress deployments on your RunCloud-managed server.
Please note that access to the RunCloud API is an exclusive feature available on our Business and Enterprise subscriptions. This is because API keys are generated and managed within a Workspace, which is designed for team collaboration and advanced project management. By centralizing API key creation within a Workspace, we provide greater security, organizational control, and the ability to manage access for your entire team. To use the API and unlock these capabilities, you will need to upgrade your account to a plan that includes Workspaces.
Step 1: Creating the CSV File
Before we get started, you’ll need to prepare a CSV file containing all the necessary information for your bulk WordPress installations. This file must contain the following mandatory fields:
name
: Internal app identifier in RunCloudsiteTitle
: WordPress site titleadminUsername
: WordPress admin usernamepassword
: WordPress admin passworddomainName
: Primary domain for the siteadminEmail
: Admin contact email
While these are the core requirements, you have the flexibility to expand your CSV with additional fields to customize each deployment further; for example, you could add a phpVersion
column if different sites require different PHP environments.
If your goal is to configure not just the basic WordPress setup but also pre-install specific plugins and themes or apply particular WordPress settings from the outset, then you should know that RunCloud offers a powerful feature called “WordPress Canvas”.
To use this, you will need to include a canvas_id column in your CSV, specifying the ID of a pre-configured Canvas template for each web application. However, this tutorial will not use WordPress Canvas for simplicity and to focus on the core bulk creation process.
We strongly encourage you to read our documentation on WordPress Canvas to understand how this feature can significantly enhance and automate your WordPress deployment workflows.
You should consult the official RunCloud API documentation for the WordPress creation endpoint to list all configurable parameters.
You can easily create your CSV file using any spreadsheet software, such as Google Sheets or Microsoft Excel. After making your spreadsheet, export it as CSV and use it in the next step. The final spreadsheet should look something like this:
name | siteTitle | adminUsername | password | domainName | adminEmail |
D9ABA9B2 | 8x4957mc | 63K2X1a2 | LVQDQANd | 1.example.com | |
7AA38516 | d87cnWv1 | zB71j023 | Vzhc3C29 | 2.example.com | |
E8A8956A | 8lq8Q9l1 | 12lcb00O | LtsS66b5 | 3.example.com | |
08B21F32 | WY3b1lYj | eR67F426 | QaiE7hn9 | 4.example.com | |
D9B3A4BA | 728lrZ2t | i2J4dF74 | DmEVKH9J | 5.example.com |
We strongly recommend using random values for usernames and passwords instead of similar passwords for all applications for better security. If you want a quick and easy way to create this random data, you can enter the following formula in Google Sheets to generate random passwords.
=JOIN(, BYROW(SEQUENCE(10), LAMBDA(x, IF(COINFLIP(), IF(COINFLIP(), CHAR(RANDBETWEEN(65, 90)), CHAR(RANDBETWEEN(97, 122))), RANDBETWEEN(0, 9)))))
If you are using Microsoft Excel instead, you will need to use this formula instead:
=TEXTJOIN("", TRUE, CHAR(CHOOSE(RANDBETWEEN(1,3), RANDBETWEEN(48,57), RANDBETWEEN(65,90), RANDBETWEEN(97,122))) & CHAR(CHOOSE(RANDBETWEEN(1,3), RANDBETWEEN(48,57), RANDBETWEEN(65,90), RANDBETWEEN(97,122))) & CHAR(CHOOSE(RANDBETWEEN(1,3), RANDBETWEEN(48,57), RANDBETWEEN(65,90), RANDBETWEEN(97,122))) & CHAR(CHOOSE(RANDBETWEEN(1,3), RANDBETWEEN(48,57), RANDBETWEEN(65,90), RANDBETWEEN(97,122))) & CHAR(CHOOSE(RANDBETWEEN(1,3), RANDBETWEEN(48,57), RANDBETWEEN(65,90), RANDBETWEEN(97,122))) & CHAR(CHOOSE(RANDBETWEEN(1,3), RANDBETWEEN(48,57), RANDBETWEEN(65,90), RANDBETWEEN(97,122))) & CHAR(CHOOSE(RANDBETWEEN(1,3), RANDBETWEEN(48,57), RANDBETWEEN(65,90), RANDBETWEEN(97,122))) & CHAR(CHOOSE(RANDBETWEEN(1,3), RANDBETWEEN(48,57), RANDBETWEEN(65,90), RANDBETWEEN(97,122))) & CHAR(CHOOSE(RANDBETWEEN(1,3), RANDBETWEEN(48,57), RANDBETWEEN(65,90), RANDBETWEEN(97,122))) & CHAR(CHOOSE(RANDBETWEEN(1,3), RANDBETWEEN(48,57), RANDBETWEEN(65,90), RANDBETWEEN(97,122))))
Step 2: Running the Python Script
In this step, we will create a Python script that reads your CSV file, constructs the API payload for each WordPress application, and makes the API calls to RunCloud.
Important Considerations Before Running:
- API Key and Secret: You’ll need to configure your RunCloud API Key.
- Server ID (serverId): This is the ID of the server on RunCloud where you want to deploy these WordPress applications. You will need to replace 0000 with your server’s ID. When viewing a server, you can find this in the RunCloud dashboard URL (e.g.,
manage.runcloud.io/servers/YOUR_SERVER_ID/summary
). - CSV FILE PATH: You should store the CSV file in the same folder as your Python script and replace
sheet.csv
with the name of your file.
Note: While the following script will configure the nginx web server on your RunCloud server to recognize and serve your WordPress application under the specified domain name, it does not manage your external DNS records.
This means you will need to manually access your domain registrar’s or DNS provider’s control panel to create or update the necessary DNS records to direct your domain name to your server. This DNS configuration step can be completed either before you run the script or after the applications have been created on RunCloud. But, it must be done for your websites to be accessible to visitors via their domain names.
import http.client
import json
import csv
import time
# --- Configuration ---
RUNCLOUD_API_HOST = "manage.runcloud.io"
CSV_FILE_PATH = 'sheet.csv'
SERVER_ID = 0000
auth_string = 'See RunCloud API documentation for generating API credentials. (https://runcloud.io/docs/generating-api-keys-enabling-api-access)'
AUTH_HEADER = {"Authorization": f"Bearer {auth_string}",
"user-agent": "My Python App"}
REQUEST_DELAY_SECONDS = 2 # Delay between API calls
# --- Helper Function for API Requests (as provided, with minor adjustments) ---
def make_api_request(method, path, payload=None, extra_headers=None):
conn = None
# Use the global AUTH_HEADER which should be populated with the Bearer token
if not AUTH_HEADER or 'Authorization' not in AUTH_HEADER:
print("[Error] AUTH_HEADER is not configured with Bearer token.")
return None, {"error": "Authentication not configured", "details": "Bearer token is missing."}
try:
conn = http.client.HTTPSConnection(RUNCLOUD_API_HOST)
headers = AUTH_HEADER.copy()
if extra_headers:
headers.update(extra_headers)
body_data = None
if payload:
if 'Content-Type' not in headers: # Ensure Content-Type for JSON payload
headers['Content-Type'] = 'application/json'
body_data = json.dumps(payload).encode('utf-8')
conn.request(method, path, body=body_data, headers=headers)
res = conn.getresponse()
status = res.status
response_body_str = res.read().decode("utf-8")
try:
response_json = json.loads(response_body_str)
return status, response_json
except json.JSONDecodeError:
if 200 <= status < 300: # Success, but not JSON
return status, {"raw_response": response_body_str}
else:
return status, {"error": "Invalid JSON response", "details": response_body_str, "message": f"Error status {status} with non-JSON response."}
except http.client.HTTPException as e:
print(f"[Error] HTTP Exception during {method} {path}: {e}")
return None, {"error": "HTTP Exception", "details": str(e)}
except ConnectionRefusedError as e:
print(f"[Error] Connection Refused during {method} {path}: {e}")
return None, {"error": "Connection Refused", "details": str(e)}
except Exception as e:
print(f"[Error] Unexpected error during {method} {path}: {e}")
return None, {"error": "Unexpected Error", "details": str(e)}
finally:
if conn:
conn.close()
def create_wordpress_app_via_api(app_data, current_server_id):
"""
Uses make_api_request to create a WordPress application.
"""
api_path = f"/api/v3/servers/{current_server_id}/webapps/wordpress"
status, response_data = make_api_request("POST", api_path, payload=app_data)
if status is not None:
if 200 <= status < 300:
print(f"SUCCESS: WordPress application '{app_data['name']}' creation initiated (ID: {response_data.get('id')}).")
return True
else:
print(f"ERROR: Failed to create WordPress application '{app_data['name']}'.")
if isinstance(response_data, dict):
if 'message' in response_data:
print(f"API Message: {response_data['message']}")
if 'errors' in response_data: # Common for validation errors
print(f"API Errors: {response_data['errors']}")
return False
else:
# make_api_request already printed an error
print(f"ERROR: API request failed for '{app_data['name']}'.")
return False
def main():
try:
with open(CSV_FILE_PATH, mode='r', newline='', encoding='utf-8') as csvfile:
reader = csv.DictReader(csvfile)
required_csv_headers = ['name', 'siteTitle', 'adminUsername', 'password', 'domainName', 'adminEmail']
if not reader.fieldnames or not all(f in reader.fieldnames for f in required_csv_headers):
print(f"Error: CSV file must contain headers: {', '.join(required_csv_headers)}")
return
apps_to_create = list(reader)
print(f"Found {len(apps_to_create)} applications to process from {CSV_FILE_PATH}.")
successful_creations = 0
failed_creations = 0
for i, row in enumerate(apps_to_create):
print(f"\nProcessing row {i+1}/{len(apps_to_create)}: {row.get('name', 'N/A')}")
if not all(row.get(field) for field in required_csv_headers):
print(f"Skipping row {i+1} due to missing one or more required fields: {', '.join(required_csv_headers)}")
failed_creations += 1
continue
if create_wordpress_app_via_api(row, SERVER_ID):
successful_creations += 1
else:
failed_creations += 1
if i < len(apps_to_create) - 1:
print(f"Waiting for {REQUEST_DELAY_SECONDS} seconds before next API call...")
time.sleep(REQUEST_DELAY_SECONDS)
print("\n--- Summary ---")
print(f"Successfully initiated creation for: {successful_creations} application(s)")
print(f"Failed to create/initiate: {failed_creations} application(s)")
except FileNotFoundError:
print(f"Error: The file '{CSV_FILE_PATH}' was not found.")
except Exception as e:
print(f"An unexpected error occurred during script execution: {e}")
import traceback
traceback.print_exc()
if __name__ == "__main__":
main()
To use the above script for bulk WordPress creation, first, you will need to save the Python code provided in a file on your local system; a common and suitable name for this file would be main.py
.
Once the script is saved, you can execute it by opening your terminal or command prompt, navigating to the directory where you saved main.py, and then running the command python main.py.
As the script runs, it will process the information from your CSV file, interact with the RunCloud API to set up each WordPress application, and print status messages of these operations directly to your terminal.

After the script has finished its execution, you can log in to your RunCloud dashboard to visually inspect and confirm that all the intended web applications have been successfully created and are configured according to your CSV specifications.

We love seeing how users build with the RunCloud API. Share your scripts and ideas with the RunCloud community.