This guide covers latency expectations, cache TTL tuning, Horizon dependency optimization, and how to identify performance bottlenecks in a production StellarKit deployment.
See also:
- Monitoring Guide — alert thresholds, tool integrations, and the full monitoring checklist
- Observability Guide — cache stats, request tracing, and log queries
- Caching Strategy — full per-endpoint TTL reference
- Latency Expectations
- Cache TTL Tuning
- Horizon Dependency Optimization
- Identifying Bottlenecks
- Scaling Recommendations
Response times in StellarKit API depend on whether a response is served from cache or requires a live Horizon call.
| Request type | Expected P50 | Expected P95 | Notes |
|---|---|---|---|
| Cache hit | < 5 ms | < 20 ms | Pure in-memory lookup |
| Horizon single-call miss | 200–800 ms | 1 500 ms | Public Horizon latency varies |
| Horizon multi-call miss | 500–2 000 ms | 3 000 ms | Endpoints like /account/:id/summary fan out to several Horizon requests |
| Soroban RPC call | 300–1 500 ms | 3 000 ms | Depends on RPC provider |
Alert thresholds (P95 > 2 000 ms warning, P99 > 5 000 ms critical) are defined in the Monitoring Guide.
The cache is the primary lever for controlling Horizon load and improving response times. See Caching Strategy for a complete per-endpoint TTL reference.
- High-traffic deployments: Increase TTLs for stable data (validators, asset metadata, account balances) to reduce redundant Horizon calls. A 2–5× increase from defaults is reasonable.
- Low-latency requirements: Keep fee estimate and price TTLs short (≤ 5 seconds). These endpoints change with every ledger close.
- Debugging stale data: Use
?fresh=trueon individual requests to bypass cache without changing TTLs globally.
# Check current hit rate
curl -s https://your-api.example.com/cache/stats | jq '.data.hitRate'A sustained hit rate below 0.70 in production means TTLs may be too short. Increase the relevant CACHE_TTL_*_MS variables and monitor whether the hit rate recovers. See Observability Guide for details on the /cache/stats endpoint.
# .env — production TTL overrides for high-traffic deployment
CACHE_TTL_NETWORK_STATUS_MS=10000 # 10s (default: 5s)
CACHE_TTL_BASE_FEE_MS=10000 # 10s (default: 5s)
CACHE_TTL_FEE_ESTIMATE_MS=10000 # 10s (default: 5s)
CACHE_TTL_ASSET_MS=60000 # 60s (default: 30s)
CACHE_TTL_ACCOUNT_MS=20000 # 20s (default: 10s)
CACHE_TTL_VALIDATORS_MS=600000 # 10 min (default: 5 min)The Stellar Horizon API is StellarKit's only upstream dependency. Its performance directly determines your worst-case response times.
| Option | Latency | Rate limits | Cost |
|---|---|---|---|
SDF public Horizon (horizon.stellar.org) |
200–800 ms | Shared, can throttle under load | Free |
| Private Horizon instance | 50–200 ms (co-located) | Dedicated | Infrastructure cost |
For high-throughput production workloads, running a private Horizon instance and setting HORIZON_URL to point at it significantly reduces tail latency and eliminates shared rate-limit risk.
Some StellarKit endpoints make multiple sequential or parallel Horizon requests per call (e.g. /account/:id/summary, /account/:id/risk-score). For these:
- Set longer cache TTLs so the fan-out only happens on cache misses.
- Avoid calling them in tight loops — batch data at the application layer instead.
- Use the dedicated single-purpose endpoints (e.g.
/account/:id/balancesinstead of the full summary) when you only need a subset of the data.
# Top 10 slowest endpoint paths by average response time
cat production.ndjson \
| jq 'select(.responseTimeMs != null) | {path: (.path | split("?")[0]), responseTimeMs}' \
| jq -s 'group_by(.path) | map({path: .[0].path, avg_ms: (map(.responseTimeMs) | add / length), count: length}) | sort_by(-.avg_ms) | .[0:10]'Check the X-Cache response header on slow requests:
X-Cache: HIT— the slow response came from cache; investigate application processing overhead.X-Cache: MISS— the slow response required a Horizon call; investigate Horizon latency.
curl -I https://your-api.example.com/network-status | grep X-CacheIf StellarKit is slow on cache misses, verify Horizon response time independently:
time curl -s https://horizon.stellar.org/ > /dev/nullIf Horizon itself is slow, check https://stellar.statuspage.io and consider routing to a private Horizon instance.
Sustained high CPU or a blocked event loop will cause all requests to slow uniformly, even cache hits. Symptoms:
- All endpoints slow, not just Horizon-dependent ones.
responseTimeMshigh even for/health.- Node.js process at high CPU in
pm2 monitordocker stats.
Use clinic.js or a Node.js APM (Datadog APM, New Relic) to profile the event loop.
- Default
max_memory_restartin the PM2 config is1G. If heap regularly exceeds 800 MB, increase instance memory before raising this limit. - StellarKit is I/O-bound (Horizon calls), not CPU-bound. Adding CPU cores helps concurrency but won't fix Horizon latency.
StellarKit is stateless — no session or shared state between instances. Scale out freely:
# PM2 cluster mode (uses all CPU cores)
pm2 start ecosystem.config.js --env production
# ecosystem.config.js: instances: 'max', exec_mode: 'cluster'
# Kubernetes: increase replica count
kubectl scale deployment stellarkit-api --replicas=4Note: Each instance has its own in-memory cache. With multiple instances, cache hit rates per-instance will be lower than with a single instance, but aggregate throughput increases. If cache coherence across instances is important, consider replacing the default in-memory cache with a shared Redis cache.
The default per-IP rate limits (RATE_LIMIT_MAX, RATE_LIMIT_WINDOW_MS) are tracked in-memory per instance. In a multi-instance deployment behind a load balancer, a single client's requests may hit different instances, effectively multiplying their effective rate limit. If consistent rate limiting across instances is required, use a Redis-backed rate limiter.
- Monitoring Guide — Alert thresholds and tool integrations
- Observability Guide — Cache stats endpoint and log queries
- Caching Strategy — Full per-endpoint TTL reference and
?fresh=trueusage - Deployment Guide — PM2 cluster mode, Docker, and Kubernetes configuration
- Rate Limiting — Rate limit configuration and retry strategies