Usage and metrics in the Aetherfy vector API
The Aetherfy vector API answers two analytics reads. GET /api/v1/analytics/usage
reports what you are storing against your plan. GET /api/v1/analytics/metrics
reports request telemetry that Aetherfy actually measured. Both take the same
Bearer API key as every other data-plane call.
Read the two rules below before you build anything on /metrics. They are the
difference between a dashboard that reports what happened and one that quietly
invents it.
The Aetherfy zero rule: null is not zero
An empty counter result means one of two very different things — you made no requests, or nothing was counting. Aetherfy separates them rather than letting you guess.
A producer writes a heartbeat row on every flush, whether or not there was
traffic. coverage.fresh reports whether the collectors behind your window are all
reporting recently.
coverage.fresh | What the numbers mean |
|---|---|
true | Attested. A 0 is a measured zero: that thing did not happen |
false | Not attested. Every value is unknown, including the ones that look like numbers |
When coverage.fresh is false the Aetherfy API sets every field in totals
to null and returns empty arrays — it does not return zeros. A client must
render that state as no-data. Showing 0 requests for an unobserved window is
the exact failure this endpoint was built to avoid, and it is indistinguishable
from a genuinely quiet week to everyone except the person paying for it.
coverage.regions carries per-region freshness, so a client that wants to say
“two of three regions reporting” can. The top-level flag is an AND across all of
them: one dark region must not let its absent rows render as real zeros.
A failed read is an error response, never a body of zeros.
Aetherfy percentiles are histogram upper bounds, not interpolations
p50_ms and p95_ms are not interpolated percentiles. Aetherfy keeps eight
fixed latency buckets, and the reported figure is the smallest bucket bound whose
cumulative count reaches the rank.
The bounds are 5, 10, 25, 50, 100, 250 and 1000 milliseconds, plus an unbounded bucket above 1000.
So p95_ms: 100 means at least 95% of requests finished within 100 ms. It
does not mean the 95th percentile was 100 ms. Treat every value as an upper
bound and never subtract two of them to claim a change.
When the rank falls into the unbounded bucket above 1000 ms there is no honest
bound to report, and Aetherfy returns null rather than inventing one. That is
what max_ms is for: it is exact, not a bound, and it is the number to read when
a percentile is null.
max_ms is also null when nothing was measured. With a fresh heartbeat and
zero requests, the counts are 0 and all three latency fields are null —
there is no p50 of nothing, and 0 ms would be a fabrication.
Aetherfy measures latency inside the API process
Every duration these endpoints report is measured server-side, inside the Aetherfy API process. It excludes your network path, TLS negotiation, and any time spent in your own client.
The latency your users experience is therefore always larger than what
/metrics reports, and the gap is mostly geography. If you need the number your
application actually feels, time your own calls at your own call site — that is
the only figure that includes everything between you and Aetherfy.
Reading Aetherfy plan usage: GET /api/v1/analytics/usage
Returns storage and collection usage against your plan. No parameters.
| Field | Type | Meaning |
|---|---|---|
storage_bytes_used | integer | Bytes stored across every active collection |
storage_limit_bytes | integer or null | Plan storage limit. null means unlimited |
collections_count | integer | Active collections |
collections_limit | integer or null | Plan collection limit. null means unlimited |
tier | string | Your plan |
active_regions | string array | The regions your collections occupy |
usage_percentage | integer | Storage used as a percentage of the limit; 0 when there is no limit |
Both limit fields use null for unlimited. Not 0, not -1, not a string
— one sentinel, the same for each. A client that treats null as 0 will show
an account on an unlimited plan as being permanently over quota.
active_regions is the union of every active collection’s regions, so it
reflects where your data actually is. Region placement is plan-scoped in
Aetherfy: Free and Starter collections occupy a single region, and replication
across more than one region begins at the plan named Performance. On a
single-region plan this array holds exactly one entry, and it is empty when the
account has no active collections.
curl -s https://vectors.aetherfy.com/api/v1/analytics/usage \
-H "Authorization: Bearer $AETHERFY_API_KEY"Reading Aetherfy request telemetry: GET /api/v1/analytics/metrics
Returns measured request telemetry for your account. Every number is a sum over counter rows written after observing a real response.
window accepts 24h (default), 7d or 30d. The first two bucket by hour;
30d buckets by UTC day, because 720 hourly points is a chart nobody can read.
Any other value returns 400, and the Aetherfy error body names the offending
parameter and the accepted set:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "window must be one of 24h, 7d, 30d",
"field": "window",
"allowed": ["24h", "7d", "30d"]
}
}field and allowed sit directly on the error object, so a client can branch on
them without parsing the message.
The response carries seven top-level keys.
| Key | Contents |
|---|---|
window | The resolved name, from, to and bucket, from one server clock read |
coverage | fresh, stale_after_seconds, last_flush_at, last_record_at, regions[] |
totals | requests, errors_4xx, errors_429, errors_5xx, p50_ms, p95_ms, max_ms |
series | One entry per bucket: bucket_start, the four counts, and p95_ms |
by_op | Per operation: search, upsert, retrieve, delete, collections, other |
by_collection | Top 25 collections by requests; requests carrying no collection are excluded |
by_region | The Aetherfy region that served each request |
window is echoed back rather than left for you to re-derive. A client
recomputing the boundaries from its own clock would disagree with the server by
its own skew plus the request’s flight time, and would have no way to tell that
apart from a real gap in the data.
Every field in totals is nullable, and null never means zero — see the zero
rule above.
curl -s "https://vectors.aetherfy.com/api/v1/analytics/metrics?window=7d" \
-H "Authorization: Bearer $AETHERFY_API_KEY"Reading Aetherfy usage from the SDKs
Both Aetherfy SDKs expose the usage endpoint as a method. Neither wraps
/metrics — call it over REST.
The full field list and the SDKs’ shared return shape are on the SDK reference. The machine-readable contract for both endpoints is in the OpenAPI index, and the error codes are on the errors page.