AltoRank
OfficialPublishing destination that allows generated articles to be published to Framer sites.
Publishing destination that allows generated articles to be published to Ghost blogs.
Publishing target that allows generated content to be committed to a Git repository.
Search analytics integration that provides daily clicks and impressions from Google Search Console.
Publishing destination that allows generated articles to be published to HubSpot.
Publishing destination that allows generated articles to be published to Notion.
Publishing destination that allows generated articles to be published to Shopify.
Publishing destination that allows generated articles to be published to Webflow.
Publishing destination that allows generated articles to be published to Wix.
Publishing destination that allows generated articles to be published to WooCommerce.
Publishing destination for WordPress sites, supporting both application password authentication and the recommended WordPress plugin route with a per-site token.
AltoRank
Open source alternative to Outrank and Jasper. AI SEO content engine: keyword research → article → Markdown, HTML, or one of 13 publishing adapters.
It researches a keyword, writes the article, scores it and checks its claims. The draft leaves as Markdown or HTML for you to paste wherever you publish, or through a CMS adapter (see Publishing for how far those have been tested). Who decides that it ships is yours to choose per workspace: approve each draft by hand, or set a rule that publishes after a hold unless you hold it. Either way the approval is recorded under a named person, and the article is tracked after - indexing and rank.
Open source, the whole product. No feature-gated tier, no ee/ directory.
Status
The hosted app at app.altorank.co is this repository, deployed from main
(docs/deploy.md). A self-hosted install runs the same code
against your own Supabase and your own API keys. Research, drafting, scoring,
fact-checking, audits, rank tracking and Search Console / Bing sync run today;
the table below has the detail. What is not finished yet:
No paying customers, no case studies. Nothing here has a growth figure attached to it, and that is on purpose.
CMS publishing is built but not yet verified end to end. The thirteen adapters are in
apps/web/lib/cms/and the connection form covers all of them, but none has been watched publishing to a live site yet, so the platform tiles on/connectask you to request an integration instead of offering Connect. Every draft can be copied as Markdown or HTML today. Details under What works today.AI visibility tracking is built but not switched on. The probe, the weekly cron and the
/geopage exist, but nothing in the app turns tracking on for a workspace or adds the prompts it measures, so the sidebar marks it "soon" (apps/web/lib/constants.ts).The CLI is publishable, not yet on npm.
packages/altorank-clibundlesapps/web/scripts/cli.tsinto analtorankbinary (npx altorank auth whoamionce it is published); until then,npm run clifromapps/webdrives the same agent API from a checkout.skills/altorank/SKILL.mdis the skill file a coding agent reads, and it does install withnpx; see below.
Whether you want to read how it works, run it yourself or send a patch, everything is here.
Related MCP server: ActableSite MCP
The publishing decision
One gate, two ways through it. lib/publishing/core.ts refuses any article
that is not approved (or scheduled with approved_by set), and nothing else
in the code path writes live. What can write an approval:
A person, from the editor (
approveArticle), recorded asapproved_by.A rule a named member of the account set (
auto_approve, migration 079): the publish cron runs the same checks the Approve button runs - active plan, no unsourced figure, no failing audit item, SEO score at or above the floor - after a hold window (default 24h) during which the drafted email carries a one-click Hold.approved_byis whoever turned the rule on;approval_kindsaysauto. Held drafts carry the reason on their own row. Seelib/publishing/auto-approve.ts.
What cannot write an approval: an agent. The MCP server and the agent API
expose no publish or approve tool - not disabled, absent. See the comment at
the top of apps/web/scripts/mcp.ts.
Competing tools ship the words "you stay in control" as copy. The difference
is checkable here: every live row points at a person, by click or by rule.
What works today
Keyword research + SERP analysis | DataForSEO |
Domain audit | 9 readiness checks, crawl, PageSpeed |
Article generation | research → draft → score → fact-check |
Brand voice | per-workspace voice profiles |
Publishing | Markdown and HTML export of every draft; 13 adapters (below), built but not yet verified end to end |
Locales | 35 ( |
Rank tracking | scheduled SERP checks |
Search analytics | Google Search Console; Bing Webmaster Tools (clicks and impressions per day) |
AI visibility | not switched on yet: built ( |
Publishing destinations (apps/web/lib/cms/adapter.ts) — thirteen adapters
covering ten CMS platforms; WordPress is reachable two ways, and git and
webhook are publishing targets rather than CMSs. Framer, Ghost, git, HubSpot, Magento,
Notion, Shopify, Webflow, webhook, Wix, WooCommerce, WordPress, and the
WordPress plugin — a second, recommended route to WordPress that installs a
plugin and takes a per-site token instead of an application password
(apps/web/lib/cms/wordpress-plugin.ts), which is why WordPress appears twice.
None of the thirteen has yet been verified end to end against a live site, so
CONNECTABLE_CMS in apps/web/lib/cms/connectable.ts is empty on purpose and
every CMS tile on /connect offers "Request integration" instead of Connect;
adding an id to that set brings that tile's Connect button back. The
connection form itself is unchanged and still covers all thirteen: the
page's "New connection" button, a draft's publish panel and
/connect?connect=<id> all open it. Without a connection, the editor copies
any draft as Markdown (front matter included) or HTML, or downloads it as a
.md file.
Install the skill
npx skills add AltoRank/altorankThat puts skills/altorank/SKILL.md where Claude Code, Cursor, Codex, Copilot,
Windsurf, Gemini CLI, Cline and the other agents the
skills CLI supports will read it. The
skill teaches an agent to drive the agent API (/api/agent/v1): preflight the
key, check a site's readiness, list or suggest keywords, generate a draft into
a human's review queue, edit a draft by find-and-replace, move the content
plan. It never publishes, approves or deletes; the API has no such calls.
It needs an API key, created at /settings/api-keys in the dashboard and
exported as ALTORANK_API_KEY. Self-hosted installs also set
ALTORANK_BASE_URL. skills/altorank/commands/ holds five thin slash
commands for the common calls; the skills CLI does not register slash commands,
so copy them into .claude/commands/ (Claude Code) or your agent's equivalent
if you want them.
Running it
Requires Node 22+, and a Supabase project (local via Docker, or hosted). The full deployment story, hosted and self-hosted, is in docs/deploy.md; the Cloudflare assessment is in docs/deploy-cloudflare.md.
npm install
cp docker/.env.example apps/web/.env.local # every variable, with what each one enables
npm run devFour things are genuinely required before it will run: your Supabase URL and
its two keys, an ANTHROPIC_API_KEY, and an ENCRYPTION_KEY
(openssl rand -hex 32). Add DATAFORSEO_LOGIN / DATAFORSEO_PASSWORD for
keyword volume and rank tracking; without them those steps skip rather than
fail. Everything else in the example file is optional and each one disables
exactly one feature. The file says which.
Other entry points:
npm run mcp # MCP server, for driving the engine from an AI assistant
npm run test # vitest, unit tier: mocks only, needs no services
npm run test:db # vitest, db tier: *.db.test.ts against the local Supabase only
npm run smoke # research → prompt → model → fact check, against real APIsand from apps/web:
npm run cli -- --help # the agent API from a shell; auth with ALTORANK_API_KEY
npm run readiness -- <domain> # the agent-readiness checks on their ownHosted MCP endpoint
The same tools are served at /api/mcp (Streamable HTTP) by the running app,
so an MCP client adds one URL as a connector instead of running the stdio
server: https://app.altorank.co/api/mcp on the hosted install, or your own
origin when you self-host. Authentication is a bearer token, either an API key
from /settings/api-keys or the token the built-in OAuth flow issues:
discovery at
/.well-known/oauth-authorization-serverand/.well-known/oauth-protected-resource/api/mcpdynamic client registration at
/api/oauth/register(public clients, PKCE S256 only)consent at
/oauth/authorize, owner or admin only, write scope opt-intoken at
/api/oauth/token, authorization-code grant, no refresh token
The token a connector receives is an api_keys row (migration 080), so it is
listed, expires and is revoked on /settings/api-keys like any other key.
Claude Code registers it with
claude mcp add --transport http altorank https://app.altorank.co/api/mcp;
ChatGPT and Claude.ai take the same URL as a custom connector.
Neither the CLI nor the MCP server can approve an article or delete anything —
not by configuration, but because the agent API has no such call and no DELETE
handler at all. Nor can either of them publish: the single endpoint that reaches
a CMS is POST /articles/{id}/retry-publish, which re-runs a publish that
a human already approved and that then failed. An article in draft or review
is refused, and the refusal tells the caller to hand it to a person
(apps/web/app/api/agent/v1/articles/[id]/retry-publish/route.ts:13-35).
Database
apps/web/supabase/ carries a config.toml and the numbered migrations, which
apply in order. With the Supabase CLI:
cd apps/web
supabase start # local Postgres + auth, prints the URL and keys for .env.local
supabase db push # applies the migrationsAgainst a hosted project, supabase link --project-ref <ref> first, then
supabase db push. The app will not get far without the migrations: every
query targets tables they create.
docker/ has a container setup if you would rather not run Node directly.
Layout
apps/web/ the engine and dashboard (Next.js)
app/(dashboard)/ dashboard routes
lib/audit/ domain analysis, readiness checks
lib/seo/ recommendations, scoring, locales, topical profile
lib/content/ generation (one implementation, shared by route + cron)
lib/ai/ fact checking
lib/cms/ 13 publishing adapters
lib/geo/ AI-answer visibility
scripts/mcp.ts MCP server
scripts/cli.ts CLI over /api/agent/v1
skills/altorank/ the skill file a coding agent reads (`npx skills add AltoRank/altorank`)
packages/altorank-cli/ npm package `altorank`: bundles scripts/cli.ts into a bin
docker/ container setup for self-hosting
tools/agent-readiness/ standalone agent-readiness scannerThe marketing site (altorank.co) lives in its own private repository,
AltoRank/altorank-marketing. It moved out of this tree on 2026-09-06 so the
product stays open while positioning and pricing copy do not. Nothing here
imports it; the two are coupled only by the plan limits noted in
apps/web/lib/stripe.ts and apps/web/lib/billing/quota.ts, which must be
changed in both places together.
Two conventions worth knowing before you send a patch:
Never render an unknown as zero. A number nobody measured, displayed as a measurement, is a fabricated claim even when no human typed it. Use
number | null, render an em dash, and average only over rows that have a value.One implementation per behaviour. Generation lives in
lib/content/generate.ts; the streaming route and the cron both call it. Resist the second copy.
Licence
GNU AGPL-3.0-only. Full text in LICENSE.
In practice: self-host it, run it for your own clients, commercially, and you owe nothing and publish nothing. The one obligation only reaches people rebuilding the product. If you modify the source and offer that modified version to others over a network, your changes have to be shared back under the same licence.
Running an account on it is free and unencumbered. Relaunching a modified copy as a closed competing service is not.
Contributing
Issues and pull requests are welcome. Two things that will get a patch rejected regardless of how good the code is:
Adding a way to publish that is not attributable to a person - a click, or a rule a named member set and can veto. An agent-triggered publish is the canonical example.
Adding a claim the repository cannot support. If
grepcannot find the feature, the README does not get to mention it.
This server cannot be deployed
Maintenance
Related MCP Connectors
Full AI visibility audits: schema, citations, agentic readiness, agent journeys, WordPress deploys.
Full-cycle SEO automation for AI agents: technical audits, SEO articles, machine-readable pricing.
SEO that executes: keywords, calendar, articles written, scored and published to your CMS, backlinks
Weekly benchmark of AI search visibility tools, plus on-demand AI-readiness site audits.
31
Related MCP Servers
AlicenseNot gradedqualityDmaintenanceSEO articles that sound like your brand — not generic AI output. TopicForge runs a four-stage pipeline — outline, draft, voice, and CTA — to turn topics into publish-ready articles with FAQ schema, meta, and editorial guardrails.762 npmMIT- AlicenseNot gradedqualityDmaintenanceAudits public websites for AI crawler access, public technical signals, and deployment readiness. Includes a focused path to the full readiness report when deeper remediation guidance is needed.MIT
- AlicenseBqualityBmaintenanceCombines Bing Webmaster data, GA4 AI-traffic opportunity matching, technical SEO scanning, AI-search content audits, approval-gated WordPress fix preparation, live verification, Bing URL submission, and IndexNow integration to help marketers improve pages for human readers and AI search.4625 npm1MIT
- AlicenseNot gradedqualityAmaintenanceRun the full agency SEO loop from your AI assistant: Search Console insights, prioritized actions, article generation, CMS publishing, indexing, and performance measurement.MIT