keywordseverywhere_api.py calls a removed endpoint — 404 with a valid key (v2.3.1)
Summary
The Keywords Everywhere / Open PageRank source added in v2.3.1 cannot work as shipped: scripts/keywordseverywhere_api.py requests an endpoint that no longer exists. Verified with a valid opr_live_… key created today in the Keywords Everywhere dashboard — the legacy path returns HTTP 404, while the same key succeeds against the current API.
This lines up with the caveat already in the v2.3.1 changelog: "The live API path is unverified: landing this required no Keywords Everywhere account, and none was available to exercise the real endpoint end to end (#262)."
What the code does
scripts/keywordseverywhere_api.py:37
KWE_BASE = "https://openpagerank.keywordseverywhere.com/api/v1.0/getPageRank"scripts/keywordseverywhere_api.py:70
headers = {"API-OPR": api_key}That is the legacy DomCop Open PageRank shape. After Open PageRank moved to Keywords Everywhere, the service exposes a different path and a different auth scheme.
Evidence
Four requests, same host, same valid opr_live_… key (value redacted), all within one minute:
| # | Request | Result |
|---|---|---|
| A | GET /api/v1.0/getPageRank?domains[]=example-domain, header API-OPR: <valid key> |
404, HTML error page |
| B | same path, header Authorization: Bearer <valid key> |
404, HTML error page |
| C | POST /v1/domains/bulk, Authorization: Bearer <valid key>, body {"domains":["example-domain"]} |
200, JSON payload |
| D | GET /v1/domains/example-domain, Bearer |
404 but JSON: {"error":{"type":"not_found_error","message":"Unknown endpoint: GET /v1/domains/…"}} |
C is the positive control: the key is valid, so A and B are not authentication failures. D shows that unknown routes under /v1/ return structured JSON, whereas /api/v1.0/... returns an HTML 404 — that prefix is not served by the API at all.
Shape of the successful response (C):
{
"as_of": "2026-08-01",
"count": 1,
"results": [
{
"domain": "…",
"found": true,
"open_page_rank": 1.16,
"rank": 10836226,
"referring_domains": 8,
"history": [ … ]
}
]
}Current API
Per https://openpagerank.keywordseverywhere.com/docs:
- Auth:
Authorization: Bearer opr_live_… - Bulk lookup:
POST /v1/domains/bulkwith{"domains": [...]}, up to 100 domains per call - The payload now carries
referring_domainsand a monthlyhistoryarray (2018 → present), neither of which the legacy API returned
Worth noting: legacy DomCop keys stop working on 2026-09-30, so the legacy shape has no remaining lifetime even if some compatibility route existed.
Suggested fix
- Point
KWE_BASEathttps://openpagerank.keywordseverywhere.com/v1/domains/bulk - Switch from
GET+ repeateddomains[]params toPOSTwith a JSON body - Replace the
API-OPRheader withAuthorization: Bearer - Map
results[](the legacyresponse[]key is gone), and treatstatus_code/erroraccordingly
Since the response now includes referring_domains, this source could contribute to the referring-domain factor in seo-backlinks scoring instead of being documented as "0-10 domain rank only" (skills/seo-backlinks/SKILL.md:54).
There is also an official MCP server at https://openpagerank.keywordseverywhere.com/mcp (streamable HTTP, same Bearer auth) exposing get_domain_rank, get_domain_history and bulk_domain_rank. Its initialize and tools/list respond without a key, which makes it straightforward to smoke-test the integration in CI without holding an account.
Environment
- claude-seo 2.3.1 (
9279553) - Keywords Everywhere free plan (30,000 domains/month)
- Python 3.13, Windows 11
Source: AgriciDaniel/claude-seo