# duplicate_site

Source: https://modulify.ai/docs/api/sites/duplicate-site

Copies an existing site into a workspace as a new, independent site with its own subdomain and URL.

- Title: Duplicate a site
- Scope: `sites:create`
- Access: Makes changes
- Endpoint: `POST /v1/duplicate_site`

The copy takes every table and row of the site database with it, on either database engine. You need access to both the workspace of the site and the one it is copied into.

It creates real infrastructure and counts against the site limit of the target workspace, so a workspace with no site slot left refuses it with `site-limit-reached` and the current count and limit.

A site on the newer database is refused while its database has a full text search table, is larger than 25 MB, is being backed up, or is already being copied, and the refusal names the reason. A refused copy leaves nothing behind, and a busy database can be retried a few minutes later.

On the newer database, copying the rows pauses the original database for a moment, as a [database backup](https://modulify.ai/docs/editor/backups#your-database-pauses-while-it-is-backed-up) does. [Copying a database](https://modulify.ai/docs/projects/sharing-and-cloning#copying-a-database) has the messages.

## Request

Call it with a `POST` to `https://api.modulify.ai/v1/duplicate_site`, sending the inputs below as a JSON object. The token needs the `sites:create` scope.

It makes changes, so send an [Idempotency-Key](https://modulify.ai/docs/api/idempotency) header whenever you might retry it. A retry with the same key gets the first answer back instead of running again.

```bash
curl -X POST https://api.modulify.ai/v1/duplicate_site \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"projectId":"PROJECT_ID","workspaceId":"WORKSPACE_ID"}'
```

Over MCP, the same method is the [duplicate_site tool](https://modulify.ai/docs/mcp/sites/duplicate-site).

## Inputs

| Input | Type | Required | Description |
| --- | --- | --- | --- |
| `projectId` | string | Yes | The site to copy. |
| `workspaceId` | string | Yes | The workspace to copy it into, which can be the site's own. |

## Response

Every call answers with the [JSON envelope](https://modulify.ai/docs/api/requests-and-responses#the-response) of `success`, `message`, `data`, `code` and `version`. `data` holds the result described above, and on a method that returns a total, `count` carries it. The [response headers](https://modulify.ai/docs/api/requests-and-responses#headers-on-every-method-call) carry the call's `X-Request-Id` and what is left of your per-minute budget in `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset`. [Errors](https://modulify.ai/docs/api/errors) explains every status code a call can answer with.