BlogSEO
SEO that executes, not just reports.
What it can do
- Get Account: Recommended first call. Returns the signed-in user's email and every website this connection may use (the websites chosen on the consent screen, or all of them when none were excluded), e
- Get Credit Balance: Returns the website's current credit balances: ai_brain_credits (spent by AI edits, keyword research, image previews and SEO checks), articles_credit (one per generated article), b
- Get Website Settings: Returns the website's configuration grouped as it appears in the app: info (read-only facts: base_url, blog_url, favicon, domain rating, detected CMS, articles per month, locales
What data it sees
Do you need an account
No: the server works without sign-in
SEO that executes, not just reports. BlogSEO connects your AI assistant to your whole SEO workflow: keywords, content calendar, articles, on-page scoring and publishing to your CMS.
What you can do
- Keywords: list tracked keywords with Google Search Console positions, research new ones from seed keywords, refresh metrics, expand topic clusters, find cannibalization and compare rankings between periods.
- Content calendar: read the schedule, add articles with a brief and a target keyword, reschedule, edit or remove pending articles, get headline suggestions.
- Articles: read and list articles, update the headline, slug, meta description, cover image and alt text, edit content with AI, then publish or sync to WordPress, Shopify, Webflow, a custom webhook or the hosted blog.
- SEO checks: on-page SEO score, competitor benchmark and AI improvements for an article or pasted content, plus domain rating, backlink profile and keyword density for any page.
- Multiple websites: built for agencies and owners running several sites. Pick which websites the assistant can use on the consent screen.
Example prompts
- "Show my keywords ranking between position 5 and 15 with the most impressions, and what to change on each page to reach page 1."
- "Schedule an article for next Monday targeting 'cold email deliverability' and brief it to compare Gmail and Outlook filtering."
- "Score my latest article and apply the top three fixes."
Access and safety
Sign in with OAuth, no API key to paste. The assistant acts only on websites where you are an admin or owner. Actions that spend credits ask for confirmation first and report the remaining balance. No tokens, payment data or integration credentials are ever returned. Revoke access any time under Settings > Security.
Requires a BlogSEO account. How it works · Documentation
How to connect
How to connect BlogSEO to Claude
The server address to paste into Claude settings:
https://blogseo--vincent-dzqe.run.tools
- Open Claude (claude.ai or the desktop app).
- Go to Settings → Connectors.
- Click Add custom connector.
- Paste the server address copied below into Remote MCP server URL and click Add.
- In a chat, click + → Connectors and switch the new connector on.
Custom connectors are available on Free, Pro, Max, Team and Enterprise plans (Free is limited to one). On Team and Enterprise an organization Owner adds the connector first under Organization settings → Connectors.
Authorization
After you click Add, a sign-in window for the service opens. Sign in with your own account and approve access. Claude never sees your password.
How to connect BlogSEO to ChatGPT
The server address to paste into ChatGPT settings:
https://blogseo--vincent-dzqe.run.tools
- Open ChatGPT in a browser (chatgpt.com). A Plus, Pro, Business, Enterprise or Edu plan is required.
- Turn on developer mode once: Settings → Apps → Advanced settings → Developer mode.
- Open Settings → Connectors and click Create.
- Fill in the form: Name (anything), Description (one line about what the service does), MCP server URL (copy it below).
- Under Authentication choose OAuth if the service requires sign-in, otherwise None. Click Create.
- In a new chat open + → Apps/Connectors and enable the connector.
OpenAI has renamed this section before (Connectors → Apps/Plugins). If the label differs, search settings for "developer mode". On Business/Enterprise workspaces an admin must allow custom connectors first.
Authorization
On first use ChatGPT opens the service's sign-in window. Sign in and approve access.
How to connect BlogSEO to Cursor
The server address to paste into Cursor settings:
https://blogseo--vincent-dzqe.run.tools
Fastest: click Open in Cursor below and confirm the prompt.
Manually:
- In Cursor open Settings → Cursor Settings → MCP and click Add new global MCP server.
- Paste the JSON copied below into
~/.cursor/mcp.json(per project:.cursor/mcp.jsonin the repo root). - Save the file. The server appears in the MCP list; authorize it there if asked.
Authorization
If the service needs sign-in, an authorize button appears next to the server in the MCP list.
Server tool list (60)
Raw names from tools/list. Only developers need these.
| get_account | Recommended first call. Returns the signed-in user's email and every website this connection may use (the websites chosen on the consent screen, or all of them when none were excluded), each with its website_id (needed by the other tools when the account has several websites), the user's role on it (owner or admin: websites where the user is only a member are never available over MCP and are not listed), its credit balances (AI brain credits for AI features, article credits for generation, backlink credits) and its plan (subscription status, whether it is active, period end, billing interval, articles per month, whether the current user is the payer). Credits cannot be bought through this server: send the user to buy_credits_url. |
| get_credit_balance | Returns the website's current credit balances: ai_brain_credits (spent by AI edits, keyword research, image previews and SEO checks), articles_credit (one per generated article), backlink_credits, plus has_active_subscription. Pass website_id when the account has several websites (see get_account). |
| get_website_settings | Returns the website's configuration grouped as it appears in the app: info (read-only facts: base_url, blog_url, favicon, domain rating, detected CMS, articles per month, locales, brand logo URL), general (sitemap, auto-scheduling, publishing time), content (locale, audiences, offer summary, writing instructions), images (styles, brand color, image instructions, AI images vs media library) and competitors. Read it before calling an update_* tool so you only change what the user asked for. Pass website_id when the account has several websites (see get_account). |
| update_general_settings | Partially updates the General settings tab: sitemap URL, skipping non-custom articles, owner cross-linking, automatic scheduling, daily publishing time and timezone, and optionally the organization name. Only the fields you pass change. autopublish_time and autopublish_timezone must end up both set or both null. Returns the general section after the update. Pass website_id when the account has several websites (see get_account). |
| update_content_settings | Partially updates the Content settings tab: default locale, target audiences, offer summary and custom writing instructions. Only the fields you pass change; target_audiences replaces the whole list. The default locale cannot be one of the website's additional translation locales. Changing the offer summary re-embeds the website profile. Returns the content section after the update. Pass website_id when the account has several websites (see get_account). |
| update_image_settings | Partially updates the Images settings tab: cover and inline image styles (sketch, photo_realistic, digital_illustration, cinematic_realism, polygon, pixel_art, packshot), brand color, cover and inline image instructions and inline images per post (0-3). Only the fields you pass change; the brand logo, the AI images vs media library choice and media variations can only be changed in the app. Returns the images section after the update. Pass website_id when the account has several websites (see get_account). |
| update_competitors | Replaces the website's competitor list (up to 10 entries of name + domain). Domains are normalized to their hostname and duplicates dropped. Pass the complete list you want to keep; an empty list clears it. When AI Visibility is active the tracked competitors are reconciled with this list. Returns the saved competitors. Pass website_id when the account has several websites (see get_account). |
| regenerate_headlines | Queues a rewrite of the headlines of every upcoming automatically planned article that has not been generated yet (custom articles are untouched). Use it after changing the offer summary, audiences or instructions so the calendar reflects them. Asynchronous: returns as soon as the job is queued and the new headlines appear in the calendar within a few minutes. Limited to 3 requests per 24 hours per website and needs at least one article credit. Pass website_id when the account has several websites (see get_account). |
| list_scheduled_articles | Lists the content calendar of a website: the articles scheduled for generation, one row per day slot, ordered by date. Defaults to today through the next 60Returns 50 rows per page with has_more. Pass website_id when the account has several websites (see get_account). |
| schedule_article | Adds a custom article to the content calendar on a given date. The scheduler generates it around that date and consumes 1 article credit at generation time (not when scheduling), so the account needs an active subscription or at least 1 article credit. Limits: at most 90 pending (not yet generated) articles per website, 30 new schedules per hour, one scheduled article per keyword, 3 products per article. The website's publishing frequency is enforced: when the date already holds as many articles as the website publishes per day, the call fails and names the next free date, so pass that date, or move the existing article with reschedule_article. Pass keyword_id (from list_keywords, or add_keywords then list_keywords) to target a keyword, and brief for writing instructions. Returns the created row; nothing is written yet in the articles list until generation runs. Pass website_id when the account has several websites (see get_account). |
| update_scheduled_article | Edits the headline, brief, target keyword or products of a pending scheduled article; omitted fields keep their current value and keyword_id=null clears the keyword. Refuses rows that were already generated (edit the generated article instead). To change the date use reschedule_article. Returns the updated row. Pass website_id when the account has several websites (see get_account). |
| delete_scheduled_article | Unschedules an article: removes a pending article from the content calendar. Irreversible. Refuses rows that were already generated (use the articles tools to archive those). Nothing is refunded because scheduling never charged anything. Pass website_id when the account has several websites (see get_account). |
| suggest_headline | Asks the AI for an alternative headline for a pending scheduled article, using its brief, target keyword and the website profile, and avoiding headlines already in the calendar. It only returns a suggestion: nothing is saved. Apply it with update_scheduled_article once the user picks one. Limited to 25 suggestions per hour per user; each call takes a few seconds and costs no credits. Requires an active subscription or free trial. Pass website_id when the account has several websites (see get_account). |
| reschedule_article | Moves a scheduled article to another date (YYYY-MM-DD). Pass swap_with_scheduled_article_id to exchange dates with the article occupying the target day instead of stacking two articles on it. Without a swap, the website's publishing frequency is enforced: a full day is refused and the next free date is named. Refuses articles that are published or currently generating. Repeating a swap swaps the dates back. Returns the moved row. Pass website_id when the account has several websites (see get_account). |
| list_articles | Lists the website's generated articles, newest first, 20 per page, with their publication status and target keyword. Filter by headline search, status (generating, generated, preview_failed = failed to publish, preview_draft = published in draft, published, out_of_sync) and creation date range. Returns summaries only: call get_article with include_content=true to read an article's body. There is no total count; keep paging while has_more is true. Pass website_id when the account has several websites (see get_account). |
| get_article | Returns one article: headline, slug, status, target keyword, meta description, cover image and its alt text (main_image_alt, shared by every locale), dashboard URL and the list of its translations. Set include_content=true to also get the markdown body (can be long). Pass locale to read a translation instead of the default-language version; the base status and slug are shared by every locale. Never contacts the CMS. Pass website_id when the account has several websites (see get_account). |
| update_article_headline | Renames an article (or one of its translations when locale is given). Editing an article that is already published (or published in draft) marks it Out of Sync until it is synced with sync_article_to_cms or published again. Renaming an article that already lives on a CMS freezes its current slug so the live URL does not move; use update_article_slug to change the URL on purpose. Pass website_id when the account has several websites (see get_account). |
| update_article_meta_description | Replaces the SEO meta description of an article (or of one translation when locale is given); keep it under 160 characters. Editing an article that is already published (or published in draft) marks it Out of Sync until it is synced with sync_article_to_cms or published again. Pass website_id when the account has several websites (see get_account). |
| update_article_slug | Changes the URL slug of an article; the value is normalised (lowercase, hyphens) and must be unique on the website. Translations share the parent slug, so there is no locale option. Editing an article that is already published (or published in draft) marks it Out of Sync until it is synced with sync_article_to_cms or published again. On a published article the old slug is kept as a redirect on the hosted blog only; other CMSs will serve a 404 on the old URL. Pass website_id when the account has several websites (see get_account). |
| update_article_cover_alt_text | Replaces the alt text of an article's cover image. The alt text is stored once per article and used for every locale. An empty string clears it, in which case the headline is used. Inline images keep their alt text in the markdown body (): edit it with update_article_content. Free. Editing an article that is already published (or published in draft) marks it Out of Sync until it is synced with sync_article_to_cms or published again. Pass website_id when the account has several websites (see get_account). |
| update_article_content | Replaces the body of a generated article with the markdown you pass (the whole body, not a diff: read it first with get_article include_content=true, edit, then send it back). Headings, lists, links and images use standard markdown; inline images are , where url can be an image hosted with upload_article_image. The SEO score is recomputed and saved with the content. Pass locale to edit a translation instead of the default-language article. Free. Editing an article that is already published (or published in draft) marks it Out of Sync until it is synced with sync_article_to_cms or published again. For instruction-driven rewrites use edit_article_with_ai instead. Pass website_id when the account has several websites (see get_account). |
| create_image_upload | Prepares the upload of an image file from the user's machine, for set_article_cover_image or upload_article_image when the image has no public URL. Returns an upload_url to send the file to with an HTTP PUT (a ready-to-run curl command is included) and the upload_id to pass to those tools afterwards. Accepts JPEG, PNG, WebP, GIF and AVIF up to 10 MB. The upload_url expires after 2 hours; an uploaded file is deleted as soon as it is used, or after 6 hours if it is not; at most 10 unused uploads per connection. Free. Not needed for images that already have a public URL: pass image_url directly. |
| set_article_cover_image | Replaces the cover (main) image of a generated article with an image stored on the website's media host: either downloaded from a public URL (image_url) or uploaded from the user's machine (upload_id, see create_image_upload). Pass alt_text to describe the new image at the same time (recommended). Pass locale to change the cover of a translation only. Free. Editing an article that is already published (or published in draft) marks it Out of Sync until it is synced with sync_article_to_cms or published again. Inline images inside the body are part of the markdown: host them with upload_article_image and edit the body with update_article_content. Pass website_id when the account has several websites (see get_account). |
| upload_article_image | Hosts an image on the website's media host for use inside an article body, either downloaded from a public URL (image_url) or uploaded from the user's machine (upload_id, see create_image_upload). Returns the hosted URL: reference it in markdown as  and save the body with update_article_content. Does not change the article by itself. Free. Pass website_id when the account has several websites (see get_account). |
| edit_article_with_ai | Rewrites an article's body with AI following your instructions (tone, length, add a section, fix facts...) and saves it. Targets the default-language version unless locale is given; also_apply_to_locales runs the same instructions on extra translations, one credit each. Synchronous and slow: the call takes one to several minutes, wait for it instead of retrying. Editing an article that is already published (or published in draft) marks it Out of Sync until it is synced with sync_article_to_cms or published again. Only locales that were actually rewritten are charged. Use batch_edit_articles_with_ai for many articles, and the update_* tools for headline, meta description or slug. Costs AI brain credits (see the cost rule above), charged after the work, not refunded on failure. Requires an active subscription or free trial. Pass confirm=true to acknowledge the AI brain credits charge; without it the tool only returns the cost and your balance. Pass website_id when the account has several websites (see get_account). |
| batch_edit_articles_with_ai | Queues an AI rewrite of up to 50 articles with the same instructions (default-language version only). Asynchronous: articles are edited in the background over the next minutes; check them later with get_article. Articles still generating are skipped and not charged. Editing an article that is already published (or published in draft) marks it Out of Sync until it is synced with sync_article_to_cms or published again. Use edit_article_with_ai for a single article or a translation. Costs AI brain credits (see the cost rule above), charged before the work, refunded if the provider fails. Requires an active subscription or free trial. Pass confirm=true to acknowledge the AI brain credits charge; without it the tool only returns the cost and your balance. Pass website_id when the account has several websites (see get_account). |
| publish_article | Queues an article for publishing to one of the website's connected integrations (WordPress, Shopify, Webflow, webhook...). When provider is omitted and exactly one integration is eligible it is used; otherwise the eligible options are returned. Eligibility: generated or failed-to-publish articles can go to any active integration; an article published in draft can only be re-pushed to its own CMS; a live article can only be re-sent to a webhook (use sync_article_to_cms to push edits to a CMS). Asynchronous: the publish runs in the background within a few minutes; poll get_article until status is published or the status label reads Failed to publish. Pass website_id when the account has several websites (see get_account). |
| sync_article_to_cms | Pushes the current content of a published (or published-in-draft) article to every connected CMS, clearing its Out of Sync state. Synchronous: returns once the CMS accepted the update (a few seconds; Shopware, Umbraco and BigCommerce finish in the background). Fails for articles that are not on a CMS yet: publish those with publish_article. Framer and webhook connections are never synced. Pass website_id when the account has several websites (see get_account). |
| list_keywords | Lists the website's tracked keywords (Google, in the website's default locale) with volume, difficulty, CPC, opportunity score/label, search intent, favorite flag, topic cluster label and the latest Google Search Console position, clicks, impressions and top ranking page. Pages of 50; sort by opportunity (default), volume, position (best first, unranked last) or recent. Use search to filter by keyword text and favorites_only to keep starred keywords. Positions are null when Google Search Console is not connected. Returns keyword_id values used by every other keyword tool. Pass website_id when the account has several websites (see get_account). |
| get_keyword | Returns one keyword with its metrics, the last 90 days of Google Search Console position history (per day and country), the scheduled and generated articles targeting it, and up to 20 sibling keywords of the same topic cluster. Position history is empty when Google Search Console is not connected. Pass website_id when the account has several websites (see get_account). |
| get_keyword_rankings | Rank-tracking view from Google Search Console, covering the website's tracked keywords only and refreshed at the Search Console sync cadence (see gsc_last_synced_at): for each keyword the current average position, the page ranking for it, clicks, impressions, ctr (clicks / impressions), the country of that snapshot and the last sync time. Pass keyword_ids (max 50) or get the top N keywords (default 50, max 50). Filters run in the database: position_min and position_max bound the current position (unranked keywords are excluded when either is set), min_impressions drops low-visibility keywords, and sort orders by position (best first, unranked last, default), impressions or clicks (highest first). Page-2 quick wins: position_min 5, position_max 15, min_impressions 200, sort impressions. position_30d_ago is included when a history point exists around 30 days ago for the snapshot country (or the country you pass, lowercase alpha-3 like usa, or wwd). Not for changes over time: use get_keyword_trends for click, impression and position deltas between periods. Tells you when Google Search Console is not connected instead of returning empty positions silently. Pass website_id when the account has several websites (see get_account). |
| get_keyword_trends | Google Search Console trend view for the website's tracked keywords, refreshed at the Search Console sync cadence: compares the last window_days (7, 28 or 90, default 28) with the window_days before it. Per keyword: clicks, impressions and impression-weighted average position in both windows plus the deltas (current minus previous), and the page currently ranking for it. by_page groups the clicks and impressions of every keyword with data by that page, most decayed first (clicks_delta ascending), so it answers which pages are losing traffic. Pass keyword_ids (max 200) or let it consider every keyword with history; rows are sorted by clicks_lost (default), clicks_gained, position_lost or position_gained and cut to top (default 50, max 200). by_page is computed before the cut. Rows use each keyword's snapshot country, or the country you pass (lowercase alpha-3 like usa, or wwd). Keywords without history in either window are omitted; a position is null when its window has no impressions. Search Console data lags 2 to 3 days, so the newest days of the current window are usually missing. Not for the current position or ctr (use get_keyword_rankings) nor volume and difficulty (use list_keywords). Tells you when Google Search Console is not connected instead of returning empty rows silently. Pass website_id when the account has several websites (see get_account). |
| add_keywords | Adds the given keywords to the website's keyword plan exactly as written (1 to 20 per call) with their monthly volume, difficulty and CPC from the search-volume provider; nothing is expanded or invented. Keywords already tracked are skipped. Clustering and search-intent classification run in the background afterwards, so cluster_label and intent can be empty for a minute. Limited to 5 calls per hour per website, shared with research_keywords. The credit is charged even when every keyword was already tracked. To discover related keyword ideas from a few seeds, use research_keywords instead. Costs 1 AI brain credit, charged after the work, not refunded on failure. Requires an active subscription or free trial. Pass confirm=true to acknowledge the AI brain credits charge; without it the tool only returns the cost and your balance. Pass website_id when the account has several websites (see get_account). |
| research_keywords | Discovers new keywords from seed keywords and adds them to the website: each seed (at most 20) is expanded into related keyword ideas via the search-volume provider, filtered for relevance to the website by AI, de-duplicated against existing keywords and inserted (at most 250 new keywords per call). Clustering and search-intent classification run in the background afterwards, so cluster_label and intent can be empty for a minute. Limited to 5 calls per hour per website, shared with add_keywords. The credit is charged even when nothing new is added. To add specific keywords as written, use add_keywords; to look up metrics of existing keywords, use refresh_keyword_metrics. Costs 1 AI brain credit, charged after the work, not refunded on failure. Requires an active subscription or free trial. Pass confirm=true to acknowledge the AI brain credits charge; without it the tool only returns the cost and your balance. Pass website_id when the account has several websites (see get_account). |
| refresh_keyword_metrics | Re-fetches monthly volume, difficulty, CPC and the opportunity score of one keyword, plus up to 49 companion keywords of the same website and locale, from the search-volume provider in a single call. Keywords the provider does not return are left untouched. Use it when metrics look stale (see metrics_updated_at); it does not change Google Search Console positions. Costs 1 AI brain credit, charged before the work, refunded if the provider fails. Requires an active subscription or free trial. Pass confirm=true to acknowledge the AI brain credits charge; without it the tool only returns the cost and your balance. Pass website_id when the account has several websites (see get_account). |
| expand_keyword_cluster | Grows the topic cluster of a keyword: fetches related ideas from the search-volume provider seeded with up to 5 cluster members, keeps only ideas semantically close to the cluster (drops near-duplicates, navigational and other-city keywords) and adds at most 30 of them to the cluster. A keyword without a cluster becomes the pillar of a new one. The credit is refunded only when the provider fails, not when zero ideas pass the filters. Costs 1 AI brain credit, charged before the work, refunded if the provider fails. Requires an active subscription or free trial. Pass confirm=true to acknowledge the AI brain credits charge; without it the tool only returns the cost and your balance. Pass website_id when the account has several websites (see get_account). |
| schedule_articles_for_keywords | Queues one article per keyword in the content calendar. Asynchronous: the call enqueues the work and returns immediately; headlines are generated and the scheduled articles appear in the calendar about a minute later. Fails when any keyword already has a scheduled article. The calendar holds at most 90 pending articles: keywords beyond the remaining slots are dropped and reported in skipped_over_limit_count. Limited to 30 calls per hour. Scheduling is free; each article consumes 1 article credit when it is generated. Requires an active subscription or free trial. Pass website_id when the account has several websites (see get_account). |
| delete_keywords | Permanently deletes keywords from the website. Refused when any of them already has a generated article. Pending scheduled articles that targeted a deleted keyword are kept and their ids returned so their headlines can be regenerated. Pass website_id when the account has several websites (see get_account). |
| set_keyword_favorite | Stars or unstars a keyword. Favorites are scheduled first when the website auto-selects keywords for its calendar and can be filtered with list_keywords favorites_only. Pass website_id when the account has several websites (see get_account). |
| find_keyword_cannibalization | Finds tracked keywords for which two of the site's own pages will compete. This is BlogSEO's definition, not Search Console's "two URLs for one query" (Search Console gives one top page per keyword): a keyword is cannibalized when the page Google already ranks for it is not the landing page and is either a blog article (any position) or another page ranking within the website's cannibalization_max_position (top 30 by default), AND a BlogSEO article targets that same keyword without being the ranking page (with include_planned, a still-scheduled article counts too). Scans the Google keywords of the default locale that have a Search Console ranking page; snapshots older than 30 days are ignored, so it needs a recent sync. Returns up to top keywords (max 200), best position first, each with the ranking URL and its kind (blog_article or other_page), ranking_article_id when the ranking page is a BlogSEO article, the competing articles (article_id, headline, slug, status, live url when known; status scheduled means article_id is a scheduled_article_id) and a deterministic suggestion with its reason: consolidate when a published article competes with a ranking blog article, differentiate otherwise (for a planned article: rewrite its brief or drop the plan). Tells you when Search Console is not connected instead of returning nothing silently. Not a rank tracker: use get_keyword_rankings for positions and get_keyword for one keyword's articles. Pass website_id when the account has several websites (see get_account). |
| list_integrations | Lists the CMS, e-commerce and webhook integrations connected to the website's organization (provider, connection status, auto-publish and draft-mode flags, publishing targets such as blogs or collections) plus the Google Search Console connection of the website (property, status, last sync). Use it to know where articles get published and whether Search Console analytics are available. Credentials and tokens are never returned. A custom webhook or the hosted blog can be connected with connect_custom_webhook and create_hosted_blog; other integrations are connected, and any integration disconnected, in the app. Pass website_id when the account has several websites (see get_account). |
| get_website_setup_guide | Returns the guide to connect a custom-built website (Next.js, Astro, Rails, any framework or AI builder) so generated articles get published on it. Two options: a hosted blog on a subdomain of the user's domain (one CNAME record, no code) or a custom webhook received by an endpoint in the user's codebase. Call it first when the user asks to plug, connect or integrate their website, then follow its steps with the tools it names. Free. |
| get_webhook_contract | Returns the custom webhook contract an endpoint must implement to receive BlogSEO articles: request headers, the JSON payload with every field, multi-language delivery, republish and retry semantics, response and timeout rules, how to verify the shared secret and what the pages must render. Read it before writing or reviewing a webhook receiver. Free. |
| connect_custom_webhook | Connects a custom webhook: from then on BlogSEO POSTs every published article as JSON to the endpoint (see get_webhook_contract), and auto-publish is on so generated articles are delivered as soon as they are ready. The endpoint must be deployed and reachable over HTTPS first. With auth_method=generated the tool returns a shared secret exactly once: store it in the deployment's environment variables and verify the X-Webhook-Secret header with it. One webhook per organization (Lovable, Bolt, v0, Replit, Base44 and Next.js connections are webhooks too); it works next to a CMS integration. Call test_custom_webhook afterwards. Free. Pass website_id when the account has several websites (see get_account). |
| test_custom_webhook | Sends a sample article payload to the connected webhook with its saved settings and secret or, with url, to an endpoint that is not connected yet (without the secret, like the dashboard test before saving). Reports whether the endpoint answered 2xx within 30 seconds and the error otherwise. The sample uses random ids and fills every optional field; it never reproduces the multi-language fan-out. Limited to 30 tests per hour. Free. Pass website_id when the account has several websites (see get_account). |
| create_hosted_blog | Creates the hosted blog: a blog served by BlogSEO on a subdomain of the user's domain (blog.<domain> by default) over HTTPS, with a sitemap, an RSS feed, Open Graph images, structured data and the website's branding. Returns the CNAME record to add at the DNS provider; nothing is live until DNS points to the target, so call get_hosted_blog_status afterwards. Subdomains only (no apex domain), one hosted blog per organization, and not next to a CMS integration such as WordPress or Shopify. Every generated article is published automatically once connected. Free. Pass website_id when the account has several websites (see get_account). |
| get_hosted_blog_status | Re-checks the hosted blog's DNS and certificate like the Refresh status button and returns the current status (Waiting for DNS propagation, Issuing SSL certificate, Ownership verification needed, DNS misconfigured, CAA blocked, Connected...), the DNS records still to add (the CNAME plus any TXT ownership record), the last error and the detected DNS provider with a link to its settings. Poll it every few minutes after adding the record; DNS changes take 5 to 60 minutes. Limited to 30 checks per hour. Free. Pass website_id when the account has several websites (see get_account). |
| get_backlinks_overview | Returns the website's backlink exchange status: current Domain Rating, backlink credit balance, counts of backlinks received by status (queued, placed, verified, lost) and the last 20 received backlinks with the linking site, anchor and target page. Use it to report on link building progress; backlinks are matched automatically by the exchange network, there is nothing to trigger. Pass website_id when the account has several websites (see get_account). |
| convert_credits_to_backlink_credits | Converts AI brain credits into backlink credits at a fixed rate of 1 AI brain credit = 5 backlink credits, so the exchange network can place more backlinks to the website. The conversion is irreversible: backlink credits can never be turned back into AI brain credits. Cost rule: the amount passed is the number of AI brain credits spent (1 to 100 per call). Returns the new balances. Costs AI brain credits (see the cost rule above), charged before the work, not refunded on failure. Requires an active subscription or free trial. Pass confirm=true to acknowledge the AI brain credits charge; without it the tool only returns the cost and your balance. Pass website_id when the account has several websites (see get_account). |
| get_ai_visibility | Reads the AI Visibility dashboard of the website: how often AI assistants (ChatGPT, Claude, Gemini, Perplexity, Copilot, Google AI Overview and AI Mode, plus Grok and Mistral when the website pays for them) mention and cite the brand when answering its tracked prompts. For the range (7d, 30d, 3m or 6m, default 30d) it returns the overall mention rate and cited rate with the number of prompts and answers checked; the same per platform (only platforms with checks in the range); the trend (mention rate over the first half of the range vs the second half); the leaderboard of the brand (is_own) and its tracked competitors by mention rate; each prompt with its latest state per platform from the last 21 days of checks (mentioned_cited, mentioned, not_mentioned, or pending while a check is queued, running or failed), its mention rate across those latest checks and the competitor mentioned most for it; the most cited domains and the brand's own cited pages; and the sentiment drivers (strengths and weaknesses AI answers express about the brand) once enough brand-mentioning answers exist. Pass surface to restrict every number to one platform and include_answers=true for the latest answer text per prompt (600 characters max). Rates are percentages, null when nothing was checked. Fails with an activation link when the AI Visibility add-on is not active. Read-only: it cannot add prompts or competitors, and it is not Google Search Console data (use get_keyword_rankings for that). Pass website_id when the account has several websites (see get_account). |
| check_article_seo_score | Computes the on-page SEO score (0-100, graded S+ to D) of an article from its saved content: keyword checks (title, intro, meta, slug, subheading, density, image alt, cannibalization), sub-keyword or competitor-term coverage, and structure checks (length, meta and title length, subheadings, links, images, FAQ, paragraph and sentence length). When a done advanced analysis exists (run_advanced_seo_check) the competitor benchmark is included: competitor-relative score, topic gaps, recommendations and strengths. Pass locale to score a translation against its localized keyword (no benchmark or cannibalization check for translations). Returns every check with status good/ok/bad/na, the semantic keyword coverage and a prioritized list of fixes. The internal and external link checks are skipped when the website has no base URL set. Free and instant; nothing is modified. Pass website_id when the account has several websites (see get_account). |
| run_advanced_seo_check | Starts a competitor-relative SEO analysis of the default-language article: fetches the pages ranking in Google's top results for its target keyword, compares their depth and topic coverage with the article and stores a report (competitor-relative score, summary, topic gaps, recommendations, competitor terms, target word count). Asynchronous: returns as soon as the analysis is queued. It takes about 1 to 2 minutes; poll get_advanced_seo_report until status is done. The article must have a target keyword. Refused while an analysis is already running. Once done, check_article_seo_score and improve_article_seo use the benchmark automatically; re-run it only when the article changed substantially (see is_stale). Costs 1 AI brain credit, charged before the work, refunded if the provider fails. Requires an active subscription or free trial. Pass website_id when the account has several websites (see get_account). |
| get_advanced_seo_report | Reads the stored competitor-relative SEO analysis of an article (default-language version). status is none (never run), pending or processing (poll again in a minute), failed (error included, credit refunded) or done. A done report carries the competitor-relative score, summary, coverage gaps (with resolved flags from later re-checks), strengths, prioritized recommendations (with applied flags), the competitor terms and whether the article uses them, the target word count and the benchmarked competitor pages. is_stale is true when the article body changed since the analysis without being re-verified. Free and instant. Pass website_id when the account has several websites (see get_account). |
| improve_article_seo | Rewrites the article body with AI to fix its failing on-page SEO checks, exactly like the Improve with AI button: the current score is computed (with the competitor benchmark when a done advanced analysis exists), the failing body-level checks, missing sub-keywords and open competitor gaps become the rewrite instructions, the article is rewritten and saved, then re-scored. Title, meta description, slug, links and images are never changed; use the update_* tools for those. Synchronous and slow: takes 1 to 3 minutes, wait for it instead of retrying. The body is replaced in place and the previous version is not kept. Fails without charging when nothing is left to improve. Pass locale to improve a translation. Editing a published article marks it Out of Sync until sync_article_to_cms is called. Returns previous_score, new_score and the instructions that were applied. Costs 1 AI brain credit, charged after the work, not refunded on failure. Requires an active subscription or free trial. Pass confirm=true to acknowledge the AI brain credits charge; without it the tool only returns the cost and your balance. Pass website_id when the account has several websites (see get_account). |
| check_seo_score | Scores any content you pass inline (markdown or plain text) with the same on-page SEO checks as BlogSEO articles: keyword in title, introduction, meta description and subheadings, keyword density, sub-keyword coverage, length, meta and title length, subheading distribution, internal and external links, inline images, FAQ section, paragraph and sentence length. Returns the 0-100 score with its letter grade, every check grouped by keyphrase/structure, the semantic coverage of secondary_keywords and the top fixes ordered by impact. The content does not need to be in BlogSEO: use it on drafts, pages from other tools or competitor copy. Without target_keyword the keyword checks are skipped; without site_url the link checks are not applicable. Pasted content has no URL, so no slug is scored: the keyword-in-slug check always reads as a reminder to put the keyword in the URL when you publish. Free and instant; use get_article for articles stored in BlogSEO. Pass website_id when the account has several websites (see get_account). |
| run_advanced_seo_check_for_content | Benchmarks content you pass inline against the pages currently ranking on Google for target_keyword in the given locale: fetches the top organic results, reads up to 6 of them (title, headings, word count) and asks the AI for a competitor-relative report with a 0-100 advanced score, summary, coverage gaps, strengths, prioritized recommendations and the semantic terms ranking pages cover, plus the median word count to aim for. Also returns the on-page score of the content recomputed with that benchmark (competitor keyword coverage and length target). The content does not need to be in BlogSEO. Competitor pages are read as raw HTML, so ranking pages that build their content with JavaScript are skipped and the advanced score can differ from the one run_advanced_seo_check gives on a stored article, which reads rendered pages. Synchronous and slow: it takes 1 to 3 minutes, wait for it instead of retrying. Fails (and refunds) when fewer than 2 ranking pages can be read. Limited to 10 calls per hour per website. Use check_seo_score first for a free on-page-only check. Costs 1 AI brain credit, charged before the work, refunded if the provider fails. Requires an active subscription or free trial. Pass website_id when the account has several websites (see get_account). |
| improve_content_seo | Rewrites content you pass inline to fix its failing on-page SEO checks for target_keyword (keyword in introduction and subheadings, density, sub-keyword coverage, length, subheading distribution, FAQ, paragraph and sentence length) while keeping its topic, facts, tone, language, links and images. Returns the improved markdown verbatim with the score before and after. The content does not need to be in BlogSEO and nothing is saved: apply the result yourself, or use edit_article_with_ai for stored articles. Fails before charging when every body-level check already passes. Synchronous and slow: it takes 1 to 3 minutes, wait for it instead of retrying. Limited to 20 calls per hour per website. Costs 1 AI brain credit, charged after the work, not refunded on failure. Requires an active subscription or free trial. Pass website_id when the account has several websites (see get_account). |
| check_domain_rating | Returns the Ahrefs Domain Rating (0-100 authority score) of any domain, not only the user's own websites: competitors, prospects for link building, or a site found in the backlinks overview. Accepts a bare domain or a full URL; subdomains and paths are reduced to the registrable domain (blog.example.com/page -> example.com). Results are cached for 24 hours, so repeated calls for the same domain return the same value. Limited to 60 lookups per hour. |
| check_keyword_density | Computes keyword density for a public page URL or a block of text (exactly one of url or text): total and unique words, sentences, reading time, and the most frequent one-, two- and three-word phrases with count and density. With target_keyword it also returns its count, density, a verdict (ideal between 0.5% and 2.5%) and whether it appears in the title, meta description, H1 and first 100 words (placement flags are null for pasted text). Pages are fetched server-side as Googlebot with a Chrome fallback, JavaScript is not executed, at most 1 MB of HTML is read and the extracted text is cached for 1 hour. Fails for pages behind bot protection, non-HTML URLs or JavaScript-only pages: pass the page text instead. Any public page can be checked, not only the user's websites. Limited to 20 checks per 15 minutes. Free. |
| check_backlinks | Returns the backlink profile of any domain (competitors, prospects or the user's own site) from the DataForSEO backlinks index: Ahrefs Domain Rating, total live backlinks, referring domains, main domains and IPs, broken backlinks and pages, nofollow counts, the top TLDs of the linking pages and the 20 strongest backlinks (one per referring domain, sorted by domain rank) with source URL, target URL, anchor, dofollow flag, first-seen date and page title. Accepts a bare domain or a full URL; subdomains and paths are reduced to the registrable domain. Profiles are cached for 7 days (1 hour when the Domain Rating is unavailable) and the response says whether it came from the cache; the credit is charged for every call, cached or not, so do not call it twice for the same domain. Takes up to two and a half minutes on a cache miss. Limited to 30 checks per hour per website. Use check_domain_rating when only the Domain Rating is needed (free). Costs 1 AI brain credit, charged before the work, refunded if the provider fails. Requires an active subscription or free trial. Pass website_id when the account has several websites (see get_account). |