This guide helps integrators handle StellarRoute API errors in production clients, SDKs, and UIs.
| Error code | HTTP status | Action | Retry guidance |
|---|---|---|---|
bad_request |
400 | Fail fast | Do not retry; fix the request payload. |
invalid_asset |
400 | Fail fast | Do not retry; validate asset identifiers before retrying. |
validation_error |
400 | Fail fast | Do not retry; present validation messages and correct inputs. |
not_found |
404 | Fail fast | Do not retry; the resource is missing or the pair is unsupported. |
no_route |
404 | Fail fast | Do not retry immediately; the market is currently untradeable. |
stale_market_data |
422 | Retry with refresh | Refresh market data and retry after a short delay. |
rate_limit_exceeded |
429 | Retry with backoff | Retry after Retry-After / reset header or exponential backoff. |
overloaded |
503 | Retry with backoff | Retry after a longer delay and stop after a few attempts. |
internal_error |
500 | Retry carefully | Retry once or twice for transient failures, but fail fast if repeated. |
- Prefer
Retry-Afterwhen the API returns it. - Otherwise, use an exponential backoff sequence:
1s,2s,4s. - Limit retries to a small fixed number (e.g. 3 attempts) to avoid traffic bursts.
- Example: if you receive
X-RateLimit-Reset, schedule the next attempt for that reset time.
- Treat this as temporary operational backpressure.
- Wait
1sthen retry, then increase to2s,4s, and stop after 3–4 attempts. - If the service remains overloaded, surface a safe fallback path instead of retrying endlessly.
- Consider a short early retry for transient failures, but avoid retry loops.
- Example: retry once after
500msand give up if the same error persists.
- This error means the quote could not be fulfilled because the API's market snapshot was stale.
- Integrators should refresh their quote input and retry after a short pause.
- Example flow:
- Detect
stale_market_data. - Re-fetch the latest available pair/orderbook data if applicable.
- Retry the quote request after
500msto1s. - Fail gracefully if the error repeats more than 2–3 times.
- Detect
Note:
stale_market_datais not the same as an invalid request. It is a transient market freshness issue.
Use SDK helpers to branch on API errors cleanly.
import {
StellarRouteClient,
StellarRouteApiError,
isStellarRouteApiError,
} from '@stellarroute/sdk-js';
const client = new StellarRouteClient({ baseUrl: 'https://api.stellarroute.io' });
try {
await client.getQuote('native', 'USDC', 100);
} catch (err) {
if (!isStellarRouteApiError(err)) {
console.error('Unexpected failure', err);
throw err;
}
if (err.isValidationError()) {
console.warn('Invalid quote request:', err.message);
} else if (err.isStaleMarketData()) {
console.warn('Market data stale, retrying...');
// Refresh market data and retry after a short delay.
} else if (err.isRateLimited()) {
console.warn('Request was rate limited, backoff and retry later.');
} else if (err.isOverloaded()) {
console.warn('Service overloaded, retry with backoff or degrade gracefully.');
} else if (err.isNotFound()) {
console.warn('Pair not available.');
} else {
console.error('API error:', err.code, err.message);
}
}err.isNotFound()— 404 /not_founderr.isValidationError()— 400 /validation_errororinvalid_asseterr.isRateLimited()— 429 /rate_limit_exceedederr.isOverloaded()— 503 /overloadederr.isStaleMarketData()— 422 /stale_market_dataerr.isNetworkError()— network failure / timeout
Use the typed SdkError enum and the ApiErrorCode values.
use stellarroute_sdk::{ApiErrorCode, ClientBuilder, QuoteRequest, QuoteType, SdkError};
let client = ClientBuilder::new("http://127.0.0.1:3000").build()?;
match client.quote(QuoteRequest::sell("native", "USDC")).await {
Ok(quote) => println!("price: {}", quote.price),
Err(err) => match err {
SdkError::RateLimited { info } => {
println!("rate limited until {:?}", info.reset);
}
SdkError::Api { code, message, status } => {
if code == ApiErrorCode::StaleMarketData {
println!("market data stale, refresh and retry");
} else if code == ApiErrorCode::Overloaded {
println!("service overloaded, try again later");
} else if code == ApiErrorCode::NotFound {
println!("pair not found");
} else {
println!("api error {} {}", status, message);
}
}
SdkError::Http(_) => eprintln!("transport failure"),
SdkError::InvalidConfig(_) => eprintln!("invalid SDK config"),
SdkError::Deserialization(_) => eprintln!("invalid response payload"),
},
}err.is_not_found()err.is_validation_error()err.is_rate_limited()err.is_stale_market_data()err.is_overloaded()
{ "error": "bad_request", "message": "Malformed request" }{
"error": "invalid_asset",
"message": "Invalid asset identifier",
"details": { "asset": "USDC:INVALID" }
}{
"error": "validation_error",
"message": "Amount must be a positive integer",
"details": { "field": "amount", "reason": "must be greater than zero" }
}{ "error": "not_found", "message": "Trading pair not found" }{ "error": "no_route", "message": "No trading route available for this pair" }{
"error": "stale_market_data",
"message": "Quote data is too old",
"details": { "last_updated": "2026-05-31T14:33:00Z" }
}{
"error": "rate_limit_exceeded",
"message": "Too many requests",
"details": { "retry_after_seconds": 10 }
}{ "error": "overloaded", "message": "Service overloaded, please retry later" }{ "error": "internal_error", "message": "Unexpected server failure" }Integrators should pin an explicit API version path, for example /api/v1/....
Follow the release and deprecation headers described in API Versioning and Deprecation Policy.
Frontend teams should translate API error codes into trader-facing copy instead of exposing raw codes. If there is an active design error copy issue, use it as the source of truth for messaging.
- Use
rate_limit_exceeded/overloadedto show polite retry messaging. - Use
stale_market_datato show a refresh or retry prompt with minimal friction. - Use
validation_errorandinvalid_assetto surface actionable input corrections. - Use
not_foundandno_routeto explain that the requested market is unavailable.