Skip to content
← How to connect

SEO MCP tools: all 49, with their inputs

TheSerpAgent is the rank tracker your AI runs. This is every tool on its SEO MCP server, for agents and for the people connecting them: what each one does, when to call it, what it costs and what comes back.

Server name
TheSerpAgent. Config files use the key theserpagent. It is not listed in a directory yet, so connect it by this address
Address
https://api.theserpagent.ai/api/mcp
Transport
Streamable HTTP, JSON replies, stateless
Protocol versions
2025-06-18, 2025-03-26, 2024-11-05
Key
Authorization: Bearer KEY, created in the app under AI assistants by the account owner
Rate limit
120 tool calls a minute per account
Reach
One account. Tools only, with no resources or prompts
Depth
The top 30. A position of null means not in the top 30

Use it when

The person wants positions tracked over time, asks what moved, compares two dates, watches competitors or Google AI Overview, needs a client report, or wants an alert when a page drops.

Do not use it when

The person wants keyword research, backlinks, Bing, the Map Pack, positions below 30, or a one-off lookup with nothing to track.

Tools do not reach billing, login or API keys, which stay in the dashboard with CSV import and export. This is a rank tracker, and it offers no public SERP API.

Start here

list_projects

Read only

The tracked websites, with keyword counts and average position.

When
First, to get the project ids most other tools need.
Cost
Not metered.

It takes no inputs.

What comes back
{
  "projects": [
    {
      "id": 1,
      "domain": "alpinerun.example",
      "name": "Alpine Run",
      "location": "New York, New York, United States",
      "language": "en",
      "device": "desktop",
      "keywords": 18,
      "has_icon": false,
      "average_position": 8.5
    }
  ]
}

search_locations

Read only

Finds a country or city and returns its location code, up to 10 matches.

When
Before create_project or update_project.
Cost
Not metered.
Inputs of search_locations
InputType and meaning
queryrequired string. Part of a country or city name, e.g. "Berlin".
What comes back
{
  "locations": [
    {
      "location_code": 1023191,
      "name": "New York, New York, United States",
      "type": "City"
    }
  ]
}

list_languages

Read only

The Google languages a project can use, with their codes.

When
Before create_project or update_project, to set a language.
Cost
Not metered.

It takes no inputs.

What comes back
{
  "languages": [
    {
      "code": "en",
      "name": "English"
    },
    {
      "code": "es",
      "name": "Spanish"
    }
  ]
}

get_usage

Read only

The plan, its limits and what is left: keywords, projects and refresh checks this month and this hour.

When
Before refreshing many keywords.
Cost
Not metered.

It takes no inputs.

What comes back
{
  "plan": "starter",
  "keywords": {
    "used": 34,
    "limit": 200
  },
  "projects": {
    "used": 4,
    "limit": 10
  },
  "refresh_checks": {
    "left_this_month": 1000,
    "monthly_limit": 1000,
    "left_this_hour": 500,
    "hourly_limit": 500
  }
}

Read the record

get_overview

Read only

A project's headline numbers: average position and the one before it, how many keywords sit in the top 3, 10 and 30, and today's biggest winners and losers. The average covers only the keywords in the top 30, which buckets.ranked counts. A loser with a null position left the top 30 from dropped_from.

When
For the state of a project at a glance.
Cost
Not metered.
Inputs of get_overview
InputType and meaning
project_idrequired integer. Project id from list_projects.
What comes back
{
  "project": {
    "id": 1,
    "domain": "alpinerun.example"
  },
  "average_position": 8.47,
  "previous_average_position": 8.35,
  "buckets": {
    "top3": 3,
    "top10": 11,
    "top30": 17,
    "ranked": 17,
    "not_ranked": 1,
    "pending": 0,
    "total": 18
  },
  "winners": [
    {
      "id": 6,
      "term": "trail running poles",
      "position": 3,
      "change": 1
    }
  ],
  "losers": [
    {
      "id": 1,
      "term": "trail running shoes",
      "position": 7,
      "change": -3,
      "dropped_from": null
    }
  ]
}

