API reference
e-sat.ly has a small HTTP API for creating short links from your own systems. There are two endpoints for creating a link and one for listing them, and everything authenticates with a project's API key.
Base address: https://e-sat.ly
Which endpoint should I use?
Use /api/v1/links for anything new — it's JSON, it returns the full link, and it can list links back to you.
/create is the original endpoint and it isn't going anywhere: it's what the suite's own shortener client calls, and it's kept compatible on purpose. Use it if you're already using it, or if you need to post form-encoded data.
Authentication
Every request carries your project's API key in the Authorization header:
Authorization: Bearer YOUR_PROJECT_KEY
This works on every endpoint. The key identifies the project and its organization, so you never send an organization id or project id — the link simply lands in the project the key belongs to.
A missing or unrecognized key gets a 401. Copy your key from Configuration → Projects, and see Projects & API keys for re-issuing.
Create a link
POST https://e-sat.ly/api/v1/links
Content-Type: application/json
| Parameter | Type | Required | Description |
|---|---|---|---|
url | string | Yes | The destination. Must be an absolute http:// or https:// URL. |
code | string | No | Your own short code — 1–10 letters and numbers. Omit it and one is generated for you. |
expiresAt | string | No | When the link should stop redirecting, as a date. Omit it and the link never expires. |
pinned | boolean | No | true pins the link so it's never cleaned up. Defaults to false. |
Example
curl -X POST https://e-sat.ly/api/v1/links \
-H "Authorization: Bearer YOUR_PROJECT_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/survey?id=12345"}'
{
"code": "4kQm2",
"url": "https://example.com/survey?id=12345",
"shortUrl": "https://e-sat.ly/4kQm2",
"createdTime": "2026-07-17T09:31:04.000Z",
"expiresAt": "2123-09-20T17:14:30.000Z",
"pinned": false
}
shortUrl is the link to send. Returns 201.
That expiry date in the year 2123
A link that never expires reports a placeholder date far in the future rather than an empty value. If expiresAt is in 2123, read it as "never expires". Don't read it as "kept until 2123" — how long a link is kept is data retention, which is a different thing entirely.
With an expiry
curl -X POST https://e-sat.ly/api/v1/links \
-H "Authorization: Bearer YOUR_PROJECT_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/promo",
"expiresAt": "2026-12-31T23:59:59Z"
}'
A pinned link
curl -X POST https://e-sat.ly/api/v1/links \
-H "Authorization: Bearer YOUR_PROJECT_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/poster-campaign",
"pinned": true
}'
pinned and expiresAt can't be combined
Sending both is rejected with a 400 rather than resolved by guessing. A pinned link never expires, so the two instructions contradict each other — and picking a winner silently would either kill a link meant to live forever or keep one meant to die. Send one or the other.
A custom code
curl -X POST https://e-sat.ly/api/v1/links \
-H "Authorization: Bearer YOUR_PROJECT_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/spring",
"code": "spring26"
}'
If the code is already taken you get a 409. Codes are case-sensitive.
Creating the same URL twice
Creating a link for a URL your project has already shortened returns the existing link — the same code — instead of minting a second one. Retries and re-runs are safe.
Two details worth knowing:
- The check is per project. A different project shortening the same URL gets its own separate link.
- Sending
pinned: truefor a URL that already has a link pins the existing link rather than returning it unchanged — you asked for a pinned link, so you get one. A normal create (pinnedomitted orfalse) never unpins an existing link.
List links
GET https://e-sat.ly/api/v1/links
Returns up to 100 links from the key's project, newest first, each with its click count.
curl https://e-sat.ly/api/v1/links \
-H "Authorization: Bearer YOUR_PROJECT_KEY"
{
"data": [
{
"code": "4kQm2",
"url": "https://example.com/survey?id=12345",
"shortUrl": "https://e-sat.ly/4kQm2",
"clicks": 3,
"createdTime": "2026-07-17T09:31:04.000Z",
"expiresAt": "2123-09-20T17:14:30.000Z",
"pinned": false
}
]
}
This endpoint is a convenience for checking your own recent links, not a reporting API — there's no paging, filtering or date range. For real analysis use the console's Clicks screens.
The /create endpoint
POST https://e-sat.ly/create
The original endpoint, kept compatible for existing integrations. Same authentication; different parameter names and a different response shape.
| Parameter | Type | Required | Description |
|---|---|---|---|
url | string | Yes | The destination. Absolute http:// or https://. |
expiration_date | string | No | When the link stops redirecting. Omit and it never expires. |
pinned | boolean-ish | No | Pins the link. Accepts true, 1, on or yes. |
Unlike /api/v1/links, this endpoint accepts JSON, form-encoded or multipart bodies interchangeably — which is why pinned takes string forms like "1" here, since a form field can't carry a real boolean.
curl -X POST https://e-sat.ly/create \
-H "Authorization: Bearer YOUR_PROJECT_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/survey?id=12345"}'
{
"status": true,
"long_url": "https://example.com/survey?id=12345",
"short_url": "https://e-sat.ly/4kQm2",
"pinned": false
}
Form-encoded, which is equally valid:
curl -X POST https://e-sat.ly/create \
-H "Authorization: Bearer YOUR_PROJECT_KEY" \
-d "url=https://example.com/survey?id=12345" \
-d "pinned=1"
Like /api/v1/links, it deduplicates per project and it rejects pinned together with expiration_date.
Errors
Both create endpoints use standard status codes. /api/v1/links returns {"error": "…"}; /create returns {"status": false, "error": "…"}.
| Code | Meaning | Common causes |
|---|---|---|
| 400 | The request was wrong | url missing or not an absolute http(s) URL; an unparseable date; a code that isn't 1–10 letters and numbers; pinned sent with an expiry; malformed JSON |
| 401 | Authentication failed | Key missing, mistyped, or re-issued since your system last got it |
| 409 | Conflict | The code you asked for is already taken |
A 401 that appears suddenly on an integration that was working almost always means the project key was re-issued and the new value hasn't reached your system yet.
Practical notes
- Store the key as a secret. It creates links in your project and lists what's there. Keep it in your environment configuration or secret store, never in front-end code or a repository.
- Send the
shortUrlyou get back. Don't build the link yourself by gluing the domain to the code. - A created link is not a kept link. Every link you create through the API is transactional unless you pin it, which means it's cleaned up once it ages past your project's retention window. If your integration creates links that need to live forever, pass
pinned: true. - You don't need this for survey sends. The suite already shortens SMS and Viber links for you — see URL shorteners. The API is for links your own systems create.