A bearer-token caller for the official Ahrefs remote MCP server, plus the thing that is genuinely hard to find: what each tool actually costs, measured across 1,102 real calls rather than estimated from the pricing page.
Ahrefs documents the cost model in one line, "rows times fields, minimum fifty units per billable request". That is accurate and almost useless for planning, because it tells you nothing about which of the forty-odd tools you will actually use is going to eat your month. Three of them ate 78 % of ours.
export AHREFS_API_KEY="..." # must be a key with MCP scope
./scripts/ahrefs-mcp.sh --units # remaining budget, costs 0
./scripts/ahrefs-mcp.sh --dr example.com
./scripts/ahrefs-mcp.sh --batch example.com,competitor.com
./scripts/ahrefs-mcp.sh site-explorer-referring-domains \
'{"target":"example.com","mode":"subdomains","limit":50,"select":"domain,domain_rating"}'Requires bash, curl and python3. Nothing to install, no daemon, no OAuth
round trip. Every call prints its real cost as [units spent: N].
OAuth is the documented path and it is the right one when you are sitting in front of a browser. It gets awkward the moment a scheduled job needs to pull data at seven on a Sunday morning: the initial authorisation needs a human, and after that you are maintaining a token refresh that has to keep working unattended. A bearer token has neither problem.
Same endpoint, same tools, no browser.
Ahrefs has shipped two different MCP servers, and the key types are not interchangeable. Only one combination is a sensible choice today:
| API v3 key | MCP-scoped key | |
|---|---|---|
local @ahrefs/mcp (npm) |
worked, repo now archived | fails |
remote api.ahrefs.com/mcp/mcp |
fails | correct |
The failure mode is quiet. A wrong pairing typically shows a server that looks connected and exposes zero tools, so people go hunting for a config typo that is not there. If you see that, check the key type before anything else.
Full table in COSTS.md. The short version, from 1,102 logged calls:
| rows returned | units spent | rows per unit | |
|---|---|---|---|
| free + flat-rate surfaces | ~18,000 | 3,400 | 5.3 |
| the three row-priced heavyweights | ~30,000 | 592,000 | 0.05 |
That is a ratio of about 103 to 1 for the same budget. The practical rule is not "make fewer calls", it is "use the other tools first", because in most cases they answer the same question.
Free or nearly free: everything tied to a verified project of your own.
Search Console endpoints, management endpoints, rank tracker, subscription info
and public-domain-rating-free all billed zero. Both Site Audit tools bill a
flat 50 per request no matter how many rows come back, which worked out to 0.3
units per row with twenty-plus technical fields per URL. Best value in the
product by a wide margin.
One booby trap: gsc-anonymous-queries carries the same gsc- prefix as
the free endpoints but bills from 50 units per call. On small sites it returns
almost nothing, because they never cross the anonymisation threshold it exists
to reveal.
Ahrefs gates two things by tier: monthly units, and rows per request.
| Plan | units / month | max rows / request |
|---|---|---|
| Lite | 100,000 | 100 |
| Standard | 400,000 | 250 |
| Advanced | 1,000,000 | 500 |
| Enterprise | 2,000,000 | unlimited |
The single most expensive call in our log ran at limit: 250 — exactly the row
cap of the plan it ran on. That was not an analytical decision, it was reaching
for the ceiling because it was there. 5,250 units per call. At limit: 50 the
same query costs 1,050 and supports the same conclusion.
Those caps were raised across all tiers on 28 April 2026 (Standard went from 25
rows to 250). An explicit limit in your code is unaffected. What is affected:
calls that pass no limit, calls that ask for "the maximum", and calls that used
to be clipped by the old cap and now return up to ten times more rows.
Ten parameter-level gotchas, each with the error message it produces, are in TRAPS.md. The ones that cost the most time:
whereis JSON, never a string expression, and needs one clause per conditionselectis a comma-separated string on most tools but an array onbatch-analysis- keyword matching is literal, so do not transliterate umlauts or accents
- filtering organic keywords by country can return zero rows for a domain that demonstrably ranks
org_trafficis a model, not a measurement; on your own domains Search Console is the truth
SKILL.md is a ready-made skill file for Claude Code that bakes the
cost discipline in, so the assistant checks the free surfaces before spending
anything. Drop it in ~/.claude/skills/ahrefs/. It works as plain documentation
for any other assistant.
The measurements come from a Standard plan over two heavy days at the end of a
subscription, logged from the apiUsageCosts field that every response carries
and nobody reads. We did not renew, which is also why the numbers could be
published: they stopped being operational detail.
So treat the table as a shape, not as a guarantee. The pricing model moved noticeably in April, caching visibly pulls some averages down, and your mix of tools will differ from ours. The method generalises even where the numbers do not: log that field for a month, group by tool, divide by rows returned. It costs nothing and it will tell you more about your own usage than any pricing page can.
Background reading: what 1,102 calls actually cost and wiring the server into Claude, Codex and the rest.
MIT. Not affiliated with or endorsed by Ahrefs.