A tangled line flows into a pair of curly braces and leaves as one short, straight arrow — an API turning a long URL into a short link.

A Free URL Shortener API With No Authentication

POST a long URL, get a short one back. No API key, no account, no monthly quota — just an HTTP request from cURL, Python, JavaScript, Go, or anything else that speaks JSON.

No API Key No Signup JSON In, JSON Out No Monthly Cap

Quick start

Pick a language and run it. There is no authentication step — the request below is the entire integration.

curl -X POST http://zip1.io/api/create \
  -H "Content-Type: application/json" \
  -d '{"url": "https://your-long-url.com/some/very/deep/path"}'
import requests

response = requests.post(
    "http://zip1.io/api/create",
    json={"url": "https://your-long-url.com/some/very/deep/path"},
)
print(response.json()["short_url"])  # http://zip1.io/abc123
const response = await fetch("http://zip1.io/api/create", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ url: "https://your-long-url.com/some/very/deep/path" })
});

const data = await response.json();
console.log(data.short_url); // http://zip1.io/abc123
package main

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
)

func main() {
	body := []byte(`{"url": "https://your-long-url.com/some/very/deep/path"}`)
	resp, err := http.Post("http://zip1.io/api/create", "application/json", bytes.NewBuffer(body))
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	data, _ := io.ReadAll(resp.Body)
	fmt.Println(string(data))
}

A successful request returns HTTP 200 with the short URL and creation timestamp:

{
  "short_url": "http://zip1.io/abc123",
  "created_at": "2026-07-06T12:00:00+00:00"
}

Every option in one request — a branded slug that dies after 100 clicks, expires at year end, and asks visitors for a password:

curl -X POST http://zip1.io/api/create \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-long-url.com/campaign",
    "alias": "spring-launch",
    "max-clicks": 100,
    "expiration-time": "2026-12-31T23:59:00Z",
    "password": "open.sesame1",
    "description": "Spring launch — email cohort A"
  }'

Endpoints

Endpoint Rate limit (per IP) What it does
POST /api/create 10 / minute Create a short link
GET /api/stats/<code> 30 / minute Click stats as JSON
GET /api/check-alias?alias=<slug> 60 / minute Check if a custom slug is free
GET /export/<code>/<format> Export click data as csv, json, xlsx, or xml

Parameters for POST /api/create

Field Required Rules
url Required A valid http(s) URL — the redirect destination
alias Optional Custom slug, up to 12 characters: letters, numbers, -, _
password Optional 8+ characters with a letter, a number, and @ or .
max-clicks Optional Positive integer — the link stops redirecting after N clicks
expiration-time Optional ISO 8601 with timezone, at least 5 minutes ahead (e.g. 2026-12-31T23:59:00Z)
description Optional Label of up to 100 characters, shown on the stats page

Stats response

GET /api/stats/<code> returns totals, uniques, and top countries for any short link — no ownership check, no token:

{
  "message": "Statistics retrieved successfully",
  "data": {
    "short_code": "spring-launch",
    "total_clicks": 87,
    "unique_clicks": 64,
    "creation_date": "2026-07-06T12:00:00+00:00",
    "last_click": "2026-07-06T15:42:11+00:00",
    "click_limit": "100",
    "expiration_time": "2026-12-31T23:59:00+00:00",
    "is_password_protected": true,
    "top_countries": [{"country": "United States", "clicks": 31}],
    "recent_clicks": []
  }
}

Need spreadsheets instead? The same analytics export as CSV, XLSX, JSON, or XML via /export/<code>/<format>.

Rate limits

Because there are no API keys, limits apply per IP address: 10 link creations per minute, 30 stats reads per minute, 60 alias checks per minute. There is no daily or monthly quota — create as many links over time as you need. The limits exist to stop abuse, not to sell an upgrade.

Exceed a limit and you get HTTP 429:

{"error": "ratelimit exceeded 10 per 1 minute"}

Back off until the minute window resets and retry. If you legitimately need more throughput, get in touch.

Error responses

Errors return a JSON body with a message field and a conventional status code — except 429, where the rate limiter responds with an error field:

Status When Example message
400 Missing or malformed url "Invalid URL format or missing required fields"
400 Invalid alias, password, max-clicks, expiration-time, or description "max-clicks must be a positive integer"
403 Destination is on the abuse blocklist "URL is blocked"
409 Requested alias is already taken "Alias already exists"
404 Stats requested for an unknown short code "Short link not found"
429 Rate limit exceeded {"error": "ratelimit exceeded 10 per 1 minute"}

How it compares to the bit.ly and TinyURL APIs

Most URL shortener APIs assume an account, an access token, and a plan. If you just need short links from a script, a CI job, or an AI agent, that setup is the slowest part. Here's the honest comparison:

Capability zip1.io API bit.ly API TinyURL API
Authentication None Account + OAuth token Account + API token
Monthly link quota None — rate limit only Capped on the free plan Capped on the free plan
Custom slugs via API, free Yes Limited on free plan Limited on free plan
Password-protected links Yes, free Not offered Not offered
Click caps (max-clicks) Yes No No
Click analytics via API, free Yes Limited free tier Paid plans
Signup required No Yes Yes

Plan details for bit.ly and TinyURL are as published on their pricing pages and may change. The structural difference is stable: both authenticate every request and meter link creation by plan; zip1.io does neither.

Built for AI agents too

The same API is exposed as a Model Context Protocol server, so agents like Claude can mint and track short links as native tools instead of raw HTTP calls:

claude mcp add --transport http zip1 http://zip1.io/mcp

No key to provision for the agent either — the no-auth design is what makes zero-config agent integration possible.

FAQ

  • Do I need an API key?

    No. Zero authentication — no API key, no OAuth token, no account. Send a POST request to /api/create and you get a short URL back. Rate limiting is per IP address instead of per key.

  • Is the API free, and what are the rate limits?

    Free, with no paid tier and no monthly link quota. Limits are per IP: 10 link creations per minute, 30 stats requests per minute, 60 alias checks per minute. Exceeding one returns HTTP 429 until the window resets.

  • Can I set a custom slug through the API?

    Yes, and it's free. Pass an alias field — up to 12 characters: letters, numbers, hyphens, underscores. Taken aliases return HTTP 409; pre-check availability with GET /api/check-alias.

  • What options does /api/create support besides the URL?

    alias (custom slug), password (visitors must enter it before redirecting), max-clicks (link stops working after N clicks), expiration-time (ISO 8601 timestamp when the link dies), and description (a label of up to 100 characters shown on the stats page).

  • Is zip1.io a bit.ly API alternative?

    For programmatic link creation, yes. The bit.ly API needs an account and an OAuth token, and its free plan caps monthly link creation. zip1.io needs no token, has no monthly quota, and includes custom slugs, click caps, and password protection for free.

Ship your integration in five minutes

The full reference covers every endpoint, parameter, and response schema — or skip the reading and send your first request now.