SpecEnvoy
EN·中文

Strategic

Vantage data API

No key. No quota. No sign-up. The dataset is static JSON on a CDN — which makes it faster than a metered API, impossible for us to bill you for, and impossible for anyone to knock over.

Why there is no x-api-key

The usual shape for this kind of product is a metered REST API: an issued key, a few hundred requests a day, a monthly price. We deliberately did not build that, for two reasons.

It would be worse for you. Our data changes weekly, not per-request. A query endpoint would give you a rate limit and a key to rotate in exchange for nothing you cannot get by fetching a file.

It would be worse for us. A compute-metered endpoint on a free service is an open invitation to a bill. Static files on Cloudflare's CDN cost us nothing per request and cannot be made to cost us anything — so we can leave them wide open, honestly, instead of throttling you to protect ourselves.

If you need server-side querying — bounding boxes, since-timestamps, filters — say so and we will look at it. It would need its own worker with per-IP limits and a hard cost cap; it is not something to bolt onto this.

Endpoints

All paths are relative to https://www.specenvoy.com. Everything is GET, CORS-open (Access-Control-Allow-Origin: *), so browser fetch() works directly.

  • /data/vantage/data/meta.jsonstart here. Build date, schema version, every layer with its record count, precision mix and top countries, and the full source manifest with licences.
  • /data/vantage/data/layers/<layerId>.json — one layer's records. Layer ids: npp, icbm, space, radar, cmd, navalbase, airbase, garrison, milbase, dam, power, fab, oilgas, port, choke.
  • /data/vantage/data/base/{land,lakes,places,admin1}-<lod>.json — the basemap, Natural Earth derived, public domain. Coordinates are delta-encoded integers; divide by q after accumulating. LOD 0/1/2 = coarse/medium/fine.
  • /vantage/live/mil — live military ADS-B, refreshed about every two minutes. Best effort, no SLA, and the one endpoint we reserve the right to switch off. Returns {"stale":true} rather than an error when upstream is down.

Versioning

meta.json carries "schema": 1. Additive changes — new fields, new layers, new records — happen inside schema 1 without notice.

A breaking change ships under a new filename (layers/npp-v2.json) and bumps schema; the old files keep serving. So pin nothing, read schema, and you will not be broken by us.

Record ids are permanent. A harvested id is <layer>:q<wikidata-qid> precisely so it survives upstream renames.

Licences — per file, and they differ

Each file states its own licence in a licence field. Check it; you cannot treat the whole thing as one licence.

  • Basemap — derived from Natural Earth, public domain. No conditions.
  • Layer files — a mixture, so read each record's src. Wikidata-derived records are CC0. Records carrying status, capacity or dates from Global Energy Monitor are CC BY 4.0 and require attribution to GEM if you redistribute them — about 5,500 records across npp, dam and power, each naming the tracker and release in its src. Curated records and all written descriptions are ours, reusable with attribution to SpecEnvoy. Cited government sources (EIA, DoD, CASI) are US public domain.
  • Live ADS-BODbL 1.0, upstream adsb.lol contributors. Share-alike applies to a derived database, which is why it lives at its own endpoint and is never merged into the layer files. Attribution travels in the payload and in the X-Data-Licence header.

Suggested citation: Vantage, SpecEnvoy — retrieved <date>, https://www.specenvoy.com/data/vantage/, plus the upstream credit for whichever file you used.

Fields worth understanding

  • precexact | site | area | approx. Location precision. If you aggregate without filtering on this, your numbers mean less than you think.
  • sg — significance percentile 0–100, computed within that layer only. It is what the map uses to decide what to draw at low zoom. Curated records are pinned at 100. Do not compare an sg across layers: a dam's and an airbase's are ranked against different populations, and the number is meaningless between them.
  • count vs total on a layer file — count is the headline figure and excludes approx records; total is everything in the file. meta.json mirrors this as n and all, and publishes a countries index of headline counts by ISO 3166-1 alpha-2 across every layer.
  • tierA hand-verified, B machine-harvested.
  • ra — if 1, the description was generated from the cited structured fields rather than written by a human.
  • weak — if 1, sourced only to a finding aid; a primary source is still owed.
  • src — always present, always at least one entry, always with a URL and a date.
  • conflict — present when open sources disagree, with an explanation. We record the disagreement instead of picking a winner.
  • ll[longitude, latitude], WGS84. Longitude first.

