Search SOPs
Reference for GET /search, which returns up to 50 SOPs matching a query, with phrase, or and exclude syntax.
On this page
GET https://app.sopcompass.com/api/v1/search?q={query}Scope: read only or read and write. Paginated: no; returns the best matches only.
The same full-text search as the app, over the SOPs the workspace can see: titles, card content and step content, in the workspace’s own libraries and those shared into it. Archived SOPs are left out. Results are ordered best match first.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
q |
string | Yes | The search query, 1 to 200 characters after trimming spaces. |
limit |
integer, 1 to 50 | No | How many results to return. Default 20. |
Query syntax
| You type | Matches |
|---|---|
refund policy |
SOPs containing both words, in any order. |
"deep clean" |
The exact phrase. |
refund or return |
Either word. |
refund -wholesale |
refund but not wholesale. |
Matching is in English and word-based: refunds and refunded match refund. Very common words such as “the” and “how” are ignored, so a query made only of them returns no results. Matching is not case sensitive.
Request
curl -G https://app.sopcompass.com/api/v1/search \
--data-urlencode 'q="deep clean" kitchen' \
--data-urlencode "limit=5" \
-H "Authorization: Bearer $SOPCOMPASS_API_KEY"const params = new URLSearchParams({ q: '"deep clean" kitchen', limit: "5" });
const res = await fetch(`https://app.sopcompass.com/api/v1/search?${params}`, {
headers: { Authorization: `Bearer ${process.env.SOPCOMPASS_API_KEY}` },
});
const { data: results } = await res.json();import os
import requests
res = requests.get(
"https://app.sopcompass.com/api/v1/search",
params={"q": '"deep clean" kitchen', "limit": 5},
headers={"Authorization": f"Bearer {os.environ['SOPCOMPASS_API_KEY']}"},
timeout=30,
)
results = res.json()["data"]Response
200 OK
{
"data": [
{
"sop": {
"id": "6c8e0a2b-4d6f-4c8e-9a1b-3d5f7a9c1e08",
"title": "Deep clean a commercial kitchen",
"description": "Weekly deep clean for client kitchens. Acme Cleaning Co. (example).",
"library_id": "4a6c8e0b-2d4f-4a1c-8e3a-5c7e9b1d3f07",
"library_title": "Client sites",
"board_id": "1b3d5f7a-9c2e-4a6b-8d0f-2a4c6e8b0d14",
"board_name": "Fulfillment and delivery",
"tags": ["kitchen", "weekly"],
"departments": ["operations"],
"owner_email": "sam.ortiz@example.com",
"published": true,
"shared": false,
"next_review_on": "2027-03-01",
"archived_at": null,
"created_at": "2026-09-03T15:12:44.201877Z",
"updated_at": "2026-09-12T08:05:19.660231Z",
"app_url": "https://app.sopcompass.com/sops/6c8e0a2b-4d6f-4c8e-9a1b-3d5f7a9c1e08?t=7c2e4a6b-8d1f-4a3c-9e5b-0d2f4a6c8e04"
},
"rank": 0.0759,
"snippet": "Deep clean a commercial kitchen. Wear gloves and eye protection … Spray the hood filters in the kitchen and leave for 10 minutes"
}
]
}| Field | Type | Description |
|---|---|---|
sop |
object | The matching SOP, as the SOP object in GET /sops, including app_url. It doesn’t include cards; fetch them with GET /sops/{id}. |
rank |
number | Relevance score. Higher is better. Only meaningful for ordering results of the same query. |
snippet |
string | Plain text around the matches: up to two short fragments, joined by …. No HTML or highlighting. |
No matches returns {"data": []} with 200 OK.
Errors
| Status | Code | When |
|---|---|---|
| 422 | invalid_query |
q is missing, blank, or longer than 200 characters. |
| 422 | invalid_parameter |
limit isn’t a whole number from 1 to 50. |
| 405 | method_not_allowed |
POST instead of GET. |
Authentication, plan and rate limit errors apply to every endpoint; see Errors.