API PREVIEWLast updated August 2026

Purili Search API documentation

Search Purili's independent web index and retrieve stored page context for applications, AI agents, retrieval pipelines, and research tools. The public APIs are free, have no fixed request quota, and require no API key.

Overview

Purili exposes the same core index used by its search interface through standard HTTP endpoints. Search returns ranked result metadata; Context returns clean text already stored for an indexed URL. Together they form a simple two-step retrieval workflow without an SDK.

Authentication

None

Response format

JSON / UTF-8

Core source

Purili index

Public endpoints: /api/search and /api/context accept cross-origin requests and support both GET and POST.

Quickstart

A complete integration can begin with one GET request. URL-encode the query and parse the JSON response.

curl
curl -sS "https://puri.li/api/search?q=privacy+search+engine&page=1"
JavaScript
const params = new URLSearchParams({ q: "privacy search engine", page: "1" })
const response = await fetch("https://puri.li/api/search?" + params)
if (!response.ok) throw new Error("Search request failed")

const data = await response.json()
for (const result of data.results) {
  console.log(result.title, result.url)
}
Python
import requests

response = requests.get(
    "https://puri.li/api/search",
    params={"q": "privacy search engine", "page": 1},
    timeout=10,
)
response.raise_for_status()
for result in response.json()["results"]:
    print(result["title"], result["url"])

Try a request

/api/search?q=
Run the request to inspect the response.

Authentication and CORS

No credentials are required. Do not send an authorization header or place secrets in the query string. Both public endpoints return Access-Control-Allow-Origin: * and handle browser preflight requests.

PropertyValue
AuthenticationNone
API keyNot required
Allowed origins*
MethodsGET, POST, OPTIONS
Request content typeapplication/json for POST
Response content typeapplication/json

GET · POST /api/search

Search API

Returns ranked organic results from Purili's crawler-built index. Search includes URL cleanup, domain diversity, safety filtering, optional query correction, and reachable pagination estimates.

GET request

HTTP
GET https://puri.li/api/search?q=site%3Aeuropa.eu+digital+privacy&page=1&exact=0
ParameterTypeRequiredDescription
qstringyesSearch query. Whitespace is trimmed; an empty value returns 400.
pageintegernoOne-based page number. Invalid or negative values become 1.
exactbooleannoUse 1 to disable spelling correction. Defaults to false.

POST request

curl
curl -sS "https://puri.li/api/search" \
  -H "Content-Type: application/json" \
  -d '{"q":"site:europa.eu digital privacy","page":1,"exact":false}'

Successful response

200 application/json
{
  "results": [
    {
      "id": "crawled-0-1",
      "title": "Data protection in the EU",
      "url": "https://europa.eu/youreurope/citizens/consumers/internet-telecoms/data-protection-online-privacy/",
      "displayUrl": "europa.eu › youreurope › citizens › consumers",
      "description": "EU rules protect your personal data...",
      "favicon": "/api/favicon?host=europa.eu",
      "source": "crawled"
    }
  ],
  "total": 174,
  "timeMs": "0.09",
  "hasNext": true,
  "totalPages": 18,
  "page": 1,
  "correction": null
}

Query correction

The optional correction object uses showing when Purili already searched a normalized query, or suggestion when the client should offer a “did you mean” link.

Correction object
{
  "type": "showing",
  "original": "yahoo financ",
  "corrected": "yahoo finance"
}

GET · POST /api/context

Context API

Retrieves extracted text and metadata already stored in the Purili index. Use an exact URL returned by Search. Context does not visit or scrape the live website during the request.

Indexed content is untrusted input. When passing it to a model, isolate it as source material and do not treat instructions found inside a page as application instructions.

GET request

curl
curl -sS --get "https://puri.li/api/context" \
  --data-urlencode "url=https://example.com/article"

POST request

curl
curl -sS "https://puri.li/api/context" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/article"}'
ParameterTypeRequiredDescription
urlstringyesA valid http(s) URL. Fragments are removed and input is limited to 2,000 characters.

