{
"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"
}
Field
Type
Always present
Notes
status
string
✓
Human-readable outcome — see Status Codes section below
status_code
integer
✓
Mirrors the HTTP status code
message
string
On errors & creates
Description of what happened or went wrong
data
object
On success
The 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
status
HTTP
When 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
status
HTTP
When 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/meAccount & plan info▾
Returns your account details, Pro status, and current link usage vs your plan limit.
Shorten a URL. Send parameters as a JSON body. Query string params are accepted for legacy compatibility.
Field
Type
Notes
urlrequired
string
Destination URL. Must include https://
custom_slugoptionalPRO
string
Custom code e.g. launch → minifyurl.in/launch
expires_inoptionalPRO
integer
Days until expiry
passwordoptionalPRO
string
Visitors must enter this before redirecting
activate_atoptionalPRO
ISO 8601
Link 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");
}
const res = await fetch('https://minifyurl.in/api/v1/links?limit=20',
{ headers: { 'Authorization': 'Bearer mfy_xxxx' } });
const { status, data } = await res.json();
if (status === 'success') data.links.forEach(l => console.log(l.short_url, l.hits));
r = requests.get('https://minifyurl.in/api/v1/links',
params={'limit': 20, 'search': 'my-link'},
headers={'Authorization': 'Bearer mfy_xxxx'}).json()
if r['status'] == 'success':
for link in r['data']['links']:
print(link['short_url'], link['hits'])
import base64, re
r = requests.get('https://minifyurl.in/api/v1/links/launch26/qr',
headers={'Authorization': 'Bearer mfy_xxxx'}).json()
if r['status'] == 'success':
b64 = re.sub(r'^data:image/png;base64,', '', r['data']['qr_data_url'])
with open('qr.png', 'wb') as f: f.write(base64.b64decode(b64))
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:
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);
}
}
const crypto = require('crypto');
// Express handler
app.post('/webhook/minifyurl', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.headers['x-minifyurl-signature'];
const secret = process.env.MINIFYURL_WEBHOOK_SECRET;
// Verify signature
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(req.body)
.digest('hex');
if (sig !== expected) {
return res.status(401).send('Invalid signature');
}
const payload = JSON.parse(req.body);
if (!payload.test) {
console.log(`Click on /${payload.code} from ${payload.country} via ${payload.browser}`);
// Your logic here
}
res.status(200).send('ok');
});
import hmac, hashlib
from flask import Flask, request, abort
app = Flask(__name__)
SECRET = 'your-webhook-secret'
@app.route('/webhook/minifyurl', methods=['POST'])
def webhook():
sig = request.headers.get('X-MinifyURL-Signature', '')
expected = 'sha256=' + hmac.new(
SECRET.encode(), request.data, hashlib.sha256
).hexdigest()
if not hmac.compare_digest(sig, expected):
abort(401)
payload = request.json
if not payload.get('test'):
print(f"Click on /{payload['code']} from {payload['country']}")
# Your logic here
return 'ok', 200
Managing Webhooks via API
You can also manage webhooks programmatically using your session cookie (dashboard endpoints — not the v1 API key endpoints).
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.