Skip to content

Bulk site operations

POST
/wp-json/wp2/site-manager/v1/sites/bulk
curl --request POST \
--url https://example.com/wp-json/wp2/site-manager/v1/sites/bulk \
--header 'Content-Type: application/json' \
--header 'X-WP-Nonce: <X-WP-Nonce>' \
--data '{ "blog_ids": [ 5, 8 ], "action": "archive" }'

Applies one action to all blog_ids in the current network: archive, unarchive, activate, deactivate, delete, or patch.

    Up to 10 sites inclusive — synchronous 200 `BulkSiteResult` (per-site errors in `failed`, HTTP still 200). More than 10 — enqueue `site_manager.bulk` and 202.

    Non-empty `blog_ids` and non-empty `action` are required, otherwise 400 `missing_fields`. Requires `delete_sites` or `manage_network`. Multisite only.
Media typeapplication/json
object
blog_ids
required

IDs of sites in the current network. Required; empty array — 400 missing_fields. More than 10 items — background job 202 (BulkSiteService::SYNC_THRESHOLD).

Array<integer>
action
required

Action for each blog_id. Empty string — 400 missing_fields. Unknown value in a sync run goes into failed with invalid_action, HTTP 200.

string
Allowed values: patch archive unarchive activate deactivate delete
patch

Metadata body for action=patch. Omit for archive/delete. PHP passes the array as-is to SiteMetadataPatch::from_array.

object
title

New blogname. Optional; missing key — leave unchanged. PHP sanitizes and writes the blog option plus site meta blogname.

string
slug

New address slug. Optional. PHP normalizes and checks domain_exists; another site with the same domain+path — 409 slug_conflict. Network main site — 403 main_site_slug_forbidden.

string
description

New blogdescription. Optional; PHP writes sanitize_text_field to the blog option.

string
status_id

Site Manager status ID or null to clear it. The key must be present (array_key_exists); otherwise PHP leaves status alone. Unknown term — 400 invalid_term.

integer
nullable
category_ids

Full set of site category IDs. If the key is present (even an empty array) PHP overwrites meta. Each id must be a category term; otherwise 400 invalid_term.

Array<integer>
archived

True — update_blog_status(..., archived, 1); false — clear archive. Main site — 403 main_site_forbidden. Not the integer flag in SiteDto.

boolean
public

1 — activate the site (WP_Site->public=1); otherwise deactivate (0). Main site — 403 main_site_forbidden.

integer
is_template

Writes site meta wp2platform_site_manager_is_template (1 or 0). JSON key must be present, otherwise PHP leaves the flag alone.

boolean
drop_tables

Only for action=delete: true — wpmu_delete_blog($id, true) (drop site tables). PHP ignores it for other actions.

boolean
Example
{
"blog_ids": [
5,
8
],
"action": "archive"
}

Synchronous run (≤10 sites). succeeded — ok, failed — per-site WP_Error (main_site_forbidden, etc.).

Media typeapplication/json
object
succeeded

Blog_ids for which the action completed without WP_Error.

Array<integer>
failed

Sites that failed; the overall HTTP status of a synchronous bulk remains 200.

Array<object>
object
blog_id

Site on which the action returned WP_Error.

integer
code

WP_Error code (main_site_forbidden, invalid_action, slug_conflict, etc.).

string
message

WP_Error message for this site.

string
Example
{
"succeeded": [
5,
8
],
"failed": []
}

Async job (more than 10 sites). Location is poll_url, Retry-After=2.

Media typeapplication/json
object
job_id

PlatformJob UUID. The same id in GET /jobs/{job_id} and SSE /jobs/{job_id}/events.

string format: uuid
status

Job status at the 202 moment (usually pending right after enqueue).

string
type

Site Manager job type. Duplicate create — site_manager.create_from_source; bulk — site_manager.bulk.

string
poll_url

JSON poll URL (rest_url(…/jobs/{job_id})). Also sent as Location; Retry-After=2.

string format: uri
events_url

SSE progress URL (…/jobs/{job_id}/events), not the JSON poll.

string format: uri
Example
{
"job_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"status": "pending",
"type": "site_manager.bulk",
"poll_url": "https://example.com/wp-json/wp2/site-manager/v1/jobs/7c9e6679-7425-40de-944b-e07fc1f90ae7",
"events_url": "https://example.com/wp-json/wp2/site-manager/v1/jobs/7c9e6679-7425-40de-944b-e07fc1f90ae7/events"
}
Location
string

GET URL for job state.

Retry-After
integer

PHP sets 2.

Missing_fields — missing blog_ids or action; not_multisite.

Media typeapplication/json
object
code

Machine-readable error or event code.

string
message

Message text (log, PIN, or password reset).

string
data

Extra WP_Error or license payload.

object
Examplegenerated
{
"code": "example",
"message": "example",
"data": {}
}

Not authenticated. DualRest: rest_forbidden (status 401) when there is no cookie session. Cookie + X-WP-Nonce or Application Password.

Media typeapplication/json
object
code

Machine-readable error or event code.

string
message

Message text (log, PIN, or password reset).

string
data

Extra WP_Error or license payload.

object
Examplegenerated
{
"code": "example",
"message": "example",
"data": {}
}

Missing delete_sites/manage_network (code forbidden) or invalid REST nonce.

Media typeapplication/json
object
code

Machine-readable error or event code.

string
message

Message text (log, PIN, or password reset).

string
data

Extra WP_Error or license payload.

object
Examplegenerated
{
"code": "example",
"message": "example",
"data": {}
}