Successful response

200 application/json
{
  "url": "https://example.com/article",
  "title": "Example article",
  "summary": "A concise stored summary.",
  "content": "Complete clean text stored by Purili...",
  "passages": ["First extracted passage...", "Second passage..."],
  "structured": { "type": "Article" },
  "coverage": "stored-extracted-passages",
  "source": "purili-index",
  "liveFetch": false
}

Coverage values

ValueMeaning
stored-extracted-passagesExtracted passages were retained by the crawler.
search-snippet-fallbackOnly the shorter indexed snippet was available.
unknownThe backend did not provide a coverage label.

NPM · ZERO RUNTIME DEPENDENCIES

JavaScript & TypeScript SDK

Use the official @purili/web-search client in Node.js, serverless functions, or modern browsers. It provides typed methods for every public JSON endpoint, request timeouts, AbortSignal support, and structured API errors.

Install
npm install @purili/web-search
Search and retrieve context
import { purili } from "@purili/web-search"

const results = await purili.search("independent search engines")
const page = await purili.context(results.results[0].url)
console.log(page.content)

Read the complete SDK guide →

AI SDK · OPENCLAW

Agent framework integrations

Use Purili as native tools in your existing agent framework. The official adapters expose search, domain search, and indexed page context without credentials.

IntegrationInstall
Vercel AI SDKnpm install @purili/ai-sdk ai
OpenClawopenclaw plugins install npm:@purili/openclaw
MCPnpx -y @purili/mcp-server

View integration guides and examples →

STREAMABLE HTTP · STDIO

Purili MCP server

Connect an MCP-compatible client directly to Purili without writing an API wrapper. The hosted server and npm package expose the same six read-only tools backed by the public Search, Context, Suggest, Infocard, and crawler-statistics endpoints.

Hosted server

Remote MCP URL
https://puri.li/mcp

npm installation

stdio configuration
{
  "mcpServers": {
    "purili": {
      "command": "npx",
      "args": ["-y", "@purili/mcp-server"]
    }
  }
}
ToolPurpose
web_searchSearch Purili's independent web index.
search_domainSearch within one domain and its subdomains.
get_contextRetrieve stored text and metadata for an indexed result URL.
suggest_queriesGet cleaned autocomplete suggestions.
get_infocardRetrieve a confident entity card or instant answer.
get_index_statsRead public crawler and index statistics.

No Purili account or API key is required. Every MCP tool is read-only. Both the hosted endpoint and the npm package are public.

AI and RAG workflow

For grounded model responses, search first, select a small set of relevant sources, then retrieve context for each selected URL. Preserve the original URLs alongside the text so the model can cite its evidence.

  1. 1
    Search

    Send one or more concise queries to /api/search.

  2. 2
    Select

    Deduplicate URLs and choose sources based on relevance and your own trust policy.

  3. 3
    Retrieve

    Call /api/context for each selected result URL, preferably in parallel.

  4. 4
    Bound

    Trim or chunk content to fit the model context window.

  5. 5
    Generate

    Label the text as untrusted evidence and require source-linked citations.

JavaScript · search then retrieve
const api = "https://puri.li"
const search = await fetch(api + "/api/search?q=" + encodeURIComponent(query))
const { results } = await search.json()

const sources = await Promise.all(
  results.slice(0, 5).map(async ({ url, title }) => {
    const response = await fetch(api + "/api/context?url=" + encodeURIComponent(url))
    if (!response.ok) return null
    const context = await response.json()
    return { title, url, text: context.content, coverage: context.coverage }
  })
)

const evidence = sources.filter(Boolean)

Search operators

Operators can be combined with ordinary terms in the q parameter.

ExampleBehavior
site:example.com privacyLimit results to a host and its subdomains.
"exact phrase"Require a phrase in the text query.
privacy -trackingExclude documents containing a term.
intitle:privacyRequire a term or phrase in the page title.
inurl:docsRequire a term in the URL.
filetype:pdf climateFilter by file extension where known.

