This guide covers the core patterns and techniques you'll use when working with the Sprinklr API. Once you understand these fundamentals, you'll be able to build robust integrations that handle real-world scenarios effectively.
Every interaction with the Sprinklr API follows the same basic pattern: you send a request, and you get back a response. Let's break down what that looks like.
Here's a typical API request:
GET /api/v3/account?accountId=600057112 HTTP/1.1
Host: api-v3.sprinklr.com
Authorization: Bearer SPRD_tjACKHVlwb3D7Xd2...
Key: ebgw8cwygvgc7vptgxty2ekq
Content-Type: application/jsonLet's examine each part:
1. HTTP method (GET) This tells the API what kind of operation you want to perform. GET retrieves data, POST creates new data, PUT/PATCH updates existing data, and DELETE removes data.
2. Endpoint path (/api/v3/account) This identifies which resource you're working with. The /api/v3/ prefix is on all endpoints, then comes the resource name.
3. Query parameters (?accountId=600057112) These filter or modify the request. Different endpoints support different parameters.
4. Headers
Authorization: Your access token for authenticationKey: Your API key for identificationContent-Type: Tells the API you're sending JSON (for POST/PUT/PATCH)
5. Request body (not shown in this GET request) For POST, PUT, and PATCH requests, you'll send data in JSON format in the request body.
Every Sprinklr API response uses a consistent structure. This makes it easy to handle responses in your code:
Success response:
{
"success": true,
"data": {
"id": "600057112",
"accountType": "TWITTER",
"accountDisplayName": "@YourBrand",
"followerCount": 15420,
"status": "ACTIVE"
}
}Error response:
{
"success": false,
"errors": [
{
"code": "NOT_FOUND",
"message": "Account with ID 600057112 not found",
"field": "accountId"
}
]
}Notice the key differences:
successis always present and tells you if the request worked- Successful responses have a
datafield with your results - Failed responses have an
errorsarray explaining what went wrong - Error objects include a machine-readable
code, human-readablemessage, and sometimes afieldindicating which parameter caused the problem
This consistent structure means you can write generic error handling code that works across all endpoints.
The Sprinklr API uses standard REST conventions for HTTP methods. Here's when to use each one:
Use GET when you want to read data without making any changes:
# Get account information
GET /api/v3/account?accountId=600057112
# Search for messages
GET /api/v3/message?messageId=MSG_12345
# Get multiple profiles
GET /api/v3/profile?profileId=PROF_1,PROF_2,PROF_3GET requests:
- Don't have a request body
- Pass all parameters in the URL
- Are safe to retry - they won't change anything
- Can be cached by browsers and proxies
POST has two main uses in the Sprinklr API:
1. Creating new resources:
POST /api/v3/case
Content-Type: application/json
{
"subject": "Customer inquiry about pricing",
"priority": "MEDIUM",
"source": "TWITTER",
"customerId": "CUST_789"
}2. Complex searches:
POST /api/v3/message/search
Content-Type: application/json
{
"startTime": "2026-02-01T00:00:00Z",
"endTime": "2026-02-11T23:59:59Z",
"accountIds": ["600057112"],
"sentiment": ["POSITIVE", "NEUTRAL"],
"page": 0,
"size": 50
}Why use POST for searches? When you need to send complex filter criteria (like arrays, date ranges, or nested conditions), the data doesn't fit well in a URL. POST lets you send structured JSON that's easier to work with.
PUT replaces an entire resource with new data:
PUT /api/v3/case/CASE_789
Content-Type: application/json
{
"subject": "Updated subject",
"priority": "HIGH",
"status": "IN_PROGRESS",
"assignedTo": "agent@yourbrand.com",
"notes": "Updated by API"
}With PUT, you typically need to send all fields, not just the ones you're changing. If you omit a field, it might be set to null or left unchanged (depending on the endpoint).
PATCH lets you update just specific fields:
PATCH /api/v3/case/CASE_789
Content-Type: application/json
{
"priority": "URGENT",
"assignedTo": "senior-agent@yourbrand.com"
}This is more convenient than PUT because you only send what you want to change. Other fields remain untouched.
DELETE removes a resource:
DELETE /api/v3/case/CASE_789Be careful with DELETE operations - they're usually permanent. Some resources might support soft deletes (marking as inactive) instead of hard deletes (permanent removal).
The simplest pattern is fetching a single resource by its ID:
curl "https://api-v3.sprinklr.com/api/v3/account?accountId=600057112" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Key: YOUR_API_KEY"Response:
{
"success": true,
"data": {
"id": "600057112",
"accountType": "TWITTER",
"accountDisplayName": "@YourBrand",
"accountScreenName": "YourBrand",
"verified": true,
"followerCount": 15420,
"status": "ACTIVE",
"createdTime": "2024-03-15T10:30:00Z"
}
}Even though you requested one account, data is still an array (in some endpoints) or a single object (in others). Check the API reference for each endpoint to see which structure it uses.
One of the most powerful features of the Sprinklr API is batch operations. Instead of making multiple individual requests, you can fetch up to 50 resources at once.
Without batching:
// 10 separate API calls
// Takes ~3-5 seconds
// Uses 10 of your rate limit quota
const accounts = [];
for (const id of accountIds) {
const account = await fetchAccount(id);
accounts.push(account);
}With batching:
// 1 API call
// Takes ~300ms
// Uses 1 of your rate limit quota
const accounts = await fetchAccounts(accountIds);The difference becomes huge when you're dealing with hundreds or thousands of resources.
Just provide comma-separated IDs in your request:
curl "https://api-v3.sprinklr.com/api/v3/account?accountId=600057112,600057113,600057114" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Key: YOUR_API_KEY"Response:
{
"success": true,
"data": [
{
"id": "600057112",
"accountType": "TWITTER",
"accountDisplayName": "@YourBrand",
"followerCount": 15420
},
{
"id": "600057113",
"accountType": "FACEBOOK",
"accountDisplayName": "Your Brand",
"followerCount": 28500
},
{
"id": "600057114",
"accountType": "INSTAGRAM",
"accountDisplayName": "@yourbrand",
"followerCount": 42300
}
]
}What happens if some IDs exist but others don't? The API returns what it can and tells you about the failures:
Request:
GET /api/v3/account?accountId=600057112,999999999,600057114Response:
{
"success": false,
"data": [
{
"id": "600057112",
"accountDisplayName": "@YourBrand"
},
{
"id": "600057114",
"accountDisplayName": "@yourbrand"
}
],
"errors": [
{
"code": "NOT_FOUND",
"message": "Account with ID 999999999 not found",
"field": "accountId"
}
]
}Notice:
successisfalsebecause the request partially faileddatacontains the accounts that were founderrorslists what went wrong
Here's how to handle this in code:
async function getAccountsWithErrorHandling(accountIds) {
const response = await fetchAccounts(accountIds);
// Track which ones succeeded
const foundAccounts = response.data || [];
console.log(`Found ${foundAccounts.length} accounts`);
// Track which ones failed
if (response.errors && response.errors.length > 0) {
console.log(`${response.errors.length} accounts not found:`);
response.errors.forEach(error => {
console.log(` - ${error.message}`);
});
}
return foundAccounts;
}You can request up to 50 resources in a single batch:
# ✅ Valid - 50 IDs
GET /api/v3/account?accountId=1,2,3,...,50
# ❌ Invalid - 51 IDs (will return an error)
GET /api/v3/account?accountId=1,2,3,...,51If you need more than 50, break your request into multiple batches:
function chunkArray(array, size) {
const chunks = [];
for (let i = 0; i < array.length; i += size) {
chunks.push(array.slice(i, i + size));
}
return chunks;
}
async function getAllAccounts(accountIds) {
const chunks = chunkArray(accountIds, 50);
const results = [];
for (const chunk of chunks) {
const response = await fetchAccounts(chunk);
if (response.data) {
results.push(...response.data);
}
}
return results;
}
// Fetch 237 accounts in 5 batches (50+50+50+50+37)
const accounts = await getAllAccounts(array237Ids);For more complex queries, you'll use search endpoints that accept POST requests with JSON filter criteria.
Let's find all open customer service cases with high priority:
curl -X POST "https://api-v3.sprinklr.com/api/v3/case/search" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"status": ["OPEN"],
"priority": ["HIGH"],
"page": 0,
"size": 20,
"sort": "createdTime,desc"
}'This returns the 20 most recent high-priority open cases.
Many searches support date ranges:
curl -X POST "https://api-v3.sprinklr.com/api/v3/message/search" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"startTime": "2026-02-01T00:00:00Z",
"endTime": "2026-02-11T23:59:59Z",
"accountIds": ["600057112"],
"messageTypes": ["TWITTER_POST", "TWITTER_REPLY"]
}'Important: Dates must be in ISO 8601 format with UTC timezone:
- ✅
2026-02-11T14:30:00Z - ❌
2026-02-11(missing time) - ❌
02/11/2026(wrong format) - ❌
2026-02-11 14:30:00(missing timezone)
You can combine filters to narrow your search:
{
"startTime": "2026-02-01T00:00:00Z",
"endTime": "2026-02-11T23:59:59Z",
"accountIds": ["600057112", "600057113"],
"messageTypes": ["TWITTER_POST"],
"sentiment": ["NEGATIVE"],
"hasMedia": true,
"minEngagement": 100,
"page": 0,
"size": 50,
"sort": "engagementScore,desc"
}This finds negative sentiment posts with media and at least 100 engagements, sorted by engagement score.
Most filters use AND logic by default:
{
"status": ["OPEN"],
"priority": ["HIGH"]
}This means: status = OPEN AND priority = HIGH
When you provide multiple values for a single filter, it's usually OR logic:
{
"status": ["OPEN", "PENDING"]
}This means: status = OPEN OR status = PENDING
Check the API reference for each endpoint to understand its specific filter behavior.
Most list and search endpoints support sorting with the sort parameter:
# Sort by creation time, newest first
sort=createdTime,desc
# Sort by creation time, oldest first
sort=createdTime,asc
# Sort by name alphabetically
sort=name,ascThe format is: fieldName,direction
asc= ascending (A→Z, 0→9, oldest→newest)desc= descending (Z→A, 9→0, newest→oldest)
Some endpoints support sorting by multiple fields:
# Sort by priority first, then by creation time
sort=priority,desc&sort=createdTime,descThis means: sort by priority (urgent first), and within each priority level, sort by creation time (newest first).
The Sprinklr API uses ISO 8601 format with UTC timezone for all timestamps.
Timestamps in responses look like this:
{
"createdTime": "2026-02-11T14:30:00Z",
"modifiedTime": "2026-02-11T15:45:00Z"
}The Z at the end means UTC (Coordinated Universal Time). All times from the API are in UTC, regardless of your local timezone.
When you send timestamps in requests, use the same format:
// ✅ Correct
{
"startTime": "2026-02-01T00:00:00Z",
"endTime": "2026-02-11T23:59:59Z"
}
// ❌ Wrong - missing time
{
"startTime": "2026-02-01"
}
// ❌ Wrong - missing timezone
{
"startTime": "2026-02-01T00:00:00"
}Always convert your local times to UTC before sending them to the API:
// JavaScript example
const localDate = new Date('2026-02-11 14:30:00'); // Your local time
const utcString = localDate.toISOString(); // "2026-02-11T14:30:00.000Z"
// Python example
from datetime import datetime, timezone
local_time = datetime(2026, 2, 11, 14, 30, 0)
utc_time = local_time.astimezone(timezone.utc)
utc_string = utc_time.isoformat().replace('+00:00', 'Z')When displaying times to users, convert from UTC to their local timezone:
// JavaScript example
const utcString = "2026-02-11T14:30:00Z";
const localDate = new Date(utcString);
const localString = localDate.toLocaleString(); // Displays in user's timezoneWhen you're working with endpoints that return lots of results (like searching messages or cases), the API breaks the results into pages.
curl -X POST "https://api-v3.sprinklr.com/api/v3/case/search" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"status": ["OPEN"],
"page": 0,
"size": 20,
"sort": "createdTime,desc"
}'Response:
{
"success": true,
"data": [
{...}, {...}, ... // 20 cases
],
"pagination": {
"page": 0,
"size": 20,
"totalElements": 847,
"totalPages": 43,
"hasNext": true,
"hasPrevious": false,
"first": true,
"last": false
}
}The pagination object tells you:
page: Which page you're on (0-indexed, so 0 is the first page)size: How many items per pagetotalElements: Total number of matching items across all pagestotalPages: Total number of pageshasNext: Whether there are more pages after this onehasPrevious: Whether there are pages before this onefirst: Whether this is the first pagelast: Whether this is the last page
To get the next page, just increment the page parameter:
{
"status": ["OPEN"],
"page": 1, // ← Changed from 0 to 1
"size": 20,
"sort": "createdTime,desc"
}Here's how to fetch all results across multiple pages:
async function getAllCases(filters) {
const allCases = [];
let page = 0;
let hasMore = true;
while (hasMore) {
const response = await searchCases({
...filters,
page,
size: 50
});
if (response.success && response.data) {
allCases.push(...response.data);
}
hasMore = response.pagination?.hasNext || false;
page++;
// Optional: Add a small delay to avoid rate limits
if (hasMore) {
await new Promise(resolve => setTimeout(resolve, 100));
}
}
return allCases;
}
// Usage
const cases = await getAllCases({ status: ["OPEN", "PENDING"] });
console.log(`Found ${cases.length} total cases`);The API supports page sizes from 1 to 100. Here's how to choose:
| Page size | Best for | Pros | Cons |
|---|---|---|---|
| 20-30 | Real-time UI | Fast responses, responsive | More API calls needed |
| 50-75 | Background processing | Balanced | Middle ground |
| 100 | Bulk exports | Fewest API calls | Slower responses, more memory |
General guideline: Start with 50 items per page and adjust based on your needs.
Most endpoints support these standard parameters:
- Type: Integer
- Default: 0
- Range: 0 to totalPages-1
- Description: Which page of results to return (0-indexed)
page=0 # First page
page=1 # Second page- Type: Integer
- Default: 20
- Range: 1 to 100
- Description: How many items per page
size=20 # 20 items per page
size=50 # 50 items per page
size=100 # Maximum allowed- Type: String
- Format:
fieldName,direction - Description: How to sort results
sort=createdTime,desc # Newest first
sort=name,asc # Alphabetically
sort=priority,desc # Highest priority firstUnderstanding the data types used in the API helps you construct valid requests and interpret responses correctly.
Plain text values:
{
"accountDisplayName": "@YourBrand",
"subject": "Customer inquiry",
"notes": "This is a text field"
}Whole numbers (no decimals):
{
"followerCount": 15420,
"priority": 3,
"retweetCount": 234
}True or false values:
{
"verified": true,
"hasMedia": false,
"isPublic": true
}Lists of values:
{
"accountIds": ["600057112", "600057113"],
"tags": ["urgent", "vip", "escalated"],
"messageTypes": ["TWITTER_POST", "TWITTER_REPLY"]
}Nested structures:
{
"customerProfile": {
"name": "John Customer",
"email": "john@example.com",
"vipStatus": true
},
"engagementMetrics": {
"likes": 234,
"shares": 45,
"comments": 12
}
}Explicitly absent values:
{
"assignedTo": null,
"resolvedTime": null,
"parentCaseId": null
}The API has several limits to ensure reliability and fair usage:
You can request up to 50 resources at once in batch operations:
# ✅ Valid
GET /api/v3/account?accountId=1,2,3,...,50
# ❌ Invalid - will return error
GET /api/v3/account?accountId=1,2,3,...,51Paginated endpoints return at most 100 items per page:
{
"page": 0,
"size": 100 // Maximum
}If you specify size: 150, the API will either return an error or silently cap it at 100 (check the specific endpoint documentation).
API requests that take longer than 30 seconds will timeout. If you're hitting timeouts:
- Add more specific filters to your search
- Reduce your page size
- Break large batch requests into smaller ones
API calls are rate-limited based on your Sprinklr plan. See the Rate Limiting guide for details.
Don't assume requests will succeed. Always check the success field and handle errors:
async function getAccountSafely(accountId) {
try {
const response = await fetchAccount(accountId);
if (response.success) {
return response.data;
} else {
// Handle API-level errors
console.error('API Error:', response.errors);
throw new Error(response.errors[0]?.message || 'Unknown error');
}
} catch (error) {
// Handle network errors, timeouts, etc.
console.error('Request failed:', error);
throw error;
}
}Whenever you need multiple resources, batch them:
// ❌ Bad - 10 API calls
const accounts = [];
for (const id of accountIds) {
accounts.push(await getAccount(id));
}
// ✅ Good - 1 API call
const accounts = await getAccounts(accountIds);When requests fail (especially rate limits), don't immediately retry. Use exponential backoff:
async function fetchWithRetry(fetchFunction, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fetchFunction();
} catch (error) {
if (i === maxRetries - 1) throw error;
// Wait 2^i seconds before retrying (1s, 2s, 4s, 8s...)
const waitTime = Math.pow(2, i) * 1000;
console.log(`Retry ${i + 1}/${maxRetries} after ${waitTime}ms`);
await new Promise(resolve => setTimeout(resolve, waitTime));
}
}
}Some data doesn't change often. Cache it to reduce API calls:
class AccountCache {
constructor() {
this.cache = new Map();
this.ttl = 60 * 60 * 1000; // 1 hour
}
async getAccount(accountId) {
const cached = this.cache.get(accountId);
if (cached && Date.now() - cached.timestamp < this.ttl) {
return cached.data;
}
const account = await fetchAccount(accountId);
this.cache.set(accountId, {
data: account,
timestamp: Date.now()
});
return account;
}
}Log your API calls to help with debugging:
async function fetchWithLogging(url, options) {
const startTime = Date.now();
console.log(`→ ${options.method || 'GET'} ${url}`);
try {
const response = await fetch(url, options);
const duration = Date.now() - startTime;
const data = await response.json();
console.log(`← ${response.status} in ${duration}ms`);
return data;
} catch (error) {
const duration = Date.now() - startTime;
console.error(`✗ Failed after ${duration}ms:`, error.message);
throw error;
}
}Check your data before making API calls:
function validateCaseSearch(filters) {
// Check date range
if (filters.startTime && filters.endTime) {
const start = new Date(filters.startTime);
const end = new Date(filters.endTime);
if (start > end) {
throw new Error('startTime must be before endTime');
}
// Warn if range is very large
const daysDiff = (end - start) / (1000 * 60 * 60 * 24);
if (daysDiff > 90) {
console.warn('Date range spans more than 90 days - this might be slow');
}
}
// Check page size
if (filters.size && (filters.size < 1 || filters.size > 100)) {
throw new Error('Page size must be between 1 and 100');
}
return true;
}Now that you understand API basics, you're ready to:
- Handle errors properly - Learn about all error codes and how to fix them
- Understand rate limiting - Optimize your API usage
- Master pagination - Handle large datasets efficiently
- Explore specific APIs - Dive into accounts, messages, cases, and more
The patterns in this guide apply to all Sprinklr API endpoints. Once you master these basics, working with any endpoint becomes straightforward.