Endpoints
Each resource needs an endpoint. That is the URL a client sends a request to. The first part of the URL is the same for every endpoint, so we decide it once and call it the base URL. I will use https://registrar.university.edu/api. Every endpoint in this chapter is written relative to it, so /courses means https://registrar.university.edu/api/courses.
Notice the API is under a path, /api, and not under a subdomain like the api.weather.example we saw earlier. Both forms are common. A subdomain is the usual choice when the API will run on its own server, separate from the website, because a subdomain can point anywhere. A path is the usual choice when the API and the website are served from the same place and a prefix is enough to tell them apart. The constraints say the registrar’s API runs on the registrar’s existing hosting platform, next to the registrar’s system, so the path fits. Everything after the base URL is where the resource goes.
The path for a collection is its name. A request for all of the courses goes to /courses. A request for all of the terms goes to /terms. A collection with two words in its name gets a hyphen, so subject areas go to /subject-areas. A URL is case sensitive on most servers, so we keep every path in lowercase and never mix.
To request an individual item, put its identifier after the collection name. One course is /courses/EN.601.226. One offering is /offerings/8431, where 8431 is whatever the registrar’s identifier looks like. The path reads left to right, from the collection to the individual item. The requirements do not call for requesting an individual term or subject area, so we do not give them individual-item paths.
The requirements let a client narrow a list and search it. Those go in query parameters, after the path. Here are the courses in one subject area:
/courses?subject_area=computer-science
Here is a search of courses by number or title. One parameter does both, because it is more convenient for a client to send one search string than two. The API will look for it in both places.
/courses?search=data
Here are the offerings in one term, for one course, and both at once:
/offerings?term=fall-2026
/offerings?course=EN.601.226
/offerings?term=fall-2026&course=EN.601.226
The rule is this. The path identifies a collection or an individual item. Query parameters narrow or search a collection. A client that wants all of the offerings sends no parameters. A client that wants fewer adds a parameter for each way it wants to narrow. The endpoint does not change. There is one collection endpoint for offerings, and every request for a list of offerings goes to it.
The values a client sends for term and subject_area have to come from somewhere. That is what the terms and subject areas endpoints are for. A client requests /terms, gets back the terms with the value each one goes by, and sends that value back as a query parameter. The response tells the client what to send.
Here is the full list.
| Request | Endpoint |
|---|---|
| All terms | /terms |
| All subject areas | /subject-areas |
| All courses, narrowed | /courses?subject_area=... |
| Courses, searched | /courses?search=... |
| One course | /courses/EN.601.226 |
| All offerings, narrowed | /offerings?term=...&course=... |
| Offerings, searched | /offerings?search=... |
| One offering | /offerings/8431 |
Endpoint design mistakes
A RESTful HTTP API is a resource-oriented API. The path names a resource, and the HTTP method names the action. You should not include a verb in the path. For example, /getCourses is wrong because it names an action along with a resource. The client is getting courses by making a GET request to the /courses endpoint.
Request headers carry information about the client, such as which client it is, what format it wants, what version of the API it expects. That information belongs in the headers, not in the path or query parameters. For example, /courses?format=json is wrong because it puts the format in a query parameter instead of a header. The client should send an Accept header with the value application/json to indicate it wants JSON.
Turning query parameters into the path is also wrong. For example, /offerings?term=fall-2026 is correct, but /offerings/terms/fall-2026 is wrong. Terms have their own collection; they are not a collection nested under offerings. The client is narrowing the offerings by sending a query parameter, not by changing the path.
These are examples of good practice, not absolute rules. You can deviate from them, but you need a good reason. Sometimes nesting a resource in the path makes sense: if a resource is always accessed in the context of another resource, nest it. Take /posts/123/comments for comments on a specific post. There, the path reflects the relationship between the two resources.