Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

Most APIs that end up with ten GET endpoints for one application did not need ten. The sprawl usually comes from modelling questions and screens instead of resources. The lesson behind the title is that a client should read a small set of stable resources, each returned as a representation that answers real use cases, while every change goes through a method whose meaning matches that change. The goal is not a single GET route for an entire system. The goal is coherent resource modelling, where the URL names a thing and the HTTP method says what you are doing to it.

What GET is actually for

RFC 9110, the IETF standard for HTTP semantics (June 2022), defines the method in one sentence: “The GET method requests transfer of a current selected representation for the target resource.” In other words, GET asks for a representation of something. It does not ask the server to do work, create anything, or change a record.

Two properties matter for design. GET is safe, meaning the client is not requesting a state change. GET is also idempotent, meaning repeating the same request has the same intended effect as sending it once. Neither property promises that every response is identical. A request for a station’s current bike count can return different numbers an hour apart, because the resource changed. Idempotency describes the effect of the request on the server, not the stability of the data.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

This distinction is the root of good GET design. If GET is for reading representations, then a route like /getStationsWithFreeBikes is usually a question wrapped in a URL, not a resource. The same information is often better expressed as the station collection, with filtering handled by query parameters.

How GET routes multiply

GET sprawl tends to follow a predictable pattern. Teams add an endpoint each time a screen needs a slightly different slice of data. Over a few releases the API accumulates routes that overlap, duplicate each other, and encode verbs in the path. Typical symptoms include:

  • Verbs in paths, such as /getUser, /fetchUserOrders, or /listActiveRentals, when the HTTP method and the collection already carry that meaning.
  • Near-identical routes that differ only by one field or one filter, each maintained separately.
  • Separate endpoints for each attribute of one thing, forcing clients into several round trips to build a single view.
  • Read and write operations mixed on the same URL, with GET sometimes used to trigger changes.
  • Clients that depend on the database layout, because the routes mirror tables and joins rather than things a user cares about.

Each of these is a modelling problem first. Adding more GET routes hides the problem for one release and makes it harder to fix afterwards.

Start from resources, not verbs

The most reliable way to avoid this is to design from the user’s needs outward. Ximenes and Juvenal’s O’Reilly walkthrough on designing a bike-rental API (published December 21, 2017) follows this approach. It starts with what users want to do, pulls out the nouns and verbs, and turns actions into resources. Their illustrative sentence is “The correct way to rent something via HTTP is to POST a Rent.” That is a worked example for one domain, not a universal rule, but the reasoning transfers.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  1. List the user goals in plain language, such as “see which stations have bikes,” “rent a bike,” “change where I will return it,” and “cancel.”
  2. Mark the nouns. Here they are stations, bikes, and rentals. A noun that a client needs to refer to, store, or retrieve is usually a resource.
  3. Turn each action into a resource change. “Rent” becomes creating a rental, not a verb-named endpoint.
  4. Assign a URI to each resource collection and to individual items within it, such as /stations/ and /rents/{id}/.
  5. Map each operation to the method whose semantics match it: GET to read, POST to create, PUT to update, DELETE to remove or cancel.
  6. Check that each representation answers the client’s use case without returning fields nobody needs.

A worked example: bike rentals

The O’Reilly example models stations and rentals. Its routes illustrate how few resource URIs can cover a set of user goals:

User action Method URI What it does
See stations and available bikes GET /stations/ Returns the station collection, including each station’s available-bike quantity
Rent a bike POST /rents/ Creates a rental representation
Review rental history GET /rents/ Returns the rental collection
Change the return destination PUT /rents/{id}/ Updates an existing rental
Cancel the active rental DELETE /rents/{id}/ Cancels the rental

Notice what is absent. There is no /getAvailableBikes route, because availability is a property of the station representation. There is no /cancelRent route, because cancelling is a DELETE on the rental. The method carries the verb, and the URI carries the noun.

Should related data live in one representation?

Designers often ask whether related fields belong in one response or should be fetched through separate resources. There is no fixed answer, but the trade-off can be decided with a few questions:

  • Do most clients need the fields together? If so, one representation avoids extra round trips.
  • Does the field change at a different rate from the rest? A fast-changing value can justify its own resource, or at least a clearly separate representation.
  • Would including it make the payload heavy for clients that do not need it? Consider a summary representation for lists and a detail representation for a single item.
  • Is the field owned by another resource? If a station’s bike list is managed elsewhere, linking to it is usually cleaner than copying it.

The comparison below shows the difference in practice:

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Aspect Verb-driven routes Resource-driven routes
URL pattern Names an operation, such as /getStationsWithBikes Names a thing, such as /stations/
Adding a new view of the same data Usually adds a new route Often adds a query parameter or representation option
Writes Often reuse GET or ad hoc paths Use POST, PUT, or DELETE on the resource
Client dependence on storage Often high, since routes mirror queries Lower, since routes mirror user concepts
Route count over time Tends to grow with each screen Tends to grow with new resources
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Caching and retries

Caching

Because GET is defined as a read, it is the natural method for cached responses. But caching is not automatic for every GET. Whether a response can be stored and reused is governed by HTTP caching rules and response directives such as Cache-Control. Resource design affects this indirectly: different URIs and query strings produce different cache entries, so a stable collection URI with a few well-named parameters is easier to cache predictably than many one-off paths that differ in unrelated ways.

Retries

Idempotency is what makes retrying a GET generally safe. If a request times out, the client can send it again without intending a second effect. The same reasoning does not transfer to POST, which creates a new rental each time it succeeds. For creation, clients need a retry strategy that avoids duplicates, such as an idempotency key or a check before retrying. That is a separate design decision and should be made explicitly.

When more GET routes are justified

Consolidation is not always correct. Separate GET routes make sense when the operation is genuinely different: a search with its own relevance rules, a report that is expensive to compute, or a resource that clients consume through a different contract. The test is whether the new route names a resource or a question that could be answered by an existing collection with a parameter. If it names a question, first check whether the question is a filter on something you already expose.

A review checklist

  • Every URI names a resource or a collection, not an action.
  • Reads use GET and never change state.
  • Creates, updates, and deletes use POST, PUT, and DELETE on the resource they affect.
  • Two routes that differ only by one field or filter are merged, unless their contracts really differ.
  • Representations include what the client needs for its main use case and nothing that forces extra requests.
  • Clients do not need to know how the database is organised to use the API.

Run this list against the route table before adding the next endpoint. The question to ask is usually not “which GET do we need?” but “which resource is this, and what method changes it?”

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

For the protocol details, the RFC 9110 specification is the primary reference. For a worked resource-modelling example, O’Reilly’s December 21, 2017 article by Filipe Ximenes and Flávio Juvenal uses a bike-rental domain similar to the one above.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.