#What this API solves
Teams usually need more than “shorten this URL”. You want safe naming (alias vs code), campaign controls (expiry, one-time, click limits, password), and readable stats that answer “what’s working?”. This suite provides exactly that — with two creation modes (basic vs advanced) so you only pay for analytics you actually use.
#Endpoints & when to use them
#POST /v1/short-links/create-basic — Create link (cheaper)
- Best for: Operational links where you only need totals & last click (fast & low cost).
- Output:
advanced_analytics=false,total_clicksmay be present; no per-country/device buckets.
#POST /v1/short-links/create-advanced — Create link with rich analytics
- Best for: Campaigns where you’ll later slice by country, device, browser, OS.
- Output:
advanced_analytics=true. The stats endpoint returns buckets.
#POST /v1/short-links/get — Fetch by alias or short_code
- Ownership enforced: You’ll only see links owned by your API key/user.
#POST /v1/short-links/update — Change URL/alias/code/limits
- Conflicts handled: Server rejects duplicate
alias/short_codeacross live & soft-deleted rows. - Settings: Passing
settingsreplaces the whole key/value set (idempotent).
#POST /v1/short-links/delete — Soft delete by code
- Tip: Deleted aliases/codes still count for conflict checks to prevent accidental reuse collisions.
#POST /v1/short-links/stats — Summary & buckets
- Period:
7d|30d|90d(default30d). - Recent clicks: up to 200 latest (
limit_recent). - Buckets: Country/Device/Browser/OS (when
advanced_analytics=true). - Password metrics: When derivable, returns
password_page_views_*andpassword_attempts_total. - Debug mode: Include
"debug":trueto surface query diagnostics in the response.
#Quick start
# Create a basic link
curl -sX POST "https://api.yeb.to/v1/short-links/create-basic" \
-H "Accept: application/json" -H "Content-Type: application/json" \
-d '{ "api_key":"<YOUR_KEY>", "original_url":"https://example.com/pricing", "alias":"docs-demo" }'
# Retrieve stats (30d default) with 10 recent clicks
curl -sX POST "https://api.yeb.to/v1/short-links/stats" \
-H "Accept: application/json" -H "Content-Type: application/json" \
-d '{ "api_key":"<YOUR_KEY>", "code":"docs-demo", "limit_recent":10 }'
#Full “everything on” request (covers most options)
Turn features on, then trim to your needs.
POST /v1/short-links/create-advanced
{
"api_key": "YOUR_KEY",
"original_url": "https://example.com/launch?utm_source=short",
"alias": "docs-advanced-demo",
"short_code": "AB12CDE",
"password": "letmein",
"expires_at": "2025-12-31T23:59:00Z",
"click_limit": 1000,
"one_time": false,
"advanced_analytics": true,
"settings": [
{"key":"campaign","value":"winter-2025"},
{"key":"owner","value":"growth-team"}
]
}
#Parameters that actually matter
Create / Update
| Param | Type | Required | Practical guidance |
|---|---|---|---|
original_url | string (URL) | Yes (create) | Scheme auto-added if missing; keep UTM here for external analytics. |
alias | string | No | Readable path (e.g., /l/black-friday). Great for campaigns. |
short_code | string | No | Fixed-length code. If omitted, server generates 7 chars. Use for printed collateral. |
password | string | No | Gate sensitive pages. On update, send empty string to clear. |
expires_at | ISO 8601 | No | Hard stop for promo windows. Combine with stats to prune stale links. |
click_limit | int | No | Cap total visits (e.g., limited seats). Pair with one_time for single-use passes. |
one_time | bool | No | First successful click consumes the link. |
advanced_analytics | bool | No | Enable per-country/device/browser/OS buckets in /stats. |
settings | array<{key,value}> | No | Free-form metadata. On update, the set is replaced atomically. |
Stats request
| Param | Type | Required | Notes |
|---|---|---|---|
code | string | Yes | Either the alias or the short_code. |
period | enum | No | 7d | 30d | 90d (default 30d). |
limit_recent | int | No | 0–200 (default 20). Returns latest clicks with IP/UA timestamped. |
tz | string | No | Informational field echoed back (visualization hint). |
debug | bool | No | true adds query diagnostics to response (helpful in staging). |
#Reading & acting on responses
Create (basic vs advanced)
{
"data": {
"original_url": "https://example.com/pricing",
"short_code": "AB12CDE",
"alias": "docs-demo",
"public_url": "https://yeb.to/l/docs-demo",
"expires_at": null,
"click_limit": null,
"one_time": false,
"advanced_analytics": false,
"total_clicks": 0,
"created_at": "2025-01-01T12:00:00Z",
"updated_at": "2025-01-01T12:00:00Z"
}
}
- Print/embed: Use
public_urlon sites or QR codes. - Auditing: Keep
aliasandshort_codein your DB for future updates.
Get / Update
{
"data": {
"original_url": "https://example.com/pricing",
"short_code": "AB12CDE",
"alias": "docs-demo",
"public_url": "https://yeb.to/l/docs-demo",
"expires_at": "2025-12-31T23:59:00Z",
"click_limit": 100,
"one_time": false,
"advanced_analytics": false,
"total_clicks": 0,
"created_at": "2025-01-01T12:00:00Z",
"updated_at": "2025-01-10T12:00:00Z"
}
}
- Conflicts: If you change
aliasorshort_codeto one that exists (even soft-deleted), you’ll get a 422 explaining the conflict. - Password: On update, send empty string to clear. Non-empty strings are stored hashed.
Stats
{
"data": {
"code": "docs-demo",
"from": "2025-01-01T00:00:00Z",
"to": "2025-01-31T00:00:00Z",
"tz": "UTC",
"summary": {
"total_clicks": 42,
"last_click_at": "2025-01-30T20:05:00Z",
"unique_countries": 8,
"password_page_views_total": 12,
"password_page_views_unique": 10,
"password_attempts_total": 3
},
"recent_clicks": [
{"ip":"1.2.3.4","user_agent":"...","ts":"2025-01-30T20:05:00Z"}
],
"by_country": [{"key":"US","count":20}],
"by_device": [{"key":"mobile","count":30}],
"by_browser": [{"key":"Chrome","count":25}],
"by_os": [{"key":"Android","count":18}]
}
}
- Basic vs Advanced: Buckets populate only when the link was created with
advanced_analytics=trueand underlying tables exist. - Recent list: Use it for moderation/debugging; don’t store full UA/IP long-term if you don’t need them.
#Practical recipes
- Campaign naming: Use
aliasfor human-readable slugs (/l/summer-sale), keepshort_codefor printed QR where length matters. - Time-boxed promos: Set
expires_atandclick_limit. After the window, update the link to a “campaign over” page. - Single-use access: Combine
one_time=truewith a password. Track attempts via/statspassword metrics. - Attribution: Include UTMs in
original_url. The API doesn’t change your query string. - Governance: Store
settingslikecampaign,owner,cost_centerfor internal reporting.
#Errors & safeguards
422— Validation (missingoriginal_url,code, or alias/code conflict).404— Not found (wrongcodeor not owned by your key/user).- Ownership: All read/write endpoints scope by API key/user; non-owned links behave as “not found”.
#API Changelog (Short Links)
debug flag to /stats response for query introspection.
password_page_views_* and password_attempts_total when derivable.
create-basic and create-advanced with bucketed stats support.
Use the endpoint playgrounds on this page to test payloads and lock defaults (alias pattern, expiry policy, analytics mode).
#Copy-ready cURL (common flows)
# Create (basic)
curl -sX POST "https://api.yeb.to/v1/short-links/create-basic" -H "Content-Type: application/json" \
-d '{"api_key":"YOUR_KEY","original_url":"https://example.com/pricing","alias":"docs-demo"}'
# Create (advanced)
curl -sX POST "https://api.yeb.to/v1/short-links/create-advanced" -H "Content-Type: application/json" \
-d '{"api_key":"YOUR_KEY","original_url":"https://example.com/pricing","alias":"docs-demo","click_limit":100,"expires_at":"2025-12-31T23:59:00Z"}'
# Get
curl -sX POST "https://api.yeb.to/v1/short-links/get" -H "Content-Type: application/json" \
-d '{"api_key":"YOUR_KEY","code":"docs-demo"}'
# Update
curl -sX POST "https://api.yeb.to/v1/short-links/update" -H "Content-Type: application/json" \
-d '{"api_key":"YOUR_KEY","code":"docs-demo","click_limit":100,"expires_at":"2025-12-31T23:59:00Z"}'
# Stats
curl -sX POST "https://api.yeb.to/v1/short-links/stats" -H "Content-Type: application/json" \
-d '{"api_key":"YOUR_KEY","code":"docs-demo","period":"30d","limit_recent":10}'
# Delete
curl -sX POST "https://api.yeb.to/v1/short-links/delete" -H "Content-Type: application/json" \
-d '{"api_key":"YOUR_KEY","code":"docs-demo"}'