Worked examples

Everything below runs as-is. No key, no sign-up, no build step.

1 — Start with the manifest, and only re-fetch what changed. This is the one thing we ask: built tells you whether a layer moved since you last looked. Layers update weekly, so anything more often than daily is wasted bytes for both of us.

const meta = await (await fetch(
  "https://www.specenvoy.com/data/vantage/data/meta.json"
)).json();

console.log(meta.total, "records across", Object.keys(meta.layers).length, "layers");
console.log(meta.layers.npp);
// { n: 368, tierA: 0, weak: 368, prec: { site: 368 }, top: ["US:94","CN:32", ...] }

2 — One layer, filtered to a country. cc is ISO 3166-1 alpha-2 on every record.

const npp = await (await fetch(
  "https://www.specenvoy.com/data/vantage/data/layers/npp.json"
)).json();

const chinese = npp.records.filter(r => r.cc === "CN");
console.log(chinese.map(r => `${r.n} (${r.zn}) — ${r.cap?.mw ?? "?"} MW`));

3 — Filter on precision before you aggregate. If you skip this, your numbers mean less than you think: an approx record is a point placed from a sentence, ±50 km. We exclude those from any count we headline, and so should you.

const solid = npp.records.filter(r => r.prec === "exact" || r.prec === "site");
const hand  = npp.records.filter(r => r.tier === "A");   // human-verified
const owed  = npp.records.filter(r => r.weak);           // finding-aid source only

4 — Straight onto a map. ll is [longitude, latitude] — longitude first, GeoJSON order. Most mapping libraries want latitude first, so this is the one place people trip.

const geojson = {
  type: "FeatureCollection",
  features: npp.records.map(r => ({
    type: "Feature",
    geometry: { type: "Point", coordinates: r.ll },   // already [lon, lat]
    properties: { name: r.n, name_zh: r.zn, status: r.status, precision: r.prec },
  })),
};
// Leaflet wants [lat, lon]:  L.marker([r.ll[1], r.ll[0]])

5 — Python, if you would rather.

import requests, pandas as pd

B = "https://www.specenvoy.com/data/vantage/data"
dams = requests.get(f"{B}/layers/dam.json").json()["records"]
df = pd.DataFrame(dams)
df[["lon", "lat"]] = pd.DataFrame(df["ll"].tolist(), index=df.index)

# height in metres lives under cap.h
df["height_m"] = df["cap"].apply(lambda c: (c or {}).get("h"))
print(df.nlargest(10, "height_m")[["n", "cc", "height_m"]])

6 — The live feed, and how to read it honestly. Best effort, no SLA, and the one endpoint we may switch off. It returns HTTP 200 with {"stale": true} rather than an error when upstream is unavailable — check the flag, not the status code.

const live = await (await fetch("https://www.specenvoy.com/vantage/live/mil")).json();

if (!live.ok || live.stale) {
  console.warn("degraded:", live.detail || "upstream unavailable");
} else {
  console.log(live.total, "military aircraft transmitting right now");
}
// live.caveat carries the sentence you should pass on to your own users.

Please carry that caveat. ADS-B shows the aircraft that chose to transmit; military aircraft transmit at their discretion and receiver coverage is thin outside Europe and North America. Presenting it as a complete picture is the one genuinely harmful thing you could do with this data.

Attribution. Check the licence field on each file — they genuinely differ. Vantage, SpecEnvoy — retrieved <date>, https://www.specenvoy.com/data/vantage/, plus the upstream credit for whichever file you used (Natural Earth, Wikidata, Global Energy Monitor, EIA, adsb.lol).

Fair use, and the one thing we ask

Fetch as much as you like — the CDN is built for it. If you are polling, please read meta.json first and only re-fetch layers whose built date changed; the layers update weekly, so anything more often than daily is wasted bytes for both of us.

Do not proxy the live feed to your own users at scale — hit adsb.lol directly and, better, feed them. They run the receiver network this depends on.

And please carry the caveat that ships in the live payload. ADS-B shows the aircraft that are transmitting; military aircraft transmit at their discretion. Passing that on as a complete picture would be the one genuinely harmful thing you could do with this data.