---
title: Docs Search API
description: Search your Jamdesk docs programmatically. Power chatbots, Slack bots, custom search, and AI agents with up-to-date answers.
---

The Docs Search API gives you programmatic access to your documentation content via semantic search. One endpoint (`POST /_api/search`) takes a natural language query and returns the most relevant passages from your docs, ranked by relevance.

## Use Cases

<Columns cols={2}>
  <Card title="Support Chatbots" icon="comment-dots">
    Connect Intercom Fin, Zendesk AI, or a custom chatbot to your docs so it answers questions with accurate, cited content.
  </Card>
  <Card title="Slack Bots" icon="slack">
    Build a `/docs` Slack command that searches your documentation and posts the top results to any channel.
  </Card>
  <Card title="Custom Search" icon="magnifying-glass">
    Add a search interface to your product, dashboard, or internal tools that surfaces relevant docs in context.
  </Card>
  <Card title="AI Agents" icon="robot">
    Give AI agents like Claude or GPT a tool that retrieves up-to-date documentation, not stale training data.
  </Card>
</Columns>

## Quick Start

<Steps>
  <Step title="Generate an API key">
    Go to **Project Settings → API Keys** in the [Jamdesk dashboard](https://dashboard.jamdesk.com). Click **Generate Key**, give it a name, and copy the key. It starts with `jd_live_` followed by 32 hex characters (40 chars total) and is only shown once.
  </Step>
  <Step title="Make your first search request">
    Send a `POST` request to `/_api/search` on your docs subdomain:

    ```bash
    curl -X POST https://your-project.jamdesk.app/_api/search \
      -H "Authorization: Bearer jd_live_c83003a54ae0a83123454c3f7ec82f0a" \
      -H "Content-Type: application/json" \
      -d '{"query": "How do I set up a custom domain?", "limit": 5, "language": "en"}'
    ```
  </Step>
  <Step title="Use the results">
    The response returns an array of matching passages with relevance scores and page metadata:

    ```json
    {
      "query": "How do I set up a custom domain?",
      "language": "en",
      "results": [
        {
          "title": "Custom Domains",
          "section": "Step 4: Deploy",
          "slug": "deploy/custom-domains",
          "content": "To add a custom domain, go to Project Settings and enter your domain. You'll need to add a CNAME record pointing to your Jamdesk subdomain.",
          "url": "https://your-project.jamdesk.app/deploy/custom-domains",
          "score": 0.94
        }
      ],
      "total": 1,
      "durationMs": 85
    }
    ```
  </Step>
</Steps>

## Authentication

All requests require a Bearer token in the `Authorization` header.

```http
Authorization: Bearer jd_live_c83003a54ae0a83123454c3f7ec82f0a
```

### Generating API Keys

<Steps>
  <Step title="Open Project Settings">
    In the Jamdesk dashboard, navigate to your project and click **Settings**.
  </Step>
  <Step title="Go to API Keys">
    Select the **API Keys** tab.
  </Step>
  <Step title="Create a key">
    Click **Generate Key**, enter a descriptive name (e.g. "Intercom chatbot"), and click **Create**.
  </Step>
  <Step title="Copy the key">
    Copy the key immediately. It starts with `jd_live_` followed by 32 hex characters and is shown **only once**. Store it in your secrets manager or environment variables.
  </Step>
</Steps>

### Key Management

<Info>
API keys are scoped to a single project. A key for `acme.jamdesk.app` cannot query a different project's documentation.
</Info>

| Rule | Detail |
|------|--------|
| **Format** | `jd_live_<32 hex chars>` (40 chars total, never expires) |
| **Scope** | One key per project (cannot access other projects) |
| **Rotation** | Revoke and regenerate anytime from Project Settings |
| **Storage** | Store in environment variables or a secrets manager; never commit to source control |

### Revoking Keys

To revoke a key, go to **Project Settings → API Keys**, find the key by name, and click **Revoke**. Revoked keys stop working immediately. Generate a new key to replace it.

## Rate Limits

Requests are rate-limited per API key.

| Plan | Limit |
|------|-------|
| **Pro** | 60 requests / minute |
| **Enterprise** | Custom; contact [support](mailto:support@jamdesk.com) |

When you exceed the limit, the API returns `429 Too Many Requests` with a `Retry-After: 60` header and `{"error": "Rate limit exceeded"}` in the body.

<Warning>
If you need higher rate limits for a production integration, [contact us](mailto:support@jamdesk.com) to discuss Enterprise options.
</Warning>

## Query Limits

Each request accepts a `limit` parameter controlling how many results to return. The maximum is **20**, the default is **5**, and the minimum is **1**. There is no pagination; all matching results come back in a single response. If you need more context, try a more specific query rather than increasing the limit.

A query with no matches returns HTTP 200 with an empty results array:

```json
{"query": "quantum entanglement", "results": [], "total": 0, "durationMs": 48}
```

## Filtering by language

If your docs site supports multiple languages, the API filters results to a single language per request. Pass `language` in the request body with a BCP-47 code (e.g. `en`, `es`, `fr`, `pt-BR`, `zh-Hans`).

```bash
curl -X POST https://your-project.jamdesk.app/_api/search \
  -H "Authorization: Bearer jd_live_c83003a54ae0a83123454c3f7ec82f0a" \
  -H "Content-Type: application/json" \
  -d '{"query": "¿Cómo configuro un dominio personalizado?", "language": "es"}'
```

| Rule | Detail |
|------|--------|
| **Default** | `en` (English). Omit the field, or pass `null`, to use the default. |
| **Format** | BCP-47 (`^[a-zA-Z]{2,3}([-_][a-zA-Z]{2,4})?$`). Examples: `en`, `es`, `fr`, `pt-BR`, `zh-Hans`. |
| **Validation** | Malformed values return `400` with `{"error": "Invalid language code"}`. |
| **3-segment tags** | Not currently supported. Codes like `zh-Hant-HK` and `sr-Latn-RS` return `400`. [Contact support](mailto:support@jamdesk.com) if you need them. |
| **Multi-language projects** | The filter is strict — only chunks tagged with the requested language are returned. A request for `de` against a project that only has English and French returns an empty result set, not a `400`. |
| **Single-language projects** | The filter is ignored; you always get the full result set. Sending `language` is harmless — not an error. |
| **Echoed in response** | Every successful response includes a `language` field with the value the server resolved (request value, or `en` default). |

<Info>
A project is multi-language when its `docs.json` has a `navigation.languages` array with two or more entries. To check whether your site is multi-language, open the **Settings → Languages** tab in the dashboard, or open `docs.json` directly.
</Info>

<Warning>
The default `en` applies even on projects that don't have an English version. If your multi-language project is e.g. French and Spanish only, calling the endpoint without a `language` field will filter for `en` and return an empty result set. Always pass an explicit `language` from non-English-only sites.
</Warning>

## Error Handling

All error responses include a machine-readable `error` field you can branch on programmatically.

| Status | `error` value | Meaning | Action |
|--------|---------------|---------|--------|
| **400** | `Missing or empty "query" field` | Request body is missing or has no `query` | Add a non-empty `query` string |
| **400** | `Invalid language code` | The `language` field is not a string or doesn't match the BCP-47 pattern (`null` is fine; empty/whitespace strings and 3-segment tags are not) | Use a valid 1- or 2-segment code like `en`, `es`, `fr`, or `pt-BR` |
| **401** | `invalid_key_format` | `Authorization` header is missing or the key doesn't match `jd_live_<32 hex>` | Check the header format; it must be `Bearer jd_live_...` |
| **401** | `invalid_key` | Key is not recognised or has been revoked | Generate a new key in the dashboard |
| **403** | `wrong_project` | Key is valid but was generated for a different project | Use a key that matches the project slug in the URL |
| **429** | `Rate limit exceeded` | Exceeded 60 requests per minute | Wait the number of seconds in the `Retry-After` header |
| **502** | `Search temporarily unavailable` | Vector search backend is down | Retry after a short backoff |
| **503** | `lookup_failed` or `redis_unavailable` | Key verification backend is unreachable | Retry after a short backoff |

<Info>
401 and 403 are permanent failures. Retrying with the same key won't help. 429, 502, and 503 are transient, so retry with exponential backoff.
</Info>

## CORS

CORS is enabled on all endpoints. Browser-based clients (single-page apps, browser extensions, static sites) can call `/_api/search` directly without a backend proxy. All origins are allowed.

## SDKs

There are no official language SDKs at this time. Use the REST API directly via `fetch`, `requests`, `curl`, or any HTTP client. The [Postman collection](#postman-collection) below provides ready-to-fork examples.

## Versioning

The API is currently at **v1.0.0**. Breaking changes (field renames, removed endpoints, changed auth) will be announced via the [Jamdesk blog](https://www.jamdesk.com/blog) and a deprecation notice in the `X-Deprecation` response header at least 90 days before removal.

## OpenAPI Specification

The full OpenAPI 3.1 spec is available as YAML. Import it into your codegen tool, API client, or contract-testing pipeline.

<Columns cols={2}>
  <Card title="Download OpenAPI YAML" icon="file-arrow-down" href="https://raw.githubusercontent.com/jamdesk/jamdesk-docs/main/openapi/docs-search-api.yaml">
    `docs-search-api.yaml` — OpenAPI 3.1, always in sync with the latest published version.
  </Card>
  <Card title="Browse on GitHub" icon="github" href="https://github.com/jamdesk/jamdesk-docs/blob/main/openapi/docs-search-api.yaml">
    Read the spec source, file issues, or watch for changes.
  </Card>
</Columns>

## Postman Collection

We publish an official Postman workspace with the full OpenAPI spec and a ready-to-fork collection so you can test requests in the Postman UI without writing any code.

<Columns cols={2}>
  <Card title="Jamdesk Docs API workspace" icon="rocket" href="https://www.postman.com/jamdesk/jamdesk-docs-api">
    Fork the collection and run requests in Postman. Includes a Getting Started folder and working examples.
  </Card>
  <Card title="All Jamdesk APIs" icon="layer-group" href="https://www.postman.com/jamdesk">
    Browse every public Jamdesk API workspace and stay current as new APIs ship.
  </Card>
</Columns>

<Warning>
After forking the collection, you **must** update two collection variables before any request will work:

- **`baseUrl`**: set to your own Jamdesk docs site. For most customers this is `https://your-project.jamdesk.app` (replace `your-project` with your project slug). Custom-domain customers use their own host. Customers serving docs under a sub-path include the full path (e.g. `https://example.com/docs`).
- **`apiKey`**: replace the placeholder with a real key generated in **Dashboard → Project Settings → API Keys**.
</Warning>

## Next Steps

<Columns cols={2}>
  <Card title="Search Endpoint" icon="magnifying-glass" href="/jamdesk-api/search">
    Full reference with request/response schemas and interactive playground
  </Card>
  <Card title="Integration Guides" icon="plug" href="/jamdesk-api/integrations">
    Step-by-step guides for Intercom, Zendesk, Slack bots, and custom chatbots
  </Card>
</Columns>
