UUID in REST API Design — Best Practices
- UUID path parameters (/resources/{uuid}) make URLs unguessable and remove sequential enumeration attacks.
- Always validate UUID format at the API boundary and return 400 (not 500) for malformed IDs.
- UUID idempotency keys on POST requests prevent duplicate operations on network retries.
- Never expose internal integer IDs alongside UUIDs in API responses — this defeats the security benefit.
- Consider short URL-safe aliases (NanoID, CUID) for user-facing URLs — UUIDs are long and not copy-friendly.
Table of Contents
UUIDs in API design solve a specific problem: auto-increment integer IDs leak information. A URL like /orders/4521 tells the caller your application has roughly 4,521 orders and that order 4,520 exists. UUID-based URLs like /orders/550e8400-e29b-41d4-a716-446655440000 reveal nothing. This guide covers the right patterns — and the common mistakes that erode these benefits.
UUID in URL Path Parameters
The standard pattern:
GET /orders/{uuid} → fetch a single order
PUT /orders/{uuid} → replace an order
PATCH /orders/{uuid} → update fields on an order
DELETE /orders/{uuid} → delete an order
GET /orders/{uuid}/items → nested resources
// Example:
GET /orders/550e8400-e29b-41d4-a716-446655440000
GET /orders/550e8400-e29b-41d4-a716-446655440000/items/a6edc906-2f9f-5fb2-a373-efac406f0ef2
Validation rules:
- Validate UUID format before any database query — return
400 Bad Requestwith a clear message, not a500from a database error - Return
404 Not Foundwhen the UUID is valid format but not found — never leak whether an ID exists to unauthorized callers - Return
403 Forbiddenwhen the UUID exists but the caller lacks permission — do not return404to hide its existence from authenticated users who should know it exists
Idempotency Keys — UUID for Safe Retries
Idempotency keys prevent duplicate operations when POST requests are retried (network timeout, client retry logic). The pattern: client generates a UUID before the first attempt, sends it as a header. If the server has already processed this key, it returns the original response instead of creating a duplicate.
// Client sends:
POST /orders
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Content-Type: application/json
{"productId": "...", "quantity": 1}
// Server logic (pseudocode):
idempotencyKey = request.headers["Idempotency-Key"]
if (idempotencyKey):
cached = cache.get(idempotencyKey)
if (cached):
return cached.response // Return original response, don't re-create
result = createOrder(request.body)
cache.set(idempotencyKey, result, ttl=24h)
return result
Stripe, PayPal, and most financial APIs use this pattern. The UUID is generated client-side because the client is responsible for ensuring uniqueness per attempt. Store idempotency keys in Redis or a database with a TTL of 24 hours.
Sell Custom Apparel — We Handle Printing & Free ShippingUUID for Request Correlation and Tracing
// Generate a trace ID per request:
GET /orders/550e8400-...
X-Request-ID: a6edc906-2f9f-5fb2-a373-efac406f0ef2
// Server propagates to downstream services:
X-Request-ID: a6edc906-2f9f-5fb2-a373-efac406f0ef2 // same ID throughout
// Log every service with the same trace ID:
{"level":"info","trace_id":"a6edc906-...","message":"order fetch started"}
// If client does not send one, generate at the API gateway:
if (!req.headers["x-request-id"]) {
req.headers["x-request-id"] = crypto.randomUUID();
}
Correlation IDs turn scattered logs from multiple services into a traceable request chain. Always echo the request ID back in the response headers so clients can include it in bug reports.
Common Mistakes When Using UUID in APIs
Exposing both integer ID and UUID in the response:
// Bad — leaks your internal integer ID:
{"id": "550e8400-...", "internal_id": 4521, "status": "pending"}
// Good — UUID only in external-facing response:
{"id": "550e8400-...", "status": "pending"}
Not validating format before querying:
// Bad — can produce a 500 error from the database:
const order = await db.findById(req.params.id); // raw string passed to DB
// Good — validate first:
let id;
try { id = UUID.fromString(req.params.id); }
catch (e) { return res.status(400).json({error: "Invalid ID format"}); }
const order = await db.findById(id);
Using UUID as a slug in user-facing URLs: UUIDs are 36 characters — difficult to copy, ugly in emails, and impossible to remember. For URLs shared with users, consider a slug (/orders/my-gym-order-march) alongside the internal UUID, or use a shorter identifier like NanoID for the public-facing URL while keeping UUID internally.
UUID in API Response Bodies
Standard conventions for UUID fields in JSON responses:
{
"id": "550e8400-e29b-41d4-a716-446655440000", // always string, always hyphenated
"orderId": "a6edc906-2f9f-5fb2-a373-efac406f0ef2", // camelCase for related IDs
"createdAt": "2026-04-14T12:00:00Z"
}
Conventions:
- Always serialize UUID as a lowercase hyphenated string — not uppercase, not bytes, not Base64
- The primary ID field should be
"id"— avoid"uuid"or"guid"as field names - Related entity IDs use the entity name as a prefix:
"customerId","productId" - Do not strip hyphens in API responses — the hyphenated format is the canonical standard and is parseable by all UUID libraries without extra conversion
Generate UUIDs for API Testing
The Cheetah UUID Generator produces 1 or 10 RFC-compliant UUID v4 values — paste into request bodies, headers, or Postman data files instantly.
Open Free UUID GeneratorFrequently Asked Questions
Should REST API resource IDs be UUIDs or integers?
UUIDs for most cases. They prevent sequential enumeration attacks, work naturally in distributed systems, and can be generated client-side before the resource is created. Integers are fine for internal-only APIs or when ID density is critical.
What HTTP status code for an invalid UUID in a path parameter?
400 Bad Request — the client sent a malformed identifier. Do not return 404 for format errors, as 404 implies "valid format but not found." A clear 400 error body like {"error": "Invalid ID format"} is most helpful.
Can the client generate the UUID for a POST request?
Yes — this is a valid pattern (sometimes called "client-generated IDs"). The client generates a UUID, uses it as the resource ID in the URL: PUT /orders/{uuid} instead of POST /orders. This makes the operation inherently idempotent.
What is the difference between an idempotency key and a request ID?
An idempotency key is tied to a business operation — it prevents duplicate order creation. A request ID (trace ID) is for observability — it correlates log entries across services. Both are UUIDs, but they serve different purposes and are stored differently.
Should I expose UUID or a shorter ID in public-facing URLs?
Depends on the context. For API calls (machine-to-machine), UUID is fine. For URLs shared with humans (emails, dashboards, support tickets), consider a shorter URL-safe ID like NanoID (21 chars) or a readable slug alongside the UUID.

