linkspect API docs linkspect.com

Reading results

A check is split into cohorts: one per country, operator and connection type. Each finished cohort has a result. This page explains its fields. Field-by-field definitions are in the reference.

Outcomes

Every cohort result has exactly one outcome. There are nine, and the set is closed: no new outcomes appear within /v1. New block methods and reasons can.

Four outcomes are verdicts about the site (verdict: true):

OutcomeMeaning
okThe site opens: content came back and the certificate is valid for the name.
blockedThe site is blocked. layer and method say where and how.
target_downThe site does not work from anywhere, our control measurements included. Not a block.
target_policyThe site itself refuses this visitor: anti-bot protection, a geofence, a rate limit.

Five outcomes are not verdicts (verdict: false). They mean we could not get a trustworthy answer, and they say nothing about whether the site works. Do not show them to your users as "up" or "down".

OutcomeMeaning
provider_policyThe proxy network we measure through does not allow this site. Not censorship.
exit_regimeThe whole network is under a special regime, such as an allowlist or a shutdown.
proxy_failureOur measuring point failed (the exit changed or disappeared, or the proxy network failed), not the site.
inconclusiveToo little data, or the data contradict each other.
not_measuredNothing to measure with here, or measuring is not allowed. Not charged.

Every outcome except ok and blocked has a reason, for example exit_changed for a proxy_failure or no_vantage for a not_measured. The reference lists every reason.

Layers and methods of a block

For blocked, read layer and method together, as in dns.redirect. Some common ones:

Layer.methodWhat happens
dns.redirectThe operator's DNS points the name to its block page.
dns.nxdomainThe operator's DNS says the name does not exist, and that answer is forged.
tls_sni.resetThe connection is reset when the TLS handshake names the site.
ip.dropTraffic to the site's IP address is dropped.
http.blockpageAn HTTP block page comes back instead of the site.
tls_mitm.substitutionA certificate from an issuer that is not publicly trusted replaces the site's.

Both lists can grow within /v1. If you meet a value you do not know, show it as it is rather than failing.

Confidence and uncalibrated

Verdicts carry a confidence label. It is a label, never a probability:

uncalibrated: true means the thresholds behind these labels have not been calibrated against real data yet. While it is set, results show no percentages, and a verdict without direct evidence is at most likely. Until calibration is done, results carry uncalibrated: true and versions.calibration: "none".

Coverage and the yellow ok

coverage.checked and coverage.unchecked list the layers that were and were not measured (dns, ip, tcp, tls, http, content, quic, ech). A layer we did not check is never counted as fine.

coverage_gaps lists the country's main layers that were not checked. An ok with a non-empty coverage_gaps is a "yellow" ok: the site opened, but part of the picture is missing. For example, "coverage_gaps": ["dns"] means the site loaded through the operator's network, but we could not observe the operator's DNS answer. In countries where blocks are done in DNS, that matters, so tell your users the ok is incomplete.

Evidence and explanation

evidence says what the result rests on: how many independent subscribers, subnets and pools of exits were used, and how many measurements were left out and why (excluded). measurement_ids point to the measurements; GET /v1/measurements/{id} returns their steps and timings.

explanation.text is a short summary built from the facts of the measurements. explanation.against lists the facts that speak against the conclusion, if any. Set Accept-Language: ru to get the explanation in Russian; everything else is in English.

Populations

Subscribers of one operator do not always see the same thing. When groups differ, populations shows each group separately instead of averaging them:

For example, a site blocked in Telekom's DNS but reachable for subscribers who use Cloudflare DNS:

"outcome": "blocked",
"layer": "dns",
"method": "redirect",
"populations": [
  {"group": "isp_default", "outcome": "blocked", "layer": "dns", "method": "redirect"},
  {"group": "public:cloudflare", "outcome": "ok"}
],
"conditions": {"groups": ["isp_default"]}

populations is empty when all groups see the same. conditions says under which conditions a block shows when it does not show everywhere: only on IPv4 (family), only for some groups, only in a time window, or only for a share of the measured subscribers.

Registry status

If the name is in a known national block registry, the result has registry with its status (listed, pending, delisted, not_listed, non_public, unknown_stale), the source and dates. This is kept apart from the outcome: a name can be listed and still open, for example while operators still have time to apply the order.

Filters outside the operator: user_filters

A site can also be blocked by something the visitor uses on top of their operator: a filtering public DNS, an ad blocker, a browser warning. These checks are the same for every operator, so they sit in one list on the check, user_filters, and never change a cohort's outcome.

Each entry has a kind, service, mode, verdict, observed_from (where it was seen) and checked_at:

Verdicts here are blocked (a red warning page instead of the site), warning (a warning the visitor can click through), partial (the site opens without some parts, such as ads), notice, ok, not_loaded and not_checked.

Real browsers

Add browser to the request to open the URL in real, current Chrome and Edge, the way a visitor would:

{
  "url": "https://example.org/",
  "targets": [{"country": "DE"}],
  "browser": {"browsers": ["chrome", "edge"], "country": "DE"}
}

Each browser adds one user_filters entry with kind: "browser_reputation", service: "chrome" or "edge", observed_from: "browser_worker" and a browser object: the browser version, which warning page it showed (interstitial, none if none), the final URL, load time, how old its warning lists were, and screenshot_url. Fetch the screenshot (a JPEG) with the same key; screenshots are kept for 30 days.

With country, the page loads through a home connection in that country, so the site answers as it does to visitors there. Without it, the browsers open it from our server in Germany. The browsers' own warning lookups are the same everywhere.

Linux browsers do not show download warnings or Chrome's "Did you mean …?" page for names in look-alike letters, so those are never reported. Test keys do not open browsers.

Versions

Each cohort result has versions: the engine, the decision rules, the calibration and the country's geo pack, with the pack's status. If two results have different versions, they may have been produced by different rules.

geo_pack_status: "shadow" means the country's rules are still being validated on real data. Results are shown, but they may be incomplete and may change once the pack reaches production.