MinifyURL API

Programmatic access to all MinifyURL features. Create, manage and analyse short links from any language, automation tool, or automation script.

🔑 Requires Pro plan
⚡ Fastest URL Shortener in India

Base URL

HTTPS https://minifyurl.in/api/v1

All responses are application/json. Send POST request bodies as JSON with Content-Type: application/json.

Authentication

Pass your API key as a Bearer token in the Authorization header on every request.

Authorization: Bearer mfy_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Generate your key from Dashboard → API Access. Keys start with mfy_ and are shown only once — store them securely.

Tip: Never hardcode your key. Store it in an environment variable or encrypted secrets manager.

Response Format

Every response — success or error — follows the same consistent envelope. There is no ok field. Use status to determine what happened.

success Success response
{ "status": "success", "status_code": 200, "data": { ... } }
error Error response
{ "status": "error", "status_code": 404, "message": "Link not found" }
created New resource
{ "status": "created", "status_code": 201, "message": "Short link created successfully", "data": { ... } }
duplicate Already exists
{ "status": "duplicate", "status_code": 409, "message": "Already shortened. Use GET /api/v1/links" }
FieldTypeAlways presentNotes
statusstringHuman-readable outcome — see Status Codes section below
status_codeintegerMirrors the HTTP status code
messagestringOn errors & createsDescription of what happened or went wrong
dataobjectOn successThe resource or result — shape varies by endpoint
Deluge pattern: Always check response.get("status") — not a boolean field. On error, read response.get("message") for a plain-English description.

Status & Codes

The status string tells you the exact outcome. The status_code mirrors the HTTP status code. Use either in your code — both are always present.

Success statuses

statusHTTPWhen returned
success 200 GET request returned data — /me, /links, /links/:code, /stats, /qr
created 201 New short link was created — POST /shorten first time
updated 200 Link was modified — PATCH /links/:code
deleted 200 Link was permanently deleted — DELETE /links/:code

Error statuses

statusHTTPWhen returned
duplicate 409 POST /shorten called with a URL you've already shortened, or custom slug already taken. No data returned — use GET /api/v1/links to retrieve the existing link.
error 400 Bad request — missing or invalid parameter, URL blocked by spam filter
error 401 Unauthorized — missing or invalid API key
error 403 Forbidden — feature requires Pro plan, or link limit reached
error 404 Not found — link code doesn't exist or doesn't belong to your account
error 429 Rate limited — too many requests, slow down
error 500 Server error — something went wrong on our side

Handling in Deluge

