Skip to content
thesarfo

Reference

Interview Prep: API Design

What an API is, REST principles, SOAP vs REST, endpoint design, versioning, idempotence, rate limiting, OAuth, pagination, and documentation — with code examples.

views 0

1. What is an API and what are its main purposes?

An Application Programming Interface (API) is a set of definitions and protocols that enables different software applications to communicate with one another. It serves as an intermediary tool that abstracts the underlying complexity of a system, making it easier for programmers to integrate and use specific features or data.

Core Functions of an API

  1. Abstraction — hides complex internal workings and provides a simpler external interface. For example, using an API to send an email, a developer doesn’t need to understand the intricate steps of establishing a network connection to a mail server.
  2. Standardization — establishes common rules and formats, ensuring consistent interactions. This centralizes and streamlines processes, making them easier to implement and manage.
  3. Decoupling — separates components, allowing them to evolve independently. When an underlying system changes, its API can remain largely unaffected as long as the external interface is maintained.
  4. Reusability — encapsulates functionality in a modular form, portable across different systems or applications.
  5. Security and Access Control — provides methods for authentication, ensuring only authorized users or software can interact with the API, and centralizes security management.
  6. Consolidation of Data and Services — aggregates data or services from different sources, presenting a unified view to the consumer. Particularly valuable in distributed systems where diverse data may be located across multiple servers or cloud services.

2. Can you explain the difference between an API and a Web service?

APIs and web services both facilitate communication between two distinct systems, but in different ways.

API

An API is primarily focused on enabling communication between a web service and a client application. It typically has narrower scope and may offer functions or methods as clear-cut entry points.

Web Service

A web service is more expansive, enabling interaction not just with clients but also with other software, resulting in a more comprehensive service-oriented architecture.

Key Differences

  • Data and Functionality Exposure — web services are primarily concerned with data (often XML or JSON) and don’t explicitly expose business logic. APIs are more varied, offering data and functionality.
  • Communication Protocols — web services aren’t tied to a particular protocol; RESTful APIs typically use HTTP, and SOAP-based services frequently use protocols like SMTP and TCP.
  • Interface Structure — a web service often adheres to standard formats and protocols, like XML, SOAP, or WSDL. APIs can use more varied structuring mechanisms like REST and GraphQL.
  • Ease of Use — APIs are generally more user-friendly, with direct HTTP calls and a common shorthand for responses (like JSON). Web services can be more complex, requiring specific tooling, protocols, and data formats.
  • Security Focus — web services have a stronger focus on security and are often wrapped in layers of security protocols.

Building Blocks

  • Endpoints — API calls are made to specific URLs known as endpoints; web services have URLs to which different actions are tied.
  • Methods — APIs often have different methods for different operations (e.g., POST for creating, GET for reading). Web services typically incorporate a single method, POST, to handle various operations.
  • Request/Response — both function around requests and responses.

Code Example: API Endpoint

import requests
# Make a GET request to a specific API endpoint
response = requests.get('https://api.example.com/data')
print(response.json())

Code Example: Web Service Endpoint

import requests
from datetime import datetime
# Make a POST request to a specific web service endpoint
url = 'https://webservice.example.com/process_data'
data = {
'action': 'process',
'data': 'some data',
'timestamp': str(datetime.now())
}
response = requests.post(url, data=data)
print(response.text)

3. What are the principles of a RESTful API?

RESTful APIs adhere to a set of architectural principles that combine the openness of the Web with the power of modern APIs. They’re designed to be stateless, allowing for logical resource management and navigation through hyperlinks.

Key Principles

  1. Client-Server Separation — the client and server are independent. The client handles the interface and user experience; the server manages resources and data storage.
  2. Statelessness — each client request must contain all information necessary for the server to fulfill it. The server doesn’t store client state between requests.
  3. Cacheability — responses should be explicitly marked as cacheable or non-cacheable, via standardized cache control mechanisms.
  4. Layered System — API systems can be composed of multiple layers (gateways, proxies). The client doesn’t need to know the exact location of the server, improving scalability and security.
  5. Uniform Interface — all capabilities of a REST API can be accessed via a standard command-independent interface: HTTP methods (GET, POST, PUT, DELETE), status codes, and content types (JSON, XML).
  6. Code on Demand (optional) — the ability to transfer executable code from server to client. Not a required constraint, but seen in things like web apps modifying behavior based on in-browser scripts.

