SearchOps exposes a small REST API for public job discovery, event tracking, analytics, experimentation, health checks, and local observability.
The product API uses JSON envelopes. Infrastructure endpoints may use different response formats when appropriate, such as Prometheus-style text for /metrics.
| Area | Base URL | Purpose |
|---|---|---|
| Infrastructure | http://localhost:5000 |
Health, metrics, and developer docs |
| Product API | http://localhost:5000/api |
Jobs, events, analytics, and experiments |
| Endpoint | Purpose |
|---|---|
GET /health |
Verify the backend service is running |
GET /metrics |
Return Prometheus-style operational metrics |
GET /api/jobs |
List, search, filter, sort, and paginate jobs |
GET /api/jobs/:slug |
Fetch one job by SEO-friendly slug |
POST /api/events |
Track job views, apply clicks, search, and filter events |
GET /api/analytics/summary |
Return lightweight product analytics |
POST /api/experiments/apply-cta-assignment |
Return and persist anonymous apply CTA assignment |
Developer docs:
| Route | Purpose |
|---|---|
GET /api-docs |
Swagger UI for the checked-in OpenAPI file |
GET /api-docs/openapi.yaml |
Raw OpenAPI YAML served by the backend |
/api-docs is not a product API endpoint.
Product API success response:
{
"data": {},
"meta": {
"timestamp": "2026-05-11T06:30:00.000Z",
"requestId": "req_abc123"
}
}Product API error response:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid query parameter",
"details": []
},
"meta": {
"timestamp": "2026-05-11T06:30:00.000Z",
"requestId": "req_abc123"
}
}Current error codes:
| Code | HTTP | Description |
|---|---|---|
VALIDATION_ERROR |
400 | Invalid query string, path parameter, or request body |
NOT_FOUND |
404 | Requested route, job, or referenced job does not exist |
INTERNAL_ERROR |
500 | Unhandled application error |
Returns the standard JSON envelope:
{
"data": {
"status": "ok"
},
"meta": {
"timestamp": "2026-05-11T06:30:00.000Z",
"requestId": "req_abc123"
}
}Returns text/plain; version=0.0.4; charset=utf-8 and does not use the JSON envelope.
Implemented metrics:
http_requests_total
http_errors_total
http_request_duration_ms
search_latency_simulation_totalExample:
http_requests_total{method="GET",route="/api/jobs",statusCode="200"} 1
http_request_duration_ms_count{method="GET",route="/api/jobs",statusCode="200"} 1Returns a paginated list of jobs.
Query parameters:
| Parameter | Type | Default | Notes |
|---|---|---|---|
q |
string | none | Keyword search across title, description, location, company name, and company slug |
keyword |
string | none | Alias for q; q wins when both are provided |
location |
string | none | Case-insensitive partial match |
category |
enum | none | SOFTWARE_ENGINEERING, DATA_ANALYTICS, CLOUD_DEVOPS, PRODUCT, DESIGN, CYBERSECURITY |
workMode |
enum | none | ONSITE, HYBRID, REMOTE |
employmentType |
enum | none | FULL_TIME, PART_TIME, CONTRACT, CASUAL, INTERNSHIP |
company |
string | none | Case-insensitive partial match on company name or slug |
salaryMin |
integer | none | Jobs whose salaryMax is greater than or equal to this value |
salaryMax |
integer | none | Jobs whose salaryMin is less than or equal to this value |
page |
integer | 1 |
Must be positive |
limit |
integer | 20 |
Must be positive, max 100 |
sort |
enum | recent |
recent, salary_desc, salary_asc |
Fetches one public job by SEO-friendly slug. Unknown slugs return 404 NOT_FOUND.
Request body:
{
"eventType": "JOB_VIEW",
"jobId": "clxjob001",
"variant": "A",
"searchTerm": "backend"
}Rules:
eventTypeis required:JOB_VIEW,APPLY_CLICK,SEARCH_PERFORMED, orFILTER_USED.JOB_VIEWandAPPLY_CLICKrequirejobId.variant, when provided, must beAorB.- If
jobIdis provided, the referenced job must exist.
Success status: 201.
Returns:
- total job views
- total apply clicks
- overall apply click-through rate
- top searched keywords
- most viewed jobs
- conversion by variant
This is intentionally lightweight and not a production analytics warehouse.
Request body:
{
"anonymousUserId": "anon_demo_user"
}The backend checks for an existing assignment for apply_cta_copy. If none exists, it uses a stable hash of anonymousUserId, persists variant A or B, and returns the corresponding CTA text.
| Variant | CTA text |
|---|---|
A |
Apply now |
B |
Start your application |
The current OpenAPI contract is maintained in openapi.yaml.
When the backend is running, Swagger UI is available at:
http://localhost:5000/api-docs- Authentication and authorization
- Rate limiting
- Admin APIs
- Real job application submission
- Full-text search index and relevance ranking
- Production analytics pipeline