Developer reference
HTTP Endpoint Reference
This reference describes the current handler. It is not a multi-CDN telemetry service or a guarantee of transport protocol, clock synchronization, or network performance.
Origins, timing and scope
For requests with an allowed Origin, the handler echoes that origin in Access-Control-Allow-Origin and varies on Origin. Allowed origins are the HTTPS www and apex VelocityVerify domains, the HTTPS velocityverify.pages.dev host and its deployment subdomains, and HTTP/HTTPS localhost or 127.0.0.1 with an optional port. Other supplied origins receive HTTP 403. Requests without Origin receive no Access-Control-Allow-Origin header from this handler.
The handler does not emit Timing-Allow-Origin. It does not promise wildcard CORS. HTTP request duration includes browser, connection and server effects: it is not an ICMP/UDP probe, packet-loss measurement, or verified ISP benchmark. Read the measurement limits.
Preflight OPTIONS advertises GET, HEAD, POST and OPTIONS, the Content-Type request header, and a max age of 86400 seconds. Individual endpoints still enforce their own methods.
Diagnostic routes
GET or HEAD /api/health
Returns fixed status labels and an epoch-millisecond timestamp. These labels do not actively verify clocks, every route, a database, or a telemetry pipeline.
{"status":"ok","timestamp":0,"services":{"routing":"healthy","static":"healthy","diagnostics":"healthy"},"engine":"VelocityVerify Cloudflare Core v2.0"}
The timestamp above is a placeholder, not an observed response.
GET or HEAD /api/getIP
Returns JSON with processedString and rawIspInfo. The latter is either an object containing organization, country and city or an empty string when provider information is unavailable. Available request/platform information is descriptive, not independent provider verification. No separate ASN field is promised.
{"processedString":"203.0.113.42 - Example city, ZZ (Example provider)","rawIspInfo":{"organization":"Example provider","country":"ZZ","city":"Example city"}}
All identifying values above are illustrative. Do not assume IP or network information is anonymous.
GET, HEAD or POST /api/empty
GET and HEAD return an empty body with no-store caching headers. POST consumes an upload of at most 8 MiB and reports the received byte count in X-Received-Bytes. Larger uploads are rejected. This is an HTTP timing/upload endpoint, not a pristine network-only RTT measurement.
GET or HEAD /api/garbage?ckSize=4
ckSize is parsed as an integer, defaults to 4 and is clamped to 1–8 MiB (1 MiB = 1,048,576 bytes). HEAD describes the payload without downloading it. GET uses an optional R2 object if configured and present, otherwise a generated-byte fallback. No compression-resistance or concurrency performance guarantee is made.
GET or HEAD /api/test-config
Returns configured endpoint paths and payload limits. It is configuration, not a live measurement or service-health guarantee.
POST /api/results
Send JSON with the short keys below. dl and ul are Mbps; ping and jitter are milliseconds. Values must parse as finite numbers and are clamped to 0–100000 for throughput and 0–60000 for timing. ISP text is sanitized and limited to 80 characters. Malformed bodies or missing metrics return HTTP 400; bodies over 64 KiB return HTTP 413.
{"dl":384.2,"ul":41.5,"ping":14,"jitter":2,"isp":"Fixture provider"}
A successful response contains status: "success", a generated transactionId, an epoch-millisecond timestamp, metrics with the same short keys, and databaseLogged. A successful request is not proof of an independently verified measurement. Letter grades and packet-loss measurements are not part of this schema.
Persistence is optional: the handler attempts to write these fields to Supabase only when SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY are configured. Without those bindings, the response has databaseLogged: false. Do not infer an active global index or anonymous aggregation from this endpoint. Provider infrastructure may separately process request information; consult the privacy policy.
POST /api/subscribe
Accepts a JSON email field. Without a working database configuration, subscription storage is unavailable and returns HTTP 503. The handler does not implement a double-opt-in email flow or a retention schedule; do not present an attempted request as a confirmed subscription.
Best-effort request limits
Current per-IP counters live in memory in each running isolate and use 60-second windows. They are not distributed quotas, durable bans, or guaranteed abuse protection. HTTP 429 includes Retry-After when a bucket is exceeded.
- Garbage: 600 requests per window.
- Empty GET/HEAD: 1200; empty POST: 240.
- Results: 10; subscribe: 5.
- Health, getIP and test-config have no explicit rate-limit bucket in this handler.
There is no implemented 500 MB per-origin quota or ten-test-per-fifteen-minute allowance. Avoid unattended bandwidth tests and obtain permission before testing a shared connection.