MisarMisar Docs
MisarMailMisarBlogMisarReachMisarPostMisarSEOMisarDevMisarCoderMisar PlatformMisar SSO
API Reference

Search API

Web-grounded search-and-answer endpoint — returns a synthesized answer with inline url_citation source annotations, powered by live web retrieval and misar-pro.

Overview

POST /dev/v1/search answers a question against the live web. It retrieves fresh results (via searxng), synthesizes an answer with misar-pro, and returns that answer together with url_citation annotations that map character ranges in the answer back to their source pages.

Use this endpoint when you need citations and sources. For an ungrounded chat reply, use the Chat Completions endpoint; for a conversational web-grounded reply without structured citations, pass model: "online" to Chat Completions.

Every response includes "grounded": true and a sources array, so you can render footnotes or a "Sources" list without a second request.

Endpoint

POST/v1/search

Runs a web-grounded search and returns a synthesized answer with source citations. Requires Authorization: Bearer YOUR_API_KEY.

Request body

Provide either query or messages (not both).

querystringbody

The question to answer. Use this for a single-shot search.

messagesArray<{role, content}>body

A conversation (role = system | user | assistant) when you need multi-turn context. The last user message is treated as the search query.

modelstringbodydefault: misar-pro

Synthesis model. Defaults to misar-pro for grounded answers; pass misar-flash for faster, lower-cost synthesis. See Models.

Response fields

idstring

Unique identifier for the search response, e.g. search-abc123.

objectstring

Always search.completion.

modelstring

The model that synthesized the answer.

groundedboolean

true when the answer was produced from retrieved web sources.

answerstring

The synthesized answer text. Character indices in annotations refer to positions in this string.

annotationsArray<object>

Inline citations. Each item has type: "url_citation" and a url_citation object with start_index, end_index (character range in answer), url, and title.

sourcesArray<object>

The retrieved web sources: url, title, and a short snippet.

usageobject

Token counts: prompt_tokens, completion_tokens, total_tokens.

{
  "query": "When did the James Webb Space Telescope launch?"
}
{
  "id": "search-abc123",
  "object": "search.completion",
  "model": "misar-pro",
  "grounded": true,
  "answer": "The James Webb Space Telescope launched on 25 December 2021 aboard an Ariane 5 rocket from French Guiana.",
  "annotations": [
    {
      "type": "url_citation",
      "url_citation": {
        "start_index": 0,
        "end_index": 62,
        "url": "https://www.nasa.gov/mission/webb/",
        "title": "James Webb Space Telescope — NASA"
      }
    }
  ],
  "sources": [
    {
      "url": "https://www.nasa.gov/mission/webb/",
      "title": "James Webb Space Telescope — NASA",
      "snippet": "Webb launched on 25 Dec 2021 aboard an Ariane 5 from Kourou, French Guiana."
    }
  ],
  "usage": {
    "prompt_tokens": 512,
    "completion_tokens": 88,
    "total_tokens": 600
  }
}

Citations

Each entry in annotations marks a span of the answer string that a specific source supports. start_index and end_index are character offsets into answer, so you can slice the substring and wrap it in a link:

function withCitations(res: SearchResponse): string {
  // Apply from the end so earlier offsets stay valid as we insert markup.
  let out = res.answer;
  const spans = [...res.annotations].sort(
    (a, b) => b.url_citation.start_index - a.url_citation.start_index,
  );
  for (const { url_citation: c } of spans) {
    const cited = out.slice(c.start_index, c.end_index);
    out =
      out.slice(0, c.start_index) +
      `[${cited}](${c.url})` +
      out.slice(c.end_index);
  }
  return out;
}

Examples

curl https://api.misar.io/dev/v1/search \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "What is the latest stable Node.js LTS release?"
  }'
const res = await fetch("https://api.misar.io/dev/v1/search", {
  method: "POST",
  headers: {
    Authorization: "Bearer YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    query: "What is the latest stable Node.js LTS release?",
    model: "misar-pro",
  }),
});

const { answer, sources, grounded } = await res.json();
console.log(grounded ? answer : "(ungrounded)");
for (const s of sources) console.log(`- ${s.title}: ${s.url}`);
import requests

res = requests.post(
    "https://api.misar.io/dev/v1/search",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    json={"query": "What is the latest stable Node.js LTS release?"},
)
data = res.json()

print(data["answer"])
for s in data["sources"]:
    print(f"- {s['title']}: {s['url']}")

Pass messages instead of query to keep conversational context across turns — the API grounds against the most recent user message while using the earlier turns for context.

Status codes

StatusMeaning
200 OKGrounded answer returned.
400 Bad RequestNeither query nor messages supplied, or an unknown model.
401 UnauthorizedMissing, malformed, revoked, or wrong-product API key.
429 Too Many RequestsRate limit exceeded — retry after the Retry-After header.
503 Service UnavailableWeb retrieval was temporarily unavailable; retry with backoff.

MisarDev keys are product-bound. A key from another Misar product returns 401 here — see Authentication.