Security Accelerated

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.

https://api.securityaccelerated.com v0.74.1
support@securityaccelerated.com

Usage Statistics

GET /v1/usage

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

NameInTypeDescription
days Query int Days of history to aggregate, 1365. 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

FieldTypeDescription
total_requestsintRequests you made in the period, counting every status code
cache_hitsintIndividual lookups served from cache
cache_missesintIndividual lookups fetched live
cache_hit_ratefloatcache_hits over the sum of both, 01
avg_latency_msfloatMean server-side response time in milliseconds
total_domainsintDomains submitted across all requests, counting duplicates
total_ipsintIPs submitted across all requests, counting duplicates
status_codesobjectRequest counts keyed by HTTP status code
feature_usageobjectCounts keyed by lookup name — see below
requests_by_pathobjectRequest counts keyed by endpoint path
period_startstringStart of the window, RFC 3339
period_endstringEnd 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.

Usage is recorded asynchronously and flushed in batches, so a request you made moments ago may not appear yet. Everything older than a few seconds is complete.

Key Rotation

POST /v1/keys/rotate

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

FieldTypeDescription
api_keystringThe new key, 64 hex characters. Shown exactly once
old_keys_expire_atstringWhen every previous key stops authenticating, RFC 3339
messagestringA reminder of both of the above, for logs and CLI output
The new key is returned in this response and never again. It is stored as a hash, so nobody — including support — can recover it for you. If you lose it before the grace window ends, rotate again using a key that still works; if you lose it after, contact support to have the account re-keyed.

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:

  1. Call POST /v1/keys/rotate and store api_key in your secret manager.
  2. Deploy it everywhere that talks to the API — every service, job runner, and CI secret.
  3. Confirm the rollout before old_keys_expire_at. A single lookup with the new key is enough.
  4. At old_keys_expire_at the previous keys stop working. Anything still holding one starts receiving 401.

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.

Suspect a key is compromised? Rotate, deploy, and then contact support@securityaccelerated.com to have the grace window cut short. Until it ends, the exposed key still works — that is the cost of not breaking deployed clients, and it is the one case where you want it shortened.

Error Handling

StatusEndpointMeaning
400 Bad Request/v1/usagedays is not an integer between 1 and 365
401 UnauthorizedBothMissing or invalid API key — including a previous key after its grace window ended
429 Too Many RequestsBothRate limit exceeded — check the Retry-After header
500 Internal Server ErrorBothServer error — contact support@securityaccelerated.com
503 Service UnavailableBothThe 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.