get_rankings

Read only

Current positions for a project's keywords, with previous and best position, monthly searches and CPC when known. Each keyword also says whether it is starred, whether Google shows an AI Overview and whether you are cited in it. A position of null means not in the top 30, or not checked yet when last_checked is also null.

When
To read the current state, or to get keyword ids. Filter by list, search words, stars, position range or only what moved at the last check.
Cost
Not metered.
Inputs of get_rankings
InputType and meaning
project_idrequired integer. Project id from list_projects.
tag string. Only keywords in this list (tag name).
search string. Only keywords containing this text.
starred boolean. Only starred keywords.
min_position integer. Best position to include, e.g. 4 to skip the top 3.
max_position integer. Worst position to include, e.g. 10 for page one.
changed_only boolean. Only keywords whose position changed at the last check.
sort term | position | best | last_checked | volume | cpc
limit integer. Rows per page, default 50, max 200.
page integer
What comes back
{
  "keywords": [
    {
      "id": 3,
      "keyword": "best running socks",
      "position": 1,
      "previous_position": 1,
      "best_position": 1,
      "monthly_searches": 4400,
      "cpc_usd": 0.64,
      "url": "https://alpinerun.example/guides/best-running-socks",
      "lists": [
        "socks"
      ],
      "starred": false,
      "ai_overview": true,
      "ai_cited": true,
      "last_checked": "2026-11-07"
    }
  ],
  "page": 1,
  "pages": 18,
  "total": 18
}

get_keyword_history

Read only

One keyword's daily record: date, position and ranking page, up to 365 days back. A position of null means not in the top 30.

When
To quote a past position, or to see how a keyword got where it is.
Cost
Not metered.
Inputs of get_keyword_history
InputType and meaning
project_idrequired integer. Project id from list_projects.
keyword_idrequired integer. Keyword id from get_rankings.
days 7 | 30 | 90 | 365. How far back, default 30.
What comes back
{
  "keyword": "trail running shoes",
  "history": [
    {
      "date": "2026-11-06",
      "position": 4,
      "url": "https://alpinerun.example/shoes/trail"
    },
    {
      "date": "2026-11-07",
      "position": 7,
      "url": "https://alpinerun.example/shoes/trail"
    }
  ]
}

get_changes

Read only

The biggest winners and losers of a project over 1, 7 or 30 days, up to 20 on each side. Change is places gained, so negative means lost. A keyword that left the top 30 is a loser with a null position and dropped_from saying where it was. One that entered the top 30 has no earlier number to compare, so it is not listed.

When
When the person asks what moved.
Cost
Not metered.
Inputs of get_changes
InputType and meaning
project_idrequired integer. Project id from list_projects.
period 1d | 7d | 30d. Compare with 1 day, 7 days (default) or 30 days ago.
What comes back
{
  "period": "7d",
  "winners": [
    {
      "keyword": "running shoes for wide feet",
      "position": 6,
      "change": 2
    }
  ],
  "losers": [
    {
      "keyword": "trail running shoes",
      "position": 7,
      "change": -3
    },
    {
      "keyword": "ultramarathon training plan",
      "position": null,
      "change": null,
      "dropped_from": 28
    }
  ]
}

explain_change

Read only

Compares a keyword on two dates: its position, the stored top results and which sites entered or dropped out. It returns a difference, never a cause. By default it compares the latest check with the last check at a different position. Top results are kept 90 days; older dates give the position only.

