TL;DR
REST API URLs should contain nouns (resources), not verbs (actions). HTTP methods (GET, POST, PUT, DELETE) are the verbs. Using action verbs like /getUser or /createOrder violates REST principles, creates inconsistency, and makes APIs harder to maintain. Modern PetstoreAPI uses resource-oriented URLs throughout.
Introduction
You’re designing an API endpoint to search for pets by status. Your first instinct might be: GET /findPetsByStatus?status=available. It’s descriptive, clear, and tells you exactly what it does. It’s also wrong.
REST APIs should use nouns in URLs, not verbs. The HTTP method is the verb. GET /pets?status=available is the correct design. The URL represents the resource (pets), and the method represents the action (get).
The old Swagger Petstore made this mistake with endpoints like /pet/findByStatus and /pet/findByTags. These action verbs in URLs violate REST principles and create maintenance problems. Modern PetstoreAPI fixes this by using resource-oriented URLs consistently.
In this guide, you’ll learn why verbs don’t belong in REST URLs, how to design resource-oriented endpoints, and how Modern PetstoreAPI implements this correctly.
The Verb Problem in REST APIs
Action verbs in URLs indicate you’re thinking in RPC (Remote Procedure Call) terms, not REST terms.
RPC-Style URLs (Wrong)
POST /createUser
GET /getUser?id=123
PUT /updateUser
DELETE /deleteUser?id=123
GET /findUsersByRole?role=admin
POST /sendEmail
GET /calculateTotal
These URLs describe actions. They read like function calls: createUser(), getUser(), sendEmail().
REST-Style URLs (Correct)
POST /users
GET /users/123
PUT /users/123
DELETE /users/123
GET /users?role=admin
POST /emails
GET /orders/123/total
These URLs describe resources. The HTTP method provides the action.
Why This Matters
Consistency: REST URLs follow a predictable pattern. Once you know the resource name, you know all the endpoints:
GET /pets- List petsPOST /pets- Create petGET /pets/{id}- Get petPUT /pets/{id}- Update petDELETE /pets/{id}- Delete pet
With verb-based URLs, every endpoint is unique. There’s no pattern to follow.
Scalability: As your API grows, verb-based URLs multiply:
/findPetsByStatus/findPetsByTags/findPetsByOwner/findPetsByBreed/searchPets/queryPets
Resource-oriented URLs use query parameters:
GET /pets?status=availableGET /pets?tags=friendlyGET /pets?owner=johnGET /pets?breed=labrador
One endpoint handles all filtering.
Why HTTP Methods Are the Verbs
REST leverages HTTP’s built-in verbs. You don’t need to invent your own.
HTTP Methods Map to CRUD Operations
POST → Create
GET → Read
PUT → Update (replace)
PATCH → Update (partial)
DELETE → Delete
These methods have defined semantics. GET is safe and idempotent. POST creates resources. DELETE removes them.
Example: User Management
Wrong (verbs in URLs):
POST /createUser
GET /getUser?id=123
POST /updateUser
POST /deleteUser
Every operation uses a different URL. The HTTP method doesn’t convey meaning.
Correct (HTTP methods as verbs):
POST /users ← Create user
GET /users/123 ← Get user
PUT /users/123 ← Update user
DELETE /users/123 ← Delete user
The URL stays the same. The method changes.
Benefits of Using HTTP Methods
1. Caching: GET requests can be cached. Browsers and proxies know this. If you use POST /getUser, caching breaks.
2. Idempotency: PUT and DELETE are idempotent. Calling them multiple times has the same effect as calling once. This matters for retry logic.
3. Safety: GET is safe—it doesn’t modify state. Tools and crawlers can safely call GET endpoints.
4. Standards Compliance: HTTP clients, proxies, and caches understand HTTP methods. They don’t understand your custom verbs.
Real Examples from Swagger Petstore
The old Swagger Petstore includes several verb-based endpoints.
Example 1: Finding Pets by Status
Swagger Petstore (Wrong):
GET /pet/findByStatus?status=available
Problems:
findByStatusis a verb phrase- Inconsistent with
/pet/{id}endpoint - Can’t be extended easily (what about finding by other criteria?)
Modern PetstoreAPI (Correct):
GET /pets?status=AVAILABLE
Benefits:
- Resource-oriented (
/pets) - Uses query parameters for filtering
- Consistent with other endpoints
- Easy to extend:
GET /pets?status=AVAILABLE&species=dog
See the Modern PetstoreAPI REST documentation for the complete implementation.
Example 2: Finding Pets by Tags
Swagger Petstore (Wrong):
GET /pet/findByTags?tags=tag1,tag2
Modern PetstoreAPI (Correct):
GET /pets?tags=friendly,trained
Example 3: User Login
Swagger Petstore (Wrong):
GET /user/login?username=john&password=secret
Multiple problems:
loginis a verb- Using
GETfor authentication (security disaster) - Passwords in URL query parameters
Modern PetstoreAPI (Correct):
POST /auth/login
Content-Type: application/json
{
"username": "john",
"password": "secret123"
}
Benefits:
- Resource-oriented (
/auth) - Correct HTTP method (
POST) - Credentials in request body, not URL
- Returns JWT token for subsequent requests
How Modern PetstoreAPI Fixes This
Modern PetstoreAPI uses resource-oriented URLs throughout.
Pet Management
GET /pets ← List all pets
GET /pets?status=AVAILABLE ← Filter by status
GET /pets?species=dog ← Filter by species
GET /pets/{id} ← Get specific pet
POST /pets ← Create new pet
PUT /pets/{id} ← Update pet
PATCH /pets/{id} ← Partial update
DELETE /pets/{id} ← Delete pet
No verbs. Just resources and HTTP methods.
Order Management
GET /orders ← List orders
GET /orders/{id} ← Get order
POST /orders ← Create order
PUT /orders/{id} ← Update order
DELETE /orders/{id} ← Cancel order
GET /orders/{id}/items ← Get order items
Complex Operations
For operations that don’t map cleanly to CRUD, Modern PetstoreAPI uses sub-resources:
POST /orders/{id}/payment ← Process payment for order
POST /orders/{id}/shipment ← Create shipment for order
POST /pets/{id}/adoption ← Start adoption process
These are still resource-oriented. /orders/{id}/payment represents the payment resource for an order.
When Verbs Seem Necessary
Some operations don’t fit the CRUD model. Here’s how to handle them without verbs in URLs.
Search Operations
Wrong:
GET /searchPets?query=labrador
Correct Option 1 (Query Parameters):
GET /pets?search=labrador
Correct Option 2 (Search Resource):
GET /pets/search?q=labrador
Correct Option 3 (QUERY Method):
QUERY /pets
Content-Type: application/json
{
"query": "labrador",
"filters": {
"status": "AVAILABLE"
}
}
Modern PetstoreAPI supports all three patterns depending on complexity.
Calculations
Wrong:
GET /calculateShipping?weight=10&destination=NY
Correct:
GET /shipping-estimates?weight=10&destination=NY
The resource is “shipping estimates,” not the calculation action.
Batch Operations
Wrong:
POST /batchDeletePets
Correct:
DELETE /pets?ids=1,2,3
Or use a batch resource:
POST /pets/batch-operations
Content-Type: application/json
{
"operation": "delete",
"ids": [1, 2, 3]
}
Actions That Change State
Wrong:
POST /activateUser
POST /deactivateUser
Correct:
PATCH /users/{id}
Content-Type: application/json
{
"status": "ACTIVE"
}
State changes are updates to the resource.
Testing URL Design with Apidog
Apidog helps you validate REST API design and catch verb usage in URLs.
Import Modern PetstoreAPI
- Import the Modern PetstoreAPI OpenAPI spec
- Apidog automatically generates test cases
- Review endpoint structure and naming
Check for Verbs in URLs
Create a custom validation rule in Apidog:
// Check if URL contains common action verbs
const verbs = ['get', 'create', 'update', 'delete', 'find', 'search',
'calculate', 'process', 'send', 'fetch'];
const url = request.url.toLowerCase();
for (const verb of verbs) {
if (url.includes(`/${verb}`)) {
throw new Error(`URL contains verb: ${verb}. Use resource-oriented URLs instead.`);
}
}
Test Endpoint Consistency
Apidog can verify that related endpoints follow consistent patterns:
✓ GET /pets
✓ POST /pets
✓ GET /pets/{id}
✓ PUT /pets/{id}
✓ DELETE /pets/{id}
All use the same base resource (/pets).
Compare Against Modern PetstoreAPI
Use Modern PetstoreAPI as a reference:
- Import both your API and Modern PetstoreAPI into Apidog
- Compare endpoint structures side-by-side
- Identify inconsistencies and verb usage
- Refactor your API to match REST principles
Migration Strategies
If you have an existing API with verbs in URLs, here’s how to migrate.
Strategy 1: Versioning
Create a new API version with correct URLs:
# Old API (v1)
GET /api/v1/findPetsByStatus?status=available
# New API (v2)
GET /api/v2/pets?status=available
Maintain v1 for backward compatibility, encourage migration to v2.
Strategy 2: Aliasing
Support both old and new URLs temporarily:
# Old URL (deprecated)
GET /pet/findByStatus?status=available
# New URL (preferred)
GET /pets?status=available
Return deprecation warnings in responses:
{
"data": [...],
"warnings": [
{
"code": "DEPRECATED_ENDPOINT",
"message": "This endpoint is deprecated. Use GET /pets?status=available instead.",
"sunset": "2027-01-01"
}
]
}
Strategy 3: Redirects
Use HTTP 301 redirects for simple migrations:
GET /pet/findByStatus?status=available
→ 301 Moved Permanently
Location: /pets?status=available
This works for GET requests but not for POST, PUT, or DELETE.
Conclusion
REST API URLs should contain nouns (resources), not verbs (actions). HTTP methods provide the verbs. This principle creates consistent, scalable, and maintainable APIs.
The old Swagger Petstore violated this with endpoints like /pet/findByStatus. Modern PetstoreAPI fixes this by using resource-oriented URLs throughout: /pets?status=AVAILABLE.
Key takeaways:
- Use nouns in URLs:
/pets,/orders,/users - Let HTTP methods be the verbs:
GET,POST,PUT,DELETE - Use query parameters for filtering:
/pets?status=available - For complex operations, use sub-resources:
/orders/{id}/payment - Test your API design with Apidog to catch verb usage early
Check out the Modern PetstoreAPI documentation for complete examples of resource-oriented URL design.
FAQ
Can I ever use verbs in REST URLs?
Rarely. If an operation truly doesn’t fit the resource model (like /search or /login), a verb might be acceptable. But 95% of the time, you can model it as a resource.
What about /login and /logout?
These are common exceptions. Many APIs use /auth/login and /auth/logout. Alternatively, model them as resources: POST /sessions (login) and DELETE /sessions/{id} (logout).
How do I handle complex queries?
Use query parameters for simple filtering: /pets?status=available&species=dog. For complex queries, use the QUERY HTTP method or a search resource: POST /pets/search.
What if my operation doesn’t map to CRUD?
Model it as a sub-resource. Instead of POST /processPayment, use POST /orders/{id}/payment. The payment is a resource related to the order.
How do I test if my URLs are resource-oriented?
Use Apidog to import your OpenAPI spec and check for verbs in URLs. Compare your API structure against Modern PetstoreAPI as a reference.
Should I use /pets/search or /pets?search=query?
Both are acceptable. /pets?search=query is simpler for basic search. /pets/search or QUERY /pets works better for complex search with multiple parameters.
How do I migrate from verb-based URLs?
Use API versioning (/v2/pets instead of /v1/findPets), add deprecation warnings, and give clients time to migrate. See the Migration Strategies section for details.
Does Modern PetstoreAPI use any verbs in URLs?
Modern PetstoreAPI avoids verbs in URLs. Operations like search, filter, and authentication are modeled as resources or use query parameters. Check the REST API documentation for examples.



