Appearance
Redirects endpoints
These REST routes manage redirects that send an old address on your site to a new URL. They also review addresses that stopped matching after a site move.
Sign-in, the response envelope and paging are explained in the REST API overview.
All paths are relative to the base URL /wp-json/link-manager-pro/v1. The examples use https://example.com and the Application Password placeholder USER:APPLICATION_PASSWORD.
The redirect object
A redirect is one row in link_manager_pro_redirects (Database tables). It holds the old address. The destination, the redirect type and the click count live on a hidden short link that the plugin creates for the redirect. That link never shows in the short links list. Its ID is backing_link_id.
When a visitor opens the old address, the plugin finds the redirect and sends the visitor through the hidden link. Every visit is a click on that link, so the redirect has a click report.
The redirect routes return the stored columns plus four fields taken from the hidden link:
| Field | Type | What it holds |
|---|---|---|
id | integer | The redirect ID. |
source_url | string | The old address in its saved form: scheme://host/path, with ?query when the source has one. |
source_url_hash | string | The md5 of source_url, 32 characters. The plugin looks redirects up by this hash. |
target_link_id | integer | The ID of the hidden short link. |
status | string | active or inactive. Only an active redirect fires. |
created_at | string | When the redirect was created, as Y-m-d\TH:i:s.v. |
updated_at | string | When it was last saved, in the same format. |
redirect_type | integer or null | 301, 302 or 307, read from the hidden link. null when that link is missing. |
target_url | string or null | The destination, read from the hidden link. null when that link is missing. |
hits | integer | The hidden link's click counter. 0 when the link is missing. |
backing_link_id | integer | The same value as target_link_id. Use it with the /links/{id}/report and /links/{id}/clicks routes (Short links endpoints) to report on the redirect. |
How a source is saved and matched
When you save a source, the plugin turns it into one saved form. The same rules apply when a visitor's address is compared to the saved sources.
- Host. A source must be on this site, with or without
www.. The saved form always uses the site's current scheme and host and drops the port. A request overhttporhttps, with or withoutwww., matches the same redirect. - Trailing slash. It is dropped.
/old-pageand/old-page/are the same source. - Case. The path keeps its letter case and is compared exactly, so
/Old-Pageand/old-pageare different sources. A redirect imported from BetterLinks can be marked to match in any case. For such a redirect, the plugin retries the request with a lowercased path. - Encoded characters. Each path segment is decoded once and encoded again with upper-case hex. A typed
/blog/привет, a pasted%d0%bf...form and the browser's%D0%BF...form are one source. - Query string. The saved form keeps the query with its keys sorted and empty values dropped, so
/?p=390never turns into the home page. A request matches a source with a query only when its query is the same after the same clean-up. A source saved without a query matches its path with any query, such as?utm_source=news. - Hash. A fragment (
#section) is dropped when you save a source. Browsers never send it to the server, so it cannot take part in matching. - Home page. A source that resolves to the home page is refused.
Redirects saved before an address move keep matching. The plugin also tries this site's other scheme and www. variants, and any earlier address it recorded. Review old addresses explains the earlier addresses.
Redirects
List redirects
GET /redirects
Returns one page of redirects, newest first, with their destination, type and hit count.
Permission: link_manager_pro_access (Permissions)
Query parameters:
| Name | Type | Required | Default | What it does |
|---|---|---|---|---|
search | string | No | none | Matches part of the source URL or of the destination URL. |
status | string | No | none | active or inactive. Any other value shows both. |
page | integer | No | 1 | The page number. |
per_page | integer | No | 15 | Redirects per page. |
The list holds only redirects that own their hidden link. Rows that Link Health created to fix a broken link are left out.
Response: 200. data is a page object (Pagination). data.data holds redirect objects.
bash
curl -u USER:APPLICATION_PASSWORD \
"https://example.com/wp-json/link-manager-pro/v1/redirects?status=active"json
{
"success": true,
"data": {
"current_page": 1,
"data": [
{
"id": 5,
"source_url": "https://example.com/old-page",
"source_url_hash": "4b5c31ad44d7dec24d24f4e8ff46c481",
"target_link_id": 61,
"status": "active",
"created_at": "2026-10-10T12:00:00.000",
"updated_at": "2026-10-10T12:00:00.000",
"redirect_type": 301,
"target_url": "https://example.com/new-page",
"hits": 17,
"backing_link_id": 61
}
],
"first_page_url": "/wp-json/link-manager-pro/v1/redirects?page=1",
"from": 1,
"last_page": 1,
"last_page_url": "/wp-json/link-manager-pro/v1/redirects?page=1",
"links": [
{ "url": null, "label": "Previous", "active": false },
{ "url": "/wp-json/link-manager-pro/v1/redirects?page=1", "label": 1, "active": true },
{ "url": null, "label": "Next", "active": false }
],
"next_page_url": null,
"path": "/wp-json/link-manager-pro/v1/redirects",
"per_page": 15,
"prev_page_url": null,
"to": 1,
"total": 1
}
}Errors: None beyond the overview's sign-in and permission errors.
Create a redirect
POST /redirects
Creates a redirect from an address on this site to a destination URL. It also creates the hidden short link and returns the redirect.
Permission: link_manager_pro_access (Permissions)
Body parameters: JSON.
| Name | Type | Required | Default | What it does |
|---|---|---|---|---|
source_url | string | Yes | none | A path or a full URL on this site, 2048 bytes or fewer. The plugin saves the form described in How a source is saved and matched. |
target_url | string | Yes | none | The destination. A full http or https URL, 2048 bytes or fewer. |
redirect_type | integer | Yes | none | 301, 302 or 307. |
status | string | No | active | active or inactive. |
Response: 200, with message set to "Redirect created." data holds:
| Field | Type | What it holds |
|---|---|---|
redirect | object | The new redirect object. |
address_takeover | array | Short links whose other address this redirect now answers. Empty when none, and always empty when status is inactive. |
Each row of address_takeover holds:
| Field | Type | What it holds |
|---|---|---|
path | string | The other address, such as /deal. |
link_id | integer | The short link that has this address. |
slug | string | That link's slug. |
from | string | The plugin the address was imported from: Pretty Links, BetterLinks or ThirstyAffiliates. An empty string when someone added it by hand. |
live | boolean | Always true for a redirect, because an active redirect answers the address at once. |
A redirect answers before a short link's other address, so the short link's address stops opening. Other addresses and conflicts explains the order. The list is empty when no short link has other addresses.
bash
curl -X POST -u USER:APPLICATION_PASSWORD \
-H "Content-Type: application/json" \
-d '{
"source_url": "/old-page",
"target_url": "https://example.com/new-page",
"redirect_type": 301
}' \
https://example.com/wp-json/link-manager-pro/v1/redirectsjson
{
"success": true,
"data": {
"redirect": {
"source_url": "https://example.com/old-page",
"source_url_hash": "4b5c31ad44d7dec24d24f4e8ff46c481",
"target_link_id": 61,
"status": "active",
"updated_at": "2026-10-23T09:30:00.000",
"created_at": "2026-10-23T09:30:00.000",
"id": 5,
"redirect_type": 301,
"target_url": "https://example.com/new-page",
"hits": 0,
"backing_link_id": 61
},
"address_takeover": []
},
"message": "Redirect created."
}Errors:
| Status | When |
|---|---|
422 | A required field is missing, target_url is not a valid URL, redirect_type is not 301, 302 or 307, status is not active or inactive, or a value is too long. The body is the standard 422 with errors keyed by field. |
422 | The plugin refuses the source or the destination. The body has code set to lmp_redirect_invalid, data.status set to 422, message set to the first problem, and errors keyed by source_url or target_url. |
The plugin refuses these cases. Each field shows its first message only.
| Field | Message |
|---|---|
source_url | "A source URL is required." |
source_url | "The source URL must be on this site." |
source_url | "You can’t redirect the home page." |
source_url | "An active redirect for this source URL already exists." The check covers every saved form of the address, including earlier addresses of the site. |
target_url | "Enter a full URL, starting with https://" |
target_url | "The source and target URLs cannot be the same." |
Get a redirect
GET /redirects/{id}
Returns one redirect.
Permission: link_manager_pro_access (Permissions)
Path parameters:
| Name | Type | What it is |
|---|---|---|
id | integer | The redirect ID. |
Response: 200. data holds:
| Field | Type | What it holds |
|---|---|---|
redirect | object | The redirect object. |
bash
curl -u USER:APPLICATION_PASSWORD \
https://example.com/wp-json/link-manager-pro/v1/redirects/5json
{
"success": true,
"data": {
"redirect": {
"id": 5,
"source_url": "https://example.com/old-page",
"source_url_hash": "4b5c31ad44d7dec24d24f4e8ff46c481",
"target_link_id": 61,
"status": "active",
"created_at": "2026-10-10T12:00:00.000",
"updated_at": "2026-10-10T12:00:00.000",
"redirect_type": 301,
"target_url": "https://example.com/new-page",
"hits": 17,
"backing_link_id": 61
}
}
}Errors:
| Status | When |
|---|---|
404 | No redirect has that ID, or the row points to a short link that it does not own. The body is { "code": "lmp_redirect_not_found", "message": "Redirect not found.", "data": { "status": 404 } }. |
Update a redirect
PUT /redirects/{id}
Saves a redirect and returns it. The body needs the same required fields as create.
Permission: link_manager_pro_access (Permissions)
Path parameters:
| Name | Type | What it is |
|---|---|---|
id | integer | The redirect ID. |
Body parameters: JSON. The same fields as Create a redirect. A missing status leaves the saved status as it is. When the redirect's hidden short link is gone, target_url is required and the plugin creates a new hidden link. The response then has a new target_link_id and backing_link_id, and hits is 0. The source is saved again in its normal form, and the check for another active redirect with the same source skips this redirect. Nothing is saved when any field fails.
Response: 200, with message set to "Redirect updated." data has the same shape as the create response: redirect and address_takeover. address_takeover is empty when the saved status is inactive.
bash
curl -X PUT -u USER:APPLICATION_PASSWORD \
-H "Content-Type: application/json" \
-d '{
"source_url": "/old-page",
"target_url": "https://example.com/new-page",
"redirect_type": 302,
"status": "inactive"
}' \
https://example.com/wp-json/link-manager-pro/v1/redirects/5json
{
"success": true,
"data": {
"redirect": {
"id": 5,
"source_url": "https://example.com/old-page",
"source_url_hash": "4b5c31ad44d7dec24d24f4e8ff46c481",
"target_link_id": 61,
"status": "inactive",
"created_at": "2026-10-10T12:00:00.000",
"updated_at": "2026-10-23T09:35:00.000",
"redirect_type": 302,
"target_url": "https://example.com/new-page",
"hits": 17,
"backing_link_id": 61
},
"address_takeover": []
},
"message": "Redirect updated."
}Errors:
| Status | When |
|---|---|
404 | No redirect has that ID, or the row is a short link's address row. The body is { "code": "lmp_redirect_not_found", "message": "Redirect not found.", "data": { "status": 404 } }. |
422 | The same field errors as create. |
Delete a redirect
DELETE /redirects/{id}
Deletes the redirect, its hidden short link and that link's clicks. A row that points to a link it does not own loses only the row.
Permission: link_manager_pro_access (Permissions)
Path parameters:
| Name | Type | What it is |
|---|---|---|
id | integer | The redirect ID. |
Response: 200, with message set to "Redirect deleted." data holds:
| Field | Type | What it holds |
|---|---|---|
deleted | boolean | Always true. |
bash
curl -X DELETE -u USER:APPLICATION_PASSWORD \
https://example.com/wp-json/link-manager-pro/v1/redirects/5json
{
"success": true,
"data": { "deleted": true },
"message": "Redirect deleted."
}Errors:
| Status | When |
|---|---|
404 | No redirect has that ID. The body is the WordPress error not_found. |
Review old addresses
Some redirects stop matching after a site move, a search-and-replace or an update from an older version. The review routes find them. Every review route returns the same object, described next, so you can call one and read the result.
The plugin keeps a list of the addresses the site had before, up to four. It saves them in the option link_manager_pro_site_bases, as scheme://host with no port. Redirects saved under one of these addresses keep matching. The list is the site_bases field below.
A finished review is kept for a day and an unfinished one for 10 minutes. It is refreshed whenever a redirect changes, and the next call continues from where the check stopped.
| Field | Type | What it holds |
|---|---|---|
shadowing | array | Active redirects whose source is also a published page. Each row: id (integer), source_url (string) and page (the page title). Most come from an earlier version that saved a source without its encoded characters, so /blog/привет/ became /blog. The redirect now sends visitors away from the page. The plugin cannot recover the original address. |
shadowing_complete | boolean | false until every active redirect has been checked. The check resumes on the next call. |
other_address | array | Active redirects whose source host is neither this site's nor a recorded earlier address, grouped by host. Each row: base (scheme://host) and count (integer). Such redirects do nothing today. |
key_mismatch | array | Redirects whose source_url_hash no longer equals the md5 of source_url, for example after a search-and-replace on the database. Each row: id and source_url. |
dangling | array | Active redirects whose hidden short link is gone, for example after a partial restore. Each row: id and source_url. The plugin never turns them off. |
site_bases | array of strings | The recorded earlier addresses, oldest first. |
shadowing, key_mismatch and dangling hold at most 50 rows each. The shadowing check stops after about 5 seconds per call, and shadowing_complete is false until it has covered every active redirect.
json
{
"shadowing": [
{ "id": 8, "source_url": "https://example.com/blog", "page": "Blog" }
],
"shadowing_complete": true,
"other_address": [
{ "base": "https://old.example.net", "count": 12 }
],
"key_mismatch": [
{ "id": 9, "source_url": "https://example.com/moved" }
],
"dangling": [],
"site_bases": ["https://old.example.org"]
}List redirects to review
GET /redirects/review/sources
Returns the review object. It changes nothing.
Permission: link_manager_pro_access (Permissions)
Query parameters: None.
Response: 200. data is the review object.
bash
curl -u USER:APPLICATION_PASSWORD \
https://example.com/wp-json/link-manager-pro/v1/redirects/review/sourcesjson
{
"success": true,
"data": {
"shadowing": [],
"shadowing_complete": true,
"other_address": [{ "base": "https://old.example.net", "count": 12 }],
"key_mismatch": [],
"dangling": [],
"site_bases": []
}
}Errors: None beyond the overview's sign-in and permission errors.
Adopt an earlier address
POST /redirects/review/adopt
Records an address from other_address as one of the site's own earlier addresses, so the redirects saved under it start to match.
Permission: manage_options (Permissions)
Body parameters: JSON.
| Name | Type | Required | Default | What it does |
|---|---|---|---|---|
base | string | Yes | none | An address, such as https://old.example.net. Only the scheme and host are used, in lower case. It must equal the base of a row in other_address. |
The plugin keeps at most four earlier addresses. Adding a fifth drops the oldest. It then clears the redirect caches.
Response: 200. data is the review object, read again after the change. The adopted address is in site_bases and no longer in other_address.
bash
curl -X POST -u USER:APPLICATION_PASSWORD \
-H "Content-Type: application/json" \
-d '{ "base": "https://old.example.net" }' \
https://example.com/wp-json/link-manager-pro/v1/redirects/review/adoptjson
{
"success": true,
"data": {
"shadowing": [],
"shadowing_complete": true,
"other_address": [],
"key_mismatch": [],
"dangling": [],
"site_bases": ["https://old.example.net"]
}
}Errors:
| Status | When |
|---|---|
422 | errors.base: "Choose an address." The value is empty, is not an http or https address, or is not listed in other_address. |
Forget an earlier address
POST /redirects/review/forget
Removes an address from the recorded earlier addresses. Redirects saved under it stop matching.
Permission: manage_options (Permissions)
Body parameters: JSON.
| Name | Type | Required | Default | What it does |
|---|---|---|---|---|
base | string | Yes | none | An entry of site_bases, written exactly as the review shows it. A value that is not recorded changes nothing. |
Response: 200. data is the review object, read again after the change.
bash
curl -X POST -u USER:APPLICATION_PASSWORD \
-H "Content-Type: application/json" \
-d '{ "base": "https://old.example.net" }' \
https://example.com/wp-json/link-manager-pro/v1/redirects/review/forgetjson
{
"success": true,
"data": {
"shadowing": [],
"shadowing_complete": true,
"other_address": [{ "base": "https://old.example.net", "count": 12 }],
"key_mismatch": [],
"dangling": [],
"site_bases": []
}
}Errors: None beyond the overview's sign-in and permission errors.
Re-save a redirect's key
POST /redirects/{id}/resave
Fixes one row of key_mismatch. The plugin stores the hash that matches the saved address. The address text stays as it is.
Permission: link_manager_pro_access (Permissions)
Path parameters:
| Name | Type | What it is |
|---|---|---|
id | integer | The redirect ID. |
Body parameters: None.
Response: 200. data is the review object, read again after the change.
bash
curl -X POST -u USER:APPLICATION_PASSWORD \
https://example.com/wp-json/link-manager-pro/v1/redirects/9/resavejson
{
"success": true,
"data": {
"shadowing": [],
"shadowing_complete": true,
"other_address": [],
"key_mismatch": [],
"dangling": [],
"site_bases": []
}
}Errors:
| Status | When |
|---|---|
404 | No redirect has that ID. The body is the WordPress error not_found. |
422 | errors.source_url: "An active redirect for this source URL already exists." Another active redirect already holds that key. |