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"); }

Limits & Rate Limits

MinifyURL enforces limits to ensure fair usage and protect service availability.

Free Free
Saved links10 total
Analytics historyLast 7 days
QR code generation
Custom aliases
API access
Pro ★ Pro
Saved links100 total
Analytics historyLast 30 days
QR code generation
Custom aliases
API access
API Rate Limits Per API key
EndpointLimitWindow
POST/api/v1/shorten 60 requests Per hour
GET/api/v1/links Unlimited
DEL/api/v1/links/:id Unlimited
GET/api/v1/analytics/:id Unlimited
GET/api/v1/me Unlimited
Auth Rate Limits Brute-force & abuse protection
ActionLimitWindowScope
Login attempts10 attempts15 minPer email
Login attempts20 attempts15 minPer IP
Forgot password5 requests1 hourPer IP
Resend OTP5 requests1 hourPer IP
Link creation (dashboard)10 links10 minPer session
Feedback5 submissions1 hourPer IP
OTP Verification Email signup verification
10 min
OTP expiry
5
Max wrong attempts
60 s
Resend cooldown
5 / hr
Resend rate limit per IP
429 Too Many Requests — Returned when any rate limit is exceeded. Wait for the window to reset and retry. Auth endpoints return 403 if an account or IP is temporarily blocked after repeated failures.

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…