> ## Documentation Index
> Fetch the complete documentation index at: https://docs.spidra.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Hono

> Use Spidra's web scraping, site crawling, SERP search, and document parsing in Hono across Cloudflare Workers, Node.js, Bun, Deno, and Vercel Edge.

The Spidra Hono integration (`spidra-hono`) brings web intelligence to modern edge runtimes:

* **Multi-Runtime Web Standards**: 100% standard Fetch, Request, Response, and Web Crypto APIs running on Cloudflare Workers, Node.js, Bun, Deno, and Vercel Edge.
* **`spidraMiddleware()`**: Automatically resolves `c.env.SPIDRA_API_KEY` (or `process.env.SPIDRA_API_KEY`) and attaches `c.get('spidra')`.
* **`spidraWebhookHandler()`**: Pre-built route handler verifying HMAC-SHA256 signatures with `verifyHonoWebhook()`.
* **`createSpidraHonoRouter()`**: Pre-wired sub-app with turnkey endpoints.

## Installation

```bash theme={null}
npm install spidra-hono spidra
```

Compatible with Hono 3+ and 4+.

> [!NOTE]
>
> Get your API key from [app.spidra.io](https://app.spidra.io) under **API Keys**.
> Store it in `wrangler.toml` (Cloudflare Workers) or `.env` (Node/Bun).

## Configuration

In Cloudflare Workers (`wrangler.toml`):

```toml theme={null}
[vars]
SPIDRA_API_KEY = "spd_live_your_api_key"
SPIDRA_WEBHOOK_SECRET = "whsec_your_signing_secret"

# Optional overrides
SPIDRA_BASE_URL = "https://api.spidra.io/api"
```

Or in Node / Bun `.env`:

```bash theme={null}
SPIDRA_API_KEY=spd_live_your_api_key
SPIDRA_WEBHOOK_SECRET=whsec_your_signing_secret
```

## Quickstart

### 1. Basic middleware usage

```ts theme={null}
import { Hono } from "hono";
import { spidraMiddleware, getSpidra, type SpidraHonoEnv } from "spidra-hono";

const app = new Hono<SpidraHonoEnv>();

// Register Spidra middleware across routes
app.use("*", spidraMiddleware());

app.post("/scrape", async (c) => {
  const spidra = getSpidra(c);
  const { url } = await c.req.json();

  const result = await spidra.scrape({
    urls: [{ url }],
    prompt: "Extract headline and key takeaways",
    output: "markdown",
  });

  return c.json(result);
});

export default app;
```

### 2. Pre-built router sub-app

Mount all Spidra REST API endpoints under `/api/spidra`:

```ts theme={null}
import { Hono } from "hono";
import { createSpidraHonoRouter } from "spidra-hono";

const app = new Hono();

app.route("/api/spidra", createSpidraHonoRouter());

export default app;
```

### 3. Webhook receiver

```ts theme={null}
import { Hono } from "hono";
import { spidraWebhookHandler } from "spidra-hono";

const app = new Hono();

app.post(
  "/webhooks/spidra",
  spidraWebhookHandler({
    onEvent: async (event, c) => {
      console.log(`Verified Spidra event: ${event.event}`);

      if (event.event === "crawl.completed") {
        console.log(`Crawl job ${event.jobId} finished!`);
      }
    },
  })
);

export default app;
```

## Scraping & browser actions

```ts theme={null}
app.post("/scrape-actions", async (c) => {
  const spidra = getSpidra(c);

  const result = await spidra.scrape({
    urls: [
      {
        url: "https://quotes.toscrape.com/login",
        actions: [
          { type: "type", selector: "#username", value: "admin" },
          { type: "type", selector: "#password", value: "admin" },
          { type: "click", selector: "input[type='submit']" },
          { type: "waitFor", selector: "a[href='/logout']" },
        ],
      },
    ],
    prompt: "Extract top 5 quotes with author",
    output: "json",
    useProxy: true,
    proxyCountry: "us",
  });

  return c.json(result);
});
```

## Deep site crawling

```ts theme={null}
app.post("/crawl-site", async (c) => {
  const spidra = getSpidra(c);
  const { url } = await c.req.json();

  const crawlJob = await spidra.crawl({
    baseUrl: url,
    crawlInstruction: "Follow documentation pages only",
    transformInstruction: "Extract title and code snippets",
    maxPages: 30,
    includePaths: ["/docs/*"],
  });

  return c.json({ pages: crawlJob.result });
});
```

## Live SERP & web search

```ts theme={null}
app.get("/search", async (c) => {
  const spidra = getSpidra(c);
  const q = c.req.query("q") || "";

  const results = await spidra.search({
    query: q,
    sources: ["web", "news"],
    limit: 10,
  });

  return c.json(results);
});
```

## Batch scraping

```ts theme={null}
app.post("/batch", async (c) => {
  const spidra = getSpidra(c);
  const { urls } = await c.req.json();

  const batch = await spidra.batchScrape({
    urls,
    prompt: "Extract page title and meta description",
    output: "json",
  });

  return c.json(batch);
});
```

## Domain mapping

```ts theme={null}
app.get("/map", async (c) => {
  const spidra = getSpidra(c);
  const url = c.req.query("url") || "";

  const map = await spidra.map({ url, limit: 100 });
  return c.json(map);
});
```

## Document parsing (PDF, Word, Excel)

```ts theme={null}
app.post("/parse-doc", async (c) => {
  const spidra = getSpidra(c);
  const body = await c.req.parseBody();
  const file = body["file"] as File;

  const arrayBuffer = await file.arrayBuffer();

  const parsed = await spidra.parse({
    file: new Uint8Array(arrayBuffer),
    fileName: file.name,
  }, {
    prompt: "Extract key dates, amounts, and parties",
  });

  return c.json({ markdown: parsed.markdown, data: parsed.json });
});
```

## Turnkey templates & monitors

```ts theme={null}
app.post("/leads", async (c) => {
  const spidra = getSpidra(c);
  const { query, area } = await c.req.json();

  const run = await spidra.runTemplate("google-maps-leads", {
    query,
    areas: [area],
  });

  return c.json(run.rows);
});

app.post("/monitors", async (c) => {
  const spidra = getSpidra(c);
  const { url, webhookUrl } = await c.req.json();

  const monitor = await spidra.createMonitor({
    name: "Competitor Price Watch",
    sources: [{ kind: "urls", urls: [url] }],
    schedule: { every: "1d", at: "09:00" },
    alertOn: "page",
    goal: "Alert on any price change",
    notify: { webhook: { url: webhookUrl } },
  });

  return c.json(monitor);
});
```

## Error handling

```ts theme={null}
import {
  SpidraAuthenticationError,
  SpidraRateLimitError,
  SpidraPaymentRequiredError,
  SpidraError,
} from "spidra-hono";

app.onError((err, c) => {
  if (err instanceof SpidraAuthenticationError) {
    return c.json({ error: "Invalid Spidra API Key" }, 401);
  }
  if (err instanceof SpidraPaymentRequiredError) {
    return c.json({ error: "Spidra credit balance exhausted" }, 402);
  }
  if (err instanceof SpidraRateLimitError) {
    return c.json({ error: "Rate limited", retryAfter: err.retryAfter }, 429);
  }
  if (err instanceof SpidraError) {
    return c.json({ error: err.message, code: err.code }, (err.status as any) || 500);
  }
  return c.json({ error: err.message || "Internal server error" }, 500);
});
```

> [card] [Fastify](https://docs.spidra.io/sdks/fastify.md)
>
> Fastify high-performance plugin and route decoration.

> [card] [Bun](https://docs.spidra.io/sdks/bun.md)
>
> Native Bun runtime, Bun.file() parsing, and Bun.serve.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.