API reference

One endpoint returns the current OpenPageRank and full monthly history for up to 100 domains. Base URL: https://openpagerank.keywordseverywhere.com

Authentication

Send your OPR API key as a bearer token on every request. Create one on the Dashboard after signing in with your Keywords Everywhere key.

Authorization: Bearer opr_live_xxxxxxxxxxxxxxxx

Try it

Run a real request against your account. Your key is used only for this call and never leaves your browser except to this API.

From the Dashboard.
Equivalent cURL
curl -X POST https://openpagerank.keywordseverywhere.com/v1/domains/bulk …
Response
Run a request to see the response.

Look up domains

POST /v1/domains/bulk

Returns the current score and, by default, the full monthly history for each domain. Input can be a bare domain, a hostname, or a full URL. It is reduced to the registered domain. Each unique domain counts once toward your monthly limit.

FieldTypeNotes
domainsstring[]Required. Up to 100 unique domains per request (duplicates and equivalent forms, e.g. URLs of the same site, are merged first).
include_historybooleanOptional, default true. Set false for a smaller, current-only response.
curl -X POST https://openpagerank.keywordseverywhere.com/v1/domains/bulk \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"domains": ["github.com", "google.com"], "include_history": true}'
import requests

resp = requests.post(
    "https://openpagerank.keywordseverywhere.com/v1/domains/bulk",
    headers={"Authorization": "Bearer YOUR_KEY"},
    json={"domains": ["github.com", "google.com"], "include_history": True},
)
data = resp.json()
for row in data["results"]:
    print(row["domain"], row["open_page_rank"])
const resp = await fetch("https://openpagerank.keywordseverywhere.com/v1/domains/bulk", {
  method: "POST",
  headers: {
    "Authorization": "Bearer YOUR_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    domains: ["github.com", "google.com"],
    include_history: true,
  }),
});
const data = await resp.json();
data.results.forEach((r) => console.log(r.domain, r.open_page_rank));
<?php
$ch = curl_init("https://openpagerank.keywordseverywhere.com/v1/domains/bulk");
curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ["Authorization: Bearer YOUR_KEY", "Content-Type: application/json"],
    CURLOPT_POSTFIELDS     => json_encode([
        "domains"         => ["github.com", "google.com"],
        "include_history" => true,
    ]),
]);
$data = json_decode(curl_exec($ch), true);
foreach ($data["results"] as $row) {
    echo $row["domain"], " ", $row["open_page_rank"], "\n";
}
require "net/http"
require "json"

uri = URI("https://openpagerank.keywordseverywhere.com/v1/domains/bulk")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer YOUR_KEY"
req["Content-Type"] = "application/json"
req.body = { domains: ["github.com", "google.com"], include_history: true }.to_json

res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
JSON.parse(res.body)["results"].each { |r| puts "#{r["domain"]} #{r["open_page_rank"]}" }
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder()
    .uri(URI.create("https://openpagerank.keywordseverywhere.com/v1/domains/bulk"))
    .header("Authorization", "Bearer YOUR_KEY")
    .header("Content-Type", "application/json")
    .POST(HttpRequest.BodyPublishers.ofString(
        "{\"domains\": [\"github.com\", \"google.com\"], \"include_history\": true}"))
    .build();

var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
)

func main() {
	body, _ := json.Marshal(map[string]any{
		"domains":         []string{"github.com", "google.com"},
		"include_history": true,
	})
	req, _ := http.NewRequest("POST", "https://openpagerank.keywordseverywhere.com/v1/domains/bulk", bytes.NewReader(body))
	req.Header.Set("Authorization", "Bearer YOUR_KEY")
	req.Header.Set("Content-Type", "application/json")
	resp, _ := http.DefaultClient.Do(req)
	defer resp.Body.Close()

	var data map[string]any
	json.NewDecoder(resp.Body).Decode(&data)
	fmt.Println(data["count"], "domains returned")
}
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;

using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("Bearer", "YOUR_KEY");

var json = "{\"domains\": [\"github.com\", \"google.com\"], \"include_history\": true}";
var content = new StringContent(json, Encoding.UTF8, "application/json");

var resp = await client.PostAsync("https://openpagerank.keywordseverywhere.com/v1/domains/bulk", content);
Console.WriteLine(await resp.Content.ReadAsStringAsync());

Response

{
  "as_of": "2026-06-01",
  "count": 2,
  "results": [
    {
      "domain": "github.com",
      "found": true,
      "open_page_rank": 9.67,
      "rank": 30,
      "referring_domains": 282833,
      "history": [
        { "date": "2018-01-01", "open_page_rank": 9.41, "estimated": false },
        { "date": "2026-06-01", "open_page_rank": 9.67, "estimated": false }
      ]
    },
    { "domain": "google.com", "found": true, "open_page_rank": 10, "rank": 2, "referring_domains": 2308810, "history": [ ] }
  ],
  "invalid": []
}
FieldMeaning
open_page_rankThe authority score: a number from 0 to 10 with up to 2 decimal places (e.g. 10, 9.7, 4.92). null if the domain is not found.
rankGlobal position by PageRank (1 = highest).
referring_domainsAuthority-weighted count of linking domains.
historyMonthly series, oldest first. Present only when include_history is not false; omitted entirely otherwise.
history[].estimatedfalse for a month with a Common Crawl release (measured); true for a month with no release, linearly interpolated from the surrounding measured months. Interpolated months are most common before 2024, when releases were quarterly.
invalidInputs that could not be parsed to a domain (echoed back verbatim — escape before rendering in a UI). Invalid inputs are not counted against your limit.

