Custom Domain Branded URL Shortener API Documentation
Not seeing what you need? No problem, sometimes one size doesn't fit all. Our platform was designed to be flexible. Contact Us we'd love the chance to work out a custom solution.- General
- API keys
- Getting account info
- Shortening URLs
- Reading URLs
- Editing URLs
- Deleting URLs
- Stats
- Tags
- Domains
- Account Log
- Group Operations
- Error Codes
- Clint Libraries
General
This is v3 of TINYCC REST API. It conforms to the REST API concept.
API root URL: https://tinycc.com/tiny/api/3/
HTTPs and HTTP supported. But HTTPs recommended.
If you have problem using PATCH HTTP method, we support method override for POST calls via X-HTTP-Method-Override header.
Recommended encoding for request's payload - "application/json".
However "application/x-www-form-urlencoded" and "multipart/form-data" are also supported for POST requests.
If you want to make PATCH (or other non POST) request with encoding other then "application/json",
you should use POST method and set X-HTTP-Method-Override header.
GET-parameters should be "percent-encoded".
Responses always encoded with "application/json".
All date-time values in API are in UTC±00:00.
Inactive accounts can't access API. They will get error 1217.
Custom API Keys for Branded URL Shortening
Authentication: HTTP Basic.
Use your username:api-key for user:password.
johndoe:c2es9d56-045d-4d87-b3f2-419274f4e001
Username determines owner of the URLs that you shortening.
API key determines methods that you allowed to call on different API resources.
For example, you can create read-only API-key. Or you can restrict API key access to certain domains in your account. Or you can restrict IP-addresses allowed to use that API key.
Please login to your account to manage your API keys and their permissions.
Getting account info
GET /account
Input params: none
Example response:
{
"error": {
"code": 0,
"message": ""
},
"account": {
"user_id": "4274",
"username": "johndoe",
"account_expiration_date": "2015-08-30 20:57:03",
"subscription_plan": "Secure Plan (1 year)",
"counters": {
"urls": {
"count": 13,
"limit": 5000
},
"today_api_calls": {
"count": 8,
"limit": 500
},
"month_urls": {
"count": 13,
"limit": 500
}
}
}
}
Response contains information about different counters.
These fields depend on server configuration and may vary from server to server.
Shortening URLs
POST /urls
Input params:
{
"urls": [
{
"custom_hash": "mytiny",
"long_url": "http://example.com",
"note": "lots of notes...",
"email_stats": false,
"expiration_date": "2016-01-02",
"tags":["group1"]
}
]
}
All fields are optional except "long_url". Default values:- "custom_hash" - autogenerated
- "note" - ""
- "email_stats" - false
- "ping" - false
- "protected" - false
- "archived" - false
- "expiration_date" - null
- "max_clicks" - 0
- "tags" - []
Maximum length of "long_url" limited to 2048. Maximum length of "custom_hash" limited to 25. Maximum length of "note" is 255 characters.
Example of successful response:
{
"error": {
"code": 0,
"message": ""
},
"urls": [
{
"hash": "mytiny",
"long_url": "http://example.com",
"short_url": "johndoe.com/mytiny",
"short_url_with_protocol": "http://johndoe.com/mytiny",
"total_clicks": 0,
"unique_clicks": 0,
"note": "lots of notes...",
"email_stats": false,
"expiration_date": "2016-01-02",
"max_clicks": 0,
"tags":["group1"],
"links":{
"qr_small": ...,
"qr_big": ...
},
"error": {
"code": 0,
"message": "",
"details": ""
}
}
]
}
Example of shortening errors:
{
"error": {
"code": 0,
"message": ""
},
"urls": [
{
"long_url": "http://google.com",
"custom_hash": "h",
"error": {
"code": 1215,
"message": "Short URL is not correct or existed.",
"details": "That Custom hash is already in use"
}
}
]
}
In both cases (successful shortening or shortening errors)
HTTP status code - 200 OK and top-level "error" block indicates no errors.
But inside "urls" array, some URLs may have individual error message.
In above example, one URL was successfully shortened. And other URL was not shortened.
Normally our system allows creation of multiple different short links containing exactly the same long or destination URL.
But it is also possible to avoid making any new, unique short hashes if the long url had been previously submitted.
When
your API call doesn't specify a custom_hash it is possible to avoid duplication of long URLs by
passing the optional parameter "disable_long_url_duplicates":
POST /urls?disable_long_url_duplicates=account
In this case, new records won't be created for existing long URLs. Records from previously created links will be returned instead.
If you specify custom_hash together with "disable_long_url_duplicates" parameter, three scenarios are possible:
- If custom_hash does not exist, new record will be created
- If custom_hash exists with same long URL, existing records will be returned
- If custom_hash exists with different long URL, error 1215 will be indicated
If you are using several domains for shortening, you may check for existing long URL only in your working domain (controlled by "domain" parameter). To do so, use disable_long_url_duplicates=domain. Otherwise, check will be performed among all domains in your account.
Reading URLs
GET /urls
- get a batch (100) of most recent URLs.
{
"error": {
"code": 0,
"message": ""
},
"urls": [
{
"hash": "h",
"long_url": "http://google.com",
"short_url": "m.tiny.cc/h",
"total_clicks": 0,
"unique_clicks": 0,
"last_visit": null,
"email_stats": false,
"protected": false,
"ping": false,
"archived": false,
"note": "",
"expiration_date": null,
"max_clicks": 0,
"tags":[],
"links":{
"qr_small": ...,
"qr_big": ...
}
},
{
"hash": "g",
"long_url": "http://yahoo.com",
"short_url": "m.tiny.cc/g",
"total_clicks": 0,
"unique_clicks": 0,
"last_visit": null,
"email_stats": false,
"protected": false,
"ping": false,
"archived": false,
"note": "",
"expiration_date": null,
"max_clicks": 0,
"tags":[],
"links":{
"qr_small": ...,
"qr_big": ...
}
},
.
.
.
],
"page": {
"results_count": 13,
"total_count": 13,
"offset": 0
}
}
HTTP status code - 200 OK
It is also possible to paginate URLs like this
GET /urls?offset=10&limit=20
Other supported parameters:
- "search" - search string (match "long_url", "note" or "hash" fields). In case you are using "search" parameter, property page.total_count in response will indicate total number of matching URLs.
- "order_by" - possible values: "created" - Creation date, "clicks" - Total clicks, "hash" - Tiny alphabetical, "modified" - Modification date
Example:
GET /urls?offset=5&limit=1&search=john&order_by=clicks
Read single URL:
GET /urls/[hash]
To read a group of URLs you need to pass a group of hashes in query parameter "hashes", separated with comma.
For example, lets pass a group of three hashes (a,b,c):
GET /urls?hashes=a%2Cb%2Cc
Each individual URL entry has "links" section listing locations of related resources.
Editing URLs
There are three styles to edit URLs.First style - edit single URL.
PATCH /urls/[current_hash]
Input params:
{
"urls": [
{
"custom_hash": "new_hash",
"long_url": "http://example.com",
"note": "new note",
"email_stats": false,
"expiration_date": "2016-01-02",
"tags":["+new_tag","-old_tag"]
}
]
}
All fields are optional. Tags field contains instructions for adding and removing tags. Tags prefixed with "+" will be attached to URL. Tags prefixed with "-" will be detached from URL.
Second style - set same properties for many URLs.
URL hashes should be listed in "hashes" parameter and separated by comma.
PATCH /urls?hashes=a%2Cb%2Cc
Input params:
{
"urls": [
{
"note": "collective note"
}
]
}
The above call will set "note" property for URLs with hashes "a","b" and "c".
Third style - arbitrary edit many URLs
PATCH /urls
Input params:
{
"urls": [
{
"hash": "a",
"note": "polite note"
},
{
"hash": "b",
"note": "creative note"
},
{
"hash": "c",
"note": "boring note"
}
]
}
In this case "hash" field is mandatory.Response of all three styles have the same structure. And it is similar to the structure of POST call ("page" section is not included).
It is also possible to edit many URLs using tags. But is this case only "note", "expiration_date", "max_clicks", "email_stats", "ping", "protected", "archived" fields may be edited.
When editing with tags, no URLs returned with response.
Deleting URLs
DELETE /urls/[hash]
Input params: hash - tiny hash associated with URL.
It is also possible to pass a group of hashes in query parameter "hashes", separated with comma.
For example, lets pass a group of three hashes (a,b,c):
DELETE /urls?hashes=a%2Cb%2Cc
Example response:
{
"error": {
"code": 0,
"message": ""
}
}
HTTP status code - 200 OK
NOTE: URLs with "protected" flag never deleted!
Stats
Reading click stats for individual URL:GET /stats/[hash]
Example response:
{
"error": {
"code": 0,
"message": ""
},
"stats": [
{
"hash": "e",
"total_clicks": 22,
"unique_clicks": 1,
"daily_clicks": {
"2023-04-20": {
"total": 1,
"unique": 1
}
}
}
]
}
Resetting stats for individual URL:
DELETE /stats/[hash]
Input params: hash - tiny hash associated with URL.
It is also possible to pass a group of hashes in query parameter "hashes", separated with comma.
For example, lets pass a group of three hashes (a,b,c):
DELETE /stats?hashes=a%2Cb%2Cc
Example response:
{
"error": {
"code": 0,
"message": ""
}
}
HTTP status code - 200 OK
Tags
Each URL may have tags attached to it. Tags have their own endpoint /tags. Tag labels are not case-sensitive.GET /tags
- read list of all existing tags
Example response:
{
"error": {
"code": 0,
"message": ""
},
"tags": [
{
"label": "banana"
},
{
"label": "apple"
}
]
}
POST /tags
- create new tag (if it is not exist).Input params:
{
"label": "pear"
}
Example response:
{
"error": {
"code": 0,
"message": ""
},
"tags": [
{
"label": "pear"
}
]
}
PATCH /tags/[label]
- edit (rename) existing tag.Input params:
{
"label": "new_label"
}
Example response:
{
"error": {
"code": 0,
"message": ""
},
"tags": [
{
"label": "new_label"
}
]
}
DELETE /tags/[label]
- delete tag with given label.For efficiency, many operations with URLs may be performed using tags. In this case, "hashes" query parameter should be omitted. And "tags" query parameter should be used.
Example of reading first 10 URLs tagged with label "fruit":
GET /urls?tags=fruit&limit=10
Tags parameter may be used for reading URLs, patching URLs, deleting URLs and deleting stats.
Tags parameter support complex syntax with logical operators and brackets. More info here.
Domains
GET /domains
- read list of shortening domains in account
Example response:
{
"error": {
"code": 0,
"message": ""
},
"version": "3.1",
"domains": [
{
"domain": "custom.com",
"hash_character_set": "restricted",
"hash_generation": "incremental",
"status": "active",
"custom_root_page": "",
"custom_404_page": "",
"redirect_code": "303",
"use_https": false
},
{
"domain": "custom2.com",
"hash_character_set": "restricted",
"hash_generation": "random",
"status": "default",
"custom_root_page": "http://mysite.net",
"custom_404_page": "",
"redirect_code": "303",
"use_https": true
}
]
}
By default, all API calls affect only URLs in default domain (has status set to "default"). To specify different working domain (for shortening or other operations), you should add query parameter "domain":
GET /urls?domain=custom.com&hashes=a,b,c
The above call will fetch URLs from domain custom.com. URLs with same hashes, that exists in other domains won't be fetched. In case of invalid domain, you will get error 1313.
"domain" parameter doesn't work together with "tags" parameter. If "tags" parameter specified, "domain" will be ignored. In other words, tags works across all your domains.
If you are working with multiple domains, we recommend to always specify "domain" parameter. This will help you avoid many errors and ambiguous situations.
Note that internal representation of internationalized domain names handled according to RFC3490.
Account Log
GET /log
- read most recent account log entries
Example response:
{
{
"error": {
"code": 0,
"message": ""
},
"version": "3.1",
"page": {
"results_count": 2,
"total_count": 37,
"offset": 0
},
"links": {
"rss_url": "https://tinycc.com/tiny/account_log/rss?u=1&t=c4c9fbb5fafe03d1340988bdc4604b78152ce429ae64cba72433f",
"atom_url": "https://tinycc.com/tiny/account_log/atom?u=1&t=c4c9fbb5fafe03d1340988bdc4604b78152ce429ae64cba72433f"
},
"log": [
{
"message": "Logout john_smith",
"time": "2017-03-09 08:35:01",
"type": "notification",
"satellite_username": null,
"links": {
"object_url": null,
"help_url": null
}
},
{
"message": "Login john_smith",
"time": "2017-03-08 11:56:52",
"type": "notification",
"satellite_username": null,
"links": {
"object_url": null,
"help_url": null
}
}
]
}
- "rss_url" - log feed in RSS 2.0 format (with current filters applied, except "date")
- "atom_url" - log feed in Atom 1.0 format (with current filters applied, except "date")
Response may contain following optional fields:
- "satellite_username" - username of satellite account (if event associated with satellite account)
- "object_url" - page with additinal details about error or result of operation
- "help_url" - related help page
It is also possible to paginate log entries like this
GET /log?offset=10&limit=20
Other supported parameters:
- "search" - search for messages contaning keyword. Example:
GET /log?search=brand
- "date" - show only log entries from specified date. Example:
GET /log?date=2017-01-10
- "type" - show only log entries specified type. Possible values: "error" - show only errors, "warn" - show only warnings and errors.
Example:
GET /log?type=warn
- "account" - show only log entries related to scpecific account.
Value should be satellite account name or "master" (show only entries from master account).
Example:
GET /log?account=john_smith
Group Operations
Group operations with URLs are limited to a maximum number of items. Currently: 100.
If you exceed this limit, you will experience an error 1221.
This limitation doesn't apply to operations with usage of tags (PATCH, DELETE).
It, for example, means, that you are allowed to delete a limited number of URLs listing their hashes in "hashes" parameter.
But you can delete URLs, that conform with tags query all in single call.
Client libraries README file contains a "Mass shortening" reference which means group or "batch."
There is no practical difference between "batch" and "not batch" operations.
All operations are "batch". Batch can contain a single entity or multiple entities.
For convenience, two different methods are shown in clients:
• shorten() to work with a single URL.
• mass_shorten() to work with arrays of URLs.
Both methods use the same API endpoint. And produce similar results.
Error Codes
0 -- '' (empty message string means OK)
101 -- 'Unknown error'
1102 -- 'Invalid username'
1204 -- 'Not a valid Short URL hash'
1206 -- 'URL you tried to shorten was invalid'
1215 -- 'Short URL is not correct or existed'
1216 -- 'URL you tried to shorten is not allowed'
1217 -- 'Access Denied'
1218 -- 'Short URL is not correct'
1219 -- 'Target server on cooldown'
1220 -- 'URLs limit has been reached'
1221 -- 'Exceeded limit of batch operation'
1300 -- 'Invalid API key'
1301 -- 'Invalid resource'
1302 -- 'Method not supported'
1303 -- 'API call limit reached'
1305 -- 'Missing input parameters'
1306 -- 'URL not found in DB'
1307 -- 'Wrong number of input parameters'
1308 -- 'HTTP error'
1309 -- 'Tag not found'
1310 -- 'Too big POST data'
1311 -- 'Invalid or duplicate tag label'
1312 -- 'Invalid date'
1313 -- 'Domain not found'
1314 -- 'Invalid webhook parameter'
1315 -- 'Requested domain not allowed for this API key'
Example error response:
{
"error": {
"code": 1300,
"message": "Invalid API key"
}
}
HTTP status code - 400 Bad Request. Other 40x and 50x codes may be also used.
Clint Libraries
- PHP REST Client
general purpose REST client. - Native Tinycc Client.
- jQuery REST Client
general purpose REST client. - Native Tinycc Client.