REST vs. RESTful

“RESTful” represents systems that adhere to REST principles. REST evolved from a set of principles associated with web communication into something more general, whereas RESTful systems are specialized systems that utilize REST principles for exchanging data over HTTP.

Real-World Application

Imagine a weather service API using RESTful design. A client (a weather app) sends a request to the API server for weather information. The server processes the request — which includes a specific endpoint for the type of data needed (the “resource”), a cache duration, expected file types (e.g., JSON), and a standard HTTP method like GET. On success, the server responds with the weather data, marked as cacheable, complying with the standards for available content types. This lets us handle resources independently, establishes clear client-server communication, and simplifies delivering web services.

4. How does a SOAP API differ from a REST API?

Core Concepts

SOAP (Simple Object Access Protocol)

  • Coordinated by: World Wide Web Consortium (W3C)
  • Communication Protocol: primarily HTTP and SMTP
  • Data Format: XML
  • Main Focus: actions and behaviors, comprehensiveness, and security

REST (Representational State Transfer)

  • Coordinated by: no overseeing body; adheres to a set of architectural constraints
  • Communication Protocol: various, often HTTP
  • Data Format: initially XML, commonly JSON; not prescriptive
  • Main Focus: statelessness, uniform interface, performance, and simplicity

Key Distinctions

Data Format

  • SOAP — mandates XML.
  • REST — initially used XML but has adapted to popularize JSON for its simplicity.

Ease of Consumption

  • SOAP — comprehensive and stable, but raises complexity with formal contracts, WSDL usage, and tight coupling.
  • REST — promotes loosely coupled, simple interactions that are often easier to understand and use.

Standards and Formalism

  • SOAP — insists on WSDL (Web Services Description Language) to define structure and behavior.
  • REST — lacks a universal standard or formal contract.

Error Handling

  • SOAP — definitive standards for error representation using dedicated XML elements.
  • REST — generally uses HTTP status codes.

State Management

  • SOAP — can be stateful, though it doesn’t enforce it.
  • REST — designed to be stateless.

Integrations

  • SOAP — initially focused on integrating remote systems and inter-machine communication.
  • REST — evolved within the web for serving web resources, focusing on human-readable URLs.

Code Example: SOAP Request (Java)

URL url = new URL("http://webservice.example.com");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "text/xml");
connection.setRequestProperty("SOAPAction", "http://webservice.example.com/SomeAction");
String soapRequest = "<soap:Envelope ....></soap:Envelope>";
connection.setDoOutput(true);
try (OutputStream os = connection.getOutputStream()) {
byte[] input = soapRequest.getBytes(StandardCharsets.UTF_8);
os.write(input, 0, input.length);
}
try (BufferedReader br = new BufferedReader(new InputStreamReader(
connection.getInputStream(), StandardCharsets.UTF_8))) {
StringBuilder response = new StringBuilder();
String responseLine;
while ((responseLine = br.readLine()) != null) {
response.append(responseLine.trim());
}
System.out.println(response.toString());
}

Code Example: REST Request (Python)

import requests
url = "http://api.example.com/some-resource"
headers = {"Content-Type": "application/json", "Authorization": "Bearer YOUR_TOKEN"}
data = {"param1": "value1", "param2": "value2"}
response = requests.post(url, json=data, headers=headers)
if response.status_code == 200:
# Successful request, process data here
response_data = response.json()
else:
print(f"Request failed with status code: {response.status_code}")

5. What is an API endpoint?

An API endpoint is a URI that serves as a communication link between different software applications, letting them interact and exchange data in a structured manner.

Main Components

  1. Endpoint URI — a unique HTTP/HTTPS address identifying the API resource: host, base path, and additional path segments.
  2. HTTP Methods — define the type of action the request should perform: GET, POST, PUT, DELETE, and more.
  3. Data Format — how sent/received data is structured, commonly JSON or XML.

Crucial Aspects

  • Request Headers — additional information about the client or request.
  • Response Headers — metadata about the server or response.
  • Request Payload — data sent in the request body (typically POST/PUT).
  • Response Payload — data returned in the response.

General Structure