When
When the person asks what changed around a keyword.
Cost
Not metered.
Inputs of explain_change
InputType and meaning
project_idrequired integer. Project id from list_projects.
keyword_idrequired integer. Keyword id from get_rankings.
from_date string. Earlier date, YYYY-MM-DD. Must be a day the keyword was checked.
to_date string. Later date, YYYY-MM-DD. Defaults to the latest check.
depth integer. How many top results to list per date, 1-30, default 10.
What comes back
{
  "keyword": "trail running shoes",
  "summary": "\"trail running shoes\" moved down 3 places: 4 on 2026-11-06 to 7 on 2026-11-07.",
  "change": -3,
  "from": {
    "date": "2026-11-06",
    "position": 4,
    "url": "https://alpinerun.example/shoes/trail",
    "top": [
      {
        "position": 1,
        "domain": "ridgeoutfitters.example",
        "title": "Trail running shoes | ridgeoutfitters.example",
        "url": "https://ridgeoutfitters.example/trail-running-shoes"
      }
    ]
  },
  "to": {
    "date": "2026-11-07",
    "position": 7,
    "url": "https://alpinerun.example/shoes/trail",
    "top": [
      {
        "position": 1,
        "domain": "ridgeoutfitters.example",
        "title": "Trail running shoes | ridgeoutfitters.example",
        "url": "https://ridgeoutfitters.example/trail-running-shoes"
      }
    ]
  },
  "entered": [
    "gearlab-reviews.example"
  ],
  "dropped": [
    "fleetfootmag.example"
  ],
  "note": null
}

Projects

create_project

Changes data

Starts tracking a website in one location, language and device. It starts with one alert rule, moved_down 5, which update_alert or delete_alert can change.