Subdomain (host) scores

When a domain in your request is a subdomain, such as blog.example.com, the result for its root domain also includes a hosts array with one entry per queried subdomain:

{
  "domain": "github.com",
  "found": true,
  "open_page_rank": 9.5,
  "rank": 30,
  "referring_domains": 282833,
  "hosts": [
    {
      "host": "pages.github.com",
      "found": true,
      "open_page_rank": 9.15,
      "rank": 1954,
      "referring_hosts": 5888,
      "history": [ { "date": "2026-07-01", "open_page_rank": 9.15, "estimated": false } ]
    }
  ]
}
  • open_page_rank uses the same 0-10 scale as domains, so a subdomain is directly comparable with any other subdomain or with any root domain.
  • rank is the subdomain's position in the host-level web graph. referring_hosts counts the hosts that link to it, authority filtered.
  • A subdomain never scores above its root domain.
  • Subdomain data is free: it does not count toward your monthly domain limit. Up to 100 unique hostnames per request.
  • history follows include_history, like domains. Subdomains scoring below 0.5 keep only their current score, with no monthly history.
  • www.example.com means the site itself: it is served as example.com, with no host entry.
  • Sites hosted on platforms, such as yourblog.blogspot.com or project.github.io, are served as host entries. Their domain object reports found: false, because the domain-level graph files every *.blogspot.com site under blogspot.com.

Usage

GET /v1/usage. Returns your plan tier and how many domains you have left this month. Reading it does not count against your limit.

curl https://openpagerank.keywordseverywhere.com/v1/usage -H "Authorization: Bearer YOUR_KEY"
import requests
u = requests.get("https://openpagerank.keywordseverywhere.com/v1/usage", headers={"Authorization": "Bearer YOUR_KEY"}).json()
print(u["domains_remaining"], "of", u["monthly_domain_limit"], "left this month")
{ "tier": "free", "monthly_domain_limit": 30000, "domains_used": 1234, "domains_remaining": 28766, "resets_at": "2026-07-01T00:00:00+00:00", "requests_per_minute": 60 }

Health

GET /v1/health. Returns service status and the current data release. No authentication.

curl https://openpagerank.keywordseverywhere.com/v1/health

Domain limits

Each response includes your current balances in headers:

X-Domains-Limit: 30000          # your plan's monthly domain allowance
X-Domains-Remaining: 28766      # domains left this month
X-Domains-Reset: 1782864000     # unix time the monthly allowance resets (1st of next month)
X-RateLimit-Limit: 60           # requests/minute allowed on your plan
X-RateLimit-Remaining: 59       # requests left in the current minute
X-RateLimit-Reset: 1782777600   # unix time the per-minute window resets

Each unique, valid domain counts once per request (history or not); an empty request or one with only invalid inputs counts zero. A request that would exceed your monthly limit is declined with 429 (quota_error) and those domains are not counted. There is also a per-minute request limit (429 rate_limit_error) to guard against bursts. Domain limits reset on the 1st of each month. See plans for the limit on each tier.

Errors

Errors return a consistent shape:

{ "error": { "type": "authentication_error", "message": "Missing OPR API key. Provide it as: Authorization: Bearer <api_key>" } }
StatustypeWhen
400invalid_request_errorMissing domains, more than 100 unique domains per request, or include_history is not a boolean.
401authentication_errorMissing or invalid OPR API key.
404not_found_errorUnknown endpoint (path not part of the API).
405invalid_request_errorWrong HTTP method for a known endpoint. The Allow header lists the accepted methods.
429quota_error / rate_limit_errorMonthly domain limit reached, or too many requests per minute.

Prefer a machine-readable contract? The full OpenAPI 3 spec imports into Postman and generates client SDKs.

Migrating from the DomCop API

Integrations built against the original openpagerank.com API keep working here with one change: the API key. The old endpoint, query format, API-OPR header and response shape are all served on the current data:

GET https://openpagerank.keywordseverywhere.com/api/v1.0/getPageRank?domains[]=github.com&domains[]=example.org
API-OPR: opr_live_…
{ "status_code": 200,
  "response": [
    { "status_code": 200, "error": "", "page_rank_integer": 10, "page_rank_decimal": 9.54, "rank": "21", "domain": "github.com" },
    { "status_code": 404, "error": "Domain not found", "page_rank_integer": 0, "page_rank_decimal": 0, "rank": null, "domain": "example.org" }
  ],
  "last_updated": "1st Sep 2026" }
  • Old DomCop keys no longer work (that key store was retired). Sign in with your Keywords Everywhere API key, mint an OPR API key and send it in the same API-OPR header. Authorization: Bearer works too.
  • Up to 100 domains per request, one item per input in input order. page_rank_decimal is the score shown everywhere else on this site; rank is the global position.
  • Billing and limits are those of your plan, exactly as for /v1/domains/bulk without history: each unique valid domain counts once. Errors carry a top-level status_code and error string, including 429 for the monthly domain limit or the per-minute guard.
  • The legacy shape is frozen for compatibility. New work should use /v1/domains/bulk, which adds the full monthly history and subdomain scores.