Update an outreach target
POST
/sites/{siteId}/outreach/{targetId}- Scope
actions:apply- MCP tool
update_outreach_target
Set an outreach target's status, note or tags The status date moves when the status changes; status and note stay through later refreshes. Send status, note, tags or a mix; an empty or null note clears it, and tags replaces the target's tags ([] clears them). Recorded in Activity as the key owner.
Parameters
| Name | In | Details |
|---|---|---|
siteIdRequired | path | stringWebsite id, from GET /sites. |
targetIdRequired | path | stringOutreach target id, from GET /outreach. |
Request body
| Field | Details |
|---|---|
status | to_contact | contacted | added | declined · New status |
note | string or null · at most 2000 characters; Your note, at most 2,000 characters; empty or null clears it |
tags | list of string · at most 10 items; Replaces the target's tags; [] clears them |
{
"status": "contacted",
"note": "Emailed the editor on Oct 2.",
"tags": [
"gear-review"
]
}Response
200 · application/json
| Field | Details |
|---|---|
target | object · required |
target.id | string |
target.source | string |
target.url | string |
target.domain | string |
target.title | string |
target.score | number |
target.class | string |
target.reason | object |
target.reason.competitors | list of strings |
target.reason.domainRank | number |
target.reason.ourRank | number |
target.reason.spamScore | number |
target.reason.outOf | number |
target.reason.backlinks | number |
target.reason.pages | list of objects |
target.reason.pages[].competitor | string |
target.reason.pages[].url | string |
target.reason.pages[].anchor | string |
target.reason.pages[].title | string |
target.reason.pageKind | string |
target.reason.pageKindBy | string |
target.status | string |
target.statusAt | string |
target.statusAuto | true or false |
target.note | string |
target.tags | list of strings |
target.firstSeen | string |
target.lastSeen | string |
target.gone | true or false |
target.check | object |
target.check.at | string |
target.check.ok | true or false |
target.check.status | number |
target.check.linksUs | true or false |
target.check.mentionsUs | true or false |
target.check.matched | list of strings |
target.link | string |
Notes
GET /sites/{siteId}/outreach lists pages and sites worth asking for a link or a mention, ranked: open targets first by score (0 to 100), then contacted, added and declined. Targets their source no longer finds (gone) come last. MonoRanks does not send email and does not look up contact details: it lists the targets, you do the outreach and record what happened.
| What | Details |
|---|---|
source |
ai_source (AI answers to your tracked questions quote the page but do not name you), best_list (a “best X” list that names your competitors but not you), link_gap (a site that links to two or more of your competitors, not to you), lost_link (a strong site that stopped linking to you, where asking them to update the link is the fix) or manual (added by hand). |
reason |
says why, with the fields its source knows: for example questions, models and timesCited; keyword and position; competitors, domainRank, spamScore and outOf; or lostAt. |
status |
to_contact, contacted, added or declined. Filter with status and source. counts has the totals per status and source. |
| Spam | Targets with a spam score above 60 are left out unless you pass spam=1; counts.spamHidden says how many. |
| Writing | POST /sites takes status and/or note (at most 2,000 characters; an empty note clears it). The change is recorded in the website’s Activity as the key owner. Status and note stay when the source refreshes the target. |
| Added on its own | Once a week MonoRanks reads each open target page again, for free. When the page now links to the website or names it, the target turns added and statusAuto is true. |
Errors
| Status | Meaning |
|---|---|
400 | Bad request. Codes: nothing_to_change, bad_status, bad_note, note_too_long, bad_tags. |
401 | Missing, unknown or revoked key. Codes: missing_key, invalid_key. |
403 | The key lacks the scope, covers another website, or its owner lost access. Codes: missing_scope, other_site, owner_left, no_owner, forbidden. |
404 | Not found. Codes: not_found. |
429 | Too many requests this hour for this key. Codes: rate_limited. |
Example request
curl -X POST \
-H "Authorization: Bearer mr_ws_…" \
-H "Content-Type: application/json" \
-d '{"status":"contacted","note":"Emailed the editor on Oct 2.","tags":["gear-review"]}' \
"https://app.monoranks.com/api/v1/sites/SITE_ID/outreach/TARGET_ID"const res = await fetch("https://app.monoranks.com/api/v1/sites/SITE_ID/outreach/TARGET_ID", {
method: "POST",
headers: { Authorization: "Bearer mr_ws_…", "Content-Type": "application/json" },
body: JSON.stringify({
"status": "contacted",
"note": "Emailed the editor on Oct 2.",
"tags": [
"gear-review"
]
}),
});
if (!res.ok) throw new Error((await res.json()).error.message);
const data = await res.json();import requests
res = requests.post(
"https://app.monoranks.com/api/v1/sites/SITE_ID/outreach/TARGET_ID",
headers={"Authorization": "Bearer mr_ws_…"},
json={
"status": "contacted",
"note": "Emailed the editor on Oct 2.",
"tags": [
"gear-review",
],
},
)
res.raise_for_status()
data = res.json()<?php
$ch = curl_init("https://app.monoranks.com/api/v1/sites/SITE_ID/outreach/TARGET_ID");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => ["Authorization: Bearer mr_ws_…", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => '{"status":"contacted","note":"Emailed the editor on Oct 2.","tags":["gear-review"]}',
CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true);import axios from "axios";
const { data } = await axios.post(
"https://app.monoranks.com/api/v1/sites/SITE_ID/outreach/TARGET_ID",
{
"status": "contacted",
"note": "Emailed the editor on Oct 2.",
"tags": [
"gear-review"
]
},
{ headers: { Authorization: "Bearer mr_ws_…" } },
);Example response
{
"target": {
"id": "0b2d4f6a-8c0e-4a2c-9e4a-8c0e2a4c6e8a",
"source": "link_gap",
"url": "https://rideguide.example/best-bike-shops/",
"domain": "rideguide.example",
"title": "The best online bike shops",
"score": 82,
"class": "editorial",
"reason": {
"competitors": [
"bikeworld.example",
"cyclehub.example"
],
"domainRank": 47,
"ourRank": 33,
"spamScore": 4,
"outOf": 2,
"backlinks": 5,
"pages": [
{
"competitor": "bikeworld.example",
"url": "https://rideguide.example/best-bike-shops/",
"anchor": "Bike World",
"title": "The best online bike shops"
}
],
"pageKind": "review",
"pageKindBy": "ai"
},
"status": "contacted",
"statusAt": "2026-10-02T09:30:00.000Z",
"statusAuto": false,
"note": "Emailed the editor on Oct 2.",
"tags": [
"gear-review"
],
"firstSeen": "2026-09-14T03:00:00.000Z",
"lastSeen": "2026-10-05T03:00:00.000Z",
"gone": false,
"check": {
"at": "2026-10-05T03:10:00.000Z",
"ok": true,
"status": 200,
"linksUs": false,
"mentionsUs": false,
"matched": []
},
"link": "https://app.monoranks.com/sites/3f6c2a1e-8b4d-4c7a-9e21-5d0b7a6f4c13/outreach?open=0b2d4f6a-8c0e-4a2c-9e4a-8c0e2a4c6e8a"
}
}{
"error": {
"code": "nothing_to_change",
"message": "Send status, note, tags or a mix of them."
}
}{
"error": {
"code": "missing_key",
"message": "Send the key as Authorization: Bearer mr_site_…"
}
}{
"error": {
"code": "missing_scope",
"message": "This key does not have the scope this call needs."
}
}{
"error": {
"code": "not_found",
"message": "Outreach target not found."
}
}{
"error": {
"code": "rate_limited",
"message": "More than 1200 requests this hour for this key."
}
}The MCP server does the same with the tool update_outreach_target.