Getting started
The linkspect API tells you whether a URL opens for subscribers of particular operators in particular countries, on home broadband and on mobile. When it does not open, the result says where it stopped (DNS, IP, TLS, HTTP) and keeps that apart from failures of our own measuring point.
Everything is JSON over HTTPS. The base URL is
https://api.linkspect.com/v1.
1. Get a key
Keys are handed out by hand for now. Ask for one at linkspect.com/request-access. You get two kinds:
lsk_test_…: a test key. Checks run on a fixture engine that returns predictable results and are never charged. Use it to build and test your integration. See Test keys and fixtures.lsk_live_…: a live key. Checks measure through real subscriber connections and are charged.
Send the key in every request as a bearer token:
Authorization: Bearer lsk_test_…
A request without a key, or with a wrong one, gets 401.
2. Run your first check
With a test key, check a host name that the fixture engine reports as blocked in DNS at Deutsche Telekom (home broadband):
curl https://api.linkspect.com/v1/checks \
-H "Authorization: Bearer $LINKSPECT_KEY" \
-H "Content-Type: application/json" \
-H "Prefer: wait=10" \
-d '{
"url": "https://blocked-dns.fixtures.linkspect.com/",
"targets": [
{"country": "DE", "operator": "de-telekom", "connection_type": "fixed"}
]
}'
What the fields mean:
url: the full address to check, path and query included. Onlyhttpandhttpswith a host name are accepted.targets: whose connections to check. Each target has acountry(ISO 3166-1 alpha-2, likeDE) and may narrow it down withoperator(a catalog ID such asde-telekomor an ASN such asAS3320) andconnection_type(fixedormobile). Without an operator you get the country's main operators; without a connection type you get both. Each combination of country, operator and connection type is a cohort, and each cohort gets its own result. The list of countries and operators is atGET /v1/geosandGET /v1/geos/{country}.check_type(defaultavailability) andprofile(defaultquick): what to measure and how thoroughly. See the reference for all values.
Prefer: wait=10 asks the API to hold the request for up to
10 seconds. A quick fixture check finishes in about 2, so the answer is
201 with all results. Without Prefer you get
202 right away and fetch the result later; see
Waiting and polling.
To see which cohorts a check would have and what it would cost before
running it, send the same body to POST /v1/checks:preview. It
starts nothing and charges nothing.
3. Read the answer
The response, shortened:
{
"id": "chk_01M70352F0PPK2KK6JCW2W0B44",
"status": "completed",
"test": true,
"summary": {
"verdicts": {"ok": 0, "blocked": 1, "target_down": 0, "target_policy": 0},
"non_verdicts": {"provider_policy": 0, "exit_regime": 0, "proxy_failure": 0,
"inconclusive": 0, "not_measured": 0},
"blocked_at": [{"country": "DE", "operator": {"id": "de-telekom", "name": "Telekom", "asn": 3320},
"connection_type": "fixed"}],
"pending": 0
},
"cohorts": [{
"country": "DE",
"operator": {"id": "de-telekom", "name": "Telekom", "asn": 3320},
"connection_type": "fixed",
"status": "completed",
"final": true,
"result": {
"outcome": "blocked",
"verdict": true,
"layer": "dns",
"method": "redirect",
"confidence": "likely",
"uncalibrated": true,
"coverage": {"checked": ["dns"], "unchecked": ["ip", "tcp", "tls", "http", "quic", "ech"]},
"coverage_gaps": [],
"evidence": {"subscribers": 3, "subnets": 2, "pools": 1, "measurements_used": 3, "excluded": [], …},
"explanation": {"language": "en", "text": "Test key: this result comes from the fixture engine. …", …},
"populations": [],
"versions": {"engine": "fixtures-0.1.0", "rules": "0.1.0", "calibration": "none",
"geo_pack": "DE@0.1.0", "geo_pack_status": "shadow"}
}
}],
"user_filters": []
}
Read it from the top:
summarycounts cohorts by outcome.verdictsare statements about the site;non_verdictsare cohorts we could not judge, counted separately.blocked_atlists where the site is blocked.- Each cohort has exactly one
outcome. Here it isblocked, andlayerplusmethodsay how: the operator's DNS points the name to its block page. verdict: truemeans this outcome is a statement about the site.verdict: falsemeans we could not measure it properly, and says nothing about whether the site works.confidenceis a label, not a percentage.uncalibrated: truemeans the thresholds behind it are not calibrated yet.coveragelists which layers were checked.explanation.textis a short summary in plain language (English, or Russian withAccept-Language: ru).final: truemeans this cohort's result will not change.
A blocked or unreachable site is a normal successful response with an
outcome. Errors (4xx, 5xx) only mean the request
itself could not be handled; see Errors.
The next page, Reading results, goes through every
part of a result: all nine outcomes, yellow ok, populations and
the checks outside the operator's network in user_filters,
including real browsers.
What else to know
- IDs are strings with a type prefix:
chk_for checks,coh_for cohort results,msm_for measurements. Times are RFC 3339 in UTC. - Within
/v1, changes are only additive: new optional fields, new values in lists of values, new resources. Handle values you do not recognise. The nine outcomes are the exception: that set is closed. - The API is a draft (version 0.x) until linkspect leaves shadow mode. The
contract can still change within
/v1until then. - Results never contain proxy provider names, exit IP addresses or raw provider codes. An exit is described by a pseudonym, ASN, country and connection type.