spb/internetpressure
Public
TypeScript 36.3%
Python 31.8%
Go 18%
JavaScript 9.8%
Shell 1.9%
SQL 1.4%
CSS 0.5%
1import type { Metadata } from 'next';2import { Section } from '@/components/ui/primitives';3import { SITE_URL } from '@/lib/site';45export const revalidate = 3600;6export const metadata: Metadata = { title: 'API', description: 'Public, rate-limited JSON API and Server-Sent Events stream of InternetPressure.io — the same API the site uses.' };78const ENDPOINTS: { path: string; desc: string; params?: string }[] = [9 { path: '/api/v1/status', desc: 'Instrument health: engine, ingest, probes, BGP, stores, internal_status.' },10 { path: '/api/v1/pressure/global', desc: 'The index: pressure, level, Δ1h/Δ24h, velocity, acceleration, volatility, confidence, coverage, components with drivers, explain rows, 1-h sparkline.' },11 { path: '/api/v1/pressure/history', desc: 'Server-side aggregated series for any scope.', params: 'scope_type=global|region|country|asn|service|component · scope_id · range=1h|6h|24h|7d|30d|1y' },12 { path: '/api/v1/pressure/regions', desc: 'All regions with pressure, level, Δ1h, component scores, probes, targets, coverage.' },13 { path: '/api/v1/pressure/region/{id}', desc: 'Region detail: 24-h history, 7-d baseline, incidents, top ASNs/services, probes, latency matrix.' },14 { path: '/api/v1/pressure/countries', desc: 'Countries where we hold at least one probe or anchored target — nothing else is pretended.' },15 { path: '/api/v1/pressure/country/{cc}', desc: 'Country detail with history, baseline, incidents, ASNs, services, probes and targets.' },16 { path: '/api/v1/asns', desc: 'Autonomous systems index.' },17 { path: '/api/v1/pressure/asn/{asn}', desc: 'ASN detail: components, BGP stats and 24-h series, regions observed, targets, incidents.' },18 { path: '/api/v1/services', desc: 'Services index with observed availability and vendor status indicator.' },19 { path: '/api/v1/service/{slug}', desc: 'Observed vs vendor, discrepancy, probe × target matrix, affected regions.' },20 { path: '/api/v1/targets', desc: 'Target registry.' },21 { path: '/api/v1/target/{id}', desc: 'Target detail: latest measurement per probe, 24-h series, DNS resolvers.' },22 { path: '/api/v1/probes', desc: 'Probe network: status, last seen, version, uptime, clock offset, capabilities.' },23 { path: '/api/v1/incidents', desc: 'Incident list.', params: 'status=active|resolved|all · limit · offset' },24 { path: '/api/v1/incident/{slug}', desc: 'Incident detail: timeline, evidence, series with global overlay, probes, targets, BGP, annotations.' },25 { path: '/api/v1/fronts', desc: 'Active Pressure Fronts (source → destination arcs).' },26 { path: '/api/v1/bgp/stats', desc: 'BGP rates vs baseline, collectors, 1-h series, top origins.' },27 { path: '/api/v1/latency', desc: 'Global latency medians, inter-region matrix, per-probe view.' },28 { path: '/api/v1/ticker', desc: 'The live ticker counters.' },29 { path: '/api/v1/routes', desc: 'Route Explorer: baseline vs current traceroute, diff, 24-h hashes, 7-d share.', params: 'probe · target' },30 { path: '/api/v1/routes/pairs', desc: 'Sampled (probe, target) pairs with 24-h change counts.' },31 { path: '/api/v1/history/summary', desc: 'History explorer summary.', params: 'year · month' },32 { path: '/api/v1/explain', desc: 'Deep explainability: components → signals with current, median, MAD, robust z, samples, stress, contribution.' },33 { path: '/api/v1/methodology', desc: 'Public copy of the scoring configuration.' },34 { path: '/api/v1/search', desc: 'Search countries, regions, ASNs, services, targets, incidents.', params: 'q' },35 { path: '/api/v1/live', desc: 'Server-Sent Events: snapshot, global_pressure_update, regional_pressure_update, ticker, bgp_stats, probe_stats, incident_created/updated, service_degradation, front_update, internal_status.' },36];3738export default function ApiPage() {39 return (40 <div className="pb-8">41 <header className="pt-6 pb-4">42 <p className="label">Developers</p>43 <h1 className="mt-1 text-[26px] font-medium tracking-tight sm:text-[32px]">Public API</h1>44 <p className="mt-2 max-w-[780px] text-[14px] leading-relaxed text-ink-2">The website is built on the same API. It is free and rate-limited (120 requests per minute per IP, 4 concurrent SSE connections), returns JSON with UTC ISO-8601 timestamps and plain numbers, and is served with <span className="num">Cache-Control: no-store</span> except long-range history (30 s). Unknown scopes return <span className="num">404 {'{"error":"not_found"}'}</span>. Higher limits, webhooks and historical exports: contact@spboucher.ai.</p>45 </header>4647 <Section label="Quick start">48 <pre className="num overflow-x-auto rounded-[4px] border border-line bg-panel p-4 text-[12px] leading-relaxed text-ink">49 {`# the index right now50curl -s ${SITE_URL}/api/v1/pressure/global | jq '{pressure, level, delta_1h, confidence}'5152# 24 h of global pressure, 1-minute step53curl -s "${SITE_URL}/api/v1/pressure/history?scope_type=global&range=24h" | jq '.summary'5455# a country, an ASN, a service56curl -s ${SITE_URL}/api/v1/pressure/country/CA | jq '.pressure'57curl -s ${SITE_URL}/api/v1/pressure/asn/13335 | jq '.bgp'58curl -s ${SITE_URL}/api/v1/service/cloudflare | jq '{observed, vendor_status, discrepancy}'5960# live stream (SSE)61curl -N ${SITE_URL}/api/v1/live`}62 </pre>63 </Section>6465 <Section label="Endpoints" right={<span>{ENDPOINTS.length} public routes</span>}>66 <div className="scroll-x -mx-3 px-3">67 <table className="tbl min-w-[640px]">68 <thead>69 <tr>70 <th>GET</th>71 <th>Returns</th>72 <th className="hidden md:table-cell">Query</th>73 </tr>74 </thead>75 <tbody>76 {ENDPOINTS.map((e) => (77 <tr key={e.path}>78 <td className="num align-top text-ink">79 <a href={e.path.includes('{') ? undefined : e.path} className={e.path.includes('{') ? '' : 'hover:text-accent'}>80 {e.path}81 </a>82 </td>83 <td className="whitespace-normal align-top text-ink-2">{e.desc}</td>84 <td className="num hidden whitespace-normal align-top text-[11px] text-ink-3 md:table-cell">{e.params ?? ''}</td>85 </tr>86 ))}87 </tbody>88 </table>89 </div>90 </Section>9192 <Section label="Levels & status">93 <div className="grid grid-cols-[minmax(0,1fr)] gap-6 text-[13px] text-ink-2 md:grid-cols-2">94 <p>95 Every <span className="num text-ink">level</span> field is one of <span className="num text-ink">calm ≤10 · normal ≤25 · elevated ≤40 · stressed ≤55 · high ≤70 · severe ≤85 · extreme ≤100</span>; <span className="num text-ink">level_label</span> is its human label.96 </p>97 <p>98 <span className="num text-ink">internal_status</span> is <span className="num">ok</span>, <span className="num">degraded</span> (too few fresh probes, stale BGP or unhealthy stores — the score is frozen and <span className="num">stale: true</span>) or <span className="num">stale</span> (engine has not run). Clients must show a degraded state instead of interpreting a frozen number.99 </p>100 </div>101 </Section>102 </div>103 );104}105