ForHosting KIT · Developer Utilities

Resolve a slug collision with an incrementing numeric suffix

The slug collision resolver takes the exact slug you want and compares it with the slugs that are already occupied.

● BetaFree · in your browser
Use it from WebAPIEmailTelegramApp soon

When the desired value is available, it returns that value unchanged. When it is taken, the resolver checks numeric alternatives such as a trailing -2, -3, and onward until it finds the first available choice. It is a small, deterministic building block for publishing systems, import jobs, documentation generators, and any workflow that must assign a unique URL path without silently replacing existing content.

Preserve the desired slug whenever possible

A good collision resolver should change a URL only when it has to. Send the proposed value in desired_slug and provide every occupied value in used_slugs. The resolver performs exact, case-sensitive comparisons. If the proposed value does not appear in the occupied set, the result contains the original slug and reports that no collision occurred. It does not lowercase, trim, transliterate, or replace punctuation, because those transformations belong in a slugification step before uniqueness is checked. Keeping these responsibilities separate prevents unexpected URL changes and makes the result easy to reproduce in builds, migrations, and tests. An empty desired slug is rejected rather than turned into an arbitrary suffix, since such a result would hide a missing title or a broken upstream mapping. Duplicate entries in the used list do not affect the answer. They are collapsed naturally during lookup, so callers do not need to clean that list merely to obtain a stable result.

Choose the first available numeric suffix

When the exact desired slug is occupied, suffix selection begins at 2, following the familiar convention in which the unsuffixed path represents the first item and the next item receives -2. The resolver then checks candidates in ascending order: desired-2, desired-3, desired-4, and so forth. It stops at the first candidate absent from the supplied set. This means gaps are reused predictably. For example, if report, report-2, and report-4 are occupied, the returned slug is report-3. The comparison uses complete strings, so unrelated values such as annual-report or report-old never create a collision. Existing numeric endings are treated as part of the desired value rather than parsed or rewritten; if release-2 is requested and occupied, the first candidate is release-2-2. This literal behavior avoids guessing what a caller meant and guarantees that identical inputs always produce identical outputs, with no dependence on a database, clock, random generator, locale, or execution order.

Use the result safely in publishing workflows

This capability is useful at the point where your application already knows which slugs are reserved. Fetch or collect those values, pass them with the desired slug, and use the returned slug for the new record. The response also states whether a collision occurred and, for a suffixed result, which numeric suffix was selected. That metadata can support logs, previews, or a message explaining why a URL differs from the original proposal. The operation itself does not reserve the returned value, so systems handling concurrent writers should still enforce a unique constraint and retry with a refreshed used list if another writer claims the same slug. For batch imports, update your local occupied set after every successful assignment before resolving the next row. Exact comparison also means callers should apply their own URL policy consistently beforehand: normalize case, Unicode, spaces, or punctuation with a slugifier if the application requires it. Each API request costs $0.002, while the same deterministic logic can run in the browser for interactive checks.

Publish a page without replacing another

Keep the editor's preferred path when it is free, or assign the first available numbered alternative when it is already occupied.

Import records with stable unique URLs

Resolve each prepared slug against existing and newly assigned paths so repeated titles receive deterministic URL suffixes.

Generate documentation routes

Prevent duplicate headings or generated pages from claiming the same route while preserving readable, predictable paths.

What happens when the desired slug is free?

It is returned unchanged, and collision is false.

Which suffix is tried first?

The resolver starts with -2, then checks -3, -4, and higher values until it finds the first free candidate.

Does it convert text into a URL slug?

No. It compares and extends the exact value supplied. Run a slugifier first if you need case, whitespace, punctuation, or Unicode normalization.

Are comparisons case-sensitive?

Yes. Product and Product are identical, while Product and product are treated as different strings.

Does the capability reserve the returned slug?

No. It computes a candidate from the supplied list. Your storage layer should enforce uniqueness when the record is written.

What does an API request cost?

Each request costs $0.002.

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.

POSThttps://api.kit.forhosting.com/dev/slug-collision-resolve

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, by email and from Telegram — and soon from our app too.

curl -X POST https://api.kit.forhosting.com/dev/slug-collision-resolve \
  -H "Authorization: Bearer $KIT_KEY" \
  -H "Content-Type: application/json" \
  -d '{"desired_slug":"product-guide","used_slugs":["product-guide","product-guide-2"]}'
{
  "desired_slug": "product-guide",
  "used_slugs": [
    "product-guide",
    "product-guide-2"
  ]
}
{
  "task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
  "type": "dev.slug_collision_resolve",
  "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.

Per request$0.002

Published price — no tokens, no invented credits. A failed task is never charged.

max_used_slugs10000
max_slug_chars10000
HTTPCodeMeaning
401unauthorizedMissing or invalid API key.
402insufficient_balanceYour balance doesn't cover the task price.
404unknown_typeThat task type doesn't exist.
429rate_limitedToo many requests. Use the webhook instead of polling.

Read the full KIT documentation →