Searching
Table of Contents
Why Searching Matters in APIs
Searching lets clients find specific data inside a larger collection of resources. In a REST API, you rarely return every record. Instead, you provide ways to search and narrow down results.
Consider a products API:
- List all products:
GET /products - Search for "laptop":
GET /products?search=laptop - Search by multiple criteria:
GET /products?name=laptop&min_price=500&max_price=1500
Searching is closely related to filtering and sorting. In many APIs, "search" means full text search, while "filtering" means field-based equality or ranges, but the boundary is not strict. In this chapter, we focus on patterns that are specific to search behavior, especially text search and user-facing queries.
Important idea: Searching should
- be predictable,
- use clear query parameters,
- and not accidentally expose or overload your API.
Basic Searching with Query Parameters
The simplest style of search uses one or more query parameters on a collection endpoint.
Single search parameter
A common pattern for plain text search is a generic q or search parameter:
GET /users?q=johnGET /products?search=phone
The meaning of this parameter must be documented, because different APIs treat it differently:
| Example | Interpretation |
|---|---|
GET /books?q=python | Search title and description for the word "python" |
GET /users?q=john | Search name, username, or email containing "john" |
GET /posts?q=error | Search full text content of posts |
You decide which fields q searches over. Once decided, keep it consistent.
Example: simple search behavior
Imagine an API:
GET /articles?q=fastapiServer logic might be:
- Look into
titleandbodyfields of all articles. - Find those that contain "fastapi" case insensitive.
- Return matching articles.
The response could include meta information about search:
{
"query": "fastapi",
"count": 2,
"results": [
{ "id": 1, "title": "FastAPI basics" },
{ "id": 2, "title": "Deploying FastAPI" }
]
}Here search is just a filter using "contains" semantics on text fields.
Multiple field-specific parameters
You can use more specific parameters to search by exact or partial match of fields:
GET /products?name=iphoneGET /products?brand=appleGET /products?name=iphone&brand=apple
Some APIs use a generic parameter name to indicate partial search and more specific names for exact search:
| Pattern | Meaning |
|---|---|
GET /users?name_contains=john | Name contains "john" |
GET /users?name=john | Name equals "john" |
GET /products?title_like=phone | Title LIKE %phone% in SQL |
This starts to mix searching and filtering, but it is common in real APIs.
Text Search vs Field Filters
You should distinguish between:
- Text search: user types a free form query, content is searched.
- Field filters: user specifies structured filters like
status=activeorprice<100.
Both use query parameters, but you usually treat them differently in implementation and possibly in the database.
Typical combination
A common pattern looks like this:
GET /products?search=laptop&category=electronics&min_price=500&max_price=1500&sort=-priceHere:
searchis free text search on product name and description.category,min_price,max_priceare filters.sortdefines sort order.
You will often:
- Apply filters to narrow down the dataset.
- Apply text search on candidate records.
- Apply sorting and pagination to the final list.
This helps performance and keeps search behavior understandable.
Designing Search Parameters
There is no single standard, but some patterns are widely used and easy for clients to understand.
Common parameter names
| Parameter name | Typical use |
|---|---|
q | Generic search query, often full text search |
search | Same as q, but more descriptive |
query | More verbose alternative, used in some APIs |
term | Used when searching for a specific term or token |
keywords | When search is based on multiple comma separated keywords |
Choose one and stick with it. Many APIs pick q, others pick search.
Searching specific fields
You can support field specific search parameters, which is useful when you want precise control:
GET /books?title=pythonGET /books?author=martin&title=clean code
You can also support explicit "contains" or "starts with" behavior:
title_contains=pythontitle_startswith=pytitle_endswith=guide
Example:
GET /users?name_contains=john&email_endswith=@example.comThis can be mapped to SQL LIKE operations, or to specific full text search operators depending on your database.
Combining generic and field searches
You may allow both a generic query and field specific parameters:
GET /jobs?q=backend&location=remote&company=acmeDesign questions you must decide and document:
- Should
qsearch all fields or only some? - If both
qandtitleare present, are both applied with AND or OR? - Do empty values ignore the parameter or count as filters?
A clear rule is:
Rule: Apply all search and filter criteria together with logical AND,
so items must match all provided parameters, unless explicitly documented otherwise.
Pagination and Searching
Search results are often large. Always combine search with pagination.
For example:
GET /articles?q=python&page=2&limit=20Here:
- Search by
q=python. - Return the second page.
- Each page has 20 articles.
The response can include metadata:
{
"query": "python",
"page": 2,
"limit": 20,
"total": 85,
"results": [ ... 20 articles ... ]
}This is important for:
- User experience, because clients can show "Page 2 of 5".
- Performance, because you never send all results at once.
Search without pagination is a common performance mistake.
Implementing Simple Searching (Conceptually)
Without going deep into database details or any specific language, let us look at how simple search is often implemented.
Exact match vs partial match
Consider a users table with columns id, name, email.
If the API is:
GET /users?name=JohnServer logic for an exact match could be:
SELECT * FROM users WHERE name = 'John'For partial match:
GET /users?name_contains=johnYou might do:
SELECT * FROM users WHERE LOWER(name) LIKE '%john%';Lowercasing both sides makes the search case insensitive.
Simple multi field text search
For a generic q parameter searching both name and email:
GET /users?q=johnSQL style implementation:
SELECT *
FROM users
WHERE LOWER(name) LIKE '%john%'
OR LOWER(email) LIKE '%john%';This is basic and easy to understand, but not very powerful or efficient for large datasets.
Full Text Search Concepts
As your data grows, simple LIKE '%text%' search becomes slow and limited. Many systems provide full text search features that:
- Tokenize text into words.
- Ignore stop words like "the", "and".
- Support ranking and relevance scores.
- Support phrases, prefixes, and operators.
You do not need to implement full text search yourself, but you should know the API implications.
Ranking results
A full text search system can say:
- Result A is more relevant than result B.
Your API can expose this:
GET /articles?q=python performanceResponse:
{
"query": "python performance",
"count": 3,
"results": [
{ "id": 2, "title": "Python performance tips", "score": 0.95 },
{ "id": 5, "title": "Fast Python web APIs", "score": 0.78 },
{ "id": 7, "title": "General Python guide", "score": 0.40 }
]
}You can sort by score descending by default.
Even if you use a database or search engine to compute the score, your REST API should present it in a simple and stable form.
Phrase search and operators
Some APIs accept more advanced search syntax in the q parameter:
"python guide"means search that exact phrase.python -djangomeans search for python but not django.author:martinrestricts to a field.
Each extra feature makes the API more powerful but also more complex. For beginner APIs, stick to:
- Simple token search, split on spaces.
- Basic phrase support only if needed.
When you expose these features, document them clearly and test user input carefully, to avoid injection attacks or broken queries.
Search, Filtering, and Sorting Together
In a real backend, searching rarely stands alone. Clients want to:
- Search by text.
- Filter by properties.
- Sort by fields.
- Paginate.
Let us build an example for a "Job Listings" API.
Example: Job search endpoint
Endpoint:
GET /jobsSupported query parameters:
| Parameter | Type | Description |
|---|---|---|
q | string | Text search on title and description |
location | string | Exact match on location name |
remote | boolean | true or false |
min_salary | integer | Minimum salary filter |
max_salary | integer | Maximum salary filter |
sort | string | date_posted, -date_posted, salary, etc |
page | integer | Page number |
limit | integer | Items per page |
Example request:
GET /jobs?q=backend&location=Berlin&remote=true&min_salary=60000&sort=-date_posted&page=1&limit=10Server behavior conceptually:
- Start from all jobs.
- If
qis provided, apply full text search ontitleanddescription. - Apply filters:
location,remote, salary range. - Sort by
date_posteddescending. - Paginate to page 1 with 10 items.
Response structure:
{
"query": {
"q": "backend",
"location": "Berlin",
"remote": true,
"min_salary": 60000,
"sort": "-date_posted",
"page": 1,
"limit": 10
},
"total": 43,
"results": [
{
"id": 101,
"title": "Senior Backend Developer",
"location": "Berlin",
"remote": true,
"salary": 80000
}
]
}This pattern scales well as you add more search and filter options.
Handling Invalid or Ambiguous Searches
Search endpoints must handle user mistakes gracefully. Users often send:
- Empty strings.
- Very long search terms.
- Unsupported parameter combinations.
- Invalid value types.
Empty or missing search query
Examples:
GET /products?q=GET /products(noq)
You must define behavior, such as:
- If
qis empty or missing, treat as "no text search, only filters". - Or, if
qis required for this endpoint, return a 400 Bad Request.
Consistent behavior prevents confusion.
Too generic or too long queries
A query like q=a or q=aaaaaaaaaaaaaaaaaaaaaaaaaa... may be expensive or unhelpful.
You can:
- Require a minimum length, for example 3 characters.
- Truncate overly long queries.
Example API rule:
Rule: If q is provided, its length must be between 3 and 100 characters.
Otherwise, return 400 Bad Request with a clear error message.
Example response:
{
"detail": "Query parameter 'q' must be at least 3 characters long."
}Invalid parameter types
If you expect integers for pagination or for numeric filters:
GET /products?min_price=cheap
You should:
- Validate and return 400 Bad Request with details.
- Avoid silently ignoring invalid values.
This keeps search behavior predictable and helps clients fix mistakes quickly.
Security Considerations in Searching
Search features, especially text search, can open some security and performance issues.
Injection risks
If you build SQL queries directly from user input, like:
WHERE title LIKE '%" + userInput + "%'You risk SQL injection if not using parameters properly. Always:
- Use parameterized queries.
- Never insert raw user input directly into SQL strings.
- Let your ORM or query builder handle escaping.
Denial of service through expensive search
Attackers or buggy clients might:
- Use extremely long queries.
- Combine filters and sorts that create complex queries.
- Call search endpoints in rapid loops.
Mitigations:
- Limit length of
q. - Limit the maximum
limitvalue for pagination. - Use rate limiting on your API.
- Optimize database indexes for common search patterns.
Information leakage
Search can sometimes reveal information accidentally. For example:
- Searching users by email and returning detailed error messages.
- Letting clients search private resources they should not see.
Always enforce authorization checks before performing search, just as you do for reading individual resources.
Designing Search Responses
Beyond simply returning results, you can include helpful metadata so clients can build user interfaces easily.
Common response fields
| Field | Purpose |
|---|---|
query | Echo the search text or parameters used |
total | Total number of matching items, independent of pagination |
page | Current page number |
limit | Number of items per page |
results | Array of matching resource objects |
facets | Optional aggregation data like counts per category or tag |
Example:
{
"query": {
"q": "python",
"category": "books"
},
"total": 27,
"page": 1,
"limit": 10,
"results": [ /* 10 books */ ]
}Faceted search (high level)
Some APIs include summary information that helps users refine search:
{
"query": "laptop",
"total": 100,
"facets": {
"brand": [
{ "value": "Dell", "count": 35 },
{ "value": "HP", "count": 20 },
{ "value": "Lenovo", "count": 45 }
],
"price_range": [
{ "value": "0-500", "count": 10 },
{ "value": "500-1000", "count": 60 },
{ "value": "1000+", "count": 30 }
]
},
"results": [ /* laptops */ ]
}Facets are advanced, and often require a search engine like Elasticsearch, but they are useful to know about conceptually.
Examples of Search URLs
To consolidate the ideas, here is a table of example search requests and what they might mean in a typical REST API.
| URL | Meaning |
|---|---|
GET /products?q=phone | Full text search "phone" in product name and description |
GET /products?search=phone&category=electronics | Search text "phone", then filter by category "electronics" |
GET /users?name_contains=john | Search for users whose name contains "john" |
GET /users?email_endswith=@example.com | Search for users with email ending in @example.com |
GET /articles?q=python%20performance&page=2&limit=5 | Search for "python performance", second page, 5 results per page |
GET /jobs?q=backend&location=Berlin&remote=true&min_salary=60000&sort=-date_posted | Combine text search, filters, and sorting for job listings |
GET /books?title=clean%20code&author=martin | Field based search for exact title and author |
GET /movies?title_startswith=star&year=1977 | Search for movies whose title starts with "star" and year is 1977 |
GET /orders?customer_id=123&search=refund | Filter by customer, then search text "refund" in order notes or descriptions |
You can use these patterns as a starting point for your own API search design.
Summary
- Searching in REST APIs usually uses query parameters on collection endpoints.
- A generic
qorsearchparameter often indicates text search across multiple fields. - You can also support field specific search patterns, such as
name_containsortitle_startswith. - Search almost always combines with filtering, sorting, and pagination.
- Implementation can be as simple as
LIKE '%text%'or as advanced as full text search with ranking. - Validate queries to avoid errors and performance problems, and always secure your database queries.
- Well designed search responses include metadata about the query, total results, and pagination, which helps client applications build good user experiences.
Views: 7
KAHIBARO