Bulk create websites?


a short question about a potentially painful task: Is there a way to create (or even configure) multiple websites in one step? I could not find anything obvious in the admin (or api) docs about that and I have to create a three-digit number of different websites in one account :expressionless:


Hi Markus!

It’s not possible in the Administration module UI but it’s possible to automate it using the Apps API endpoint.

Some examples I can share here (written in Python):

  1. I prefer storing API credentials in a .json file. This way I can usually have a test file and “production” file in case I’m test running a complex script.
  "client_id": "client_id",
  "client_secret": "client_secret",
  "instance_url": "https://example.piwik.pro"

def get_credentials_from_file(filename):
    with open(filename, "r") as json_file:
        credentials = json.load(json_file)
    return credentials
  1. Getting the auth token.
def get_auth_token(credentials):
    auth_body = {"grant_type": "client_credentials", "client_id": credentials["client_id"], "client_secret": credentials["client_secret"]}
    return requests.post(credentials["instance_url"] + '/auth/token', data=auth_body).json()["access_token"]

  1. Creating the site/app - this function will create an output.csv file that contains website name and website UUID columns. It’s not necessary, just a nice thing to have.

def create_new_app(instance_url, token, website_name, website_urls):

    payload = json.dumps({
        "data": {
            "type": "ppms/app",
            "attributes": {
                "appType": "web",
                "name": website_name,
                "urls": website_urls

        response = requests.post(instance_url + '/api/apps/v2', headers={"Authorization": 'Bearer ' + token, "Content-Type": "application/vnd.api+json"}, data=payload)
    except requests.exceptions.HTTPError as e:
        if e.response.status_code == 401:
            print("Auth token is no longer valid.")
            token = get_auth_token()
            response = requests.post(instance_url + '/api/apps/v2', headers={"Authorization": 'Bearer ' + token, "Content-Type": "application/vnd.api+json"}, data=payload)
            print("Request error occured.")

    response_content =  json.loads(response.content)['data']
    site_id = response_content['id']
    site_name = response_content['attributes']['name']

    csv_row = [site_name, site_id]

    with open(r'output.csv', 'a') as f:
        writer = csv.writer(f)

    return website_name + " finished with code " + str(response.status_code)
  1. When it comes to the data source - there are multiple choices. I usually go with CSV files, so it should look something like this.
with open('filename.csv', newline='') as f:
    csv_file = csv.reader(f, delimiter=',', quotechar='|')
    next(csv_file, None) #skip headers
    csv_data = [row for row in csv_file]

for row in csv_data:
    # row[0] is the website name and row[1] is an array of website URLs (strings)
    print(create_new_app(credentials["instance_url"], token, row[0], row[1]))
1 Like

Thanks, that really helps. Did not find the right API as it seems :no_mouth:


finally managed to get this to work with a few adjustments (after struggeling with reading an array of website urls from a csv…). I added a few setting attributes to the payload as well, so I can define currency, timezone and a few other things.

But I did not find any way of disabling scroll tracking which seems to be activated by default - neither the App API nor Collecting & Processing Pipeline Settings API seem to include this setting. Can you confirm that or did I just overlook something (once more)?

thanks again,

Hi Markus!

This should do the trick :slight_smile:.

Ah, I see. Seems irrelevant then as we do only use the Analytics module and skip loading the whole container. There are GTMs all over the place :wink:

Will fill the list and start the magic then. Thanks again,


1 Like