When
When the person wants a new site tracked. Call search_locations first.
Cost
Counts against the plan's project limit. Needs a plan or a running trial.
Inputs of create_project
InputType and meaning
domainrequired string. Bare domain such as example.com (no https://).
location_coderequired integer. From search_locations.
language_code string. Default en.
device desktop | mobile. Default desktop.
name string
What comes back
{
  "project": {
    "id": 5,
    "domain": "summitbikes.example",
    "device": "mobile",
    "location_code": 1023191
  }
}

update_project

Destructive

Changes a project's name, domain, location, language or device. Changing location, language or device starts a new history for every keyword.

When
When the person changes a project. Confirm a new location, language or device first. The tool says so.
Cost
Needs a plan or a running trial.
Inputs of update_project
InputType and meaning
project_idrequired integer. Project id from list_projects.
name string
domain string. Bare domain such as example.com.
location_code integer. From search_locations.
language_code string. From list_languages.
device desktop | mobile
What comes back
{
  "project": {
    "id": 5,
    "name": "Summit Bikes",
    "domain": "summitbikes.example",
    "location_code": 1023191,
    "language": "en",
    "device": "mobile"
  }
}

set_project_icon

Changes data

Gives a project its own icon, from a public image address or base64: PNG, JPEG, WebP, GIF or ICO, up to 2 MB.

When
When the person wants a project's tile to show its own icon.
Cost
Needs a plan or a running trial.
Inputs of set_project_icon
InputType and meaning
project_idrequired integer. Project id from list_projects.
image_url string. Public https:// address of the image.
image_base64 string. The image itself as base64, instead of image_url.
What comes back
{
  "project_id": 5,
  "icon": {
    "format": "png",
    "size_bytes": 237
  }
}

remove_project_icon

Changes data

Removes a project's icon, so its tile goes back to the first letter of the domain.

When
When the person wants the icon gone.
Cost
Not metered.
Inputs of remove_project_icon
InputType and meaning
project_idrequired integer. Project id from list_projects.
What comes back
{
  "project_id": 5,
  "icon": null
}

delete_project

Destructive

Deletes a project with all its keywords, history, competitors, alerts and report settings. It cannot be undone, and it needs the project's exact domain as confirm_domain.

When
Only after confirming with the person. The tool says so.
Cost
Not metered.
Inputs of delete_project
InputType and meaning
project_idrequired integer. Project id from list_projects.
confirm_domainrequired string. The project's exact domain, as a safety check.
What comes back
{
  "deleted": {
    "id": 5,
    "domain": "summitbikes.example"
  }
}

Keywords

add_keywords

Changes data

Starts tracking keywords in a project, up to 500 in a call, optionally in lists. Duplicates are skipped. Read the first positions with get_rankings once the first check is back.

When
When the person gives keywords to track.
Cost
Counts against the plan's keyword limit. Needs a plan or a running trial.
Inputs of add_keywords
InputType and meaning
project_idrequired integer. Project id from list_projects.
keywordsrequired array of string. Up to 500 keywords.
lists array of string. List (tag) names to put them in.
What comes back
{
  "added": 4,
  "skipped": 0,
  "queued": 4
}

refresh_keywords

Changes data

Checks keywords now, by ids or by list, instead of waiting for the next daily check. Most fresh positions arrive within two minutes; a late one can take up to 6 hours, and a check that fails is given back. A keyword is fresh once its last_checked in get_rankings changes.

When
After a page changes, or when the person asks for a new position today.
Cost
One check per keyword within the monthly and hourly limits; a call past either is rejected whole. A search checked in the last hour reuses free. Needs a plan or a running trial.
Inputs of refresh_keywords
InputType and meaning
project_idrequired integer. Project id from list_projects.
keyword_ids array of integer. Up to 1000 ids from get_rankings.
list string. Refresh every keyword in this list instead.
What comes back
{
  "checks_used": 0,
  "reused_free": 2,
  "checks_left_this_month": 1000,
  "checks_left_this_hour": 500,
  "note": "Most fresh positions arrive within two minutes; a late one can take up to 6 hours, and a check that fails is given back. A keyword is fresh once its last_checked in get_rankings changes."
}

rename_keyword

Destructive

Changes a keyword's wording. Google is then searched for the new wording, so its position starts again and the old history no longer shows.

When
Only after confirming with the person. The tool says so.
Cost
Needs a plan or a running trial.
Inputs of rename_keyword
InputType and meaning
project_idrequired integer. Project id from list_projects.
keyword_idrequired integer. From get_rankings.
termrequired string. The new wording.
What comes back
{
  "keyword": {
    "id": 38,
    "keyword": "gravel bikes",
    "position": null
  }
}

star_keywords

Changes data

Marks keywords as starred, or takes the star off, up to 500 ids.

When
To keep the keywords that matter easy to filter in get_rankings.
Cost
Not metered.
Inputs of star_keywords
InputType and meaning
project_idrequired integer. Project id from list_projects.
keyword_idsrequired array of integer. From get_rankings.
starredrequired boolean. True to star, false to take the star off.
What comes back
{
  "changed": 2
}

remove_keywords

Destructive

Stops tracking keywords and deletes their history. It needs the exact ids, 100 at most, and there is no way to remove all.

When
Only after confirming with the person. The tool says so.
Cost
Not metered. Frees keyword allowance.
Inputs of remove_keywords
InputType and meaning
project_idrequired integer. Project id from list_projects.
keyword_idsrequired array of integer. Exact ids to remove (max 100).
What comes back
{
  "removed": 1
}

Sort keywords into lists

list_tags

Read only

A project's keyword lists, with how many keywords each holds.

When
Before filtering or refreshing by list.
Cost
Not metered.
Inputs of list_tags
InputType and meaning
project_idrequired integer. Project id from list_projects.
What comes back
{
  "lists": [
    {
      "id": 3,
      "name": "gear",
      "keywords": 7
    },
    {
      "id": 4,
      "name": "guides",
      "keywords": 3
    }
  ]
}

create_tag

Changes data

Creates an empty keyword list in a project.

When
Before sorting keywords into a new list.
Cost
Not metered.
Inputs of create_tag
InputType and meaning
project_idrequired integer. Project id from list_projects.
namerequired string. List name, up to 64 characters.
What comes back
{
  "list": {
    "id": 11,
    "name": "autumn"
  }
}

rename_tag

Changes data

Renames a keyword list. Its keywords stay in it.

When
When the person renames a list.
Cost
Not metered.
Inputs of rename_tag
InputType and meaning
project_idrequired integer. Project id from list_projects.
tag_idrequired integer. From list_tags.
namerequired string
What comes back
{
  "list": {
    "id": 11,
    "name": "autumn sale"
  }
}

tag_keywords

Changes data

Puts keywords in a list, up to 500 ids. The list is created if it does not exist.

When
To group keywords for filtering, refreshing or alerts.
Cost
Not metered.
Inputs of tag_keywords
InputType and meaning
project_idrequired integer. Project id from list_projects.
listrequired string. List (tag) name.
keyword_idsrequired array of integer. Up to 500 ids.
What comes back
{
  "keywords_affected": 2
}

untag_keywords

Changes data

Takes keywords out of a list, up to 500 ids. The keywords stay tracked.

When
When keywords no longer belong in a list.
Cost
Not metered.
Inputs of untag_keywords
InputType and meaning
project_idrequired integer. Project id from list_projects.
listrequired string. List (tag) name.
keyword_idsrequired array of integer. Up to 500 ids.
What comes back
{
  "keywords_affected": 1
}

delete_tag

Destructive

Deletes a keyword list. The keywords in it are not deleted.

When
Only after confirming with the person. The tool says so.
Cost
Not metered.
Inputs of delete_tag
InputType and meaning
project_idrequired integer. Project id from list_projects.
tag_idrequired integer. From list_tags.
What comes back
{
  "deleted_list": "autumn sale",
  "keywords_deleted": 0
}

Competitors

list_competitors

Read only

The competitor websites a project watches, with their ids and how many the plan allows.

When
Before adding or removing a competitor.
Cost
Not metered.
Inputs of list_competitors
InputType and meaning
project_idrequired integer. Project id from list_projects.
What comes back
{
  "competitors": [
    {
      "id": 2,
      "domain": "runnersdepot.example"
    },
    {
      "id": 3,
      "domain": "summitgear.example"
    },
    {
      "id": 1,
      "domain": "trailhouse.example"
    }
  ],
  "limit_per_project": 3
}

add_competitor

Changes data

Watches a competitor's website in a project. Its positions come from the daily checks already run for your keywords.

When
When the person names a rival to watch.
Cost
Uses no refresh checks. Counts against the plan's competitors per project. Needs a plan or a running trial.
Inputs of add_competitor
InputType and meaning
project_idrequired integer. Project id from list_projects.
domainrequired string. Bare domain such as rival.com.
What comes back
{
  "competitor": {
    "id": 9,
    "domain": "rivalbikes.example"
  }
}

get_competitor_rankings

Read only

Keyword by keyword: your position beside each competitor's latest position, and whether Google shows an AI Overview and cites you in it. A position of null means not in the top 30.

When
When the person asks who ranks above them.
Cost
Not metered.
Inputs of get_competitor_rankings
InputType and meaning
project_idrequired integer. Project id from list_projects.
limit integer. Rows per page, default 50, max 100.
page integer
What comes back
{
  "keywords": [
    {
      "id": 3,
      "keyword": "best running socks",
      "you": 1,
      "competitors": {
        "runnersdepot.example": 4,
        "summitgear.example": 12,
        "trailhouse.example": 3
      },
      "ai_overview": true,
      "ai_cited": true
    }
  ],
  "page": 1,
  "pages": 18,
  "total": 18
}

get_visibility

Read only

Share of visibility for you and each competitor, per day and now, over 7 to 90 days. It is an estimate from typical click rates by position, and it measures no visits.

When
When the person asks how they compare overall.
Cost
Not metered.
Inputs of get_visibility
InputType and meaning
project_idrequired integer. Project id from list_projects.
days integer. Days covered, 7 to 90. Default 30.
What comes back
{
  "domains": [
    {
      "domain": "alpinerun.example",
      "is_you": true,
      "competitor_id": null,
      "score": 15.5,
      "change": 0
    },
    {
      "domain": "runnersdepot.example",
      "is_you": false,
      "competitor_id": 2,
      "score": 14.8,
      "change": -0.8
    }
  ],
  "series": [
    {
      "date": "2026-11-07",
      "scores": {
        "alpinerun.example": 15.5,
        "runnersdepot.example": 14.8
      }
    }
  ]
}

remove_competitor

Destructive

Stops watching a competitor and deletes its stored positions.

When
Only after confirming with the person. The tool says so.
Cost
Not metered. Frees a competitor place.
Inputs of remove_competitor
InputType and meaning
project_idrequired integer. Project id from list_projects.
competitor_idrequired integer. From list_competitors.
What comes back
{
  "removed": {
    "id": 9,
    "domain": "rivalbikes.example"
  }
}

Alerts

create_alert

Changes data

Sets a rule. When a keyword crosses it, the account owner gets an email summary, at most one a day per project; a move checked after that day's summary goes in the next one. A rule watches the whole project or one list. moved_up sees a keyword in the top 30 at both checks. moved_down also fires, whatever the threshold, for one that leaves the top 30. Asking for a rule the project already has returns that rule, switched on, with created false.

When
When the person wants to hear about a drop or a gain.
Cost
Not metered.
Inputs of create_alert
InputType and meaning
project_idrequired integer. Project id from list_projects.
typerequired moved_up | moved_down | entered_top3 | left_top3 | entered_top10 | left_top10 | dropped_out
threshold integer. Places moved, 1-29. Only for moved_up and moved_down.
list string. Only watch keywords in this list.
What comes back
{
  "alert": {
    "id": 7,
    "rule": "Moved down 3+ places",
    "list": "Spring"
  },
  "created": true
}

list_alerts

Read only

A project's alert rules, with their ids and whether each is on.

When
Before adding, changing or deleting a rule.
Cost
Not metered.
Inputs of list_alerts
InputType and meaning
project_idrequired integer. Project id from list_projects.
What comes back
{
  "alerts": [
    {
      "id": 1,
      "type": "moved_down",
      "threshold": 3,
      "enabled": true,
      "rule": "Moved down 3+ places",
      "list": null
    },
    {
      "id": 2,
      "type": "entered_top3",
      "threshold": null,
      "enabled": true,
      "rule": "Entered the top 3",
      "list": null
    }
  ]
}

update_alert

Changes data

Turns an alert rule on or off, or changes how many places it watches for.

When
When the person wants a rule paused or changed.
Cost
Not metered.
Inputs of update_alert
InputType and meaning
project_idrequired integer. Project id from list_projects.
alert_idrequired integer. From list_alerts.
enabled boolean
threshold integer. Places moved, 1-29. Only for moved_up and moved_down.
What comes back
{
  "alert": {
    "id": 7,
    "type": "moved_down",
    "threshold": 5,
    "enabled": true,
    "rule": "Moved down 5+ places",
    "list": "spring"
  }
}

get_alert_history

Read only

The alerts a project has fired, newest first: date, keyword, the rule and the move, 50 to a page. A position of null means not in the top 30.

When
When the person asks what fired.
Cost
Not metered.
Inputs of get_alert_history
InputType and meaning
project_idrequired integer. Project id from list_projects.
page integer
What comes back
{
  "events": [
    {
      "date": "2026-11-07T06:00:00+00:00",
      "keyword": "trail running shoes",
      "keyword_id": 1,
      "rule": "Moved down 2+ places",
      "from": 4,
      "to": 7
    }
  ],
  "page": 1,
  "pages": 1,
  "total": 9
}

delete_alert

Destructive

Deletes an alert rule.

When
Only after confirming with the person, or turn it off with update_alert instead. The tool says so.
Cost
Not metered.
Inputs of delete_alert
InputType and meaning
project_idrequired integer. Project id from list_projects.
alert_idrequired integer. From list_alerts.
What comes back
{
  "deleted": 7
}

list_channels

Read only

The Slack channels and webhooks that receive a project's daily alert summary, with their status. The full addresses stay hidden, because they can hold a secret.

When
Before adding or testing a channel.
Cost
Not metered.
Inputs of list_channels
InputType and meaning
project_idrequired integer. Project id from list_projects.
What comes back
{
  "channels": [
    {
      "id": 1,
      "type": "slack",
      "host": "hooks.slack.com",
      "status": "active",
      "last_error": null,
      "last_success_at": "2026-11-07T09:00:00+00:00"
    },
    {
      "id": 2,
      "type": "webhook",
      "host": "hooks.larkfield.example",
      "status": "active",
      "last_error": null,
      "last_success_at": "2026-11-07T09:00:00+00:00"
    }
  ]
}

add_channel

Changes data

Sends a project's daily alert summary to a Slack incoming webhook or to any public https address. A webhook's signing secret is shown once, in the reply.

When
When the person wants alerts in Slack or in their own system.
Cost
Needs a plan or a running trial.
Inputs of add_channel
InputType and meaning
project_idrequired integer. Project id from list_projects.
typerequired slack | webhook
urlrequired string. The Slack webhook address, or your own https:// address.
What comes back
{
  "channel": {
    "id": 3,
    "type": "slack",
    "host": "hooks.slack.com",
    "status": "active",
    "last_error": null,
    "last_success_at": null
  }
}

test_channel

Changes data

Sends a test message to a Slack channel or webhook and says whether it arrived.

When
After adding a channel.
Cost
Needs a plan or a running trial.
Inputs of test_channel
InputType and meaning
project_idrequired integer. Project id from list_projects.
channel_idrequired integer. From list_channels.
What comes back
{
  "ok": true,
  "error": null
}

delete_channel

Destructive

Stops sending a project's alerts to a Slack channel or webhook.

When
Only after confirming with the person. The tool says so.
Cost
Not metered.
Inputs of delete_channel
InputType and meaning
project_idrequired integer. Project id from list_projects.
channel_idrequired integer. From list_channels.
What comes back
{
  "deleted": 3
}

Reports

generate_report

Read only

Makes the branded PDF report of a project over 7 or 30 days, 30 by default: overview, winners and losers, trend and every keyword. It returns a download link that works for one hour without signing in.

When
When the person needs a client report.
Cost
Not metered.
Inputs of generate_report
InputType and meaning
project_idrequired integer. Project id from list_projects.
days 7 | 30. Period covered, 7 or 30 days. Default 30.
What comes back
{
  "url": "https://api.theserpagent.ai/api/reports/1/download?days=30&expires=…&signature=…",
  "expires_in_minutes": 60,
  "filename": "report-alpinerun.example-2026-11-07.pdf"
}

get_report_branding

Read only

What a project's PDF report carries: company name, colour, whether it has a logo and whether our name is hidden, plus where replies to its scheduled emails go.

When
Before making or changing a client's report.
Cost
Not metered.
Inputs of get_report_branding
InputType and meaning
project_idrequired integer. Project id from list_projects.
What comes back
{
  "company_name": "Larkfield SEO",
  "color": "#1d4ed8",
  "reply_to": "reports@larkfield.example",
  "hide_branding": true,
  "has_logo": true
}

set_report_branding

Changes data

Puts a company name and colour on a project's PDF report, and chooses whether our name is hidden. The company name also goes on the From line of its scheduled emails, and replies go to reply_to; the sending address stays ours. Only the fields sent change, and null clears one.

When
When the person wants the report under their own name.
Cost
Not metered.
Inputs of set_report_branding
InputType and meaning
project_idrequired integer. Project id from list_projects.
company_name string or null. Up to 80 characters.
color string or null. Hex colour such as #0f766e.
reply_to string or null. Where replies to scheduled report emails go. Without one, a reply reaches our address and not the agency.
hide_branding boolean. True hides "Powered by TheSerpAgent".
What comes back
{
  "company_name": "Larkfield SEO",
  "color": "#1d4ed8",
  "reply_to": "reports@larkfield.example",
  "hide_branding": true,
  "has_logo": false
}

list_report_schedules

Read only

The PDF reports a project emails on a schedule: how often, to whom and when the next one goes out.

When
Before adding or removing a schedule.
Cost
Not metered.
Inputs of list_report_schedules
InputType and meaning
project_idrequired integer. Project id from list_projects.
What comes back
{
  "schedules": [
    {
      "id": 1,
      "frequency": "weekly",
      "recipients": [
        "marketing@alpinerun.example"
      ],
      "next_run_at": "2026-11-09T07:00:00+00:00"
    }
  ]
}

create_report_schedule

Changes data

Emails a project's PDF report daily, weekly or monthly to up to 5 people, at 07:00 UTC. A project can hold up to 5 schedules.

When
When the person wants a report sent on its own. It writes to the addresses given, so confirm them first. The tool says so.
Cost
Needs a plan or a running trial.
Inputs of create_report_schedule
InputType and meaning
project_idrequired integer. Project id from list_projects.
frequencyrequired daily | weekly | monthly
recipientsrequired array of string. Up to 5 email addresses.
What comes back
{
  "schedule": {
    "id": 2,
    "frequency": "weekly",
    "recipients": [
      "marketing@summitbikes.example"
    ],
    "next_run_at": "2026-11-09T07:00:00+00:00"
  }
}

delete_report_schedule

Destructive

Stops emailing a scheduled report.

When
Only after confirming with the person. The tool says so.
Cost
Not metered.
Inputs of delete_report_schedule
InputType and meaning
project_idrequired integer. Project id from list_projects.
schedule_idrequired integer. From list_report_schedules.
What comes back
{
  "deleted": 2
}

Summaries

get_summary

Read only

The latest plain-English summary of a project, and whether it is from today. Summaries are written automatically from the ranking figures, not by AI.

When
Before writing a new one.
Cost
Not metered.
Inputs of get_summary
InputType and meaning
project_idrequired integer. Project id from list_projects.
What comes back
{
  "summary": {
    "text": "17 of your 18 tracked keywords rank in the top 30, averaging position 8.47, worse than 8.35 before. 3 are in the top 3 and 11 in the top 10. Biggest gain: \"trail running poles\", up 1 to position 3. Biggest drop: \"trail running shoes\", down 3 to position 7. Your share of visibility is 15.5%; trailhouse.example leads with 17.1%. Google shows an AI Overview for 9 of your keywords and cites you in 4.",
    "source": "template",
    "date": "2026-11-07"
  },
  "up_to_date": true
}

write_summary

Changes data

Writes today's plain-English summary of a project, automatically from its ranking figures. No AI writes it on our side, so for a fuller write-up read get_overview and get_changes and write it yourself in the conversation; that text does not go into PDF reports. A summary already written today comes back as it is.

When
When the person wants the state of a project in words.
Cost
No allowance. Needs a plan or a running trial.
Inputs of write_summary
InputType and meaning
project_idrequired integer. Project id from list_projects.
What comes back
{
  "summary": {
    "text": "17 of your 18 tracked keywords rank in the top 30, averaging position 8.47, worse than 8.35 before. 3 are in the top 3 and 11 in the top 10. Biggest gain: \"trail running poles\", up 1 to position 3. Biggest drop: \"trail running shoes\", down 3 to position 7. Your share of visibility is 15.5%; trailhouse.example leads with 17.1%. Google shows an AI Overview for 9 of your keywords and cites you in 4.",
    "source": "template",
    "date": "2026-11-07"
  },
  "cached": false
}

Where this list comes from

The names and inputs are read from the server's own tool list. Each answer is the tool's real output from a test copy of the server, on a fictional agency's data, with long lists shortened. The tool hints are the server's: readOnlyHint and destructiveHint.

Stop checking rankings one by one

Add your site and keywords, and we start checking them right away.

Start free trial

14 days free with a card on file. Nothing is charged until they end.