RESTful design: uses nouns in the URI for resources (/users, /products); leverages HTTP methods as actions; uniform data representations.

Non-RESTful design: uses verbs in the URI to indicate actions (/submitOrder); often uses POST for all actions.

Code Example: HTTP Methods (C#)

using System;
using System.Net.Http;
using System.Threading.Tasks;
class Program
{
private static readonly HttpClient client = new HttpClient();
static async Task Main()
{
// Simulating a GET request to the specified API endpoint
await GetAPIEndpoint("https://example.com/api/users");
}
static async Task GetAPIEndpoint(string endPoint)
{
HttpResponseMessage response = await client.GetAsync(endPoint);
if (response.IsSuccessStatusCode)
{
Console.WriteLine("API call was successful.");
string data = await response.Content.ReadAsStringAsync();
Console.WriteLine(data);
}
else
{
Console.WriteLine($"API call failed: {response.StatusCode}");
}
}
}

6. What are the common methods (HTTP verbs) used in a REST API, and what does each do?

HTTP Verbs

  1. GET (Read) — retrieves specific data, a resource or collection of resources.
  2. POST (Create) — submits data to create a new resource.
  3. PUT (Update) — modifies a specific resource using its unique identifier.
  4. PATCH (Partial Update) — applies partial modifications to a resource.
  5. DELETE (Remove) — removes a specific resource based on its unique identifier.
  6. HEAD — requests the headers of resources, like GET without the message body.
  7. OPTIONS — communicates capabilities and allowed HTTP methods for a resource.
  8. TRACE — echoes the request, useful for diagnostics, but often disabled for security.
  9. CONNECT — initiates a tunnel to the server over an existing proxy connection.
  10. Custom Verbs — possible in specialized cases, though well-known methods aid interoperability.

Code Example: RESTful Service (Java, JAX-RS)

import javax.ws.rs.*;
import javax.ws.rs.core.*;
@Path("/books")
public class BookResource {
@GET
@Path("/{id}")
public Response getBook(@PathParam("id") int id) {
// Logic to retrieve and return a book with the given ID
}
@POST
@Path("/create")
public Response createBook(Book book) {
// Logic to validate and create a new book
}
@PUT
@Path("/update/{id}")
public Response updateBook(@PathParam("id") int id, Book updatedBook) {
// Logic to update the book with the given ID
}
@DELETE
@Path("/remove/{id}")
public Response deleteBook(@PathParam("id") int id) {
// Logic to delete the book with the given ID
}
}

7. How do you version an API?

Versioning an API is crucial to ensure backward compatibility while allowing for progress and adaptation.

Approaches to API Versioning

URI Versioning (Path-based)

Straightforward — indicates version in the URL, e.g. /api/v1/resource, /api/v2/resource. Can lead to cluttered URLs, and might be too rigid for evolving APIs.

Query Parameter Versioning

/api/resource?version=1 — flexible, but can pose caching and security challenges.

Custom Request Header for Versioning

Accept: application/json, version=1.0 — clean, but requires clients to support custom headers.

Content Negotiation

Use Accept header negotiation to choose the API version — elegant, but requires understanding header negotiation.

Media Type (MIME Type)

Define different media types per version, e.g. application/vnd.company.resource.v1+json. Clear, but can be complex for some to grasp.

Recommendations

Semantic Versioning (SemVer), Major.Minor.Patch, is a common and clear versioning system. Using it and consistently communicating changes ensures both developers and users understand what’s changed between versions.

Backwards Compatibility (BC) is critical for API longevity — add new features without breaking existing client functionality where possible, and provide migration guides plus time to adapt when breaking changes are unavoidable.

Clear documentation is key — any changes or deprecations should be well-documented, and it’s important to remember not everyone uses the latest version, so older versions may need support for an extended period.

Code Example: API Version in URL (Python/Flask)

from flask import Flask
app = Flask(__name__)
@app.route('/api/v1/resource')
def resource_v1():
return "This is v1 of the resource"
@app.route('/api/v2/resource')
def resource_v2():
return "This is v2 of the resource"
if __name__ == '__main__':
app.run()

Code Example: API Version in Custom Header (Node.js)

const express = require('express');
const app = express();
app.get('/resource', (req, res) => {
const apiVersion = req.header('api-version');
res.send(`Accessing version ${apiVersion}.`);
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});

8. What is idempotence in the context of API design, and why is it important?

Idempotence guarantees that the result of an operation remains the same even after multiple repetitive calls — additional identical requests don’t cause data corruption or side effects.

Motivation

  • Error Recovery — idempotent operations help recover from transient errors, since failed requests can be safely retried.
  • Request Safety — especially with HTTP, prevents side effects from accidental or unexpected re-execution of requests.
  • Cost and Efficiency — saves resources and avoids duplicate work.
  • User Experience — predictable, consistent outcomes.

Idempotence in HTTP Methods

Most HTTP methods aren’t idempotent by design, but can be made so. PUT (update) can be made idempotent by ensuring a repeated PUT doesn’t cause further modification. DELETE can be made idempotent too, so repeated deletion requests don’t affect state beyond the first.

Implementing Idempotence

Request-Identifier Combo

Use a unique request identifier; the server checks it to detect duplicates.

# Example using ETags for request identification
import uuid
server_side_state = {
'/update_resource': {'etag': '123'}, # The ETag acts as the request identifier
}
def update_resource(request_body):
resource_etag = server_side_state['/update_resource']['etag']
client_provided_etag = request_body.get('etag')
if client_provided_etag != resource_etag:
return 412, "Precondition Failed"
# Process the update action here
server_side_state['/update_resource']['etag'] = str(uuid.uuid4())
return 200, "Resource updated successfully"

Timestamp or Nonce

The server stores past timestamps or nonces and compares them to the request to identify duplicates.

import time
server_side_state = {
'/place_order': set(),
}
def place_order(request_id):
if request_id in server_side_state['/place_order']:
return 409, "Duplicate Request"
server_side_state['/place_order'].add(request_id)
return 200, "Order placed successfully"

Storing State at Server-Side

Maintain server state about previous requests, in-memory or persisted, so a subsequent request with the same identifier/parameters doesn’t lead to redundant processing.

import java.util.HashSet;
import java.util.Set;
public class IdempotentAPI {
private static Set<String> processedOrderIds = new HashSet<>();
public static boolean placeOrder(String orderId) {
if (processedOrderIds.contains(orderId)) {
System.out.println("Order already processed. Skipping.");
return false;
}
System.out.println("Processing order: " + orderId);
processedOrderIds.add(orderId);
return true;
}
}

9. Can you explain what API rate limiting is and give an example of why it might be used?

Sets constraints on the number of requests a client can make within specific timeframes, ensuring servers aren’t overwhelmed and resources are shared fairly.

Components

  1. Threshold — the maximum requests within a timeframe.
  2. Time Window — the duration the threshold is measured over (e.g., 100 requests per hour).

Why Rate Limit

  • Mitigating Traffic Spikes — unexpectedly high loads can harm server stability.
  • Preventing Abuse — fends off clients attempting to exploit the API.
  • Fair Usage — ensures equal access to limited resources.
  • Compliance — enforces requests within approved usage policies.

Code Example: Rate Limiting with Express.js

const express = require('express');
const rateLimit = require("express-rate-limit");
const app = express();
const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100,
message: "Too many requests from this IP, please try again after 15 minutes"
});
app.use('/api/', apiLimiter);
app.listen(3000, () => console.log('Server started on port 3000'));

