webaccessdocs

webaccess API

Go to any web address on your behalf, crawl it, extract structured content and measure it, then get the result back. webaccess is the hands and eyes: it fetches, honestly and safely. It never runs an LLM, scores, or decides. That stays on your side.

Overview

webaccess exposes a small REST API. You point it at a domain or a URL, it fetches the content behind SSRF protection and politeness rules, and returns the extracted result. It never invents data: if a page cannot be fetched, the run reports it as failed rather than fabricating content.

Base URL

base url
https://api.webaccess.searchestra.com

Conventions

  • All request and response bodies are application/json.
  • Successful responses are wrapped in a top level data field. Errors return a top level error field (see Errors).
  • Every /v1/* endpoint requires the X-API-Key header. /healthz does not.
  • You pass your own clientRef on every request; it scopes and isolates your data (see Authentication).
  • Timestamps are UTC, ISO 8601 (for example 2026-08-24T10:12:00Z).
Hands, not brain. webaccess returns evidence: raw HTML, extracted pages, headers, metrics. Search discovery, LLM analysis, scoring and business rules stay in the calling application.

Quickstart

Start a crawl, then poll for its status. Replace wa_live_your_key with the API key issued to your tenant (see Authentication).

requestcurl
curl -X POST https://api.webaccess.searchestra.com/v1/crawl/start \
  -H "X-API-Key: wa_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "domain": "godiva.com.tr",
    "scope": "domain",
    "clientRef": "brand-123"
  }'
response201
{
  "data": {
    "runId": "01a0347a-3cd9-70e1-9dce-c2dd5962a4e6",
    "status": "pending"
  }
}

Then poll the run until it is done or failed, and read the extracted pages:

requestcurl
curl "https://api.webaccess.searchestra.com/v1/crawl/pages?clientRef=brand-123&domain=godiva.com.tr" \
  -H "X-API-Key: wa_live_your_key"

Authentication

Every /v1/* request must carry an API key in the X-API-Key header. Keys begin with the wa_live_ prefix and identify a tenant, not a user.

header
X-API-Key: wa_live_your_key

The server stores only a hash of the key. The raw value is shown once, at creation, and cannot be recovered. If a key is lost, issue a new one.

Multi-tenancy and clientRef

On every request you also pass your own project or brand identifier, clientRef. The service combines it with your authenticated tenant into a composite key, so data isolation is automatic: two different customers using the same clientRef never see each other's data.

Getting a key. Keys are provisioned per tenant during onboarding. A missing or invalid key returns 401 unauthorized with a clear message.

Start a crawl

POST/v1/crawl/start

Starts an asynchronous crawl for a domain and returns a runId immediately. The work runs in the background: track it with GET /v1/crawl/{runId} or wait for the webhook.

If the same domain was crawled recently for this project (default 24 hours), the service returns the last run instead of re-crawling. Set force: true to skip that shortcut and re-crawl from scratch.

Request body

FieldTypeNotes
domain requiredstringThe domain to crawl, for example godiva.com.tr.
clientRef requiredstringYour own project/brand identifier.
scope optionalstringCrawl scope (see Scopes). Defaults to domain.
force optionalbooleantrue skips the freshness shortcut and re-crawls. Defaults to false.
maxPages optionalintegerPer-run page budget. If omitted, the service default is used.
response201
{ "data": { "runId": "01a0347a-3cd9-70e1-9dce-c2dd5962a4e6", "status": "pending" } }

Get run status

GET/v1/crawl/{runId}

Returns the current status and progress counters for a crawl run. Poll this endpoint until status is done or failed. Pass your clientRef as a query parameter.

response200
{
  "data": {
    "runId": "01a0347a-3cd9-70e1-9dce-c2dd5962a4e6",
    "status": "running",
    "domain": "godiva.com.tr",
    "scope": "domain",
    "pagesFound": 90,
    "pagesCrawled": 74,
    "pagesFailed": 2
  }
}

status is one of pending, running, done, failed. When failed, an error field explains why.

Get the latest run

GET/v1/crawl/latest

Returns the project's most recent crawl run without needing the runId: the answer to "is a crawl active right now, and what was crawled last?" If there is no run yet, data is an empty object ({}).

List crawled pages

GET/v1/crawl/pages

Returns the project's crawled pages with their extracted content: title, description, visible text, status code, canonical URL and structured data. Raw HTML is included by default; pass summary=true for a lighter response without it.

Query parameters

ParameterTypeNotes
clientRef requiredstringYour project/brand identifier.
domain optionalstringReturn only pages for this domain.
limit optionalintegerMaximum pages to return. Defaults to 100.
summary optionalbooleantrue omits raw HTML from the response.
response200
{
  "data": [
    {
      "url": "https://godiva.com.tr/urunler",
      "canonicalUrl": "https://godiva.com.tr/urunler",
      "title": "Products",
      "description": "Godiva chocolate collection",
      "textExcerpt": "Handmade Belgian chocolates...",
      "statusCode": 200,
      "fetchedVia": "http",
      "depth": 1,
      "crawledAt": "2026-08-24T10:12:00Z"
    }
  ]
}

fetchedVia is http for a plain fetch, or browser when the page needed a real headless browser.

Index a project

POST/v1/crawl/index

Chunks all of the project's crawled pages at paragraph boundaries for search and RAG, and, if an embedding provider is configured, generates vectors. Without embeddings the chunks are still stored; only classic full-text search works.

request
{ "clientRef": "brand-123" }
response200
{ "data": { "pagesIndexed": 74, "chunksCreated": 210, "chunksEmbedded": 210 } }

chunksEmbedded is 0 when no embedding provider is configured.

Fetch a single URL

POST/v1/fetch

Fetches one address with raw HTML, all HTTP headers and the full redirect chain, plus timing metrics (TTFB, total time) and body size.

request
{ "url": "https://competitora.com" }
response200
{
  "data": {
    "html": "...",
    "headers": { "Content-Type": ["text/html"] },
    "statusCode": 200,
    "finalUrl": "https://competitora.com/",
    "redirectChain": [ { "url": "http://competitora.com", "status": 301 } ],
    "ttfbMs": 120,
    "totalMs": 480,
    "bodyBytes": 51234,
    "fetchedVia": "http",
    "robotsBlocked": false,
    "fromCache": false
  }
}

Measure Core Web Vitals

POST/v1/cwv

Opens a real headless browser and measures a page's performance metrics: LCP (Largest Contentful Paint), CLS (Cumulative Layout Shift) and TBT (Total Blocking Time).

request
{ "url": "https://competitora.com" }
response200
{ "data": { "lcp": 2100, "cls": 0.04, "tbt": 150, "ttfb": 180, "lcpSupported": true } }

Support flags (lcpSupported, clsSupported, ...) tell you whether each metric could be observed for this page.

Fetch robots.txt

POST/v1/robots

Returns a domain's robots.txt rules as raw text.

request
{ "domain": "competitora.com" }
response200
{ "data": { "found": true, "rawBody": "User-agent: *\nDisallow: /admin" } }

Fetch sitemap URLs

POST/v1/sitemap

Lists the addresses in a domain's sitemap. Index sitemaps and gzip-compressed (.xml.gz) sitemaps are supported. You can hint known sitemap paths with hints.

request
{ "domain": "competitora.com", "hints": [] }
response200
{ "data": { "urls": ["https://competitora.com/product-1", "https://competitora.com/product-2"] } }

Health check

GET/healthz

Liveness probe for container orchestration. No authentication. Returns 200 when the service is up.

Scopes

The scope field on crawl/start controls how far a crawl reaches from the seed domain.

ScopeReaches
domainAll subdomains of the given domain (default).
subdomainOnly the exact host.
pathPaths under the given directory prefix.
exact_urlOnly the single given page.

Webhook

When a run completes, and if your tenant registered a webhook URL, webaccess sends an HMAC-SHA256 signed event so you do not have to poll.

eventcrawl.completed
{
  "event": "crawl.completed",
  "runId": "01a0347a-3cd9-70e1-9dce-c2dd5962a4e6",
  "clientRef": "brand-123",
  "status": "done",
  "pagesCrawled": 74,
  "pagesFailed": 2
}

Verify the signature header against your WEBHOOK_SECRET before trusting the payload.

Errors

Errors return an error object with a code and a message.

error envelope
{ "error": { "code": "unauthorized", "message": "Invalid API key." } }
StatuscodeWhen
401unauthorizedMissing or invalid API key.
404not_foundUnknown run or resource.
422validation_failedInvalid request body, for example an empty or malformed domain.
429rate_limitedToo many requests for this key. Retry shortly.
503unavailableA dependency (browser, queue) is temporarily unavailable.
500internalInternal failure. Message is masked; the real cause is in the server logs.

Limits and defaults

Crawl behaviour is bounded by configurable defaults. These protect both the target and your budget.

SettingDefaultMeaning
Max pages per crawl10Upper bound on the page budget.
Max depth3How many links away from the seed.
Delay between requests1200 msPoliteness pause per host.
Concurrency4Parallelism across different hosts.
Freshness window24 hoursRe-crawl suppression for the same project.
Text per page20,000 charsUpper bound on extracted visible text.

These are deployment-level defaults; maxPages can be overridden per run on crawl/start.