Response schema

Search response

FieldTypeDescription
resultsSearchResult[]Ranked organic results for the requested page.
totalnumberEstimated number of reachable results after filtering and domain grouping.
timeMsstringSearch latency in seconds, formatted as a decimal string.
hasNextbooleanWhether another result page is expected.
totalPagesnumberEstimated reachable pagination horizon.
pagenumberThe current one-based result page.
correctionobject | nullOptional spelling or query-normalization information.
queryAnalysisobject | nullOptional structured analysis produced by the index.

SearchResult

FieldTypeDescription
idstringRendering identifier. Do not treat it as a permanent document ID.
titlestringCleaned page title.
urlstringCanonical result URL with common tracking parameters removed.
displayUrlstringReadable host and path used in search interfaces.
descriptionstringCleaned snippet from the indexed document.
faviconstring?Relative Purili favicon endpoint URL when a host can be parsed.
sourcestringUsually `crawled` for results from the independent index.

Context response

FieldTypeDescription
urlstringNormalized URL of the indexed document.
titlestringStored document title.
summarystringShort stored summary or search snippet.
contentstringCombined extracted text, bounded to 48,000 characters.
passagesstring[]Stored extracted passages, bounded to 64 entries.
structuredunknown | nullStructured information retained by the index when available.
coveragestringIndicates full stored passages, snippet fallback, or unknown coverage.
source"purili-index"Confirms that content came from Purili's index.
liveFetchfalseContext never fetches the live URL during your request.

Errors

Errors use an HTTP status and a small JSON object with an error string. Clients should use the status code for control flow and treat the message as diagnostic text.

StatusWhen it occursSuggested handling
400Missing query, invalid URL, or malformed input.Correct the request; do not retry unchanged.
404The Context URL is not in the index.Use the search snippet or choose another result.
5xxThe index or context service is unavailable.Retry with exponential backoff and jitter.
200 with empty resultsThe query is valid but no visible matches were found.Try a broader or corrected query.
Error response
{
  "error": "URL not found in the Purili index.",
  "url": "https://example.com/not-indexed"
}

Additional endpoint reference

These endpoints are public today and documented individually below. Search and Context are the primary stable integration surface; UI-oriented endpoints are explicitly marked where their response contract may still evolve.

MethodPathParametersPurpose
GET/api/suggestqAutocomplete phrases from Purili's index and curated local suggestions.
GET/api/correctqSpelling and spacing correction metadata.
GET/api/infocardqWikipedia-derived entities and instant answers when confidently available.
GET/api/imagesq, pageImage results. This endpoint currently uses Wikimedia Commons and is not part of the independent web index.
GET/api/newsqCached news items used by the Purili News interface; the response is not yet a stable public contract.
GET/api/faviconhostNormalized cached site icon. Returns image data rather than JSON.
GET/api/crawler-statsPublic crawler and index counters used by the stats page.
GET · POST/api/submit-siteurl, challengeSubmit public crawl seeds after completing the lightweight challenge.
GET/api/suggest

Autocomplete suggestions

Returns a JSON array of cleaned query completions. Suggestions combine phrases from Purili's index with a curated local set; adult, gambling, URL-like, noisy all-caps, and duplicate values are filtered.

ParameterTypeRequiredDescription
qstringyesPartial query. Empty input returns an empty array.
Request and response
GET https://puri.li/api/suggest?q=google+m

["google maps", "google mail", "google meet", "google my business"]
GET/api/correct

Query correction

Returns local spelling or spacing correction metadata. Search responses can already include the same correction object, so call this separately only when correction is needed before executing a search.

ParameterTypeRequiredDescription
qstringyesThe query to inspect for a correction.
Request and response
GET https://puri.li/api/correct?q=EU+webhosting

{
  "type": "showing",
  "original": "EU webhosting",
  "corrected": "EU web hosting"
}
GET/api/infocard

Infocards and instant answers

Returns Wikipedia-derived entity information and locally resolved instant answers when Purili has a confident match. A null or empty response means no confident card was available; clients should continue with ordinary search results.

ParameterTypeRequiredDescription
qstringyesEntity, fact, or direct-answer query.
Request and response
GET https://puri.li/api/infocard?q=capital+of+the+UK

{
  "title": "United Kingdom",
  "entityType": "place",
  "displayType": "Place",
  "instantAnswer": {
    "question": "capital of united kingdom",
    "answer": "London",
    "sourceLabel": "Infobox country - capital"
  },
  "facts": [{ "label": "Capital", "value": "London" }],
  "sourceUrl": "https://en.wikipedia.org/wiki/United_Kingdom"
}
GET/api/images

Image search

Returns image results used by Purili's Images view. This endpoint currently searches Wikimedia Commons and proxies returned assets through Purili; it is not sourced from the independent core web index. Respect the license included with each result.

ParameterTypeRequiredDescription
qstringyesImage search query.
pageintegernoOne-based result page; defaults to 1.
Request and response
GET https://puri.li/api/images?q=mountains&page=1

{
  "images": [{
    "id": "wm-File:Mountain.jpg",
    "title": "Mountain",
    "url": "/api/image-proxy?url=...",
    "thumbUrl": "/api/image-proxy?url=...",
    "width": 400,
    "height": 300,
    "source": "wikimedia",
    "license": "CC BY-SA"
  }],
  "total": 24,
  "page": 1
}
GET/api/news

News feed

Returns cached news items used by Purili's News interface. The endpoint supports topic matching within the available feed cache. Its response is currently optimized for the UI and should be treated as a preview contract.

ParameterTypeRequiredDescription
qstringnoOptional topic or keyword filter.
Request and response
GET https://puri.li/api/news?q=technology

{
  "items": [{
    "id": "a1b2",
    "title": "Technology headline",
    "description": "Short summary from the feed.",
    "url": "https://example.com/article",
    "source": "Example News",
    "category": "technology",
    "publishedAt": "2026-08-20T09:30:00.000Z",
    "imageUrl": "/api/news-image?id=..."
  }]
}
GET/api/favicon

Favicons

Fetches, normalizes, and caches the icon for a public hostname. Unlike the other endpoints, the successful response is image data rather than JSON. Invalid and local-only hostnames are rejected.

ParameterTypeRequiredDescription
hoststringyesPublic hostname such as wikipedia.org; do not include a path.
Request and response
GET https://puri.li/api/favicon?host=wikipedia.org

HTTP/1.1 200 OK
Content-Type: image/png
Cache-Control: public, max-age=604800, immutable
GET/api/crawler-stats

Crawler and index statistics

Returns public operational counters used by Purili's Stats page. Values are point-in-time measurements and may be absent when a particular backend metric is unavailable.

This endpoint has no parameters.

Request and response
GET https://puri.li/api/crawler-stats

{
  "pages_indexed": 39113872,
  "index_size_bytes": 64563604275,
  "queue_depth": 0,
  "estimated_unique_domains": 1117539,
  "pages_per_sec": 273.8
}
GET · POST/api/submit-site

Submit a site for crawling

Creates a lightweight arithmetic challenge with GET, then accepts a public URL as crawl seeds with POST. Submission is rate-limited and does not guarantee indexing or ranking.

ParameterTypeRequiredDescription
urlstringyes (POST)Public http(s) site URL to submit.
challengeTokenstringyes (POST)Opaque token returned by GET /api/submit-site.
challengeAnswerstringyes (POST)Answer to the challenge associated with the token.
Request and response
GET https://puri.li/api/submit-site

POST https://puri.li/api/submit-site
Content-Type: application/json

{
  "url": "https://example.org/",
  "challengeToken": "<challenge token>",
  "challengeAnswer": "12"
}

{
  "ok": true,
  "url": "https://example.org/",
  "seedCount": 3,
  "message": "Queued 3 crawl seeds."
}