Documentation
Account
Every plan can inspect its own usage and rotate its own API key without contacting support. Both endpoints are scoped to the caller — the customer is taken from the API key, never from the request, so a key can only ever see and change its own account.
Usage Statistics
Aggregated usage for the authenticated customer over a rolling window ending now. Useful for tracking spend against your plan's rate limit, spotting which lookups you actually depend on, and confirming that your cache hit rate is what you expect.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
days |
Query | int |
Days of history to aggregate, 1–365. Defaults to 30. Anything outside that range returns 400. |
Example
curl 'https://api.securityaccelerated.com/v1/usage?days=7' \
-H 'X-API-Key: your-api-key'
const res = await fetch(
'https://api.securityaccelerated.com/v1/usage?days=7',
{ headers: { 'X-API-Key': process.env.API_KEY } }
);
const usage = await res.json();
console.log(usage.total_requests, usage.cache_hit_rate);
import os, requests
res = requests.get(
"https://api.securityaccelerated.com/v1/usage",
params={"days": 7},
headers={"X-API-Key": os.environ["API_KEY"]},
)
usage = res.json()
print(usage["total_requests"], usage["cache_hit_rate"])
package main
import (
"encoding/json"
"fmt"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET",
"https://api.securityaccelerated.com/v1/usage?days=7", nil)
req.Header.Set("X-API-Key", os.Getenv("API_KEY"))
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
var usage struct {
TotalRequests int `json:"total_requests"`
CacheHitRate float64 `json:"cache_hit_rate"`
}
json.NewDecoder(resp.Body).Decode(&usage)
fmt.Println(usage.TotalRequests, usage.CacheHitRate)
}
Response
{
"total_requests": 1284,
"cache_hits": 3907,
"cache_misses": 1130,
"cache_hit_rate": 0.776,
"avg_latency_ms": 412.8,
"total_domains": 2140,
"total_ips": 96,
"status_codes": { "200": 1270, "429": 14 },
"feature_usage": { "dns": 2140, "ssl": 2140, "whois": 757 },
"requests_by_path": { "/v1/domain/lookup": 1188, "/v1/ip/lookup": 96 },
"period_start": "2026-08-03T00:00:00Z",
"period_end": "2026-08-10T00:00:00Z"
}
Response Fields
| Field | Type | Description |
|---|---|---|
total_requests | int | Requests you made in the period, counting every status code |
cache_hits | int | Individual lookups served from cache |
cache_misses | int | Individual lookups fetched live |
cache_hit_rate | float | cache_hits over the sum of both, 0–1 |
avg_latency_ms | float | Mean server-side response time in milliseconds |
total_domains | int | Domains submitted across all requests, counting duplicates |
total_ips | int | IPs submitted across all requests, counting duplicates |
status_codes | object | Request counts keyed by HTTP status code |
feature_usage | object | Counts keyed by lookup name — see below |
requests_by_path | object | Request counts keyed by endpoint path |
period_start | string | Start of the window, RFC 3339 |
period_end | string | End of the window, RFC 3339 |
How Counting Works
feature_usage counts the lookups you requested, however they were served. A lookup answered from cache still counts, because otherwise caching would make a feature you depend on look unused.
cache_hits and cache_misses count individual lookups, not requests. That is why they routinely exceed total_requests: one request naming six lookups across three domains contributes eighteen to their sum and one to total_requests.
Key Rotation
Issues a fresh API key and starts a grace window on every key you held before it. The request takes no body. Rotate on a schedule, when someone with access leaves, or immediately if a key may have been exposed.
Example
curl -X POST https://api.securityaccelerated.com/v1/keys/rotate \
-H 'X-API-Key: your-current-api-key'
Response
{
"api_key": "3f9a1c... (64 hex characters)",
"old_keys_expire_at": "2026-08-11T14:22:05Z",
"message": "store this key now — it cannot be retrieved again; previous keys stop working at old_keys_expire_at"
}
Response Fields
| Field | Type | Description |
|---|---|---|
api_key | string | The new key, 64 hex characters. Shown exactly once |
old_keys_expire_at | string | When every previous key stops authenticating, RFC 3339 |
message | string | A reminder of both of the above, for logs and CLI output |
Rotating Safely
The grace window exists so that rotation is not an outage. Both the old and new keys authenticate until old_keys_expire_at, which gives you time to deploy:
- Call
POST /v1/keys/rotateand storeapi_keyin your secret manager. - Deploy it everywhere that talks to the API — every service, job runner, and CI secret.
- Confirm the rollout before
old_keys_expire_at. A single lookup with the new key is enough. - At
old_keys_expire_atthe previous keys stop working. Anything still holding one starts receiving401.
The grace window is 24 hours. Rotating again during one issues another key and starts a fresh window on the key you just replaced — but it never pushes back an expiry that is already set. Keys grace-expired by an earlier rotation keep their original deadline, so repeated rotation cannot be used to keep an exposed key alive.
Error Handling
| Status | Endpoint | Meaning |
|---|---|---|
400 Bad Request | /v1/usage | days is not an integer between 1 and 365 |
401 Unauthorized | Both | Missing or invalid API key — including a previous key after its grace window ended |
429 Too Many Requests | Both | Rate limit exceeded — check the Retry-After header |
500 Internal Server Error | Both | Server error — contact support@securityaccelerated.com |
503 Service Unavailable | Both | The feature is not configured on this deployment. Not expected in production |
All error responses use the same envelope as the rest of the API:
{
"error": "description of the problem"
}
Both endpoints count against your plan's rate limit like any other request, and both echo X-Request-ID and your X-Client-Request-ID. See Request Correlation for the header contract.