10. Describe the concept of OAuth in relation to API security.

OAuth is an open standard for authorization, allowing secure, limited access to a user’s data on one site from another site or app, without sharing personal credentials.

Key Roles

  • Resource Owner — the user with ownership and control over the resources.
  • Resource Server — hosts the protected data, responding to requests for it.
  • Client — the application requesting access on the resource owner’s behalf.
  • Authorization Server — verifies the client’s identity and issues access tokens.

Core Workflow

  1. User Consent — the client sends the user to the authorization server, which authenticates the user and obtains authorization.
  2. Token Granting — the authorization server provides an authorization code; the client presents it and authenticates directly with the server to obtain an access token.
  3. Resource Access — the client uses the access token to make requests; the resource server validates the token and permits access if valid.

Benefits

  • Security — credentials aren’t shared, reducing exposure risk.
  • Consent — users have granular control over what’s shared.
  • Limited Scope — access tokens are often time-bound and restricted in scope.
  • Ease of Management — access can be revoked and managed easily.

11. What strategies would you use to ensure the backward compatibility of an API?

  • Breaking Changes — minimize changes that break existing clients; if unavoidable, put a robust communication and migration strategy in place.
  • Behavioral Changes — equally disruptive; make them opt-in or gradual (e.g., a mandatory authToken suddenly required for all requests).
  • Data Model Evolution — allow both old and new formats via field deprecation, versioned endpoints, or mappings to older formats.
  • Response Changes — make additive changes where possible (new fields, not altering existing ones); if removal is essential, do it gradually or opt-in.
  • Data Consistency — idempotent methods mitigate issues from data inconsistencies across versions.
  • Error Handling — stay consistent with error codes/messages; new versions can add codes but shouldn’t change or remove existing ones.

