Create shortened links and vanity links through a URL shortener configured for your Sprinklr partner. Sprinklr provides its own shortening utility, spr.ly, and can also front third-party providers.
This guide covers both V3 Links operations and includes migration guidance for developers moving from the V2 URL Shortener APIs.
- Overview
- Base URL and environments
- Authentication
- Operations
- Field reference
- Worked examples
- Response format and status codes
- Migrating from V2
- Common tasks
- Best practices
- Troubleshooting
- Related articles
The URL Shortener API exposes two operations, and in practice you use them together.
| Operation | Method and path | Purpose |
|---|---|---|
| Fetch shorteners | GET /link/shorteners | List the URL shorteners available to your partner |
| Shorten a URL | POST /link/shorten | Create a short link or a vanity link |
Call GET /link/shorteners first. POST /link/shorten requires a urlShortenerId, and the shorteners list is where you obtain it. Cache the result, because the list changes only when an administrator adds or removes a shortener in Sprinklr.
The API produces two kinds of short link.
Standard short link. Send link and urlShortenerId. Sprinklr generates the link hash for you and the response carries campaignId: -11.
Vanity link. Additionally send name. That value becomes the link hash verbatim, so name: "SpringSale" produces a short link ending in /SpringSale. Vanity links carry campaignId: -10 and populate the domain field.
Vanity links require a shortener whose canUseForVanityLink field is true. Read that flag from GET /link/shorteners before you attempt a vanity link.
The
namefield is the vanity hash, not the name of the shortener. The shortener's own name isUrlShortener.name, returned byGET /link/shorteners, and it is never sent toPOST /link/shorten. Sendnameonly when you want a vanity link with that exact hash.
https://api3.sprinklr.com/{env}/api/v3/link/shorten
https://api3.sprinklr.com/{env}/api/v3/link/shortenersReplace {env} with the environment your partner is provisioned on. The supported values are:
prod, prod0, prod2, prod3, prod4, prod5, prod6, prod8, prod11, prod12, prod15, prod16, prod17, prod18, prod19, prod21, prod25
Use the environment your API key was issued against. Calling a different environment returns 401 rather than redirecting. If you are unsure which environment applies, check the host you use to sign in to Sprinklr, or contact your Sprinklr account team.
Both operations require two authentication headers. Send both on every request.
| Header | Value | Description |
|---|---|---|
Authorization | Bearer {{accessToken}} | Bearer token used to authenticate the user with the server |
Key | {{apiKey}} | API key that authenticates your application with the server |
Content-Type | application/json | Required on POST /link/shorten |
Accept | application/json | Acceptable response type |
Only one access token and refresh token pair exists per API key at a time. Requesting a new pair invalidates the previous one. If two of your services shorten links, either issue them separate API keys or centralise token refresh behind a single component.
GET /link/shortenersReturns every URL shortener visible to the partner behind your credentials, as an array.
- Parameters: none
- Request body: none
- Success:
200
Use this operation to obtain two things: the id you pass as urlShortenerId when shortening, and the canUseForVanityLink flag that tells you whether the shortener supports vanity links.
The response is returned in a single call and is scoped to your partner. Cache it at start-up rather than fetching it before every shortening request.
POST /link/shortenCreates a short link using the specified shortener.
- Request body: required
- Required fields:
link,urlShortenerId - Success:
201 Created
Each call creates a new short link. Posting the same link and urlShortenerId twice produces two short links with two distinct id values, so maintain your own mapping of original URL to short link if you need to avoid duplicates.
Store id, linkHash, and shortLink from the response at creation time. These values are returned once, when the link is created.
| Field | Type | Required | Description |
|---|---|---|---|
link | string | Yes | The URL to be shortened. Provide a fully qualified URL including the scheme |
urlShortenerId | string | Yes | The URL shortener to use. Obtain this from GET /link/shorteners |
name | string | No | Unique name or URL hash. When supplied, it becomes the link hash, producing a vanity link |
description | string | No | Description of the URL being shortened |
Returned as an array by GET /link/shorteners.
| Field | Type | Description |
|---|---|---|
id | string | ID of the shortener. Pass this as urlShortenerId when shortening |
name | string | Name of the shortener |
provider | string | Shortener service provider |
canUseForVanityLink | boolean | Whether the shortener can create vanity links |
Treat a missing canUseForVanityLink as false rather than assuming vanity support. Select shorteners by name for human-facing choices and by provider when behaviour depends on the underlying service. Keep the selection configurable rather than hard-coding an id, because shortener IDs differ across environments and partners.
Returned as data by POST /link/shorten.
| Field | Type | Description |
|---|---|---|
id | integer (int64) | Short link ID |
urlShortenerId | string | The URL shortener that was used |
linkHash | string | Unique hash for the shortened link |
originalLink | string | The original URL that was shortened |
shortLink | string | The short URL. This is the value you publish |
campaignId | integer (int64) | -10 for vanity links, -11 for links shortened directly |
description | string | Short URL description |
domain | string | Domain used to shorten the link |
provider | string | URL provider used for shortening |
createdTime | integer (int64) | Creation time of the short link |
modifiedTime | integer (int64) | Last modified time of the short link |
archived | boolean | Whether the short link is archived |
postId | integer (int64) | Post ID the link is associated with |
Six fields are consistently present on a creation response: id, urlShortenerId, linkHash, originalLink, shortLink, and campaignId. domain is additionally returned for vanity links. The remaining fields become populated later in a link's lifecycle, for example postId once the link is attached to a published post. Null-check any field beyond the six above.
Note the mixed identifier types: id is a 64-bit number, while urlShortenerId is a string ID. Do not store id in a field sized for a 24-character hexadecimal identifier.
| Case | What you send | Resulting linkHash | campaignId |
|---|---|---|---|
| Standard link | link + urlShortenerId | System-generated alphanumeric token | -11 |
| Vanity link | link + urlShortenerId + name | Exactly the name you sent | -10 |
Read linkHash and shortLink directly from the response. Do not derive either value from id.
Replace {{accessToken}}, {{apiKey}}, and {env} with your own values.
curl -X GET \
'https://api3.sprinklr.com/{env}/api/v3/link/shorteners' \
-H 'Authorization: Bearer {{accessToken}}' \
-H 'Key: {{apiKey}}' \
-H 'Accept: application/json'Example response:
{
"data": [
{
"id": "0000000000000000000000a1",
"name": "Corporate spr.ly",
"provider": "SPRINKLR",
"canUseForVanityLink": true
},
{
"id": "0000000000000000000000b2",
"name": "Marketing Bitly",
"provider": "BITLY",
"canUseForVanityLink": false
}
],
"errors": []
}curl -X POST \
'https://api3.sprinklr.com/{env}/api/v3/link/shorten' \
-H 'Authorization: Bearer {{accessToken}}' \
-H 'Key: {{apiKey}}' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{
"link": "https://www.example.com/spring-campaign-landing-page",
"urlShortenerId": "0000000000000000000000a1"
}'Example response, returned with status 201 Created:
{
"data": {
"id": 600000000,
"urlShortenerId": "0000000000000000000000a1",
"linkHash": "6003ZAI5v",
"originalLink": "https://www.example.com/spring-campaign-landing-page",
"shortLink": "https://spr.ly/6003ZAI5v",
"campaignId": -11
},
"errors": []
}Publish data.shortLink. Store data.id if you need to correlate the link later.
Send name as the hash you want, using a shortener where canUseForVanityLink is true.
curl -X POST \
'https://api3.sprinklr.com/{env}/api/v3/link/shorten' \
-H 'Authorization: Bearer {{accessToken}}' \
-H 'Key: {{apiKey}}' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d '{
"link": "https://www.example.com/spring-campaign-landing-page",
"urlShortenerId": "0000000000000000000000a1",
"name": "SpringSale"
}'Example response:
{
"data": {
"id": 600000001,
"urlShortenerId": "0000000000000000000000a1",
"linkHash": "SpringSale",
"originalLink": "https://www.example.com/spring-campaign-landing-page",
"shortLink": "https://spr.ly/SpringSale",
"campaignId": -10,
"domain": "spr.ly"
},
"errors": []
}linkHash echoes name, campaignId is -10, and domain is populated. All three confirm you received a vanity link rather than a standard one.
Vanity hashes occupy a shared namespace per shortener domain, so a given hash can be claimed only once. Choose names unlikely to collide, and handle a rejection by retrying with a different name.
import requests
BASE = "https://api3.sprinklr.com/{env}/api/v3".format(env="prod0")
HEADERS = {
"Authorization": "Bearer {{accessToken}}",
"Key": "{{apiKey}}",
"Content-Type": "application/json",
"Accept": "application/json",
}
def get_shorteners():
r = requests.get(BASE + "/link/shorteners", headers=HEADERS, timeout=30)
r.raise_for_status()
return r.json().get("data") or []
def pick_shortener(shorteners, need_vanity=False):
for s in shorteners:
if not need_vanity:
return s
if s.get("canUseForVanityLink") is True:
return s
raise RuntimeError("No suitable shortener is configured for this partner")
def shorten(link, shortener_id, name=None):
body = {"link": link, "urlShortenerId": shortener_id}
if name:
body["name"] = name
r = requests.post(BASE + "/link/shorten", headers=HEADERS,
json=body, timeout=30)
if r.status_code not in (200, 201):
r.raise_for_status()
payload = r.json()
errors = payload.get("errors") or []
if errors:
raise RuntimeError("Shortening failed: {}".format(errors))
return payload["data"]
shorteners = get_shorteners()
chosen = pick_shortener(shorteners, need_vanity=True)
result = shorten(
"https://www.example.com/spring-campaign-landing-page",
chosen["id"],
name="SpringSale",
)
print(result["shortLink"])Two details are deliberate. The status check accepts both 200 and 201, and the errors array is inspected even on a success status.
const BASE = "https://api3.sprinklr.com/prod0/api/v3";
const HEADERS = {
"Authorization": "Bearer {{accessToken}}",
"Key": "{{apiKey}}",
"Content-Type": "application/json",
"Accept": "application/json",
};
async function getShorteners() {
const res = await fetch(`${BASE}/link/shorteners`, { headers: HEADERS });
if (!res.ok) throw new Error(`Fetch shorteners failed: ${res.status}`);
const body = await res.json();
return body.data ?? [];
}
async function shorten(link, urlShortenerId, name) {
const payload = { link, urlShortenerId };
if (name) payload.name = name;
const res = await fetch(`${BASE}/link/shorten`, {
method: "POST",
headers: HEADERS,
body: JSON.stringify(payload),
});
if (res.status !== 200 && res.status !== 201) {
throw new Error(`Shorten failed: ${res.status}`);
}
const body = await res.json();
if (body.errors?.length) {
throw new Error(`Shorten failed: ${JSON.stringify(body.errors)}`);
}
return body.data;
}
const shorteners = await getShorteners();
const vanityCapable = shorteners.find((s) => s.canUseForVanityLink === true);
const link = await shorten(
"https://www.example.com/spring-campaign-landing-page",
vanityCapable.id,
"SpringSale",
);
console.log(link.shortLink);Every Sprinklr V3 response is wrapped in a standard envelope:
{
"data": { },
"errors": [ ]
}GET /link/shorteners returns an array in data. POST /link/shorten returns a single object in data.
Each entry in errors contains:
| Field | Type | Description |
|---|---|---|
id | string | Error identifier |
code | string | Error code |
message | string | Human-readable message |
Bind your client to this envelope, and inspect the errors array even when the HTTP status indicates success.
| Code | Meaning | Handling |
|---|---|---|
200 | Success | Returned by GET /link/shorteners |
201 | Created | Returned by POST /link/shorten on success |
400 | Bad request | A required field is missing, the URL is malformed, or the vanity name was rejected |
401 | Unauthorized | Expired token, wrong environment, or a missing authentication header |
403 | Forbidden | Your user lacks permission for the shortener, or the partner is not entitled |
404 | Not found | Typically an unrecognised urlShortenerId |
Accept both 200 and 201 as success on POST /link/shorten, or check for any 2xx.
Rate limiting is applied at the Sprinklr API gateway across the platform. Implement retry with exponential backoff for 429 and 5xx responses, and honour the Retry-After header when it is present. Because shortening is not idempotent, retry only on network-level failures and on 429, and be aware that retrying after a timeout may create a second link.
Validation errors for shortening are surfaced from the external shortening provider rather than generated by Sprinklr. This has three practical consequences:
- Error message text and codes are not stable across providers. A partner using Bitly and a partner using
spr.lycan receive different error bodies for the same mistake. - Branch your error handling on the HTTP status code rather than on the message text.
- Log the full
errorsarray and surface the message to an operator rather than attempting to parse it.
Change v2 to v3 in the path. For most integrations, that is the entire migration.
| V2 | V3 | |
|---|---|---|
| Shorten | POST /{env}/api/v2/link/shorten | POST /{env}/api/v3/link/shorten |
| List shorteners | GET /{env}/api/v2/link/shorteners | GET /{env}/api/v3/link/shorteners |
| Request fields | link, urlShortenerId, name | Unchanged |
| Required fields | link, urlShortenerId | Unchanged |
| Response envelope | {data, errors} | Unchanged |
| Authentication headers | Authorization, Key | Unchanged |
| Success status on shorten | 200 | 201 |
Success status code. V2 returned 200 on shortening. V3 returns 201 Created. A client written as if (status === 200) will treat a successful V3 creation as a failure. This is the most likely cause of a broken migration. Widen the check to accept 200 and 201, or accept any 2xx.
Link hash format for standard links. In V2, the standard-link linkHash was the decimal string form of the numeric id, for example id: 101612830 with linkHash: "101612830". In V3, standard links return an alphanumeric token unrelated to the id. If any code derives one value from the other, or validates that linkHash is numeric, update it to read linkHash and shortLink directly from the response.
Vanity links are unaffected, because their hash has always been the name you supply.
Request field names and semantics, the required field set, the {data, errors} envelope, the campaignId sentinels (-10 for vanity, -11 for standard), authentication, and the vanity-capability rule are all identical. Error responses are also unchanged, because validation errors originate from the external shortening provider in both versions.
- Change
api/v2toapi/v3on both endpoints. - Widen the success check on
POST /link/shortento accept201. - Remove any code that derives
linkHashfromidor assumeslinkHashis numeric. - Add null checks for
domain,createdTime,modifiedTime,provider,archived, andpostId. - Continue inspecting the
errorsarray on success statuses. - Re-verify vanity flows against a shortener where
canUseForVanityLinkistrue.
Fetch the shortener list once at start-up and cache it. For each URL, call POST /link/shorten with link and the cached urlShortenerId, then publish data.shortLink. Store data.id and data.linkHash alongside your campaign record so reporting can be correlated later.
Select a shortener where canUseForVanityLink is true, then post with name set to the hash you want. Confirm success by checking that data.linkHash equals the name you sent and that campaignId is -10. If the hash comes back different, you did not receive the vanity link you requested, so do not publish it as one.
Expose a thin wrapper over POST /link/shorten and maintain your own mapping from original URL to short link. Check your mapping first and call Sprinklr only on a miss. This keeps your short-link inventory stable and avoids creating a new link each time the same URL is submitted.
GET /link/shorteners returns name and provider for each entry. Use name for human-facing selection and provider when behaviour depends on the underlying service. Keep the choice configurable so the same code works across environments and partners.
- Cache the shorteners list. It changes only when an administrator adds or removes a shortener. Fetch it at start-up rather than before every shortening call.
- Persist the creation response. Store
id,linkHash, andshortLinkwhen the link is created. These values are returned at creation time. - Accept
200and201as success on shortening. - Null-check optional fields. Depend only on
id,urlShortenerId,linkHash,originalLink,shortLink, andcampaignId. - Read the scheme from
shortLink. Embed the returned string as-is rather than reconstructing the URL fromdomainandlinkHash. - Deduplicate before retrying. Shortening is not idempotent, so a retry after a timeout can create a duplicate link.
- Check
canUseForVanityLinkbefore requesting a vanity link, and treat a missing value asfalse. - Do not look up
campaignIdvalues of-10or-11. These are sentinel values indicating a vanity or standard link, not references to a real campaign. - Keep shortener IDs in configuration, not in code, because they differ across environments and partners.
Firebase shorteners. Google deprecated its Firebase URL shortener, and Firebase shorteners are no longer supported in Sprinklr effective 25 August 2025. If your integration selects a shortener by
provider, ensure it does not target Firebase.
| Symptom | Likely cause | Resolution |
|---|---|---|
401 on every call | Wrong {env}, expired access token, or a missing Key header | Confirm the environment, refresh the token, and send both authentication headers |
400 with an unclear message | A required field is missing, or the provider rejected the request | Confirm link and urlShortenerId are both present and that link includes a scheme |
404 on shortening | urlShortenerId does not exist or is not visible to your user | Re-fetch GET /link/shorteners and use an id from that response |
| Vanity name ignored, standard hash returned | The shortener does not support vanity links | Check canUseForVanityLink and select a different shortener |
| Vanity request rejected | The hash is already claimed on that domain | Retry with a different name |
| Client fails to deserialize the response | Client bound to the inner object rather than the envelope | Bind to {data, errors} and read the payload from data |
| Duplicate short links after a retry | Shortening is not idempotent | Deduplicate against your own mapping before retrying |
Success status but nothing usable in data | An error was returned alongside a success status | Inspect the errors array on every response |
Getting started
Reference
V2 documentation
Product documentation