Feature
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 https://zip1.io/api/create \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/some/very/deep/path"}'
import requests
response = requests.post(
"https://zip1.io/api/create",
json={"url": "https://example.com/some/very/deep/path"},
)
print(response.json()["short_url"]) # https://zip1.io/abc123
const response = await fetch("https://zip1.io/api/create", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ url: "https://example.com/some/very/deep/path" })
});
const data = await response.json();
console.log(data.short_url); // https://zip1.io/abc123
package main
import (
"bytes"
"fmt"
"io"
"net/http"
)
func main() {
body := []byte(`{"url": "https://example.com/some/very/deep/path"}`)
resp, err := http.Post("https://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": "https://zip1.io/abc123",
"created_at": "2026-07-06T12:00:00.482913+00:00"
}
Every option in one request — a custom slug that dies after 100 clicks, expires at year end, and asks visitors for a password:
curl -X POST https://zip1.io/api/create \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/campaign",
"alias": "spring-2026",
"max-clicks": 100,
"expiration-time": "2027-12-31T23:59:00Z",
"password": "Preview@2026",
"description": "<your internal note, e.g. order 1234>"
}'
Keep the alias to 12 characters. Anything longer is silently truncated rather than
rejected — spring-launch becomes spring-launc — so always read
short_url back from the response.
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 (csv arrives as a ZIP of several CSVs) |
Parameters for POST /api/create
| Field | Required | Rules |
|---|---|---|
url |
Required | A valid http(s) URL — the redirect destination |
alias |
Optional | Custom slug: letters, numbers, -, _. Truncated to the first 12 characters |
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 | Optional private note for your own integration, up to 100 characters. Not shown on the link's public pages. Keep it free of personal data you wouldn't want stored. Returned in the create response. |
Stats response
GET /api/stats/<code> returns totals, uniques, and top countries. A password-protected link needs its password in the X-Link-Password header; other links need no token. Anyone with the short link can see its stats. Password-protected links also need the password. Visitor IP addresses are stored to count unique visitors. They are never shown on the stats page or included in the export.
{
"message": "Statistics retrieved successfully",
"data": {
"short_code": "spring-2026",
"total_clicks": 87,
"unique_clicks": 64,
"creation_date": "2026-07-06T12:00:00.482913+00:00",
"last_click": "2026-07-06 15:42:11",
"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": []
}
}
Two quirks worth coding around: click_limit is a string (or
null), and last_click uses a space rather than a T
and carries no UTC offset. creation_date is a full ISO 8601 timestamp for
every link regardless of how it was made — before 2026-09-01 website-created links
reported only a clock time. top_countries is capped at three entries, and
recent_clicks is always empty. The export has the totals and the breakdown by country, browser, operating system, and referrer, not a row per click.
Full details in the API documentation.
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 and 1,000 a day, 30 stats reads per minute, 60 alias checks per minute. There is no monthly quota. 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 window resets and retry. Link creation is also capped at 1,000 a day per IP. 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 |
"Start the address with https:// — try https://example.com" (plus a reason code) |
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 make and track short links as native tools instead of raw HTTP calls:
claude mcp add --transport http zip1 https://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 to use the zip1.io URL shortener API?
No. The API requires zero authentication — no API key, no OAuth token, no account. Send a POST request to
/api/createand 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?
The API is free with no paid tier and no monthly link quota. Limits are per IP: 10 link creations per minute and 1,000 a day, 30 stats requests per minute, and 60 alias-availability checks per minute. Exceeding a limit returns HTTP 429 until the window resets.
-
Can I set a custom slug through the API?
Yes, and it's free. Pass an
aliasfield to/api/create— 12 characters of letters, numbers, hyphens or underscores. Longer slugs are truncated to the first 12 characters rather than rejected, so readshort_urlback from the response. If the alias is taken you get HTTP 409; you can pre-check availability withGET /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), anddescription(optional private note for your own integration, up to 100 characters; not shown on the link's public pages; keep it free of personal data you wouldn't want stored; returned in the create response). -
Is zip1.io a bit.ly API alternative?
For programmatic link creation, yes. The bit.ly API requires an account and an OAuth token, and its free plan caps how many links you can create per month. zip1.io needs no token, has no monthly quota, and includes custom slugs, click caps, and password protection for free.
Related
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.