# Spam Backlink Checker: Toxic Links Triage & Disavow Decision (`cygnix_labs/spam-backlink-triage`) Actor

Getting spam backlinks? Paste your Search Console, Semrush or Ahrefs backlink export: one answer (NO / MONITOR / ACT), a verdict per referring domain (OK, IGNORE, WATCH, DISAVOW_CANDIDATE), negative-SEO spike check, disavow draft for manual review only. $0.001/domain.

- **URL**: https://apify.com/cygnix_labs/spam-backlink-triage.md
- **Developed by:** [Umair Saleem Mohammad Saleem](https://apify.com/cygnix_labs) (community)
- **Categories:** SEO tools, AI, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.00 / 1,000 referring domain triageds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Spam Backlink Checker: Toxic Links Triage & Disavow Decision

**"I'm getting spam backlinks every day. Semrush shows them, Search Console doesn't. Should I care? Should I disavow?"** This Actor answers that from your own backlink export, in seconds, for **$0.001 per referring domain** (1,000 domains = $1).

Google ignores most spammy links. Its [disavow guidance](https://support.google.com/webmasters/answer/2648487?hl=en) says to consider disavowing only when you have a **substantial number** of spammy, artificial or low-quality links pointing to your site **and** they have caused a manual action, or likely will (for example, links you or an agency paid for or placed). Even then, try to get the links removed first. Many site owners still disavow by "toxic score" and cut good links by mistake. This Actor applies that guidance to your export and shows which domains (if any) deserve a closer look.

### What you get

- **One overall answer**: `NO` (background spam, do nothing), `MONITOR` (a few manipulative-looking links or a spam spike) or `ACT` (you have a manual action, or you said you built placed links that could likely lead to one: review, request removal first, and disavow only what remains)
- **A verdict per referring domain**: `OK`, `IGNORE`, `WATCH` or `DISAVOW_CANDIDATE`, with the signals and a plain-English reason
- **Negative-SEO spike check**: new spam domains per week in the last 4 weeks vs the previous 22 weeks (needs first-seen dates)
- **`DISAVOW_DRAFT`** in Google's `domain:` format, written only for `DISAVOW_CANDIDATE` domains. It is a draft for **manual review**, never an automatic upload: check every line, attempt removal first, and upload only if the links are substantial and you have, or likely face, a manual action
- **`REPORT`**: a Markdown report you can hand to a client

### Signals

SPAM_TLD, AUTOGEN_STATS (whois/"website worth" scraper pages), IP_HOST, JUNK_NAME, TOXIC_DOMAIN, TOXIC_ANCHOR (casino, pharma, adult, loans...), FOREIGN_SCRIPT_ANCHOR, HACKED_OR_SPAM_PAGE, LINK_SELLER_PAGE (links.html, partners, guest post, sponsored), KEYWORD_ANCHORS_DOFOLLOW, SITEWIDE, MANY_SUBDOMAINS, TOOL_TOXIC_SCORE (if your export has a toxic/spam score of 60 or more).

### Input

Paste any of these into **Backlink export** (or give a public CSV URL):

- **Google Search Console** > Links > Export external links (Top linking sites or Latest links). It's free.
- **Semrush** Backlink Analytics > Backlinks export, or the Backlink Audit export with Toxic score
- **Ahrefs** Site Explorer > Backlinks export
- **Moz, Majestic** or any CSV with a source URL or domain column. Comma, semicolon or tab separated.

Add your **brand terms** so brand anchors aren't read as keyword anchors. Tick **manual action** or **we bought/placed links** only if true. Only those answers can mark a domain `DISAVOW_CANDIDATE`, and a candidate still needs your manual review: a few placed links without a manual action or real manual-action risk usually don't justify a disavow.

### Output example (dataset row)

```json
{"verdict":"WATCH","domain":"seo-links-hub.com","signals":"LINK_SELLER_PAGE, KEYWORD_ANCHORS_DOFOLLOW","why":"Keyword-rich dofollow or link-seller pattern. If you (or an agency) bought or placed it, ask for removal first; if not, Google usually ignores it.","links":2,"dofollow":2,"topAnchors":"best cheap power tools | buy power tools online","overallAnswer":"MONITOR"}
```

### Pricing

Pay per event: **$0.001 per referring domain triaged**. An empty input charges nothing. Set **Max referring domains** to cap spend.

### FAQ

**Does it fetch the linking sites?** No. It works offline from your export, so it's fast, cheap and doesn't touch anyone's site. (A CSV URL you provide is downloaded once.)

**Why does Semrush show links Search Console doesn't?** Search Console shows a sample of links Google recorded and leaves out much of the junk it ignores. Third-party tools list everything they crawl, so their toxic count is not a Google penalty signal.

**Is this a Google ruling?** No. It's a heuristic recommendation from the export. `DISAVOW_CANDIDATE` means "review manually", not "disavow now". Google's guidance is to disavow only a substantial number of problematic links that caused or will likely cause a manual action, after trying removal. Always review a disavow file before uploading, and merge it with any existing one, because a new upload replaces the old file.

**Related Cygnix Labs Actors**: [Indexing Fix Planner](https://apify.com/cygnix_labs/indexing-fix-planner) (why Google won't index your pages), [SEO Change Impact Checker](https://apify.com/cygnix_labs/seo-change-impact-checker), [Redirect Chain Checker](https://apify.com/cygnix_labs/redirect-checker).

# Actor input Schema

## `csvText` (type: `string`):

Paste a backlink export: Search Console Links (Top linking sites or Latest links), Semrush Backlinks or Backlink Audit, Ahrefs Backlinks, Moz or Majestic. Columns are detected automatically.

## `csvUrl` (type: `string`):

Direct link to a CSV export (for big files).

## `linkingUrls` (type: `array`):

One linking page URL or domain per line (domain-level signals only).

## `brandTerms` (type: `array`):

So brand anchors aren't counted as keyword anchors.

## `site` (type: `string`):

Used only to label the report and disavow draft.

## `manualAction` (type: `boolean`):

Only tick if Search Console > Manual actions shows one. Lets spam-pattern domains become DISAVOW_CANDIDATE (manual review; try removal first).

## `builtLinks` (type: `boolean`):

Only tick if true. Lets paid/placed-link patterns become DISAVOW_CANDIDATE for manual review. Per Google, disavow only a substantial number of such links that caused or will likely cause a manual action, after trying removal.

## `sitewideMin` (type: `integer`):

A domain with at least this many links to 3 or fewer targets is flagged SITEWIDE.

## `maxDomains` (type: `integer`):

Each triaged referring domain is one charged event.

## Actor input object example

```json
{
  "csvText": "Source url,Target url,Anchor,Nofollow,First seen\nhttps://goodblog.com/review-acme,https://acme.com/,Acme,false,2026-01-10\nhttps://acme.com.websiteworth.info/,https://acme.com/,acme.com,false,2026-09-01\nhttps://cheap-slot-gacor-88.xyz/casino/page1,https://acme.com/,slot gacor maxwin,false,2026-10-01\nhttps://seo-links-hub.com/links.html,https://acme.com/tools,best cheap power tools,false,2025-11-01\nhttps://forum.example.org/t/1,https://acme.com/,https://acme.com,true,2026-03-01",
  "brandTerms": [
    "acme"
  ],
  "manualAction": false,
  "builtLinks": false,
  "sitewideMin": 50,
  "maxDomains": 5000
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `report` (type: `string`):

No description

## `disavow` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "csvText": `Source url,Target url,Anchor,Nofollow,First seen
https://goodblog.com/review-acme,https://acme.com/,Acme,false,2026-01-10
https://acme.com.websiteworth.info/,https://acme.com/,acme.com,false,2026-09-01
https://cheap-slot-gacor-88.xyz/casino/page1,https://acme.com/,slot gacor maxwin,false,2026-10-01
https://seo-links-hub.com/links.html,https://acme.com/tools,best cheap power tools,false,2025-11-01
https://forum.example.org/t/1,https://acme.com/,https://acme.com,true,2026-03-01`,
    "brandTerms": [
        "acme"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("cygnix_labs/spam-backlink-triage").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "csvText": """Source url,Target url,Anchor,Nofollow,First seen
https://goodblog.com/review-acme,https://acme.com/,Acme,false,2026-01-10
https://acme.com.websiteworth.info/,https://acme.com/,acme.com,false,2026-09-01
https://cheap-slot-gacor-88.xyz/casino/page1,https://acme.com/,slot gacor maxwin,false,2026-10-01
https://seo-links-hub.com/links.html,https://acme.com/tools,best cheap power tools,false,2025-11-01
https://forum.example.org/t/1,https://acme.com/,https://acme.com,true,2026-03-01""",
    "brandTerms": ["acme"],
}

# Run the Actor and wait for it to finish
run = client.actor("cygnix_labs/spam-backlink-triage").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "csvText": "Source url,Target url,Anchor,Nofollow,First seen\\nhttps://goodblog.com/review-acme,https://acme.com/,Acme,false,2026-01-10\\nhttps://acme.com.websiteworth.info/,https://acme.com/,acme.com,false,2026-09-01\\nhttps://cheap-slot-gacor-88.xyz/casino/page1,https://acme.com/,slot gacor maxwin,false,2026-10-01\\nhttps://seo-links-hub.com/links.html,https://acme.com/tools,best cheap power tools,false,2025-11-01\\nhttps://forum.example.org/t/1,https://acme.com/,https://acme.com,true,2026-03-01",
  "brandTerms": [
    "acme"
  ]
}' |
apify call cygnix_labs/spam-backlink-triage --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,cygnix_labs/spam-backlink-triage"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/TEbHDUxv9MqWFrHdt/builds/xTyjZPpLMBoPcabXG/openapi.json
