Skip to content

Instantly share code, notes, and snippets.

@vpnry
Created April 20, 2026 01:38
Show Gist options
  • Select an option

  • Save vpnry/114a68c44d1e3931a9398d3e6a827047 to your computer and use it in GitHub Desktop.

Select an option

Save vpnry/114a68c44d1e3931a9398d3e6a827047 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
"""
s.id URL Shortener CLI
Usage: python sid.py <command> [options]
Env vars required:
SID_AUTH_ID - your X-Auth-Id
SID_AUTH_KEY - your X-Auth-Key
"""
import os
import sys
import json
import argparse
import urllib.request
import urllib.error
BASE_URL = "https://api.s.id/v1"
def get_headers():
auth_id = os.environ.get("SID_AUTH_ID")
auth_key = os.environ.get("SID_AUTH_KEY")
if not auth_id or not auth_key:
print("Error: SID_AUTH_ID and SID_AUTH_KEY must be set in your environment.")
print(" export SID_AUTH_ID=your_id")
print(" export SID_AUTH_KEY=your_key")
sys.exit(1)
return {
"X-Auth-Id": auth_id,
"X-Auth-Key": auth_key,
"Content-Type": "application/json",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
}
def api_request(method, path, data=None):
url = BASE_URL + path
headers = get_headers()
body = json.dumps(data).encode() if data else None
req = urllib.request.Request(url, data=body, headers=headers, method=method)
try:
with urllib.request.urlopen(req) as resp:
return json.loads(resp.read().decode())
except urllib.error.HTTPError as e:
body = e.read().decode()
try:
err = json.loads(body)
print(f"API Error {e.code}: {err.get('message', body)}")
except Exception:
print(f"HTTP Error {e.code}: {body}")
sys.exit(1)
def print_link(data):
print(f" ID : {data.get('id')}")
print(f" Short : https://s.id/{data.get('short')}")
print(f" URL : {data.get('long_url')}")
if data.get('title'):
print(f" Title : {data.get('title')}")
# --- Commands ---
def cmd_create(args):
"""Create a new short link."""
payload = {"long_url": args.url}
result = api_request("POST", "/links", payload)
data = result.get("data", {})
print("✓ Link created:")
print_link(data)
if args.short or args.title:
link_id = data.get("id")
update_payload = {"long_url": args.url}
if args.short:
update_payload["short"] = args.short
if args.title:
update_payload["title"] = args.title
result2 = api_request("POST", f"/links/{link_id}", update_payload)
data2 = result2.get("data", {})
print("✓ Link updated with custom short/title:")
print_link(data2)
def cmd_update(args):
"""Update an existing link by numeric ID."""
# long_url is required by the API — auto-fetch existing value if not provided
if args.url:
long_url = args.url
else:
existing = api_request("GET", f"/links/{args.id}")
long_url = existing.get("data", {}).get("long_url")
if not long_url:
print("Could not fetch existing long_url. Please pass --url explicitly.")
sys.exit(1)
payload = {"long_url": long_url}
if args.short:
payload["short"] = args.short
if args.title:
payload["title"] = args.title
result = api_request("POST", f"/links/{args.id}", payload)
print("✓ Link updated:")
print_link(result.get("data", {}))
def cmd_get(args):
"""Get link detail by short code."""
result = api_request("GET", f"/links/short/{args.short}")
print("Link detail:")
print_link(result.get("data", {}))
def cmd_list(args):
"""List all your links."""
path = "/links"
if args.page:
path += f"?page={args.page}"
result = api_request("GET", path)
items = result.get("data", [])
if not items:
print("No links found.")
return
for item in items:
print(f" [{item.get('id')}] s.id/{item.get('short')} -> {item.get('long_url')}")
def cmd_archive(args):
"""Archive a link by numeric ID."""
api_request("PUT", f"/links/{args.id}/archive")
print(f"✓ Link {args.id} archived.")
def cmd_unarchive(args):
"""Unarchive a link by numeric ID."""
api_request("PUT", f"/links/{args.id}/unarchive")
print(f"✓ Link {args.id} unarchived.")
def cmd_quota(args):
"""Show your API quota."""
result = api_request("GET", "/quota")
data = result.get("data", {})
for k, v in data.items():
print(f" {k}: {v}")
def cmd_me(args):
"""Show your account info."""
result = api_request("GET", "/me")
data = result.get("data", {})
for k, v in data.items():
print(f" {k}: {v}")
# --- Main ---
def main():
parser = argparse.ArgumentParser(
description="s.id URL Shortener CLI",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
# Create a short link (random short code)
python sid.py create https://tipitakapali.org
# Create with custom short code and title
python sid.py create https://tipitakapali.org --short tipitaka --title "Tipitaka Pali"
# Update link by ID (long_url auto-fetched if omitted)
python sid.py update 36304710 --short tipitaka --title "Chaṭṭha Saṅgāyana Tipiṭaka"
# Get link detail by short code
python sid.py get tipitaka
# List all links
python sid.py list
# Archive / unarchive
python sid.py archive 36304710
python sid.py unarchive 36304710
# Show quota and account info
python sid.py quota
python sid.py me
"""
)
sub = parser.add_subparsers(dest="command")
sub.required = True
# create
p = sub.add_parser("create", help="Create a new short link")
p.add_argument("url", help="The long URL to shorten")
p.add_argument("--short", help="Custom short code (e.g. tipitaka)")
p.add_argument("--title", help="Title for the link")
p.set_defaults(func=cmd_create)
# update
p = sub.add_parser("update", help="Update a link by numeric ID")
p.add_argument("id", help="Numeric link ID")
p.add_argument("--url", help="New long URL (auto-fetched if omitted)")
p.add_argument("--short", help="New short code")
p.add_argument("--title", help="New title")
p.set_defaults(func=cmd_update)
# get
p = sub.add_parser("get", help="Get link detail by short code")
p.add_argument("short", help="Short code (e.g. tipitaka)")
p.set_defaults(func=cmd_get)
# list
p = sub.add_parser("list", help="List all your links")
p.add_argument("--page", help="Page number", default=None)
p.set_defaults(func=cmd_list)
# archive
p = sub.add_parser("archive", help="Archive a link")
p.add_argument("id", help="Numeric link ID")
p.set_defaults(func=cmd_archive)
# unarchive
p = sub.add_parser("unarchive", help="Unarchive a link")
p.add_argument("id", help="Numeric link ID")
p.set_defaults(func=cmd_unarchive)
# quota
p = sub.add_parser("quota", help="Show API quota")
p.set_defaults(func=cmd_quota)
# me
p = sub.add_parser("me", help="Show account info")
p.set_defaults(func=cmd_me)
args = parser.parse_args()
args.func(args)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment