Install any skill in seconds. Free to start, no credit card required.
Get Started Free →REST API design with resource naming, pagination, versioning, and OpenAPI spec generation
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 48% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 12% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 6% | 0% |
| case-19 | ✗→✓ | ▲ Improved | -4% | 0% |
| case-14 | ✗→✓ | ▲ Improved | 8% | 0% |
/users, /orders, /products/users/{id}/orders/user-profiles, not /userProfiles/users/{id}/activate is wrong, use POST /users/{id}/activation| Method | Purpose | Idempotent | Request Body | Success Code | |--------|---------|------------|-------------|-------------| | GET | Read resource(s) | Yes | No | 200 | | POST | Create resource | No | Yes | 201 | | PUT | Full replace | Yes | Yes | 200 | | PATCH | Partial update | No | Yes | 200 | | DELETE | Remove resource | Yes | No | 204 |
Return Location header on POST with the URL of the created resource.
200 OK - Successful read/update
201 Created - Successful creation
204 No Content - Successful delete
400 Bad Request - Validation error (include field-level errors)
401 Unauthorized - Missing or invalid authentication
403 Forbidden - Authenticated but not authorized
404 Not Found - Resource does not exist
409 Conflict - State conflict (duplicate, version mismatch)
422 Unprocessable - Semantically invalid (valid JSON, bad values)
429 Too Many Reqs - Rate limited (include Retry-After header)
500 Internal Error - Unhandled server error (never expose stack traces)json{ "error": { "code": "VALIDATION_ERROR", "message": "Request validation failed", "details": [ { "field": "email", "message": "Must be a valid email address" }, { "field": "age", "message": "Must be at least 18" } ] } }
Use consistent error codes across the API. Document every code in your API reference.
GET /users?limit=20&cursor=eyJpZCI6MTAwfQ
Response:
{
"data": [...],
"pagination": {
"next_cursor": "eyJpZCI6MTIwfQ",
"has_more": true
}
}Use cursor pagination for large or frequently changing datasets. Encode cursors as opaque base64 strings. Never expose raw IDs in cursors.
GET /users?page=3&per_page=20
Response:
{
"data": [...],
"pagination": {
"page": 3,
"per_page": 20,
"total": 245,
"total_pages": 13
}
}Only use offset pagination when total count is cheap and dataset is small.
GET /orders?status=pending&created_after=2025-01-01&sort=-created_at,+total
GET /products?category=electronics&price_min=100&price_max=500
GET /users?search=john&fields=id,name,emailUse field selection (fields param) to reduce payload size. Prefix sort fields with - for descending.
Prefer URL path versioning for simplicity:
/api/v1/users
/api/v2/usersRules:
Sunset header and 6-month noticeContent-Type: application/json
Accept: application/json
Authorization: Bearer <token>
X-Request-Id: <uuid> # For tracing
X-RateLimit-Limit: 100 # Requests per window
X-RateLimit-Remaining: 47 # Remaining in window
X-RateLimit-Reset: 1700000000 # Window reset Unix timestamp
Retry-After: 30 # Seconds until rate limit resetsAlways return X-Request-Id in responses for debugging.
$ref for shared schemas: $ref: '#/components/schemas/User'examples for every endpointoneOf/anyOf for polymorphic responsesyamlpaths: /users/{id}: get: operationId: getUser parameters: - name: id in: path required: true schema: type: string format: uuid responses: '200': description: User found content: application/json: schema: $ref: '#/components/schemas/User' '404': $ref: '#/components/responses/NotFound'
429 with Retry-After headerOther measured skills in the registry, with their headline benchmark lift.