Code: Maintaining Backward Compatibility

from flask import Flask, jsonify, request
app = Flask(__name__)
books = [{"id": 1, "title": "1984", "author": "George Orwell"}]
@app.route("/api/v1/books", methods=["GET"])
def get_books_v1():
return jsonify({"books": [book["title"] for book in books]})
@app.route("/api/v2/books", methods=["GET"])
def get_books_v2():
return jsonify({"books": books})
if __name__ == "__main__":
app.run()

/api/v1/books returns just book titles (the original behavior), while /api/v2/books returns full book objects — both coexist, so older clients keep working while new functionality is available to newer clients.

12. What are some common response codes that an API might return, and what do they signify?

1xx — Informational

  • 100: Continue
  • 101: Switching Protocols
  • 102: Processing

2xx — Success

  • 200: OK
  • 201: Created
  • 202: Accepted
  • 204: No Content
  • 206: Partial Content
  • 207: Multi Status
  • 208: Already Reported
  • 226: IM Used

3xx — Redirection

  • 301: Moved Permanently
  • 302: Found
  • 304: Not Modified
  • 307: Temporary Redirect
  • 308: Permanent Redirect

4xx — Client Error

  • 400: Bad Request
  • 401: Unauthorized
  • 403: Forbidden
  • 404: Not Found
  • 405: Method Not Allowed
  • 409: Conflict
  • 410: Gone
  • 418: I’m a Teapot

5xx — Server Error

  • 500: Internal Server Error
  • 501: Not Implemented
  • 503: Service Unavailable
  • 504: Gateway Timeout
  • 505: HTTP Version Not Supported

13. How can you design an API to be easily consumable by clients?

When designing an API, ensure simplicity, consistency, and user-friendliness to encourage adoption and minimize the learning curve.

Key Design Principles

  • Use HTTP methods for clear actionsGET for retrieval, POST for creation, PUT/PATCH for modification, DELETE for removal.
  • Predictable endpoint naming — e.g., /users/{id} for user data.
  • Clear data structures — consistent input/output shapes; JSON is the common, efficient choice.
  • Pagination and filtering for large datasets — page/limit parameters, filters like name/status.
  • Versioning — include a version number in the URL (/v1/endpoint) so clients can transition as the API evolves.

Towards Predictable Endpoint Design

  • Unique vs. shared endpoints — unique for independent actions (/calculate-tax?amount={amount}&state={state}), shared/resource-centric when there’s a clear resource connection (/orders/{id}/payment).
  • Endpoint consistency for actions — context-aware verbs: GET /health (general), GET /users/{id}/profile (resource-specific).
  • On method overloading — strive for explicit endpoints where possible (POST /users/{id}/suspend), or a shared action endpoint with a JSON body indicating the desired action (POST /users/{id}/actions).
  • Versatile query parameters — prefer GET /cars?status=verified over multiple slightly different endpoints like /cars/verified and /cars/active.
  • Singular vs. plural — support both where useful: /car/{id} (specific), /cars (collection).

Code Example: REST API Design (Python/Flask)

