Try the URL Encoder/Decoder

URL Design as API Design: REST Conventions, Versioning Strategies, and the Long-Term Cost of Changing URLs

REST URL conventions, API versioning strategies (URL path vs header vs date-based like Stripe), trailing slash canonicalization, URL length limits, and the link rot problem β€” URL design decisions made at launch determine API maintainability for years.

By sadiqbd Β· June 13, 2026

Share:
URL Design as API Design: REST Conventions, Versioning Strategies, and the Long-Term Cost of Changing URLs

The URL of an API endpoint is a public commitment that's hard to break

Good URL design for APIs is not an aesthetic choice β€” it's an architectural decision with long-term maintenance consequences. A URL that changes after publication breaks bookmarks, third-party integrations, and search engine rankings. A versioning strategy chosen at launch determines how you can evolve the API without forcing all clients to upgrade simultaneously. These decisions are worth thinking through carefully before the first version ships.


REST URL conventions that have become de facto standards

REST (Representational State Transfer) doesn't mandate a specific URL structure, but conventions have emerged that most developers recognise:

Plural nouns for collections:

GET  /users           β€” list users
POST /users           β€” create a user
GET  /users/123       β€” get user 123
PUT  /users/123       β€” replace user 123
PATCH /users/123      β€” partially update user 123
DELETE /users/123     β€” delete user 123

Nested resources for relationships:

GET /users/123/orders          β€” orders belonging to user 123
GET /users/123/orders/456      β€” specific order for user 123
POST /users/123/orders         β€” create an order for user 123

Query parameters for filtering, sorting, pagination:

GET /products?category=electronics&sort=price&order=asc&page=2&per_page=20
GET /orders?status=pending&created_after=2024-01-01

Actions that don't map cleanly to CRUD:

POST /users/123/deactivate       β€” action on a resource
POST /orders/456/cancel          β€” verb as sub-resource
POST /payments/verify            β€” action with no natural noun

The "action as sub-resource" pattern is more RESTful than a query parameter (?action=cancel) and more predictable than a custom verb.


API versioning: three approaches

Approach 1: URL path versioning

GET /v1/users/123
GET /v2/users/123

Pros: immediately obvious in the URL; easy to route at the load balancer level; curl/browser testable without headers.

Cons: violates the purist REST argument that the URL should identify the resource, not the protocol version; clients must update their base URL when upgrading.

Widely used by: Twitter/X API, Stripe, GitHub, Twilio β€” the majority of commercial APIs choose URL versioning.

Approach 2: Header versioning

GET /users/123
Accept: application/vnd.api+json;version=2

or

GET /users/123
API-Version: 2024-11-01

Pros: clean URLs; the resource URL genuinely identifies the resource regardless of version.

Cons: not testable in a browser bar; not cacheable without custom Vary headers; less visible to developers.

Stripe's date-based versioning is an elegant example: each API version is a date string. New accounts get the latest version by default; existing integrations stay locked to their date unless explicitly upgraded.

Approach 3: Query parameter versioning

GET /users/123?version=2

Pros: easy to add; visible in URLs.

Cons: pollutes the query string; query parameters are typically reserved for filtering/search; caches may behave inconsistently with version parameters.


URL canonicalization: trailing slashes and case sensitivity

Trailing slash inconsistency:

GET /users     β†’ 200 OK (list users)
GET /users/    β†’ 301 Redirect to /users? Or 404? Or 200 OK identical to /users?

The answer depends entirely on your framework's default behaviour. Rails, Django, and Express all handle this differently. The problems:

  • Two URLs that return the same content are duplicates from a search engine perspective
  • Links from external sources may use either form
  • API clients that add or omit trailing slashes break silently

Best practice: choose one canonical form and redirect the other. For APIs, trailing slash on collection endpoints (/users/) is common; trailing slash on resource endpoints (/users/123/) is rarer. Pick one and enforce it consistently.

URL case sensitivity: HTTP URLs are case-sensitive by specification (the path portion, not the host). /Users and /users are different URLs. However:

  • Windows IIS serves files case-insensitively by default
  • Apache on Linux is case-sensitive
  • S3 bucket object keys are case-sensitive

For APIs: use lowercase consistently. For web pages: canonicalize with a redirect.


URL length limits

URL length is technically unlimited in the HTTP specification, but practical limits exist:

Limit Value Notes
IE (old) 2,083 characters The lowest common denominator for years
Chrome 2MB Effectively unlimited
Firefox Unlimited
Apache 8,190 bytes (default) LimitRequestLine directive
nginx 4,096 bytes (default) large_client_header_buffers
Most CDNs 8KB–32KB Varies by provider

Practical implication: URLs over 2,000 characters are risky. For APIs that accept large sets of IDs as path or query parameters, a POST request with the IDs in the body is more reliable than a GET with hundreds of IDs in the query string.


Stable URLs and the Link Rot problem

Tim Berners-Lee's principle: "Cool URIs don't change." Every URL change breaks:

  • Links from other websites and documents
  • Browser bookmarks
  • Search engine rankings (accumulated PageRank is tied to the URL)
  • API integrations and client code

Redirect strategies when URLs must change:

  • 301 (Permanent Redirect): for content that has moved permanently. Search engines transfer link equity.
  • 302 (Temporary Redirect): for genuinely temporary moves. Search engines keep the original URL indexed.
  • 308 (Permanent Redirect): preserves the HTTP method (POST stays POST) β€” use for APIs where method preservation matters.

URL design to minimise future changes:

  • Avoid including mutable data in URLs: /blog/2024/article-title becomes stale if the article is updated in 2025 and you add the year; /blog/article-title is more durable.
  • Avoid including implementation details: /api/v1/get-user exposes the implementation verb; /api/v1/users/{id} is more stable.
  • Use opaque IDs rather than database primary keys in URLs where possible β€” UUIDs are harder to enumerate.

How to use the URL Encoder/Decoder on sadiqbd.com

  1. Encode URL components β€” paste a query string value containing special characters and get the percent-encoded form safe for inclusion in a URL
  2. Decode incoming URLs β€” paste a URL from a log or request and see the decoded parameter values
  3. Build query strings β€” ensure each parameter value is properly encoded before concatenation
  4. Debug routing issues β€” check whether special characters in path segments are causing unexpected routing behaviour

Frequently Asked Questions

Should API endpoints use hyphens or underscores in URLs? Hyphens (-) are strongly preferred for URLs visible to humans or search engines β€” Google's John Mueller has stated hyphens are treated as word separators in URLs, while underscores are treated as connectors (making word_count a single word rather than two). For internal API endpoints not indexed by search engines, the distinction is cosmetic. Be consistent either way.

When should I use query parameters vs path parameters? Path parameters (/users/{id}) identify a specific resource β€” they're part of the resource's identity. Query parameters filter, sort, or paginate a collection, or provide optional context. A user's ID goes in the path; a date range filter goes in the query string.

Is the URL Encoder/Decoder free? Yes β€” completely free, no sign-up required.

Try the URL Encoder/Decoder free at sadiqbd.com β€” percent-encode and decode any URL or URL component instantly.

Share:
Try the related tool:
Open URL Encoder/Decoder

More URL Encoder/Decoder articles