When things go wrong with your API requests (and they will), the Sprinklr API gives you detailed information about what happened and how to fix it. This guide shows you how to handle errors gracefully and build resilient integrations.
No integration is perfect. Networks fail, credentials expire, users request resources that don't exist, and rate limits get hit. The difference between a good integration and a great one is how it handles these situations.
Bad error handling:
- Your application crashes
- Users see cryptic error messages
- You have no idea what went wrong
- The same error keeps happening
Good error handling:
- Your application stays running
- Users get helpful messages
- Errors are logged for debugging
- Your code automatically recovers when possible
Let's build good error handling.
Every error response from the Sprinklr API follows the same structure, making it easy to handle errors consistently across your application.
{
"success": false,
"errors": [
{
"code": "NOT_FOUND",
"message": "Account with ID 600057112 not found",
"field": "accountId"
}
]
}Let's break this down:
success: false This tells you immediately that something went wrong. Check this field before trying to access data.
errors array Contains one or more error objects. You might get multiple errors if several things went wrong (e.g., multiple invalid parameters).
code A machine-readable error code you can programmatically handle. Always the same for a given error type.
message A human-readable explanation of what went wrong. Good for logging and debugging.
field (optional) When an error relates to a specific request parameter, this tells you which one. Useful for validation errors.
The API also uses standard HTTP status codes to categorize errors:
| Status | Name | What it means | Your action |
|---|---|---|---|
| 200 | OK | Request succeeded | Process the data |
| 201 | Created | Resource created | Process the new resource |
| 400 | Bad Request | Invalid parameters | Fix your request |
| 401 | Unauthorized | Authentication failed | Check credentials |
| 403 | Forbidden | No permission | Check access rights |
| 404 | Not Found | Resource doesn't exist | Verify the ID |
| 429 | Too Many Requests | Rate limited | Slow down, retry later |
| 500 | Server Error | Sprinklr error | Retry after delay |
| 503 | Service Unavailable | Temporary outage | Retry with backoff |
This happens when a parameter is missing, malformed, or invalid.
Example:
{
"success": false,
"errors": [
{
"code": "INVALID_PARAMETER",
"message": "accountId must be a valid Sprinklr account ID",
"field": "accountId"
}
]
}Common causes:
Typo in parameter name
// ❌ Wrong { accoundId: "600057112" } // Typo: "accound" // ✅ Correct { accountId: "600057112" }Wrong data type
// ❌ Wrong { accountId: 600057112 } // Number instead of string // ✅ Correct { accountId: "600057112" } // StringInvalid format
// ❌ Wrong { startTime: "2026-02-11" } // Missing time and timezone // ✅ Correct { startTime: "2026-02-11T00:00:00Z" } // Full ISO 8601Missing required parameter
// ❌ Wrong - missing accountId GET /api/v3/account // ✅ Correct GET /api/v3/account?accountId=600057112
How to fix it:
- Check the error's
fieldproperty to see which parameter is wrong - Verify the parameter name spelling
- Check the API reference for the correct data type and format
- Validate your data before sending the request
The ID you requested doesn't exist or you don't have access to it.
Example:
{
"success": false,
"errors": [
{
"code": "NOT_FOUND",
"message": "Account with ID 999999999 not found"
}
]
}Common causes:
Wrong ID
- Copy-paste error
- ID from a different environment (dev vs production)
- Old ID from a deleted resource
Resource was deleted
- The account was removed from Sprinklr
- The case was archived
- The message was deleted
No access to the resource
- It belongs to a different Sprinklr instance
- Your credentials don't have permission
- It's in a workspace you can't access
How to fix it:
- Double-check the ID is correct
- Verify the resource exists in your Sprinklr dashboard
- Check you're using the right environment (QA vs Production)
- Confirm your credentials have access to this resource
Handling NOT_FOUND gracefully:
async function getAccountSafely(accountId) {
try {
const response = await getAccount(accountId);
return response.data;
} catch (error) {
if (error.code === 'NOT_FOUND') {
console.log(`Account ${accountId} doesn't exist or was deleted`);
return null; // Return null instead of crashing
}
throw error; // Re-throw other errors
}
}Your credentials are missing, expired, or invalid.
Example:
{
"success": false,
"errors": [
{
"code": "UNAUTHORIZED",
"message": "Invalid or missing authentication token"
}
]
}Common causes:
Forgot to include credentials
# ❌ Missing headers curl "https://api-v3.sprinklr.com/api/v3/account?accountId=123" # ✅ Correct curl "https://api-v3.sprinklr.com/api/v3/account?accountId=123" \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Key: YOUR_API_KEY"Typo in credentials
- Extra space in the token
- Missing
Bearerprefix - Copied partial token
Expired token
- Access tokens can expire
- Generate a new one in Sprinklr settings
Token was revoked
- Someone regenerated the token
- Admin revoked your access
How to fix it:
- Verify both Authorization and Key headers are present
- Check for typos (especially spaces or line breaks)
- Ensure the Bearer prefix is there:
Bearer YOUR_TOKEN - Try generating a new access token
- Test with a simple request to isolate the issue
Prevention:
// Store credentials in environment variables
const ACCESS_TOKEN = process.env.SPRINKLR_ACCESS_TOKEN;
const API_KEY = process.env.SPRINKLR_API_KEY;
// Validate before making requests
if (!ACCESS_TOKEN || !API_KEY) {
throw new Error('Missing Sprinklr credentials');
}Your credentials are valid, but you don't have permission for this operation.
Example:
{
"success": false,
"errors": [
{
"code": "FORBIDDEN",
"message": "You do not have permission to access this resource"
}
]
}Common causes:
Accessing another account's data
- The account ID belongs to a different Sprinklr instance
- You're trying to access a competitor's data (obviously won't work!)
Missing permissions
- Your Sprinklr user lacks the required role
- The endpoint requires Admin access
- Your API credentials have restricted scope
Environment mismatch
- Using production credentials against QA
- Using QA credentials against production
How to fix it:
- Verify the resource belongs to your Sprinklr instance
- Contact your Sprinklr admin to check your permissions
- Confirm you're using credentials from the right environment
- Check if the endpoint requires special permissions
The difference between 401 and 403:
- 401 UNAUTHORIZED: "I don't know who you are" (authentication problem)
- 403 FORBIDDEN: "I know who you are, but you can't do that" (authorization problem)
You've made too many API calls in a short time period.
Example:
{
"success": false,
"errors": [
{
"code": "RATE_LIMIT_EXCEEDED",
"message": "You have exceeded the rate limit. Please try again in 60 seconds."
}
]
}Why rate limits exist: Rate limits ensure fair usage and protect the API infrastructure. Without them, one misbehaving integration could slow down the API for everyone.
What to do when rate limited:
Stop making requests immediately Don't keep retrying - you'll just stay rate limited longer
Check the Retry-After header The response includes a
Retry-Afterheader telling you how long to waitImplement exponential backoff Wait progressively longer between retries
Optimize your integration
- Use batch endpoints (50 resources at once)
- Cache frequently accessed data
- Reduce polling frequency
Handling rate limits properly:
async function fetchWithRateLimit(fetchFunction) {
const maxRetries = 3;
let retries = 0;
while (retries < maxRetries) {
try {
return await fetchFunction();
} catch (error) {
if (error.code === 'RATE_LIMIT_EXCEEDED') {
retries++;
if (retries >= maxRetries) {
throw new Error('Rate limit exceeded after max retries');
}
// Exponential backoff: 2^retry seconds
const waitTime = Math.pow(2, retries) * 1000;
console.log(`Rate limited. Waiting ${waitTime}ms before retry ${retries}/${maxRetries}`);
await new Promise(resolve => setTimeout(resolve, waitTime));
} else {
throw error; // Not a rate limit error, re-throw
}
}
}
}
// Usage
const account = await fetchWithRateLimit(() => getAccount('600057112'));See the Rate Limiting guide for more strategies.
The data you sent doesn't meet the API's requirements.
Example:
{
"success": false,
"errors": [
{
"code": "VALIDATION_ERROR",
"message": "Subject must be between 1 and 500 characters",
"field": "subject"
}
]
}Common validation errors:
String too long
// ❌ Subject is 600 characters { subject: "A".repeat(600) } // ✅ Within 500 character limit { subject: "Customer inquiry about pricing" }Invalid email format
// ❌ Invalid email { email: "not-an-email" } // ✅ Valid email { email: "customer@example.com" }Required field missing
// ❌ Missing required fields { priority: "HIGH" } // ✅ All required fields present { subject: "Issue", priority: "HIGH", customerId: "123" }
How to prevent validation errors:
function validateCaseData(data) {
const errors = [];
if (!data.subject || data.subject.length === 0) {
errors.push('Subject is required');
}
if (data.subject && data.subject.length > 500) {
errors.push('Subject must be 500 characters or less');
}
if (!['LOW', 'MEDIUM', 'HIGH', 'URGENT'].includes(data.priority)) {
errors.push('Priority must be LOW, MEDIUM, HIGH, or URGENT');
}
if (errors.length > 0) {
throw new Error(`Validation failed: ${errors.join(', ')}`);
}
return true;
}
// Use before making API call
validateCaseData(caseData);
const result = await createCase(caseData);The API encountered an internal error. This is rare but does happen.
Example:
{
"success": false,
"errors": [
{
"code": "SERVER_ERROR",
"message": "An internal server error occurred. Please try again later."
}
]
}What to do:
- Don't panic - This isn't your fault
- Retry the request - It might succeed the second time
- Use exponential backoff - Don't hammer the API
- Log the error - Include the timestamp and request details
- Contact support - If it persists, report it with your logs
Handling server errors:
async function fetchWithRetry(fetchFunction, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fetchFunction();
} catch (error) {
const isServerError = error.status >= 500;
const isLastRetry = i === maxRetries - 1;
if (isServerError && !isLastRetry) {
const waitTime = Math.pow(2, i) * 1000;
console.log(`Server error. Retrying in ${waitTime}ms...`);
await new Promise(resolve => setTimeout(resolve, waitTime));
} else {
throw error;
}
}
}
}Here's a complete example of production-ready error handling:
class SprinklrError extends Error {
constructor(code, message, statusCode, field = null) {
super(message);
this.name = 'SprinklrError';
this.code = code;
this.statusCode = statusCode;
this.field = field;
}
}
async function callSprinklrAPI(fetchFunction) {
try {
const response = await fetchFunction();
if (response.success) {
return response.data;
} else {
// API returned errors
const error = response.errors[0];
throw new SprinklrError(
error.code,
error.message,
response.status,
error.field
);
}
} catch (error) {
// Network error or HTTP error
if (error instanceof SprinklrError) {
throw error; // Already a Sprinklr error
}
// Handle HTTP errors
if (error.response) {
const { status, data } = error.response;
const apiError = data.errors?.[0] || {};
throw new SprinklrError(
apiError.code || 'UNKNOWN_ERROR',
apiError.message || 'An error occurred',
status,
apiError.field
);
}
// Network error (no response from server)
throw new SprinklrError(
'NETWORK_ERROR',
'Failed to connect to Sprinklr API',
0
);
}
}
// Usage with specific error handling
async function getAccountWithErrorHandling(accountId) {
try {
return await callSprinklrAPI(() => getAccount(accountId));
} catch (error) {
switch (error.code) {
case 'NOT_FOUND':
console.log(`Account ${accountId} not found`);
return null;
case 'UNAUTHORIZED':
console.error('Authentication failed - check credentials');
throw error;
case 'RATE_LIMIT_EXCEEDED':
console.log('Rate limited - waiting before retry');
await new Promise(resolve => setTimeout(resolve, 5000));
return getAccountWithErrorHandling(accountId); // Retry
case 'SERVER_ERROR':
console.error('Server error - will retry');
await new Promise(resolve => setTimeout(resolve, 2000));
return getAccountWithErrorHandling(accountId); // Retry
default:
console.error(`Unexpected error: ${error.code} - ${error.message}`);
throw error;
}
}
}1. Always check the success field
// ❌ Don't assume success
const accounts = response.data;
// ✅ Check first
if (response.success) {
const accounts = response.data;
} else {
console.error('Request failed:', response.errors);
}2. Log errors with context
console.error('API Error:', {
code: error.code,
message: error.message,
endpoint: '/api/v3/account',
accountId: '600057112',
timestamp: new Date().toISOString()
});3. Don't expose raw errors to users
// ❌ Don't do this
alert(error.message); // Shows technical details to user
// ✅ Do this
console.error('API Error:', error);
alert('Unable to load account. Please try again.');4. Implement circuit breakers
class CircuitBreaker {
constructor(threshold = 5, timeout = 60000) {
this.failureCount = 0;
this.threshold = threshold;
this.timeout = timeout;
this.state = 'CLOSED'; // CLOSED, OPEN, HALF_OPEN
this.nextAttempt = Date.now();
}
async call(fn) {
if (this.state === 'OPEN') {
if (Date.now() < this.nextAttempt) {
throw new Error('Circuit breaker is OPEN');
}
this.state = 'HALF_OPEN';
}
try {
const result = await fn();
this.onSuccess();
return result;
} catch (error) {
this.onFailure();
throw error;
}
}
onSuccess() {
this.failureCount = 0;
this.state = 'CLOSED';
}
onFailure() {
this.failureCount++;
if (this.failureCount >= this.threshold) {
this.state = 'OPEN';
this.nextAttempt = Date.now() + this.timeout;
}
}
}
// Usage
const breaker = new CircuitBreaker();
const account = await breaker.call(() => getAccount('600057112'));5. Monitor and alert on errors
const errorCounts = new Map();
function trackError(error) {
const key = error.code;
errorCounts.set(key, (errorCounts.get(key) || 0) + 1);
// Alert if too many errors
if (errorCounts.get('RATE_LIMIT_EXCEEDED') > 10) {
sendAlert('Too many rate limit errors - check integration');
}
if (errorCounts.get('UNAUTHORIZED') > 3) {
sendAlert('Authentication failing - credentials may be expired');
}
}Don't wait for production to find out your error handling doesn't work. Test it during development:
// Create a mock API that returns errors
function mockSprinklrAPI(scenario) {
const scenarios = {
NOT_FOUND: {
success: false,
errors: [{
code: 'NOT_FOUND',
message: 'Account not found'
}]
},
RATE_LIMITED: {
success: false,
errors: [{
code: 'RATE_LIMIT_EXCEEDED',
message: 'Rate limit exceeded'
}]
},
UNAUTHORIZED: {
success: false,
errors: [{
code: 'UNAUTHORIZED',
message: 'Invalid token'
}]
}
};
return Promise.resolve(scenarios[scenario]);
}
// Test your error handling
async function testErrorHandling() {
console.log('Testing NOT_FOUND handling...');
try {
await handleResponse(await mockSprinklrAPI('NOT_FOUND'));
} catch (error) {
console.log('✓ NOT_FOUND handled correctly');
}
console.log('Testing RATE_LIMITED handling...');
try {
await handleResponse(await mockSprinklrAPI('RATE_LIMITED'));
} catch (error) {
console.log('✓ RATE_LIMITED handled correctly');
}
}| Code | HTTP | Cause | Solution |
|---|---|---|---|
INVALID_PARAMETER | 400 | Bad parameter | Check format and type |
VALIDATION_ERROR | 400 | Data validation failed | Fix data values |
UNAUTHORIZED | 401 | Auth failed | Check credentials |
FORBIDDEN | 403 | No permission | Check access rights |
NOT_FOUND | 404 | Resource missing | Verify ID exists |
RATE_LIMIT_EXCEEDED | 429 | Too many requests | Implement backoff |
SERVER_ERROR | 500 | Sprinklr error | Retry with delay |
SERVICE_UNAVAILABLE | 503 | Temporary outage | Retry with backoff |
Now that you understand error handling:
- Learn about rate limiting - Avoid rate limit errors
- Follow best practices - Build resilient integrations
- Get support - Get help when you're stuck
If you're encountering errors you can't resolve:
- Support & FAQ - Common issues and solutions
- Email support - api-support@sprinklr.com with error logs
- Include details - Error code, full error message, request details, timestamp
Remember: Good error handling is what separates hobby projects from production-ready integrations. Take the time to handle errors properly, and your future self will thank you.