Make a URL slug
A product name typed in Vietnamese, a blog title with an em dash, or a filename full of underscores all need to become the same thing: a short, lowercase, hyphenated string that works in a URL. This slug generator API strips accents, transliterates non-Latin scripts and collapses stray punctuation so the output is safe to route, index and share, every time.
Run — free
Runs in your browser. Free, unlimited — your data never leaves this page.
The problem hiding behind every 'Save' button
Content teams rarely think about slugs until one breaks something: two articles collide on the same URL, a Cyrillic title gets URL-encoded into an unreadable string of percent signs, or a stray apostrophe closes an HTML attribute early. A slug looks like a trivial string transform, but doing it correctly means handling Unicode normalization, transliteration tables for non-Latin alphabets, and a consistent policy for what happens to numbers, dashes and reserved words like 'admin' or 'null'.
How dev.slug processes a string
POST /dev/slug takes a title and returns a task_id since generation runs asynchronously. The pipeline lowercases the text, normalizes Unicode so visually identical characters collapse to one form, transliterates accented and non-Latin characters into their closest ASCII equivalent, replaces whitespace and punctuation with a single separator, and trims leading or trailing hyphens. The result is deterministic: the same title always produces the same slug, which matters when you are deduplicating content across a large catalog.
Where slugs came from and why the rules matter
The convention of hyphen-separated, lowercase URL segments grew out of early search engine guidance and CMS platforms competing to be readable and crawlable, well before Unicode domains and multilingual content management were common. That history is why most slug libraries were built with Latin scripts in mind first, and why titles in Arabic, Japanese or Korean often get poorly transliterated or dropped entirely by tools that were never updated for global content.
Fitting slug generation into a publishing pipeline
Editorial teams call this endpoint the moment a title is saved, so the slug is ready before the page ever goes live, while e-commerce catalogs call it in bulk when importing thousands of product names from a supplier feed. Because the response arrives via signed webhook or a signed link valid 24 hours, it slots cleanly into a queue-based CMS worker or a nightly catalog sync without blocking the request that created the title.
Predictable cost, no wasted calls
A failed task is retried up to three times automatically and never billed if it still cannot complete, so batch imports with a handful of malformed rows do not inflate your invoice. At $0.002 per request, generating slugs for an entire content migration is a small, fixed line item rather than an open-ended engineering cost.
What you can do with it
Publishing blog and news articles
Generate a clean slug the moment an editor saves a headline, so the article's final URL is short, readable and free of duplicate-collision risk.
Importing multilingual product catalogs
Convert supplier-provided product names in Japanese, German or Portuguese into consistent ASCII slugs during bulk catalog imports.
Deduplicating user-generated content
Normalize community post titles into slugs to detect near-duplicate submissions before they are published under separate URLs.
Migrating legacy CMS URLs
Regenerate slugs from old, inconsistently formatted titles when moving content into a new platform with stricter URL conventions.
FAQ
What exactly is a slug?
A slug is the lowercase, hyphen-separated portion of a URL derived from a title, such as turning 'Best Coffee Shops!' into best-coffee-shops. The slug generator API automates that conversion consistently.
Does it work with non-English titles?
Yes. The endpoint transliterates accented and non-Latin characters, from Spanish tildes to Cyrillic and CJK scripts, into readable ASCII rather than dropping them or leaving raw Unicode in the URL.
Is the slug generator API free to use?
The tool above runs free in your browser. The API is paid — each call draws from your prepaid ForHosting KIT balance: top up from $10.00 (it never expires), pay each request's published price, and a call with no balance returns HTTP 402. No subscription, no tokens, and a failed task is never charged.
How much does each request cost?
Each call to POST /dev/slug costs $0.002, charged only when the task completes successfully.
Can I process thousands of titles in bulk?
Yes. Because the endpoint is asynchronous and results arrive by webhook, it is designed for batch imports and catalog migrations, not just one-off calls.
Will two different titles ever produce the same slug?
It is possible if two titles are similar enough after normalization, since slugging is a deterministic, lossy transform; teams typically append an ID or short suffix as a tiebreaker at the application level.
What happens to numbers and special characters?
Numbers are preserved as-is, punctuation and whitespace are collapsed into single hyphens, and characters with no reasonable ASCII equivalent are removed rather than left broken.
How do I retrieve the generated slug?
The result is delivered through a signed webhook, recommended for automated pipelines, or a signed link valid for 24 hours for manual retrieval.
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, and soon from our app, email and Telegram.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/dev/slug \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"items":["valor-1","valor-2"]}'const res = await fetch("https://api.kit.forhosting.com/dev/slug", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"items": [
"valor-1",
"valor-2"
]
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/dev/slug",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"items": [
"valor-1",
"valor-2"
]
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/dev/slug", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"items":["valor-1","valor-2"]}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"items":["valor-1","valor-2"]}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/dev/slug", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"items": [
"valor-1",
"valor-2"
]
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "dev.slug",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |