Service level
The targets
Measured on the API host, continuously
| Target | Objective | Measured as |
|---|---|---|
| p95 latency | ≤ 200 ms | Server-side response time per endpoint, 95th percentile |
| p99 latency | ≤ 500 ms | As above, 99th percentile — the tail that users actually feel |
| Availability | ≥ 99.9 % | Share of probe requests answered 2xx, sampled per probe run on the API host |
| Price freshness | ≤ 30 s | Age of observed_at on /v1/price/tip at read time — the rolling-window surface |
Freshness is the target most services quietly omit. A price endpoint that answers in 5 ms with a four-hour-old number is fast and useless, so staleness is bound explicitly rather than left to the latency figure to imply. The 30-second bound binds /v1/price/tip. /v1/price deliberately serves the last closed one-minute bucket so that every replay and every region returns byte-identical values, which makes its observed_at 30–150 s old by construction; it is held to that structural 150-second bound instead, and a breach of it means the closed-bucket pipeline has fallen behind its design. The one answer outside that bound is a price_type of peg: an operator-declared 1:1 constant served only when no market has priced the pair, whose observed_at is the declaration's adoption stamp, not an observation age. Read /v1/price/tip when you need the tightest freshness and /v1/price when you need reproducibility. /v1/price/tip's 30-second bound is a rolling-window target, not a hard cap: when a pair has no trade in that window, the endpoint falls back to the most recent observation it has and serves it as-is — with no synthetic age ceiling and flags.stale left false, since that fallback is in-contract per ADR-0018. Read observed_at on the response to know how old that particular answer is.
Error budget
What 99.9 % actually permits
A 99.9 % monthly availability objective is an allowance of roughly 43 minutes 12 seconds of unavailability in a 30-day month. That is the budget. It is spent by incidents, by planned maintenance that takes the API down, and by failed deploys alike — the budget does not distinguish between causes, because your integration does not either.
- Budget spent
- When an incident closes, its unavailable minutes are debited. A month that exhausts the budget triggers a freeze on non-essential change until the following month.
- Budget intact
- Normal change cadence. Deploys are gated on the post-deploy verification battery, not on the budget.
How it is measured
An on-host probe — and what it therefore cannot see
A dedicated probe binary samples the public endpoints on a schedule and records per-endpoint p50/p95/p99 latency, availability, and — for the price endpoints — freshness computed from the response's own observed_at timestamp. It writes results as metrics, so a target breach is visible as a number rather than discovered from a support ticket.
The probe is a separate process from the API, but it runs on the API host and calls the service's own listener directly. Its latency figures are therefore application time, isolated from network, TLS and reverse-proxy time — which is what makes them useful for spotting a regression in our code, and what makes them lower than the number you will measure from your own client.
The honest consequence: this probe does not measure edge availability. It cannot detect a reverse-proxy failure, an expired certificate, a DNS fault or a datacentre-network outage — most of what an availability objective exists to catch. Those are covered today only by an external dead-man's-switch that alerts when the host stops reporting, which detects a hard outage but does not produce an availability percentage. No monthly availability figure is published yet, and we will not publish one computed this way. An independent off-host probe is the work that makes the availability row above a measurement rather than an objective; until it ships, treat the latency numbers as application-level and the availability target as a commitment we have not yet instrumented end-to-end.
What this does not cover
Stated plainly, because an unstated exclusion is a broken promise
- Upstream outages
- When the Stellar network itself halts or an upstream venue stops publishing, we serve the last verified data and mark it stale. Freshness targets are measured against our pipeline, not the network’s liveness.
- Historical backfills
- Bulk historical queries and re-derivations are batch operations with no latency objective. The targets bind the serving path.
- Single-region posture
- The service runs from one region today. That is a documented, accepted risk with a tested restore path — not a redundancy claim.
Coverage and completeness are a separate, stronger guarantee than availability, and they are reported independently — see diagnostics for the live per-source verdict and methodology for how prices are computed.
Live status
Current state, not a claim about it
The status page carries current availability and any open incident. Past incidents are published with their timeline and root cause rather than summarised away.