from flask import Flask, request, jsonify
app = Flask(__name__)
users = {
1: {'id': 1, 'name': 'Alice', 'email': 'alice@example.com'},
2: {'id': 2, 'name': 'Bob', 'email': 'bob@example.com'}
}
# Non-RESTful - Not recommended
@app.route('/create-user', methods=['POST'])
def create_user():
data = request.json
user_id = max(users.keys(), default=0) + 1
users[user_id] = data
return jsonify({'id': user_id}), 201
# RESTful - Preferred
@app.route('/users', methods=['POST'])
def create_user_restful():
data = request.json
user_id = max(users.keys(), default=0) + 1
users[user_id] = data
return jsonify({'id': user_id}), 201
# Supporting both singular and plural forms
@app.route('/user/<int:user_id>', methods=['GET'])
@app.route('/users', methods=['GET'])
def get_user(user_id=None):
if user_id is not None:
return jsonify(users.get(user_id))
return jsonify(users.values())
if __name__ == '__main__':
app.run()

14. When designing an API, how would you document it for end-users?

API documentation is a comprehensive guide for developers to understand how the API functions and how to interact with it.

API Documentation Tools

  • Swagger — declarative JSON/YAML format to specify REST API details.
  • API Blueprint — embeds markdown in a code block format for API requirements.
  • RAML — uses YAML for a clear, consolidated API blueprint.
  • OAS (formerly Swagger 2.0) — consolidates API reference data to generate interactive documentation.

Self-Documenting APIs

Modern languages like Python and Java allow self-documentation via tools like Javadoc (Java, integrating structured comments into source) and Doxygen (multiple languages, processes structured comments into a range of outputs). This keeps documentation up-to-date and in sync with the codebase.

Graphical Representation

“Visual documentation” / API diagrams present resources, relationships, data flows, and function descriptions graphically. Tools: Postman (visualize APIs with a dedicated UI), API Workbench (an interactive “IDE” for API lifecycle management).

Code Example: Self-Documenting API (Python)

class Student:
"""
A simple student class.
Attributes:
name (str): The name of the student.
age (int): The age of the student.
subjects (list of str): The subjects the student is enrolled in.
"""
def __init__(self, name, age, subjects=None):
"""
Initializes a Student.
Args:
name (str): The name of the student.
age (int): The age of the student.
subjects (list of str, optional): The subjects the student is enrolled in.
"""
self.name = name
self.age = age
self.subjects = subjects if subjects else []
def enroll_in_subject(self, new_subject):
"""
Enrolls the student in a new subject.
Args:
new_subject (str): The name of the new subject to enroll in.
"""
self.subjects.append(new_subject)

15. What considerations might influence how you paginate API responses?

Common Pagination Techniques

  1. Offset-Based Paging — uses offset for a starting point and limit for record count. Simple, but can cause performance issues on very large datasets.
  2. Keyset Pagination — uses key attributes (like a unique identifier) to establish the starting point for the next set of results. Efficient, but requires monotonic attributes to ensure coverage and avoid duplication.
  3. Timestamp Pagination — uses a timestamp associated with the record to decide start and how far back to go. Straightforward, good for constantly-changing datasets.
  4. Cursor-Based Pagination — uses cursors (usually encoded tokens) to pinpoint the current position. Efficient, avoids the “skipping” behavior of offset-based paging.
  5. Hybrid Paging — a combination of the above, based on specific application requirements.

Choosing the Best Technique

  1. Dataset Characteristics — size, volatility, and distribution matter. Frequently inserted/removed records might favor cursor-based paging; keyset/timestamp work best with unique, ordered attributes.
  2. Relationships and Data Integrity — strong relationships that must be maintained across pages often favor key-driven methods.
  3. Performance — test with various dataset sizes and configurations before choosing.
  4. Security and Caching — each method affects these differently.
  5. Client Complexity — some methods introduce more complexity to client code.
  6. Ordering Requirements — ensure the chosen method reliably delivers results in the required order.
  7. Error Handling — the likelihood and resolution of errors during pagination differs between methods.
  8. Versioning and Field Evolution — data structures evolve, potentially making a once-reliable key unsuitable over time.
  9. Environmental Limitations — some platforms/tooling integrate better with particular techniques.
  10. Future Proofing and Flexibility — continually evaluate against present and foreseeable needs.

Implementing Range-Based Pagination

def paginate_using_range(start, end, limit):
results = get_results_in_range(start, end, limit)
next_start = results[-1].id + 1 # Assuming results are sorted by id
next_end = next_start + limit - 1 if len(results) == limit else None
return {
"data": results,
"links": {
"next": f"/resource?start={next_start}&end={next_end}&limit={limit}"
}
}