response = invokeurl[ ... ]; status = response.get("status"); if(status == "created") { // New link — read response.get("data").get("short_url") } else if(status == "duplicate") { // Already exists — call GET /api/v1/links to find it // response.get("message") tells you what happened } else if(status == "error") { // Something went wrong — log response.get("message") info "Error: " + response.get("message"); }

Account

GET/api/v1/me Account & plan info

Returns your account details, Pro status, and current link usage vs your plan limit.

API_KEY = "mfy_xxxx"; response = invokeurl [ url: "https://minifyurl.in/api/v1/me" type: GET headers: {"Authorization": "Bearer " + API_KEY} ]; if(response.get("status") == "success") { user = response.get("data"); info user.get("link_count") + " / " + user.get("link_limit") + " links used"; }
Response — success
{ "status": "success", "status_code": 200, "data": { "id": 1, "email": "you@example.com", "name": "Your Name", "is_pro": true, "is_paid_pro": true, "pro_expires_at": "2026-05-21T00:00:00.000Z", "trial_used": false, "link_count": 12, "link_limit": 100 } }

Links

POST/api/v1/shorten Create a short link

Shorten a URL. Send parameters as a JSON body. Query string params are accepted for legacy compatibility.

FieldTypeNotes
urlrequiredstringDestination URL. Must include https://
custom_slugoptionalPROstringCustom code e.g. launch → minifyurl.in/launch
expires_inoptionalPROintegerDays until expiry
passwordoptionalPROstringVisitors must enter this before redirecting
activate_atoptionalPROISO 8601Link inactive until this datetime
Duplicate behaviour: If you shorten the same URL again (no custom_slug), the API returns 409 duplicate — not a success. No data is returned. Use GET /api/v1/links to retrieve the existing link.
Basic
API_KEY = "mfy_xxxx"; body = Map(); body.put("url", "https://example.com/very/long/path"); response = invokeurl [ url: "https://minifyurl.in/api/v1/shorten" type: POST headers: {"Authorization": "Bearer " + API_KEY, "Content-Type": "application/json"} body: body.toString() ]; status = response.get("status"); if(status == "created") { info "New link: " + response.get("data").get("short_url"); } else if(status == "duplicate") { info response.get("message"); // "Already shortened. Use GET /api/v1/links" } else { info "Error: " + response.get("message"); }
With custom slug + expiry (Pro)
API_KEY = "mfy_xxxx"; body = Map(); body.put("url", "https://example.com/product-launch"); body.put("custom_slug", "launch26"); body.put("expires_in", 30); response = invokeurl [ url: "https://minifyurl.in/api/v1/shorten" type: POST headers: {"Authorization": "Bearer " + API_KEY, "Content-Type": "application/json"} body: body.toString() ]; if(response.get("status") == "created") { info response.get("data").get("short_url"); }
In a CRM workflow — shorten Lead's website on creation
API_KEY = "mfy_xxxx"; recordId = input.get("recordId"); record = zoho.crm.getRecordById("Leads", recordId); longUrl = record.get("Website"); if(longUrl != null && longUrl.startsWith("http")) { body = Map(); body.put("url", longUrl); response = invokeurl [ url: "https://minifyurl.in/api/v1/shorten" type: POST headers: {"Authorization": "Bearer " + API_KEY, "Content-Type": "application/json"} body: body.toString() ]; if(response.get("status") == "created") { updateMap = Map(); updateMap.put("Short_Website", response.get("data").get("short_url")); zoho.crm.updateRecord("Leads", recordId, updateMap); } }
Response — created (HTTP 201)
{ "status": "created", "status_code": 201, "message": "Short link created successfully", "data": { "short_url": "https://minifyurl.in/launch26", "code": "launch26" } }
Response — duplicate (HTTP 409)
{ "status": "duplicate", "status_code": 409, "message": "This URL has already been shortened. Use GET /api/v1/links to retrieve it." }
GET/api/v1/links List all links

Returns a paginated list of your short links. Use this to find a link after a duplicate response from POST /shorten.

Query ParamTypeDefaultNotes
limitoptionalinteger50Max 100 per page
offsetoptionalinteger0Pagination offset
searchoptionalstringFilter by URL or code (case-insensitive)
API_KEY = "mfy_xxxx"; response = invokeurl [ url: "https://minifyurl.in/api/v1/links?limit=20&offset=0" type: GET headers: {"Authorization": "Bearer " + API_KEY} ]; if(response.get("status") == "success") { links = response.get("data").get("links"); for each link in links { info link.get("short_url") + " clicks: " + link.get("hits"); } }
Response — success
{ "status": "success", "status_code": 200, "data": { "links": [ { "code": "launch26", "short_url": "https://minifyurl.in/launch26", "original_url": "https://example.com/product-launch", "hits": 42, "custom_slug": true, "password_protected": false, "expires_at": "2026-05-17T00:00:00.000Z", "activated_at": null, "created_at": "2026-04-17T08:00:00.000Z" } ], "count": 1, "total": 12, "limit": 20, "offset": 0 } }
PATCH/api/v1/links/:code Update a link

Update the destination URL, expiry, or activation date. Only params you send are changed.

Query ParamTypeNotes
urloptionalstringNew destination URL
expires_inoptionalintegerDays from now. Pass empty string to remove expiry
activate_atoptionalISO 8601New activation time. Pass empty string to remove
API_KEY = "mfy_xxxx"; response = invokeurl [ url: "https://minifyurl.in/api/v1/links/launch26?url=https://newdestination.com&expires_in=14" type: PATCH headers: {"Authorization": "Bearer " + API_KEY} ]; if(response.get("status") == "updated") { info "Updated: " + response.get("data").get("short_url"); } else { info "Error: " + response.get("message"); }
Response — updated
{ "status": "updated", "status_code": 200, "data": { "code": "launch26", "short_url": "https://minifyurl.in/launch26", "original_url": "https://newdestination.com", "hits": 42, "custom_slug": true, "password_protected": false, "expires_at": "2026-05-01T00:00:00.000Z", "activated_at": null, "created_at": "2026-04-17T08:00:00.000Z" } }
DELETE/api/v1/links/:code Delete a link

Permanently delete a link. The short code stops redirecting immediately. Cannot be undone.

API_KEY = "mfy_xxxx"; response = invokeurl [ url: "https://minifyurl.in/api/v1/links/launch26" type: DELETE headers: {"Authorization": "Bearer " + API_KEY} ]; if(response.get("status") == "deleted") { info "Deleted: " + response.get("data").get("deleted_code"); } else { info "Error: " + response.get("message"); }
Response — deleted
{ "status": "deleted", "status_code": 200, "data": { "deleted_code": "launch26" } }

Analytics

GET/api/v1/links/:code/stats Click analytics

Returns click analytics: countries, browsers, devices, referrers, hourly breakdown, daily trend, UTM data.

Query ParamTypeDefaultNotes
daysoptionalinteger30Lookback period. Max 90.
Get stats
API_KEY = "mfy_xxxx"; response = invokeurl [ url: "https://minifyurl.in/api/v1/links/launch26/stats?days=7" type: GET headers: {"Authorization": "Bearer " + API_KEY} ]; if(response.get("status") == "success") { d = response.get("data"); info "Total clicks: " + d.get("link").get("hits"); countries = d.get("countries"); if(countries.size() > 0) { info "Top country: " + countries.get(0).get("country") + " — " + countries.get(0).get("count") + " clicks"; } }
Sync click count back to a CRM record
API_KEY = "mfy_xxxx"; recordId = input.get("recordId"); record = zoho.crm.getRecordById("Deals", recordId); code = record.get("Short_Code"); response = invokeurl [ url: "https://minifyurl.in/api/v1/links/" + code + "/stats?days=30" type: GET headers: {"Authorization": "Bearer " + API_KEY} ]; if(response.get("status") == "success") { d = response.get("data"); updateMap = Map(); updateMap.put("Link_Clicks", d.get("link").get("hits")); updateMap.put("Last_Click_Date", d.get("last_clicked")); zoho.crm.updateRecord("Deals", recordId, updateMap); }
Response — success
{ "status": "success", "status_code": 200, "data": { "link": { "code": "launch26", "short_url": "https://minifyurl.in/launch26", "hits": 42, ... }, "period_days": 7, "last_clicked": "2026-04-17T10:23:00.000Z", "countries": [ { "country": "IN", "count": "38" }, { "country": "US", "count": "4" } ], "browsers": [ { "browser": "Chrome", "count": "30" }, { "browser": "Safari", "count": "8" } ], "devices": [ { "device": "mobile", "count": "25" }, { "device": "desktop", "count": "17" } ], "referrers": [ { "referrer": "twitter.com", "count": "18" }, { "referrer": "Direct", "count": "12" } ], "hourly": [ { "hour": "9", "count": "12" }, { "hour": "10", "count": "8" } ], "daily_trend": [ { "date": "2026-04-11", "count": "5" }, { "date": "2026-04-12", "count": "9" } ], "utm": [ { "utm_source": "twitter", "utm_medium": "social", "utm_campaign": "launch", "count": "10" } ] } }
GET/api/v1/links/:code/qr Get QR code

Returns a base64-encoded PNG QR code. Use qr_data_url directly in an <img> tag or any email template.

API_KEY = "mfy_xxxx"; response = invokeurl [ url: "https://minifyurl.in/api/v1/links/launch26/qr" type: GET headers: {"Authorization": "Bearer " + API_KEY} ]; if(response.get("status") == "success") { qrDataUrl = response.get("data").get("qr_data_url"); // Use in email: "" info "QR ready for: " + response.get("data").get("short_url"); }
Response — success
{ "status": "success", "status_code": 200, "data": { "code": "launch26", "short_url": "https://minifyurl.in/launch26", "qr_data_url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..." } }

Deluge — Full Examples

Complete scripts for common Zoho automation scenarios. All use invokeurl and check response.get("status") consistently.

Bulk shorten a list of URLs

API_KEY = "mfy_xxxx"; urlList = list(); urlList.add("https://example.com/page-one"); urlList.add("https://example.com/page-two"); urlList.add("https://example.com/page-three"); results = list(); for each longUrl in urlList { body = Map(); body.put("url", longUrl); resp = invokeurl [ url: "https://minifyurl.in/api/v1/shorten" type: POST headers: {"Authorization": "Bearer " + API_KEY, "Content-Type": "application/json"} body: body.toString() ]; row = Map(); row.put("original", longUrl); row.put("status", resp.get("status")); if(resp.get("status") == "created") { row.put("short", resp.get("data").get("short_url")); } results.add(row); } info results;

Send email with short link + QR code

API_KEY = "mfy_xxxx"; recordId = input.get("recordId"); record = zoho.crm.getRecordById("Contacts", recordId); longUrl = record.get("Proposal_URL"); contactName = record.get("Full_Name"); // Shorten with 7-day expiry body = Map(); body.put("url", longUrl); body.put("expires_in", 7); shortenResp = invokeurl [ url: "https://minifyurl.in/api/v1/shorten" type: POST headers: {"Authorization": "Bearer " + API_KEY, "Content-Type": "application/json"} body: body.toString() ]; if(shortenResp.get("status") == "created" || shortenResp.get("status") == "duplicate") { // On duplicate, fetch the code via list API if(shortenResp.get("status") == "duplicate") { listResp = invokeurl [ url: "https://minifyurl.in/api/v1/links?search=" + longUrl type: GET headers: {"Authorization": "Bearer " + API_KEY} ]; shortUrl = listResp.get("data").get("links").get(0).get("short_url"); code = listResp.get("data").get("links").get(0).get("code"); } else { shortUrl = shortenResp.get("data").get("short_url"); code = shortenResp.get("data").get("code"); } // Fetch QR qrResp = invokeurl [ url: "https://minifyurl.in/api/v1/links/" + code + "/qr" type: GET headers: {"Authorization": "Bearer " + API_KEY} ]; qrDataUrl = qrResp.get("data").get("qr_data_url"); emailBody = "

Hi " + contactName + ",

" + "

Proposal link: " + shortUrl + "

" + ""; sendmail [ from: zoho.loginuserid to: record.get("Email") subject: "Your Proposal" message: emailBody ]; }

Scheduled daily sync — update CRM click counts

API_KEY = "mfy_xxxx"; resp = invokeurl [ url: "https://minifyurl.in/api/v1/links?limit=100" type: GET headers: {"Authorization": "Bearer " + API_KEY} ]; if(resp.get("status") == "success") { for each link in resp.get("data").get("links") { code = link.get("code"); hits = link.get("hits"); deals = zoho.crm.searchRecords("Deals", "(Short_Code:equals:" + code + ")"); if(deals.size() > 0) { updateMap = Map(); updateMap.put("Link_Clicks", hits); zoho.crm.updateRecord("Deals", deals.get(0).get("id"), updateMap); } } }

JavaScript — Full Example

const API_KEY = 'mfy_xxxx'; const BASE = 'https://minifyurl.in/api/v1'; const H = { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' }; const mfy = { async shorten(url, opts = {}) { return fetch(`${BASE}/shorten`, { method:'POST', headers:H, body:JSON.stringify({url,...opts}) }).then(r=>r.json()); }, async list(params = {}) { return fetch(`${BASE}/links?${new URLSearchParams(params)}`, { headers:H }).then(r=>r.json()); }, async get(code) { return fetch(`${BASE}/links/${code}`, { headers:H }).then(r=>r.json()); }, async stats(code, days=30) { return fetch(`${BASE}/links/${code}/stats?days=${days}`, { headers:H }).then(r=>r.json()); }, async update(code, p) { return fetch(`${BASE}/links/${code}?${new URLSearchParams(p)}`, { method:'PATCH', headers:H }).then(r=>r.json()); }, async delete(code) { return fetch(`${BASE}/links/${code}`, { method:'DELETE', headers:H }).then(r=>r.json()); }, async qr(code) { return fetch(`${BASE}/links/${code}/qr`, { headers:H }).then(r=>r.json()); } }; // Shorten with full status handling const r = await mfy.shorten('https://example.com', { custom_slug: 'launch26', expires_in: 30 }); if (r.status === 'created') console.log('New link:', r.data.short_url); else if (r.status === 'duplicate') console.log(r.message); else console.error('Error:', r.message); // Stats const s = await mfy.stats('launch26', 7); if (s.status === 'success') console.log(`${s.data.link.hits} clicks, top: ${s.data.countries[0]?.country}`);

Python — Full Example

import requests, base64, re API_KEY = 'mfy_xxxx' BASE = 'https://minifyurl.in/api/v1' H = {'Authorization': f'Bearer {API_KEY}', 'Content-Type': 'application/json'} class MinifyURL: def shorten(self, url, **kw): return requests.post(f'{BASE}/shorten', json={'url':url,**kw}, headers=H).json() def list_links(self, **kw): return requests.get(f'{BASE}/links', params=kw, headers=H).json() def get(self, code): return requests.get(f'{BASE}/links/{code}', headers=H).json() def stats(self, code, days=30): return requests.get(f'{BASE}/links/{code}/stats', params={'days':days}, headers=H).json() def update(self, code, **kw): return requests.patch(f'{BASE}/links/{code}', params=kw, headers=H).json() def delete(self, code): return requests.delete(f'{BASE}/links/{code}', headers=H).json() def save_qr(self, code, path='qr.png'): r = requests.get(f'{BASE}/links/{code}/qr', headers=H).json() if r['status'] == 'success': b64 = re.sub(r'^data:image/png;base64,', '', r['data']['qr_data_url']) with open(path, 'wb') as f: f.write(base64.b64decode(b64)) mfy = MinifyURL() r = mfy.shorten('https://example.com', custom_slug='launch26', expires_in=30) if r['status'] == 'created': print('New link:', r['data']['short_url']) elif r['status'] == 'duplicate': print(r['message']) else: print('Error:', r['message']) s = mfy.stats('launch26', 7) if s['status'] == 'success': print(f"Clicks: {s['data']['link']['hits']}, Top: {s['data']['countries'][0]['country']}")

cURL — Quick Reference

# Account curl https://minifyurl.in/api/v1/me \ -H "Authorization: Bearer mfy_xxxx" # Shorten (JSON body) curl -X POST https://minifyurl.in/api/v1/shorten \ -H "Authorization: Bearer mfy_xxxx" \ -H "Content-Type: application/json" \ -d '{"url": "https://example.com"}' # With Pro options curl -X POST https://minifyurl.in/api/v1/shorten \ -H "Authorization: Bearer mfy_xxxx" \ -H "Content-Type: application/json" \ -d '{"url":"https://example.com","custom_slug":"launch26","expires_in":30}' # List links curl "https://minifyurl.in/api/v1/links?limit=20" \ -H "Authorization: Bearer mfy_xxxx" # Search curl "https://minifyurl.in/api/v1/links?search=my-link" \ -H "Authorization: Bearer mfy_xxxx" # Get single link curl "https://minifyurl.in/api/v1/links/launch26" \ -H "Authorization: Bearer mfy_xxxx" # Update curl -X PATCH "https://minifyurl.in/api/v1/links/launch26?url=https://newsite.com&expires_in=7" \ -H "Authorization: Bearer mfy_xxxx" # Delete curl -X DELETE "https://minifyurl.in/api/v1/links/launch26" \ -H "Authorization: Bearer mfy_xxxx" # Stats (last 14 days) curl "https://minifyurl.in/api/v1/links/launch26/stats?days=14" \ -H "Authorization: Bearer mfy_xxxx" # QR Code curl "https://minifyurl.in/api/v1/links/launch26/qr" \ -H "Authorization: Bearer mfy_xxxx"

Webhooks

Webhooks let you receive real-time HTTP notifications whenever someone clicks one of your short links. Instead of polling the stats API, your server gets a POST request the moment a click happens — perfect for Zapier, Make, or any custom automation tool.

Free plan included: Webhooks are available on all plans. Up to 5 webhooks per account. A webhook that fails 10 consecutive times is automatically disabled to protect your endpoint.

Click Payload

Every click fires a POST request to your endpoint with Content-Type: application/json and this body:

Payload shape
{ "event": "click", "code": "launch26", "short_url": "https://minifyurl.in/launch26", "original_url": "https://example.com/product", "clicked_at": "2026-04-19T10:23:00.000Z", "country": "IN", "browser": "Chrome", "device": "mobile", "referrer": "twitter.com", "utm_source": "twitter", "utm_medium": "social", "utm_campaign": "launch", "test": false }

The test field is true only when sent from the Dashboard test button — useful for distinguishing real clicks from test pings in your handler.

Verifying the Signature

If you set a secret when creating a webhook, every request includes an X-MinifyURL-Signature header. Verify it on your server to confirm the request came from MinifyURL and wasn't tampered with.

Deluge note: Deluge does not natively support HMAC-SHA256 verification. The simplest approach is to use a hard-to-guess secret URL path instead of signature verification — e.g. https://your-server.com/webhook/a8f3k2p9.
// Deluge — HTTP function triggered by MinifyURL webhook // No signature verification needed if you use a secret URL path code = input.get("code"); country = input.get("country"); clicks = input.get("clicks"); // not in payload — fetch separately if needed isTest = input.get("test"); if(isTest == false) { // Find the matching Deal by Short_Code and update click tracking searchCriteria = "(Short_Code:equals:" + code + ")"; deals = zoho.crm.searchRecords("Deals", searchCriteria); if(deals.size() > 0) { updateMap = Map(); updateMap.put("Last_Click_Country", country); updateMap.put("Last_Click_At", input.get("clicked_at")); zoho.crm.updateRecord("Deals", deals.get(0).get("id"), updateMap); } }

Managing Webhooks via API

You can also manage webhooks programmatically using your session cookie (dashboard endpoints — not the v1 API key endpoints).

MethodEndpointDescription
GET/api/webhooksList all your webhooks
POST/api/webhooksCreate a webhook
PATCH/api/webhooks/:idUpdate URL, secret, events, or enabled state
DELETE/api/webhooks/:idDelete a webhook
POST/api/webhooks/:id/testSend a test payload to verify your endpoint
Create webhook — request body
{ "url": "https://your-server.com/webhook", // required "secret": "your-signing-secret", // optional "events": ["click"] // optional, default: ["click"] }
Headers sent with every webhook request
Content-Type: application/json User-Agent: MinifyURL-Webhook/1.0 X-MinifyURL-Event: click X-MinifyURL-Delivery: a3f9b2c1d4e5f6a7 (random ID per delivery) X-MinifyURL-Signature: sha256=... (only if secret is set)
Retry behaviour: Webhooks are fired once per click with a 5-second timeout. There is no automatic retry — if your endpoint is down, the delivery is lost. Your endpoint should return any 2xx status to be counted as a success. After 10 consecutive non-2xx responses, the webhook is automatically disabled.
⚡ API Explorer
Test live API calls directly in your browser
Checking login status…